@tociva/tailng-ui 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,1120 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports$1) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./lib"), exports);
1
+ import * as i1 from '@angular/common';
2
+ import { NgStyle, CommonModule, NgTemplateOutlet } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { input, output, signal, computed, ElementRef, effect, HostListener, ViewChild, Component, viewChild, inject, Injector, runInInjectionContext, afterNextRender, HostBinding, Directive, TemplateRef, ContentChild } from '@angular/core';
5
+ import { TngFocusTrap } from '@tociva/tailng-cdk/a11y';
6
+
7
+ class TngConnectedOverlay {
8
+ /**
9
+ * Control
10
+ */
11
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
12
+ /**
13
+ * Anchor: pass either ElementRef<HTMLElement> or HTMLElement
14
+ * Example: [anchor]="inputEl" where inputEl is ElementRef<HTMLInputElement>
15
+ */
16
+ anchor = input(null, ...(ngDevMode ? [{ debugName: "anchor" }] : []));
17
+ /**
18
+ * Position / size
19
+ */
20
+ placement = input('bottom-start', ...(ngDevMode ? [{ debugName: "placement" }] : []));
21
+ offset = input(6, ...(ngDevMode ? [{ debugName: "offset" }] : []));
22
+ width = input('anchor', ...(ngDevMode ? [{ debugName: "width" }] : []));
23
+ /**
24
+ * Close behavior
25
+ */
26
+ closeOnOutsideClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnOutsideClick" }] : []));
27
+ closeOnInsideClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnInsideClick" }] : []));
28
+ closeOnEscape = input(true, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : []));
29
+ /**
30
+ * Backdrop (modal semantics)
31
+ */
32
+ hasBackdrop = input(false, ...(ngDevMode ? [{ debugName: "hasBackdrop" }] : []));
33
+ backdropClass = input('fixed inset-0 bg-black/40 z-[999]', ...(ngDevMode ? [{ debugName: "backdropClass" }] : []));
34
+ /**
35
+ * Events
36
+ */
37
+ opened = output();
38
+ closed = output();
39
+ backdropClick = output();
40
+ overlayRoot;
41
+ // Internal position state
42
+ topPx = signal(0, ...(ngDevMode ? [{ debugName: "topPx" }] : []));
43
+ leftPx = signal(0, ...(ngDevMode ? [{ debugName: "leftPx" }] : []));
44
+ widthPx = signal(null, ...(ngDevMode ? [{ debugName: "widthPx" }] : []));
45
+ minWidthPx = signal(null, ...(ngDevMode ? [{ debugName: "minWidthPx" }] : []));
46
+ // Cache whether we already emitted "opened" for current open cycle
47
+ didEmitOpened = false;
48
+ // RAF guard for resize/scroll repositioning
49
+ raf = 0;
50
+ /**
51
+ * Resolve anchor element
52
+ */
53
+ anchorEl = computed(() => {
54
+ const a = this.anchor();
55
+ if (!a)
56
+ return null;
57
+ return a instanceof ElementRef ? a.nativeElement : a;
58
+ }, ...(ngDevMode ? [{ debugName: "anchorEl" }] : []));
59
+ /**
60
+ * Overlay style for template
61
+ */
62
+ overlayStyle = computed(() => {
63
+ const top = this.topPx();
64
+ const left = this.leftPx();
65
+ const width = this.widthPx();
66
+ const minWidth = this.minWidthPx();
67
+ return {
68
+ top: `${top}px`,
69
+ left: `${left}px`,
70
+ width: width != null ? `${width}px` : undefined,
71
+ minWidth: minWidth != null ? `${minWidth}px` : undefined,
72
+ };
73
+ }, ...(ngDevMode ? [{ debugName: "overlayStyle" }] : []));
74
+ constructor() {
75
+ // When open/anchor changes, recompute position
76
+ effect(() => {
77
+ const isOpen = this.open();
78
+ const anchorEl = this.anchorEl();
79
+ if (!isOpen || !anchorEl) {
80
+ this.didEmitOpened = false;
81
+ return;
82
+ }
83
+ // Initial calc
84
+ this.updatePosition();
85
+ // Recalc after content renders so we can measure height (top placements + clamping)
86
+ queueMicrotask(() => {
87
+ if (this.open())
88
+ this.updatePosition();
89
+ });
90
+ if (!this.didEmitOpened) {
91
+ this.didEmitOpened = true;
92
+ this.opened.emit();
93
+ }
94
+ });
95
+ }
96
+ /**
97
+ * Public API: parent can call close programmatically (optional)
98
+ */
99
+ close(reason = 'programmatic') {
100
+ // Controlled component: parent must set [open]=false.
101
+ this.closed.emit(reason);
102
+ }
103
+ /**
104
+ * Recompute overlay position
105
+ */
106
+ updatePosition() {
107
+ const anchor = this.anchorEl();
108
+ if (!anchor) {
109
+ this.close('detach');
110
+ return;
111
+ }
112
+ const rect = anchor.getBoundingClientRect();
113
+ const offset = this.offset();
114
+ const placement = this.placement();
115
+ // Determine width setting (style)
116
+ const w = this.width();
117
+ if (w === 'anchor') {
118
+ this.minWidthPx.set(rect.width);
119
+ this.widthPx.set(rect.width);
120
+ }
121
+ else if (typeof w === 'number') {
122
+ this.minWidthPx.set(null);
123
+ this.widthPx.set(w);
124
+ }
125
+ else {
126
+ this.minWidthPx.set(null);
127
+ this.widthPx.set(null);
128
+ }
129
+ // Measure panel if present
130
+ const panelEl = this.overlayRoot?.nativeElement ?? null;
131
+ const panelRect = panelEl?.getBoundingClientRect() ?? null;
132
+ const panelW = panelRect?.width ?? this.getOverlayWidthForPosition(rect);
133
+ const panelH = panelRect?.height ?? 0;
134
+ // Position calculation (viewport coords; overlay is `position: fixed`)
135
+ let top = 0;
136
+ let left = 0;
137
+ switch (placement) {
138
+ case 'bottom-start':
139
+ top = rect.bottom + offset;
140
+ left = rect.left;
141
+ break;
142
+ case 'bottom-end':
143
+ top = rect.bottom + offset;
144
+ left = rect.right - panelW;
145
+ break;
146
+ case 'top-start':
147
+ top = rect.top - offset - panelH;
148
+ left = rect.left;
149
+ break;
150
+ case 'top-end':
151
+ top = rect.top - offset - panelH;
152
+ left = rect.right - panelW;
153
+ break;
154
+ }
155
+ // Viewport clamping
156
+ const pad = 8;
157
+ const vw = window.innerWidth;
158
+ const vh = window.innerHeight;
159
+ const maxLeft = Math.max(pad, vw - pad - panelW);
160
+ const maxTop = Math.max(pad, vh - pad - panelH);
161
+ left = Math.max(pad, Math.min(left, maxLeft));
162
+ top = Math.max(pad, Math.min(top, maxTop));
163
+ this.leftPx.set(left);
164
+ this.topPx.set(top);
165
+ }
166
+ getOverlayWidthForPosition(anchorRect) {
167
+ const w = this.width();
168
+ if (w === 'anchor')
169
+ return anchorRect.width;
170
+ if (typeof w === 'number')
171
+ return w;
172
+ // fallback: use rendered width if available, else anchor width
173
+ return this.overlayRoot?.nativeElement?.getBoundingClientRect().width ?? anchorRect.width;
174
+ }
175
+ requestReposition() {
176
+ if (this.raf)
177
+ return;
178
+ this.raf = requestAnimationFrame(() => {
179
+ this.raf = 0;
180
+ if (this.open())
181
+ this.updatePosition();
182
+ });
183
+ }
184
+ /**
185
+ * Backdrop pointerdown (if enabled)
186
+ */
187
+ onBackdropPointerDown(ev) {
188
+ if (!this.open())
189
+ return;
190
+ ev.preventDefault();
191
+ this.backdropClick.emit();
192
+ if (this.closeOnOutsideClick()) {
193
+ this.close('outside-click');
194
+ }
195
+ }
196
+ /**
197
+ * Close on escape
198
+ */
199
+ onDocKeydown(ev) {
200
+ if (!this.open())
201
+ return;
202
+ if (!this.closeOnEscape())
203
+ return;
204
+ if (ev.key === 'Escape') {
205
+ ev.preventDefault();
206
+ this.close('escape');
207
+ }
208
+ }
209
+ /**
210
+ * Close on outside click (and optionally inside click)
211
+ * Use pointerdown so it works for mouse/touch/pen and is earlier than click.
212
+ */
213
+ onDocPointerDown(ev) {
214
+ if (!this.open())
215
+ return;
216
+ const target = ev.target;
217
+ if (!target)
218
+ return;
219
+ const anchor = this.anchorEl();
220
+ const panel = this.overlayRoot?.nativeElement ?? null;
221
+ const inAnchor = !!anchor && anchor.contains(target);
222
+ const inPanel = !!panel && panel.contains(target);
223
+ // Anchor should never be treated as outside.
224
+ if (inAnchor)
225
+ return;
226
+ // If backdrop is enabled and we clicked backdrop, the backdrop handler will run.
227
+ // Still, keep logic safe: treat it as outside.
228
+ if (inPanel) {
229
+ if (this.closeOnInsideClick())
230
+ this.close('inside-click');
231
+ return;
232
+ }
233
+ if (this.closeOnOutsideClick())
234
+ this.close('outside-click');
235
+ }
236
+ /**
237
+ * Keep position in sync on viewport changes
238
+ */
239
+ onResize() {
240
+ this.requestReposition();
241
+ }
242
+ onScroll() {
243
+ this.requestReposition();
244
+ }
245
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngConnectedOverlay, deps: [], target: i0.ɵɵFactoryTarget.Component });
246
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngConnectedOverlay, isStandalone: true, selector: "tng-connected-overlay", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnOutsideClick: { classPropertyName: "closeOnOutsideClick", publicName: "closeOnOutsideClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnInsideClick: { classPropertyName: "closeOnInsideClick", publicName: "closeOnInsideClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, hasBackdrop: { classPropertyName: "hasBackdrop", publicName: "hasBackdrop", isSignal: true, isRequired: false, transformFunction: null }, backdropClass: { classPropertyName: "backdropClass", publicName: "backdropClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed", backdropClick: "backdropClick" }, host: { listeners: { "document:keydown": "onDocKeydown($event)", "document:pointerdown": "onDocPointerDown($event)", "window:resize": "onResize()", "window:scroll": "onScroll()" } }, viewQueries: [{ propertyName: "overlayRoot", first: true, predicate: ["overlayRoot"], descendants: true }], ngImport: i0, template: "@if (open() && anchorEl()) {\n @if (hasBackdrop()) {\n <div\n [class]=\"backdropClass()\"\n (pointerdown)=\"onBackdropPointerDown($event)\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <div\n #overlayRoot\n class=\"fixed z-[1000]\"\n [ngStyle]=\"overlayStyle()\"\n role=\"presentation\"\n >\n <ng-content />\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
247
+ }
248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngConnectedOverlay, decorators: [{
249
+ type: Component,
250
+ args: [{ selector: 'tng-connected-overlay', standalone: true, imports: [NgStyle], template: "@if (open() && anchorEl()) {\n @if (hasBackdrop()) {\n <div\n [class]=\"backdropClass()\"\n (pointerdown)=\"onBackdropPointerDown($event)\"\n aria-hidden=\"true\"\n ></div>\n }\n\n <div\n #overlayRoot\n class=\"fixed z-[1000]\"\n [ngStyle]=\"overlayStyle()\"\n role=\"presentation\"\n >\n <ng-content />\n </div>\n}\n" }]
251
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], closeOnOutsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnOutsideClick", required: false }] }], closeOnInsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnInsideClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], hasBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], backdropClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropClass", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], backdropClick: [{ type: i0.Output, args: ["backdropClick"] }], overlayRoot: [{
252
+ type: ViewChild,
253
+ args: ['overlayRoot', { static: false }]
254
+ }], onDocKeydown: [{
255
+ type: HostListener,
256
+ args: ['document:keydown', ['$event']]
257
+ }], onDocPointerDown: [{
258
+ type: HostListener,
259
+ args: ['document:pointerdown', ['$event']]
260
+ }], onResize: [{
261
+ type: HostListener,
262
+ args: ['window:resize']
263
+ }], onScroll: [{
264
+ type: HostListener,
265
+ args: ['window:scroll']
266
+ }] } });
267
+
268
+ class TngOverlayRef {
269
+ /**
270
+ * Controlled open state (parent owns state).
271
+ * Use [(open)]="signal()" style with openChange.
272
+ */
273
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
274
+ openChange = output();
275
+ /** Lifecycle outputs */
276
+ opened = output();
277
+ closed = output();
278
+ /** Read-only computed */
279
+ isOpen = computed(() => this.open(), ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
280
+ /** Request open (controlled) */
281
+ requestOpen() {
282
+ if (this.open())
283
+ return;
284
+ this.openChange.emit(true);
285
+ this.opened.emit();
286
+ }
287
+ /** Request close (controlled) */
288
+ requestClose(reason) {
289
+ if (!this.open())
290
+ return;
291
+ this.openChange.emit(false);
292
+ this.closed.emit(reason);
293
+ }
294
+ /** Convenience aliases */
295
+ openOverlay() {
296
+ this.requestOpen();
297
+ }
298
+ close(reason) {
299
+ this.requestClose(reason);
300
+ }
301
+ toggle() {
302
+ this.open() ? this.requestClose('programmatic') : this.requestOpen();
303
+ }
304
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngOverlayRef, deps: [], target: i0.ɵɵFactoryTarget.Component });
305
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: TngOverlayRef, isStandalone: true, selector: "tng-overlay-ref", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { openChange: "openChange", opened: "opened", closed: "closed" }, exportAs: ["tngOverlayRef"], ngImport: i0, template: "<ng-content />\n" });
306
+ }
307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngOverlayRef, decorators: [{
308
+ type: Component,
309
+ args: [{ selector: 'tng-overlay-ref', standalone: true, exportAs: 'tngOverlayRef', template: "<ng-content />\n" }]
310
+ }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], openChange: [{ type: i0.Output, args: ["openChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
311
+
312
+ class TngOverlayPanel {
313
+ /** Consumer-provided class overrides / extensions */
314
+ klass = input('', ...(ngDevMode ? [{ debugName: "klass" }] : []));
315
+ /**
316
+ * When true, treat panel as modal surface:
317
+ * - trap focus
318
+ * - restore focus on destroy
319
+ * - aria-modal="true"
320
+ */
321
+ modal = input(false, ...(ngDevMode ? [{ debugName: "modal" }] : []));
322
+ /** a11y */
323
+ role = input('presentation', ...(ngDevMode ? [{ debugName: "role" }] : []));
324
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
325
+ ariaLabelledby = input('', ...(ngDevMode ? [{ debugName: "ariaLabelledby" }] : []));
326
+ ariaDescribedby = input('', ...(ngDevMode ? [{ debugName: "ariaDescribedby" }] : []));
327
+ /** Focus trap options (only meaningful when modal=true) */
328
+ restoreFocus = input(true, ...(ngDevMode ? [{ debugName: "restoreFocus" }] : []));
329
+ autoCapture = input(true, ...(ngDevMode ? [{ debugName: "autoCapture" }] : []));
330
+ deferCaptureElements = input(false, ...(ngDevMode ? [{ debugName: "deferCaptureElements" }] : []));
331
+ /** Base themed Tailwind classes */
332
+ baseClasses = 'bg-bg text-fg border border-border rounded-md shadow-lg max-h-60 overflow-auto outline-none';
333
+ /** Final merged class string */
334
+ classes = computed(() => `${this.baseClasses} ${this.klass()}`.trim(), ...(ngDevMode ? [{ debugName: "classes" }] : []));
335
+ /** For ARIA: only true when modal */
336
+ ariaModal = computed(() => (this.modal() ? 'true' : null), ...(ngDevMode ? [{ debugName: "ariaModal" }] : []));
337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngOverlayPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
338
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: TngOverlayPanel, isStandalone: true, selector: "tng-overlay-panel", inputs: { klass: { classPropertyName: "klass", publicName: "klass", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null }, restoreFocus: { classPropertyName: "restoreFocus", publicName: "restoreFocus", isSignal: true, isRequired: false, transformFunction: null }, autoCapture: { classPropertyName: "autoCapture", publicName: "autoCapture", isSignal: true, isRequired: false, transformFunction: null }, deferCaptureElements: { classPropertyName: "deferCaptureElements", publicName: "deferCaptureElements", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n [class]=\"classes()\"\n [attr.role]=\"role()\"\n [attr.aria-modal]=\"ariaModal()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledby() || null\"\n [attr.aria-describedby]=\"ariaDescribedby() || null\"\n tabindex=\"-1\"\n [tngFocusTrap]=\"modal()\"\n [restoreFocus]=\"restoreFocus()\"\n [autoCapture]=\"autoCapture()\"\n [deferCaptureElements]=\"deferCaptureElements()\"\n>\n <ng-content />\n</div>\n", dependencies: [{ kind: "directive", type: TngFocusTrap, selector: "[tngFocusTrap]", inputs: ["tngFocusTrap", "deferCaptureElements", "autoCapture", "restoreFocus"] }] });
339
+ }
340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngOverlayPanel, decorators: [{
341
+ type: Component,
342
+ args: [{ selector: 'tng-overlay-panel', standalone: true, imports: [TngFocusTrap], template: "<div\n [class]=\"classes()\"\n [attr.role]=\"role()\"\n [attr.aria-modal]=\"ariaModal()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledby() || null\"\n [attr.aria-describedby]=\"ariaDescribedby() || null\"\n tabindex=\"-1\"\n [tngFocusTrap]=\"modal()\"\n [restoreFocus]=\"restoreFocus()\"\n [autoCapture]=\"autoCapture()\"\n [deferCaptureElements]=\"deferCaptureElements()\"\n>\n <ng-content />\n</div>\n" }]
343
+ }], propDecorators: { klass: [{ type: i0.Input, args: [{ isSignal: true, alias: "klass", required: false }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], restoreFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "restoreFocus", required: false }] }], autoCapture: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCapture", required: false }] }], deferCaptureElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferCaptureElements", required: false }] }] } });
344
+
345
+ class TngDialog {
346
+ /** Controlled open state */
347
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
348
+ /** Close behavior */
349
+ closeOnBackdropClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnBackdropClick" }] : []));
350
+ closeOnEscape = input(true, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : []));
351
+ /** a11y */
352
+ ariaLabel = input('Dialog', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
353
+ /** Focus trap (a11y) */
354
+ trapFocus = input(true, ...(ngDevMode ? [{ debugName: "trapFocus" }] : []));
355
+ restoreFocus = input(true, ...(ngDevMode ? [{ debugName: "restoreFocus" }] : []));
356
+ autoCapture = input(true, ...(ngDevMode ? [{ debugName: "autoCapture" }] : []));
357
+ deferCaptureElements = input(false, ...(ngDevMode ? [{ debugName: "deferCaptureElements" }] : []));
358
+ /** When no tabbables exist, focus the panel */
359
+ autoFocusPanelWhenEmpty = input(true, ...(ngDevMode ? [{ debugName: "autoFocusPanelWhenEmpty" }] : []));
360
+ /** Klass inputs */
361
+ backdropKlass = input('fixed inset-0 bg-black/40', ...(ngDevMode ? [{ debugName: "backdropKlass" }] : []));
362
+ panelKlass = input([
363
+ 'fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
364
+ 'w-[min(32rem,calc(100vw-2rem))]',
365
+ 'max-h-[calc(100vh-2rem)] overflow-auto',
366
+ 'rounded-lg border border-border bg-bg shadow-xl outline-none',
367
+ ].join(' '), ...(ngDevMode ? [{ debugName: "panelKlass" }] : []));
368
+ headerWrapKlass = input('border-b border-border px-4 py-3', ...(ngDevMode ? [{ debugName: "headerWrapKlass" }] : []));
369
+ bodyWrapKlass = input('px-4 py-4', ...(ngDevMode ? [{ debugName: "bodyWrapKlass" }] : []));
370
+ footerWrapKlass = input('border-t border-border px-4 py-3', ...(ngDevMode ? [{ debugName: "footerWrapKlass" }] : []));
371
+ /** Outputs */
372
+ closed = output();
373
+ opened = output();
374
+ /** Derived */
375
+ isOpen = computed(() => this.open(), ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
376
+ /** Panel ref */
377
+ panelRef = viewChild('panel', ...(ngDevMode ? [{ debugName: "panelRef" }] : []));
378
+ injector = inject(Injector);
379
+ didEmitOpened = false;
380
+ constructor() {
381
+ effect(() => {
382
+ if (!this.isOpen())
383
+ return;
384
+ // afterNextRender must be called inside injection context
385
+ runInInjectionContext(this.injector, () => {
386
+ afterNextRender(() => this.focusPanelIfNoTabbable());
387
+ });
388
+ });
389
+ }
390
+ ngDoCheck() {
391
+ if (this.isOpen() && !this.didEmitOpened) {
392
+ this.didEmitOpened = true;
393
+ this.opened.emit();
394
+ // Optional extra attempt
395
+ queueMicrotask(() => this.focusPanelIfNoTabbable());
396
+ }
397
+ if (!this.isOpen() && this.didEmitOpened) {
398
+ this.didEmitOpened = false;
399
+ }
400
+ }
401
+ requestClose(reason) {
402
+ if (!this.open())
403
+ return;
404
+ this.closed.emit(reason);
405
+ }
406
+ onBackdropClick() {
407
+ if (!this.closeOnBackdropClick())
408
+ return;
409
+ this.requestClose('outside-click');
410
+ }
411
+ onPanelKeydown(ev) {
412
+ if (!this.open())
413
+ return;
414
+ if (!this.closeOnEscape())
415
+ return;
416
+ if (ev.defaultPrevented)
417
+ return;
418
+ if (ev.key === 'Escape') {
419
+ ev.preventDefault();
420
+ this.requestClose('escape');
421
+ }
422
+ }
423
+ focusPanelIfNoTabbable() {
424
+ if (!this.isOpen())
425
+ return;
426
+ if (!this.trapFocus())
427
+ return;
428
+ if (!this.autoFocusPanelWhenEmpty())
429
+ return;
430
+ const panel = this.panelRef()?.nativeElement;
431
+ if (!panel)
432
+ return;
433
+ const active = document.activeElement;
434
+ if (active && panel.contains(active))
435
+ return;
436
+ if (this.hasTabbable(panel))
437
+ return;
438
+ panel.focus();
439
+ }
440
+ hasTabbable(root) {
441
+ const candidates = root.querySelectorAll([
442
+ 'a[href]',
443
+ 'button',
444
+ 'input',
445
+ 'select',
446
+ 'textarea',
447
+ '[tabindex]',
448
+ '[contenteditable="true"]',
449
+ ].join(','));
450
+ for (const el of Array.from(candidates)) {
451
+ if (el === root)
452
+ continue;
453
+ if (el.hasAttribute('disabled'))
454
+ continue;
455
+ if (el.type === 'hidden')
456
+ continue;
457
+ const tabindexAttr = el.getAttribute('tabindex');
458
+ const tabindex = tabindexAttr == null ? 0 : Number(tabindexAttr);
459
+ if (Number.isNaN(tabindex) || tabindex < 0)
460
+ continue;
461
+ if (el.getClientRects().length === 0)
462
+ continue;
463
+ return true;
464
+ }
465
+ return false;
466
+ }
467
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
468
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngDialog, isStandalone: true, selector: "tng-dialog", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, trapFocus: { classPropertyName: "trapFocus", publicName: "trapFocus", isSignal: true, isRequired: false, transformFunction: null }, restoreFocus: { classPropertyName: "restoreFocus", publicName: "restoreFocus", isSignal: true, isRequired: false, transformFunction: null }, autoCapture: { classPropertyName: "autoCapture", publicName: "autoCapture", isSignal: true, isRequired: false, transformFunction: null }, deferCaptureElements: { classPropertyName: "deferCaptureElements", publicName: "deferCaptureElements", isSignal: true, isRequired: false, transformFunction: null }, autoFocusPanelWhenEmpty: { classPropertyName: "autoFocusPanelWhenEmpty", publicName: "autoFocusPanelWhenEmpty", isSignal: true, isRequired: false, transformFunction: null }, backdropKlass: { classPropertyName: "backdropKlass", publicName: "backdropKlass", isSignal: true, isRequired: false, transformFunction: null }, panelKlass: { classPropertyName: "panelKlass", publicName: "panelKlass", isSignal: true, isRequired: false, transformFunction: null }, headerWrapKlass: { classPropertyName: "headerWrapKlass", publicName: "headerWrapKlass", isSignal: true, isRequired: false, transformFunction: null }, bodyWrapKlass: { classPropertyName: "bodyWrapKlass", publicName: "bodyWrapKlass", isSignal: true, isRequired: false, transformFunction: null }, footerWrapKlass: { classPropertyName: "footerWrapKlass", publicName: "footerWrapKlass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", opened: "opened" }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (open()) {\n <div [class]=\"backdropKlass()\" (click)=\"onBackdropClick()\"></div>\n\n <div\n #panel\n [class]=\"panelKlass()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"-1\"\n (click)=\"$event.stopPropagation()\"\n (keydown)=\"onPanelKeydown($event)\"\n [tngFocusTrap]=\"isOpen() && trapFocus()\"\n [restoreFocus]=\"restoreFocus()\"\n [autoCapture]=\"autoCapture()\"\n [deferCaptureElements]=\"deferCaptureElements()\"\n >\n <!-- Header slot -->\n @if (true) {\n <div [class]=\"headerWrapKlass()\">\n <ng-content select=\"[tngDialogHeader]\"></ng-content>\n </div>\n }\n\n <!-- Body -->\n <div [class]=\"bodyWrapKlass()\">\n <ng-content></ng-content>\n </div>\n\n <!-- Footer slot -->\n <div [class]=\"footerWrapKlass()\">\n <ng-content select=\"[tngDialogFooter]\"></ng-content>\n </div>\n </div>\n}\n", dependencies: [{ kind: "directive", type: TngFocusTrap, selector: "[tngFocusTrap]", inputs: ["tngFocusTrap", "deferCaptureElements", "autoCapture", "restoreFocus"] }] });
469
+ }
470
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngDialog, decorators: [{
471
+ type: Component,
472
+ args: [{ selector: 'tng-dialog', standalone: true, imports: [TngFocusTrap], template: "@if (open()) {\n <div [class]=\"backdropKlass()\" (click)=\"onBackdropClick()\"></div>\n\n <div\n #panel\n [class]=\"panelKlass()\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"-1\"\n (click)=\"$event.stopPropagation()\"\n (keydown)=\"onPanelKeydown($event)\"\n [tngFocusTrap]=\"isOpen() && trapFocus()\"\n [restoreFocus]=\"restoreFocus()\"\n [autoCapture]=\"autoCapture()\"\n [deferCaptureElements]=\"deferCaptureElements()\"\n >\n <!-- Header slot -->\n @if (true) {\n <div [class]=\"headerWrapKlass()\">\n <ng-content select=\"[tngDialogHeader]\"></ng-content>\n </div>\n }\n\n <!-- Body -->\n <div [class]=\"bodyWrapKlass()\">\n <ng-content></ng-content>\n </div>\n\n <!-- Footer slot -->\n <div [class]=\"footerWrapKlass()\">\n <ng-content select=\"[tngDialogFooter]\"></ng-content>\n </div>\n </div>\n}\n" }]
473
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], trapFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "trapFocus", required: false }] }], restoreFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "restoreFocus", required: false }] }], autoCapture: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCapture", required: false }] }], deferCaptureElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferCaptureElements", required: false }] }], autoFocusPanelWhenEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocusPanelWhenEmpty", required: false }] }], backdropKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropKlass", required: false }] }], panelKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelKlass", required: false }] }], headerWrapKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerWrapKlass", required: false }] }], bodyWrapKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bodyWrapKlass", required: false }] }], footerWrapKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerWrapKlass", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], opened: [{ type: i0.Output, args: ["opened"] }], panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }] } });
474
+
475
+ class TngDialogInitialFocus {
476
+ // Attribute CDK FocusTrap looks for
477
+ cdkFocusInitialAttr = '';
478
+ // Optional camelCase alias (harmless, future-proof)
479
+ cdkFocusInitialCamelAttr = '';
480
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngDialogInitialFocus, deps: [], target: i0.ɵɵFactoryTarget.Directive });
481
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: TngDialogInitialFocus, isStandalone: true, selector: "[tngDialogInitialFocus]", host: { properties: { "attr.cdk-focus-initial": "this.cdkFocusInitialAttr", "attr.cdkFocusInitial": "this.cdkFocusInitialCamelAttr" } }, ngImport: i0 });
482
+ }
483
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngDialogInitialFocus, decorators: [{
484
+ type: Directive,
485
+ args: [{
486
+ selector: '[tngDialogInitialFocus]',
487
+ standalone: true,
488
+ }]
489
+ }], propDecorators: { cdkFocusInitialAttr: [{
490
+ type: HostBinding,
491
+ args: ['attr.cdk-focus-initial']
492
+ }], cdkFocusInitialCamelAttr: [{
493
+ type: HostBinding,
494
+ args: ['attr.cdkFocusInitial']
495
+ }] } });
496
+
497
+ class TngOptionList {
498
+ // IMPORTANT: because #listbox is under @if branches (modal/non-modal),
499
+ // keep static:false so Angular always resolves the *current* element.
500
+ listbox;
501
+ /* =====================
502
+ * Modal (optional)
503
+ * ===================== */
504
+ modal = input(false, ...(ngDevMode ? [{ debugName: "modal" }] : []));
505
+ /** overlay-panel options (only used when modal=true) */
506
+ panelKlass = input('p-0', ...(ngDevMode ? [{ debugName: "panelKlass" }] : []));
507
+ restoreFocus = input(true, ...(ngDevMode ? [{ debugName: "restoreFocus" }] : []));
508
+ autoCapture = input(true, ...(ngDevMode ? [{ debugName: "autoCapture" }] : []));
509
+ deferCaptureElements = input(false, ...(ngDevMode ? [{ debugName: "deferCaptureElements" }] : []));
510
+ /** a11y (for listbox container) */
511
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : [])); // applied to listbox container
512
+ ariaLabelledby = input('', ...(ngDevMode ? [{ debugName: "ariaLabelledby" }] : []));
513
+ ariaDescribedby = input('', ...(ngDevMode ? [{ debugName: "ariaDescribedby" }] : []));
514
+ /**
515
+ * tabindex for listbox container:
516
+ * - Default: 0 (focusable, good for keyboard + typeahead)
517
+ * - Set -1 for programmatic focus only
518
+ * - Set null to omit tabindex attribute
519
+ */
520
+ tabindex = input(0, ...(ngDevMode ? [{ debugName: "tabindex" }] : []));
521
+ /* =====================
522
+ * Templates + data
523
+ * ===================== */
524
+ optionTemplate = input(null, ...(ngDevMode ? [{ debugName: "optionTemplate" }] : []));
525
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
526
+ activeIndex = input(-1, ...(ngDevMode ? [{ debugName: "activeIndex" }] : []));
527
+ displayWith = input((v) => String(v), ...(ngDevMode ? [{ debugName: "displayWith" }] : []));
528
+ emptyText = input('No results', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
529
+ containerKlass = input('py-1 overflow-auto max-h-60', ...(ngDevMode ? [{ debugName: "containerKlass" }] : []));
530
+ optionKlass = input('px-3 py-2 text-sm cursor-pointer select-none', ...(ngDevMode ? [{ debugName: "optionKlass" }] : []));
531
+ optionActiveKlass = input('bg-primary text-on-primary', ...(ngDevMode ? [{ debugName: "optionActiveKlass" }] : []));
532
+ optionInactiveKlass = input('bg-bg text-fg', ...(ngDevMode ? [{ debugName: "optionInactiveKlass" }] : []));
533
+ emptyKlass = input('px-3 py-2 text-sm text-disable', ...(ngDevMode ? [{ debugName: "emptyKlass" }] : []));
534
+ optionTpl;
535
+ /* =====================
536
+ * Outputs
537
+ * ===================== */
538
+ optionMouseDown = output();
539
+ optionHover = output();
540
+ /** Parent-controlled active index */
541
+ activeIndexChange = output();
542
+ /** Parent decides what selection means */
543
+ requestSelectActive = output();
544
+ /** Optional: parent can observe typeahead match */
545
+ requestTypeaheadMatch = output();
546
+ /** Optional: key stroke propagation */
547
+ propagateKeys = input(false, ...(ngDevMode ? [{ debugName: "propagateKeys" }] : []));
548
+ keyStroke = output();
549
+ /* =====================
550
+ * Keyboard / typeahead config
551
+ * ===================== */
552
+ keyboard = input(true, ...(ngDevMode ? [{ debugName: "keyboard" }] : []));
553
+ loop = input(true, ...(ngDevMode ? [{ debugName: "loop" }] : []));
554
+ selectOnEnter = input(true, ...(ngDevMode ? [{ debugName: "selectOnEnter" }] : []));
555
+ typeahead = input(true, ...(ngDevMode ? [{ debugName: "typeahead" }] : []));
556
+ typeaheadMode = input('startsWith', ...(ngDevMode ? [{ debugName: "typeaheadMode" }] : []));
557
+ typeaheadResetMs = input(500, ...(ngDevMode ? [{ debugName: "typeaheadResetMs" }] : []));
558
+ pageJumpSize = input(8, ...(ngDevMode ? [{ debugName: "pageJumpSize" }] : []));
559
+ typeaheadQuery = signal('', ...(ngDevMode ? [{ debugName: "typeaheadQuery" }] : []));
560
+ typeaheadTimer = null;
561
+ /* =====================
562
+ * Computeds
563
+ * ===================== */
564
+ hasItems = computed(() => (this.items()?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasItems" }] : []));
565
+ constructor() {
566
+ /**
567
+ * Scroll follows the controlled `activeIndex`:
568
+ * This guarantees scrolling even when parent changes activeIndex via:
569
+ * - hover
570
+ * - open() initialization
571
+ * - programmatic updates
572
+ * - any external state
573
+ *
574
+ * (Not just when OptionList handles keydown.)
575
+ */
576
+ effect(() => {
577
+ const i = this.activeIndex();
578
+ if (i < 0)
579
+ return;
580
+ // Wait for DOM update / projection / overlay paint
581
+ requestAnimationFrame(() => {
582
+ requestAnimationFrame(() => this.scrollIndexIntoView(i));
583
+ });
584
+ });
585
+ }
586
+ get tpl() {
587
+ return this.optionTemplate() ?? this.optionTpl;
588
+ }
589
+ display(item) {
590
+ return this.displayWith()(item);
591
+ }
592
+ isActive(i) {
593
+ return i === this.activeIndex();
594
+ }
595
+ optionClasses(i) {
596
+ const state = this.isActive(i) ? this.optionActiveKlass() : this.optionInactiveKlass();
597
+ return `${this.optionKlass()} ${state}`.trim();
598
+ }
599
+ onMouseDown(item, index, ev) {
600
+ ev.preventDefault();
601
+ this.optionMouseDown.emit({ item, index });
602
+ }
603
+ onMouseEnter(index) {
604
+ this.optionHover.emit(index);
605
+ }
606
+ /* =====================
607
+ * Keyboard handling
608
+ * ===================== */
609
+ onKeydown(ev) {
610
+ const key = ev.key;
611
+ if (!this.hasItems()) {
612
+ this.emitKeyStroke({ key, handled: false, prevented: false, action: 'none', originalEvent: ev });
613
+ return;
614
+ }
615
+ if (ev.defaultPrevented) {
616
+ this.emitKeyStroke({ key, handled: false, prevented: false, action: 'none', originalEvent: ev });
617
+ return;
618
+ }
619
+ const anyEv = ev;
620
+ if (anyEv.isComposing === true || anyEv.keyCode === 229) {
621
+ this.emitKeyStroke({ key, handled: false, prevented: false, action: 'none', originalEvent: ev });
622
+ return;
623
+ }
624
+ if (!this.keyboard()) {
625
+ this.emitKeyStroke({ key, handled: false, prevented: false, action: 'none', originalEvent: ev });
626
+ return;
627
+ }
628
+ // Navigation
629
+ if (key === 'ArrowDown') {
630
+ ev.preventDefault();
631
+ const next = this.computeNextIndex(1);
632
+ this.commitActive(next);
633
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'move', nextActiveIndex: next, originalEvent: ev });
634
+ return;
635
+ }
636
+ if (key === 'ArrowUp') {
637
+ ev.preventDefault();
638
+ const next = this.computeNextIndex(-1);
639
+ this.commitActive(next);
640
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'move', nextActiveIndex: next, originalEvent: ev });
641
+ return;
642
+ }
643
+ if (key === 'Home') {
644
+ ev.preventDefault();
645
+ const next = 0;
646
+ this.commitActive(next);
647
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'move', nextActiveIndex: next, originalEvent: ev });
648
+ return;
649
+ }
650
+ if (key === 'End') {
651
+ ev.preventDefault();
652
+ const next = this.items().length - 1;
653
+ this.commitActive(next);
654
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'move', nextActiveIndex: next, originalEvent: ev });
655
+ return;
656
+ }
657
+ if (key === 'PageDown') {
658
+ ev.preventDefault();
659
+ const next = this.computeNextIndex(this.pageJumpSize());
660
+ this.commitActive(next);
661
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'move', nextActiveIndex: next, originalEvent: ev });
662
+ return;
663
+ }
664
+ if (key === 'PageUp') {
665
+ ev.preventDefault();
666
+ const next = this.computeNextIndex(-this.pageJumpSize());
667
+ this.commitActive(next);
668
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'move', nextActiveIndex: next, originalEvent: ev });
669
+ return;
670
+ }
671
+ // Selection
672
+ if (key === 'Enter') {
673
+ if (!this.selectOnEnter()) {
674
+ this.emitKeyStroke({ key, handled: false, prevented: false, action: 'none', originalEvent: ev });
675
+ return;
676
+ }
677
+ ev.preventDefault();
678
+ this.requestSelectActive.emit();
679
+ this.emitKeyStroke({ key, handled: true, prevented: true, action: 'select', originalEvent: ev });
680
+ return;
681
+ }
682
+ // Typeahead
683
+ if (this.typeahead() && this.isPrintableKey(ev)) {
684
+ const { query, matchIndex } = this.onTypeaheadKey(key);
685
+ if (matchIndex >= 0) {
686
+ this.commitActive(matchIndex);
687
+ this.emitKeyStroke({
688
+ key,
689
+ handled: true,
690
+ prevented: false,
691
+ action: 'typeahead',
692
+ nextActiveIndex: matchIndex,
693
+ query,
694
+ originalEvent: ev,
695
+ });
696
+ return;
697
+ }
698
+ this.emitKeyStroke({ key, handled: true, prevented: false, action: 'typeahead', query, originalEvent: ev });
699
+ return;
700
+ }
701
+ this.emitKeyStroke({ key, handled: false, prevented: false, action: 'none', originalEvent: ev });
702
+ }
703
+ emitKeyStroke(e) {
704
+ if (!this.propagateKeys())
705
+ return;
706
+ this.keyStroke.emit(e);
707
+ }
708
+ computeNextIndex(delta) {
709
+ const len = this.items().length;
710
+ if (len <= 0)
711
+ return -1;
712
+ const current = this.activeIndex();
713
+ const base = current < 0 ? (delta < 0 && this.loop() ? len - 1 : 0) : current;
714
+ const nextRaw = base + delta;
715
+ if (this.loop()) {
716
+ return ((nextRaw % len) + len) % len;
717
+ }
718
+ return Math.max(0, Math.min(len - 1, nextRaw));
719
+ }
720
+ /**
721
+ * Controlled activeIndex
722
+ * - Emit activeIndexChange (parent owns state)
723
+ * - Scrolling is handled by the effect() reacting to activeIndex()
724
+ */
725
+ commitActive(index) {
726
+ if (index < 0)
727
+ return;
728
+ this.activeIndexChange.emit(index);
729
+ }
730
+ /**
731
+ * Scroll the active option into view.
732
+ * Let the browser pick the nearest scrollable ancestor.
733
+ */
734
+ scrollIndexIntoView(index) {
735
+ const root = this.listbox?.nativeElement;
736
+ if (!root)
737
+ return;
738
+ const optionEl = root.querySelector(`[data-index="${index}"]`);
739
+ if (!optionEl)
740
+ return;
741
+ optionEl.scrollIntoView({
742
+ block: 'nearest',
743
+ inline: 'nearest',
744
+ behavior: 'auto',
745
+ });
746
+ }
747
+ /* =====================
748
+ * Typeahead
749
+ * ===================== */
750
+ onTypeaheadKey(char) {
751
+ if (this.typeaheadTimer != null) {
752
+ window.clearTimeout(this.typeaheadTimer);
753
+ }
754
+ const nextQuery = (this.typeaheadQuery() + char).toLowerCase();
755
+ this.typeaheadQuery.set(nextQuery);
756
+ this.typeaheadTimer = window.setTimeout(() => {
757
+ this.typeaheadQuery.set('');
758
+ this.typeaheadTimer = null;
759
+ }, this.typeaheadResetMs());
760
+ const idx = this.findTypeaheadMatch(nextQuery);
761
+ if (idx >= 0) {
762
+ this.requestTypeaheadMatch.emit({ query: nextQuery, index: idx });
763
+ }
764
+ return { query: nextQuery, matchIndex: idx };
765
+ }
766
+ findTypeaheadMatch(query) {
767
+ const items = this.items();
768
+ const mode = this.typeaheadMode();
769
+ const start = this.activeIndex();
770
+ const from = start < 0 ? 0 : start + 1;
771
+ const matches = (item) => {
772
+ const label = this.display(item).toLowerCase();
773
+ return mode === 'includes' ? label.includes(query) : label.startsWith(query);
774
+ };
775
+ for (let i = from; i < items.length; i++)
776
+ if (matches(items[i]))
777
+ return i;
778
+ for (let i = 0; i < from && i < items.length; i++)
779
+ if (matches(items[i]))
780
+ return i;
781
+ return -1;
782
+ }
783
+ isPrintableKey(ev) {
784
+ if (ev.ctrlKey || ev.metaKey || ev.altKey)
785
+ return false;
786
+ if (ev.key.length !== 1)
787
+ return false;
788
+ if (ev.key === ' ')
789
+ return false;
790
+ return true;
791
+ }
792
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngOptionList, deps: [], target: i0.ɵɵFactoryTarget.Component });
793
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngOptionList, isStandalone: true, selector: "tng-option-list", inputs: { modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, panelKlass: { classPropertyName: "panelKlass", publicName: "panelKlass", isSignal: true, isRequired: false, transformFunction: null }, restoreFocus: { classPropertyName: "restoreFocus", publicName: "restoreFocus", isSignal: true, isRequired: false, transformFunction: null }, autoCapture: { classPropertyName: "autoCapture", publicName: "autoCapture", isSignal: true, isRequired: false, transformFunction: null }, deferCaptureElements: { classPropertyName: "deferCaptureElements", publicName: "deferCaptureElements", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "displayWith", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, containerKlass: { classPropertyName: "containerKlass", publicName: "containerKlass", isSignal: true, isRequired: false, transformFunction: null }, optionKlass: { classPropertyName: "optionKlass", publicName: "optionKlass", isSignal: true, isRequired: false, transformFunction: null }, optionActiveKlass: { classPropertyName: "optionActiveKlass", publicName: "optionActiveKlass", isSignal: true, isRequired: false, transformFunction: null }, optionInactiveKlass: { classPropertyName: "optionInactiveKlass", publicName: "optionInactiveKlass", isSignal: true, isRequired: false, transformFunction: null }, emptyKlass: { classPropertyName: "emptyKlass", publicName: "emptyKlass", isSignal: true, isRequired: false, transformFunction: null }, propagateKeys: { classPropertyName: "propagateKeys", publicName: "propagateKeys", isSignal: true, isRequired: false, transformFunction: null }, keyboard: { classPropertyName: "keyboard", publicName: "keyboard", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, selectOnEnter: { classPropertyName: "selectOnEnter", publicName: "selectOnEnter", isSignal: true, isRequired: false, transformFunction: null }, typeahead: { classPropertyName: "typeahead", publicName: "typeahead", isSignal: true, isRequired: false, transformFunction: null }, typeaheadMode: { classPropertyName: "typeaheadMode", publicName: "typeaheadMode", isSignal: true, isRequired: false, transformFunction: null }, typeaheadResetMs: { classPropertyName: "typeaheadResetMs", publicName: "typeaheadResetMs", isSignal: true, isRequired: false, transformFunction: null }, pageJumpSize: { classPropertyName: "pageJumpSize", publicName: "pageJumpSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionMouseDown: "optionMouseDown", optionHover: "optionHover", activeIndexChange: "activeIndexChange", requestSelectActive: "requestSelectActive", requestTypeaheadMatch: "requestTypeaheadMatch", keyStroke: "keyStroke" }, queries: [{ propertyName: "optionTpl", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "listbox", first: true, predicate: ["listbox"], descendants: true, read: ElementRef }], ngImport: i0, template: "<!-- option-list.component.html -->\n@let active = activeIndex();\n\n@if (modal()) {\n <tng-overlay-panel\n [modal]=\"true\"\n [klass]=\"panelKlass()\"\n [restoreFocus]=\"restoreFocus()\"\n [autoCapture]=\"autoCapture()\"\n [deferCaptureElements]=\"deferCaptureElements()\"\n >\n <div\n #listbox\n role=\"listbox\"\n [class]=\"containerKlass()\"\n [attr.tabindex]=\"tabindex()\"\n (keydown)=\"onKeydown($event)\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledby() || null\"\n [attr.aria-describedby]=\"ariaDescribedby() || null\"\n [attr.aria-activedescendant]=\"active >= 0 ? 'tng-opt-' + active : null\"\n >\n @if (hasItems()) {\n @for (item of items(); let i = $index; track i) {\n <div\n role=\"option\"\n [id]=\"'tng-opt-' + i\"\n [class]=\"optionClasses(i)\"\n [attr.aria-selected]=\"isActive(i) ? 'true' : 'false'\"\n [attr.data-index]=\"i\"\n (mousedown)=\"onMouseDown(item, i, $event)\"\n (mouseenter)=\"onMouseEnter(i)\"\n >\n @if (tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item, index: i, active: isActive(i) }\"\n />\n } @else {\n {{ display(item) }}\n }\n </div>\n }\n } @else {\n <div [class]=\"emptyKlass()\">{{ emptyText() }}</div>\n }\n </div>\n </tng-overlay-panel>\n} @else {\n <div\n #listbox\n role=\"listbox\"\n [class]=\"containerKlass()\"\n [attr.tabindex]=\"tabindex()\"\n (keydown)=\"onKeydown($event)\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledby() || null\"\n [attr.aria-describedby]=\"ariaDescribedby() || null\"\n [attr.aria-activedescendant]=\"active >= 0 ? 'tng-opt-' + active : null\"\n >\n @if (hasItems()) {\n @for (item of items(); let i = $index; track i) {\n <div\n role=\"option\"\n [id]=\"'tng-opt-' + i\"\n [class]=\"optionClasses(i)\"\n [attr.aria-selected]=\"isActive(i) ? 'true' : 'false'\"\n [attr.data-index]=\"i\"\n (mousedown)=\"onMouseDown(item, i, $event)\"\n (mouseenter)=\"onMouseEnter(i)\"\n >\n @if (tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item, index: i, active: isActive(i) }\"\n />\n } @else {\n {{ display(item) }}\n }\n </div>\n }\n } @else {\n <div [class]=\"emptyKlass()\">{{ emptyText() }}</div>\n }\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TngOverlayPanel, selector: "tng-overlay-panel", inputs: ["klass", "modal", "role", "ariaLabel", "ariaLabelledby", "ariaDescribedby", "restoreFocus", "autoCapture", "deferCaptureElements"] }] });
794
+ }
795
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngOptionList, decorators: [{
796
+ type: Component,
797
+ args: [{ selector: 'tng-option-list', standalone: true, imports: [CommonModule, TngOverlayPanel], template: "<!-- option-list.component.html -->\n@let active = activeIndex();\n\n@if (modal()) {\n <tng-overlay-panel\n [modal]=\"true\"\n [klass]=\"panelKlass()\"\n [restoreFocus]=\"restoreFocus()\"\n [autoCapture]=\"autoCapture()\"\n [deferCaptureElements]=\"deferCaptureElements()\"\n >\n <div\n #listbox\n role=\"listbox\"\n [class]=\"containerKlass()\"\n [attr.tabindex]=\"tabindex()\"\n (keydown)=\"onKeydown($event)\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledby() || null\"\n [attr.aria-describedby]=\"ariaDescribedby() || null\"\n [attr.aria-activedescendant]=\"active >= 0 ? 'tng-opt-' + active : null\"\n >\n @if (hasItems()) {\n @for (item of items(); let i = $index; track i) {\n <div\n role=\"option\"\n [id]=\"'tng-opt-' + i\"\n [class]=\"optionClasses(i)\"\n [attr.aria-selected]=\"isActive(i) ? 'true' : 'false'\"\n [attr.data-index]=\"i\"\n (mousedown)=\"onMouseDown(item, i, $event)\"\n (mouseenter)=\"onMouseEnter(i)\"\n >\n @if (tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item, index: i, active: isActive(i) }\"\n />\n } @else {\n {{ display(item) }}\n }\n </div>\n }\n } @else {\n <div [class]=\"emptyKlass()\">{{ emptyText() }}</div>\n }\n </div>\n </tng-overlay-panel>\n} @else {\n <div\n #listbox\n role=\"listbox\"\n [class]=\"containerKlass()\"\n [attr.tabindex]=\"tabindex()\"\n (keydown)=\"onKeydown($event)\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabelledby() || null\"\n [attr.aria-describedby]=\"ariaDescribedby() || null\"\n [attr.aria-activedescendant]=\"active >= 0 ? 'tng-opt-' + active : null\"\n >\n @if (hasItems()) {\n @for (item of items(); let i = $index; track i) {\n <div\n role=\"option\"\n [id]=\"'tng-opt-' + i\"\n [class]=\"optionClasses(i)\"\n [attr.aria-selected]=\"isActive(i) ? 'true' : 'false'\"\n [attr.data-index]=\"i\"\n (mousedown)=\"onMouseDown(item, i, $event)\"\n (mouseenter)=\"onMouseEnter(i)\"\n >\n @if (tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: item, index: i, active: isActive(i) }\"\n />\n } @else {\n {{ display(item) }}\n }\n </div>\n }\n } @else {\n <div [class]=\"emptyKlass()\">{{ emptyText() }}</div>\n }\n </div>\n}\n" }]
798
+ }], ctorParameters: () => [], propDecorators: { listbox: [{
799
+ type: ViewChild,
800
+ args: ['listbox', { read: ElementRef }]
801
+ }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], panelKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelKlass", required: false }] }], restoreFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "restoreFocus", required: false }] }], autoCapture: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCapture", required: false }] }], deferCaptureElements: [{ type: i0.Input, args: [{ isSignal: true, alias: "deferCaptureElements", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], tabindex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabindex", required: false }] }], optionTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionTemplate", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayWith", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], containerKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "containerKlass", required: false }] }], optionKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionKlass", required: false }] }], optionActiveKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionActiveKlass", required: false }] }], optionInactiveKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionInactiveKlass", required: false }] }], emptyKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyKlass", required: false }] }], optionTpl: [{
802
+ type: ContentChild,
803
+ args: [TemplateRef, { descendants: true }]
804
+ }], optionMouseDown: [{ type: i0.Output, args: ["optionMouseDown"] }], optionHover: [{ type: i0.Output, args: ["optionHover"] }], activeIndexChange: [{ type: i0.Output, args: ["activeIndexChange"] }], requestSelectActive: [{ type: i0.Output, args: ["requestSelectActive"] }], requestTypeaheadMatch: [{ type: i0.Output, args: ["requestTypeaheadMatch"] }], propagateKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "propagateKeys", required: false }] }], keyStroke: [{ type: i0.Output, args: ["keyStroke"] }], keyboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyboard", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], selectOnEnter: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectOnEnter", required: false }] }], typeahead: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeahead", required: false }] }], typeaheadMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadMode", required: false }] }], typeaheadResetMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadResetMs", required: false }] }], pageJumpSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageJumpSize", required: false }] }] } });
805
+
806
+ class TngPopover {
807
+ /* =====================
808
+ * Slots
809
+ * ===================== */
810
+ contentTpl;
811
+ triggerEl;
812
+ /* =====================
813
+ * Inputs / outputs
814
+ * ===================== */
815
+ open = input(null, ...(ngDevMode ? [{ debugName: "open" }] : [])); // optional controlled
816
+ placement = input('bottom-start', ...(ngDevMode ? [{ debugName: "placement" }] : []));
817
+ offset = input(6, ...(ngDevMode ? [{ debugName: "offset" }] : []));
818
+ width = input('anchor', ...(ngDevMode ? [{ debugName: "width" }] : []));
819
+ closeOnOutsideClick = input(true, ...(ngDevMode ? [{ debugName: "closeOnOutsideClick" }] : []));
820
+ closeOnEscape = input(true, ...(ngDevMode ? [{ debugName: "closeOnEscape" }] : []));
821
+ rootKlass = input('relative inline-flex', ...(ngDevMode ? [{ debugName: "rootKlass" }] : []));
822
+ triggerKlass = input('inline-flex', ...(ngDevMode ? [{ debugName: "triggerKlass" }] : []));
823
+ panelKlass = input('p-2', ...(ngDevMode ? [{ debugName: "panelKlass" }] : []));
824
+ opened = output();
825
+ closed = output();
826
+ openChange = output(); // for controlled usage if desired
827
+ /* =====================
828
+ * State
829
+ * ===================== */
830
+ internalOpen = signal(false, ...(ngDevMode ? [{ debugName: "internalOpen" }] : []));
831
+ isOpen = computed(() => (this.open() == null ? this.internalOpen() : !!this.open()), ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
832
+ constructor() {
833
+ // If externally controlled, keep internal in sync for animation consistency
834
+ effect(() => {
835
+ const ext = this.open();
836
+ if (ext == null)
837
+ return;
838
+ this.internalOpen.set(!!ext);
839
+ });
840
+ }
841
+ toggle() {
842
+ this.isOpen() ? this.requestClose('programmatic') : this.requestOpen();
843
+ }
844
+ requestOpen() {
845
+ if (this.isOpen())
846
+ return;
847
+ if (this.open() == null)
848
+ this.internalOpen.set(true);
849
+ this.openChange.emit(true);
850
+ this.opened.emit();
851
+ }
852
+ requestClose(reason) {
853
+ if (!this.isOpen())
854
+ return;
855
+ if (this.open() == null)
856
+ this.internalOpen.set(false);
857
+ this.openChange.emit(false);
858
+ this.closed.emit(reason);
859
+ queueMicrotask(() => this.triggerEl?.nativeElement?.focus());
860
+ }
861
+ onOverlayClosed(reason) {
862
+ // Map connected-overlay reasons
863
+ if (reason === 'escape')
864
+ this.requestClose('escape');
865
+ else if (reason === 'outside-click')
866
+ this.requestClose('outside-click');
867
+ else
868
+ this.requestClose('programmatic');
869
+ }
870
+ onTriggerClick() {
871
+ this.toggle();
872
+ }
873
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngPopover, deps: [], target: i0.ɵɵFactoryTarget.Component });
874
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngPopover, isStandalone: true, selector: "tng-popover", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, closeOnOutsideClick: { classPropertyName: "closeOnOutsideClick", publicName: "closeOnOutsideClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, rootKlass: { classPropertyName: "rootKlass", publicName: "rootKlass", isSignal: true, isRequired: false, transformFunction: null }, triggerKlass: { classPropertyName: "triggerKlass", publicName: "triggerKlass", isSignal: true, isRequired: false, transformFunction: null }, panelKlass: { classPropertyName: "panelKlass", publicName: "panelKlass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed", openChange: "openChange" }, queries: [{ propertyName: "contentTpl", first: true, predicate: ["popoverContent"], descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "triggerEl", first: true, predicate: ["triggerEl"], descendants: true, static: true }], ngImport: i0, template: "<div [class]=\"rootKlass()\">\n <button\n #triggerEl\n type=\"button\"\n [class]=\"triggerKlass()\"\n aria-haspopup=\"dialog\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"onTriggerClick()\"\n >\n <ng-content select=\"[tngPopoverTrigger]\"></ng-content>\n </button>\n\n <tng-overlay-ref\n [open]=\"isOpen()\"\n (openChange)=\"openChange.emit($event)\"\n (opened)=\"opened.emit()\"\n (closed)=\"onOverlayClosed($event)\"\n >\n <tng-connected-overlay\n [open]=\"isOpen()\"\n [anchor]=\"triggerEl\"\n [placement]=\"placement()\"\n [offset]=\"offset()\"\n [width]=\"width()\"\n [closeOnOutsideClick]=\"closeOnOutsideClick()\"\n [closeOnEscape]=\"closeOnEscape()\"\n (closed)=\"onOverlayClosed($event)\"\n >\n <tng-overlay-panel [klass]=\"panelKlass()\">\n @if (contentTpl) {\n <ng-container [ngTemplateOutlet]=\"contentTpl\"></ng-container>\n } @else {\n <ng-content select=\"[tngPopoverContent]\"></ng-content>\n }\n </tng-overlay-panel>\n </tng-connected-overlay>\n </tng-overlay-ref>\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TngConnectedOverlay, selector: "tng-connected-overlay", inputs: ["open", "anchor", "placement", "offset", "width", "closeOnOutsideClick", "closeOnInsideClick", "closeOnEscape", "hasBackdrop", "backdropClass"], outputs: ["opened", "closed", "backdropClick"] }, { kind: "component", type: TngOverlayPanel, selector: "tng-overlay-panel", inputs: ["klass", "modal", "role", "ariaLabel", "ariaLabelledby", "ariaDescribedby", "restoreFocus", "autoCapture", "deferCaptureElements"] }, { kind: "component", type: TngOverlayRef, selector: "tng-overlay-ref", inputs: ["open"], outputs: ["openChange", "opened", "closed"], exportAs: ["tngOverlayRef"] }] });
875
+ }
876
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngPopover, decorators: [{
877
+ type: Component,
878
+ args: [{ selector: 'tng-popover', standalone: true, imports: [
879
+ NgTemplateOutlet,
880
+ TngConnectedOverlay,
881
+ TngOverlayPanel,
882
+ TngOverlayRef,
883
+ ], template: "<div [class]=\"rootKlass()\">\n <button\n #triggerEl\n type=\"button\"\n [class]=\"triggerKlass()\"\n aria-haspopup=\"dialog\"\n [attr.aria-expanded]=\"isOpen()\"\n (click)=\"onTriggerClick()\"\n >\n <ng-content select=\"[tngPopoverTrigger]\"></ng-content>\n </button>\n\n <tng-overlay-ref\n [open]=\"isOpen()\"\n (openChange)=\"openChange.emit($event)\"\n (opened)=\"opened.emit()\"\n (closed)=\"onOverlayClosed($event)\"\n >\n <tng-connected-overlay\n [open]=\"isOpen()\"\n [anchor]=\"triggerEl\"\n [placement]=\"placement()\"\n [offset]=\"offset()\"\n [width]=\"width()\"\n [closeOnOutsideClick]=\"closeOnOutsideClick()\"\n [closeOnEscape]=\"closeOnEscape()\"\n (closed)=\"onOverlayClosed($event)\"\n >\n <tng-overlay-panel [klass]=\"panelKlass()\">\n @if (contentTpl) {\n <ng-container [ngTemplateOutlet]=\"contentTpl\"></ng-container>\n } @else {\n <ng-content select=\"[tngPopoverContent]\"></ng-content>\n }\n </tng-overlay-panel>\n </tng-connected-overlay>\n </tng-overlay-ref>\n</div>\n" }]
884
+ }], ctorParameters: () => [], propDecorators: { contentTpl: [{
885
+ type: ContentChild,
886
+ args: ['popoverContent', { read: TemplateRef }]
887
+ }], triggerEl: [{
888
+ type: ViewChild,
889
+ args: ['triggerEl', { static: true }]
890
+ }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], closeOnOutsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnOutsideClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], rootKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rootKlass", required: false }] }], triggerKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerKlass", required: false }] }], panelKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelKlass", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], openChange: [{ type: i0.Output, args: ["openChange"] }] } });
891
+
892
+ class TngSnackbarHost {
893
+ /** Controlled items array */
894
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
895
+ /** Placement on screen */
896
+ position = input('bottom-center', ...(ngDevMode ? [{ debugName: "position" }] : []));
897
+ /** Max stack size (visual). Consumer should also enforce if desired. */
898
+ max = input(3, ...(ngDevMode ? [{ debugName: "max" }] : []));
899
+ /** Outputs */
900
+ dismiss = output();
901
+ /* =====================
902
+ * Klass inputs
903
+ * ===================== */
904
+ hostKlass = input('fixed z-[1100] flex flex-col gap-2 p-4', ...(ngDevMode ? [{ debugName: "hostKlass" }] : []));
905
+ itemKlass = input('pointer-events-auto w-[min(28rem,calc(100vw-2rem))] rounded-md border border-border bg-bg shadow-lg', ...(ngDevMode ? [{ debugName: "itemKlass" }] : []));
906
+ itemInnerKlass = input('flex items-start gap-3 px-4 py-3', ...(ngDevMode ? [{ debugName: "itemInnerKlass" }] : []));
907
+ messageKlass = input('text-sm text-foreground', ...(ngDevMode ? [{ debugName: "messageKlass" }] : []));
908
+ actionKlass = input('text-sm font-medium text-primary hover:underline', ...(ngDevMode ? [{ debugName: "actionKlass" }] : []));
909
+ dismissBtnKlass = input('text-muted-foreground hover:text-foreground', ...(ngDevMode ? [{ debugName: "dismissBtnKlass" }] : []));
910
+ /** Intent -> klass mapping */
911
+ intentKlass = input((intent) => {
912
+ switch (intent) {
913
+ case 'success':
914
+ return 'border-success/30';
915
+ case 'info':
916
+ return 'border-info/30';
917
+ case 'warning':
918
+ return 'border-warning/30';
919
+ case 'error':
920
+ return 'border-danger/30';
921
+ case 'default':
922
+ default:
923
+ return '';
924
+ }
925
+ }, ...(ngDevMode ? [{ debugName: "intentKlass" }] : []));
926
+ /* =====================
927
+ * Derived / internal
928
+ * ===================== */
929
+ hostPositionKlass = computed(() => {
930
+ const base = this.hostKlass();
931
+ switch (this.position()) {
932
+ case 'top-left':
933
+ return `${base} top-0 left-0 items-start`;
934
+ case 'top-center':
935
+ return `${base} top-0 left-1/2 -translate-x-1/2 items-center`;
936
+ case 'top-right':
937
+ return `${base} top-0 right-0 items-end`;
938
+ case 'bottom-left':
939
+ return `${base} bottom-0 left-0 items-start`;
940
+ case 'bottom-center':
941
+ return `${base} bottom-0 left-1/2 -translate-x-1/2 items-center`;
942
+ case 'bottom-right':
943
+ return `${base} bottom-0 right-0 items-end`;
944
+ }
945
+ }, ...(ngDevMode ? [{ debugName: "hostPositionKlass" }] : []));
946
+ /** Track which ids have active timers */
947
+ timers = new Map();
948
+ constructor() {
949
+ effect(() => {
950
+ const incoming = (this.items() ?? []).slice(0, Math.max(1, this.max()));
951
+ // Start timers for new items with duration
952
+ for (const item of incoming) {
953
+ const dur = item.durationMs ?? 0;
954
+ if (dur > 0 && !this.timers.has(item.id)) {
955
+ const t = window.setTimeout(() => {
956
+ this.timers.delete(item.id);
957
+ this.dismiss.emit({ id: item.id, reason: 'timeout' });
958
+ }, dur);
959
+ this.timers.set(item.id, t);
960
+ }
961
+ }
962
+ // Clear timers for removed items
963
+ const ids = new Set(incoming.map((x) => x.id));
964
+ for (const [id, t] of Array.from(this.timers.entries())) {
965
+ if (!ids.has(id)) {
966
+ window.clearTimeout(t);
967
+ this.timers.delete(id);
968
+ }
969
+ }
970
+ });
971
+ }
972
+ itemClasses(item) {
973
+ const intent = item.intent ?? 'default';
974
+ const intentCls = this.intentKlass()(intent);
975
+ return `${this.itemKlass()} ${intentCls}`.trim();
976
+ }
977
+ onDismiss(id) {
978
+ const t = this.timers.get(id);
979
+ if (t != null) {
980
+ window.clearTimeout(t);
981
+ this.timers.delete(id);
982
+ }
983
+ this.dismiss.emit({ id, reason: 'dismiss' });
984
+ }
985
+ onAction(id) {
986
+ const t = this.timers.get(id);
987
+ if (t != null) {
988
+ window.clearTimeout(t);
989
+ this.timers.delete(id);
990
+ }
991
+ this.dismiss.emit({ id, reason: 'action' });
992
+ }
993
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSnackbarHost, deps: [], target: i0.ɵɵFactoryTarget.Component });
994
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngSnackbarHost, isStandalone: true, selector: "tng-snackbar-host", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, hostKlass: { classPropertyName: "hostKlass", publicName: "hostKlass", isSignal: true, isRequired: false, transformFunction: null }, itemKlass: { classPropertyName: "itemKlass", publicName: "itemKlass", isSignal: true, isRequired: false, transformFunction: null }, itemInnerKlass: { classPropertyName: "itemInnerKlass", publicName: "itemInnerKlass", isSignal: true, isRequired: false, transformFunction: null }, messageKlass: { classPropertyName: "messageKlass", publicName: "messageKlass", isSignal: true, isRequired: false, transformFunction: null }, actionKlass: { classPropertyName: "actionKlass", publicName: "actionKlass", isSignal: true, isRequired: false, transformFunction: null }, dismissBtnKlass: { classPropertyName: "dismissBtnKlass", publicName: "dismissBtnKlass", isSignal: true, isRequired: false, transformFunction: null }, intentKlass: { classPropertyName: "intentKlass", publicName: "intentKlass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismiss: "dismiss" }, ngImport: i0, template: "@if ((items()?.length ?? 0) > 0) {\n <div [class]=\"hostPositionKlass()\">\n @for (item of items().slice(0, max()); track item.id) {\n <div [class]=\"itemClasses(item)\">\n <div [class]=\"itemInnerKlass()\">\n <div class=\"flex-1\">\n <div [class]=\"messageKlass()\">{{ item.message }}</div>\n </div>\n\n @if (item.actionLabel) {\n <button type=\"button\" [class]=\"actionKlass()\" (click)=\"onAction(item.id)\">\n {{ item.actionLabel }}\n </button>\n }\n\n <button\n type=\"button\"\n [class]=\"dismissBtnKlass()\"\n aria-label=\"Dismiss\"\n (click)=\"onDismiss(item.id)\"\n >\n \u2715\n </button>\n </div>\n </div>\n }\n </div>\n}\n" });
995
+ }
996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSnackbarHost, decorators: [{
997
+ type: Component,
998
+ args: [{ selector: 'tng-snackbar-host', standalone: true, template: "@if ((items()?.length ?? 0) > 0) {\n <div [class]=\"hostPositionKlass()\">\n @for (item of items().slice(0, max()); track item.id) {\n <div [class]=\"itemClasses(item)\">\n <div [class]=\"itemInnerKlass()\">\n <div class=\"flex-1\">\n <div [class]=\"messageKlass()\">{{ item.message }}</div>\n </div>\n\n @if (item.actionLabel) {\n <button type=\"button\" [class]=\"actionKlass()\" (click)=\"onAction(item.id)\">\n {{ item.actionLabel }}\n </button>\n }\n\n <button\n type=\"button\"\n [class]=\"dismissBtnKlass()\"\n aria-label=\"Dismiss\"\n (click)=\"onDismiss(item.id)\"\n >\n \u2715\n </button>\n </div>\n </div>\n }\n </div>\n}\n" }]
999
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }], hostKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hostKlass", required: false }] }], itemKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemKlass", required: false }] }], itemInnerKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemInnerKlass", required: false }] }], messageKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageKlass", required: false }] }], actionKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionKlass", required: false }] }], dismissBtnKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissBtnKlass", required: false }] }], intentKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "intentKlass", required: false }] }] } });
1000
+
1001
+ class TngTooltip {
1002
+ triggerEl;
1003
+ /**
1004
+ * Option B: projected template support
1005
+ * Usage:
1006
+ * <tng-tooltip>
1007
+ * <ng-template #tooltipContent>...</ng-template>
1008
+ * <button>...</button>
1009
+ * </tng-tooltip>
1010
+ */
1011
+ projectedTpl;
1012
+ /** Option A: explicit input template (kept for flexibility) */
1013
+ contentTpl = input(null, ...(ngDevMode ? [{ debugName: "contentTpl" }] : []));
1014
+ /** Simple text tooltip */
1015
+ text = input('', ...(ngDevMode ? [{ debugName: "text" }] : []));
1016
+ /** Behavior */
1017
+ placement = input('top-start', ...(ngDevMode ? [{ debugName: "placement" }] : []));
1018
+ offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : []));
1019
+ showDelay = input(250, ...(ngDevMode ? [{ debugName: "showDelay" }] : []));
1020
+ hideDelay = input(100, ...(ngDevMode ? [{ debugName: "hideDelay" }] : []));
1021
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1022
+ /** Klass */
1023
+ panelKlass = input('px-3 py-2 text-xs text-foreground', ...(ngDevMode ? [{ debugName: "panelKlass" }] : []));
1024
+ surfaceKlass = input('rounded-md border border-border bg-bg shadow-md', ...(ngDevMode ? [{ debugName: "surfaceKlass" }] : []));
1025
+ /** Events */
1026
+ opened = output();
1027
+ closed = output();
1028
+ open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : []));
1029
+ showTimer = null;
1030
+ hideTimer = null;
1031
+ /** Resolved template (Option A overrides Option B) */
1032
+ tpl = computed(() => this.contentTpl() ?? this.projectedTpl ?? null, ...(ngDevMode ? [{ debugName: "tpl" }] : []));
1033
+ hasContent = computed(() => !!this.tpl() || !!this.text(), ...(ngDevMode ? [{ debugName: "hasContent" }] : []));
1034
+ clearTimers() {
1035
+ if (this.showTimer != null) {
1036
+ window.clearTimeout(this.showTimer);
1037
+ this.showTimer = null;
1038
+ }
1039
+ if (this.hideTimer != null) {
1040
+ window.clearTimeout(this.hideTimer);
1041
+ this.hideTimer = null;
1042
+ }
1043
+ }
1044
+ requestOpen() {
1045
+ if (this.disabled())
1046
+ return;
1047
+ if (!this.hasContent())
1048
+ return;
1049
+ this.clearTimers();
1050
+ const delay = Math.max(0, this.showDelay());
1051
+ this.showTimer = window.setTimeout(() => {
1052
+ this.open.set(true);
1053
+ this.opened.emit();
1054
+ }, delay);
1055
+ }
1056
+ requestClose(reason) {
1057
+ this.clearTimers();
1058
+ const delay = Math.max(0, this.hideDelay());
1059
+ this.hideTimer = window.setTimeout(() => {
1060
+ if (!this.open())
1061
+ return;
1062
+ this.open.set(false);
1063
+ this.closed.emit(reason);
1064
+ }, delay);
1065
+ }
1066
+ onMouseEnter() {
1067
+ this.requestOpen();
1068
+ }
1069
+ onMouseLeave() {
1070
+ this.requestClose('programmatic');
1071
+ }
1072
+ onFocusIn() {
1073
+ this.requestOpen();
1074
+ }
1075
+ onFocusOut() {
1076
+ this.requestClose('blur');
1077
+ }
1078
+ onDocKeydown(ev) {
1079
+ if (!this.open())
1080
+ return;
1081
+ if (ev.key === 'Escape') {
1082
+ ev.preventDefault();
1083
+ this.requestClose('escape');
1084
+ }
1085
+ }
1086
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngTooltip, deps: [], target: i0.ɵɵFactoryTarget.Component });
1087
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngTooltip, isStandalone: true, selector: "tng-tooltip", inputs: { contentTpl: { classPropertyName: "contentTpl", publicName: "contentTpl", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, showDelay: { classPropertyName: "showDelay", publicName: "showDelay", isSignal: true, isRequired: false, transformFunction: null }, hideDelay: { classPropertyName: "hideDelay", publicName: "hideDelay", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, panelKlass: { classPropertyName: "panelKlass", publicName: "panelKlass", isSignal: true, isRequired: false, transformFunction: null }, surfaceKlass: { classPropertyName: "surfaceKlass", publicName: "surfaceKlass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "opened", closed: "closed" }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focusin": "onFocusIn()", "focusout": "onFocusOut()", "document:keydown": "onDocKeydown($event)" } }, queries: [{ propertyName: "projectedTpl", first: true, predicate: ["tooltipContent"], descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "triggerEl", first: true, predicate: ["triggerEl"], descendants: true, static: true }], ngImport: i0, template: "<span #triggerEl class=\"inline-flex\">\n <ng-content></ng-content>\n</span>\n\n<tng-connected-overlay\n [open]=\"open()\"\n [anchor]=\"triggerEl\"\n [placement]=\"placement()\"\n [offset]=\"offset()\"\n [width]=\"'anchor'\"\n [closeOnOutsideClick]=\"false\"\n [closeOnEscape]=\"false\"\n>\n <tng-overlay-panel [klass]=\"surfaceKlass() + ' ' + panelKlass()\">\n @if (tpl()) {\n <ng-container [ngTemplateOutlet]=\"tpl()\"></ng-container>\n } @else {\n {{ text() }}\n }\n </tng-overlay-panel>\n</tng-connected-overlay>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: TngConnectedOverlay, selector: "tng-connected-overlay", inputs: ["open", "anchor", "placement", "offset", "width", "closeOnOutsideClick", "closeOnInsideClick", "closeOnEscape", "hasBackdrop", "backdropClass"], outputs: ["opened", "closed", "backdropClick"] }, { kind: "component", type: TngOverlayPanel, selector: "tng-overlay-panel", inputs: ["klass", "modal", "role", "ariaLabel", "ariaLabelledby", "ariaDescribedby", "restoreFocus", "autoCapture", "deferCaptureElements"] }] });
1088
+ }
1089
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngTooltip, decorators: [{
1090
+ type: Component,
1091
+ args: [{ selector: 'tng-tooltip', standalone: true, imports: [NgTemplateOutlet, TngConnectedOverlay, TngOverlayPanel], template: "<span #triggerEl class=\"inline-flex\">\n <ng-content></ng-content>\n</span>\n\n<tng-connected-overlay\n [open]=\"open()\"\n [anchor]=\"triggerEl\"\n [placement]=\"placement()\"\n [offset]=\"offset()\"\n [width]=\"'anchor'\"\n [closeOnOutsideClick]=\"false\"\n [closeOnEscape]=\"false\"\n>\n <tng-overlay-panel [klass]=\"surfaceKlass() + ' ' + panelKlass()\">\n @if (tpl()) {\n <ng-container [ngTemplateOutlet]=\"tpl()\"></ng-container>\n } @else {\n {{ text() }}\n }\n </tng-overlay-panel>\n</tng-connected-overlay>\n" }]
1092
+ }], propDecorators: { triggerEl: [{
1093
+ type: ViewChild,
1094
+ args: ['triggerEl', { static: true }]
1095
+ }], projectedTpl: [{
1096
+ type: ContentChild,
1097
+ args: ['tooltipContent', { read: TemplateRef }]
1098
+ }], contentTpl: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentTpl", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], showDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDelay", required: false }] }], hideDelay: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideDelay", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], panelKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelKlass", required: false }] }], surfaceKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "surfaceKlass", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], onMouseEnter: [{
1099
+ type: HostListener,
1100
+ args: ['mouseenter']
1101
+ }], onMouseLeave: [{
1102
+ type: HostListener,
1103
+ args: ['mouseleave']
1104
+ }], onFocusIn: [{
1105
+ type: HostListener,
1106
+ args: ['focusin']
1107
+ }], onFocusOut: [{
1108
+ type: HostListener,
1109
+ args: ['focusout']
1110
+ }], onDocKeydown: [{
1111
+ type: HostListener,
1112
+ args: ['document:keydown', ['$event']]
1113
+ }] } });
18
1114
 
19
1115
  /**
20
1116
  * Generated bundle index. Do not edit.
21
1117
  */
1118
+
1119
+ export { TngConnectedOverlay, TngDialog, TngDialogInitialFocus, TngOptionList, TngOverlayPanel, TngOverlayRef, TngPopover, TngSnackbarHost, TngTooltip };
22
1120
  //# sourceMappingURL=tociva-tailng-ui-popups-overlays.mjs.map