@tailng-ui/primitives 0.1.0 → 0.11.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.
Files changed (99) hide show
  1. package/package.json +9 -3
  2. package/src/index.d.ts +5 -0
  3. package/src/index.d.ts.map +1 -1
  4. package/src/index.js +5 -0
  5. package/src/index.js.map +1 -1
  6. package/src/lib/feedback/progress-bar/tng-progress-bar.d.ts.map +1 -1
  7. package/src/lib/feedback/progress-bar/tng-progress-bar.js +2 -1
  8. package/src/lib/feedback/progress-bar/tng-progress-bar.js.map +1 -1
  9. package/src/lib/feedback/progress-spinner/tng-progress-spinner.d.ts.map +1 -1
  10. package/src/lib/feedback/progress-spinner/tng-progress-spinner.js +2 -1
  11. package/src/lib/feedback/progress-spinner/tng-progress-spinner.js.map +1 -1
  12. package/src/lib/feedback/skeleton/tng-skeleton.d.ts +1 -0
  13. package/src/lib/feedback/skeleton/tng-skeleton.d.ts.map +1 -1
  14. package/src/lib/feedback/skeleton/tng-skeleton.js +4 -0
  15. package/src/lib/feedback/skeleton/tng-skeleton.js.map +1 -1
  16. package/src/lib/form/chips/tng-chips.d.ts +53 -1
  17. package/src/lib/form/chips/tng-chips.d.ts.map +1 -1
  18. package/src/lib/form/chips/tng-chips.js +281 -1
  19. package/src/lib/form/chips/tng-chips.js.map +1 -1
  20. package/src/lib/form/input-otp/tng-input-otp.d.ts +22 -0
  21. package/src/lib/form/input-otp/tng-input-otp.d.ts.map +1 -1
  22. package/src/lib/form/input-otp/tng-input-otp.js +105 -1
  23. package/src/lib/form/input-otp/tng-input-otp.js.map +1 -1
  24. package/src/lib/form/label/tng-label.d.ts +2 -0
  25. package/src/lib/form/label/tng-label.d.ts.map +1 -1
  26. package/src/lib/form/label/tng-label.js +9 -0
  27. package/src/lib/form/label/tng-label.js.map +1 -1
  28. package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.trigger.d.ts +1 -0
  29. package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.trigger.d.ts.map +1 -1
  30. package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.trigger.js +26 -0
  31. package/src/lib/form/multi-autocomplete/tng-multi-autocomplete.trigger.js.map +1 -1
  32. package/src/lib/form/radio/tng-radio.d.ts +26 -0
  33. package/src/lib/form/radio/tng-radio.d.ts.map +1 -1
  34. package/src/lib/form/radio/tng-radio.js +129 -1
  35. package/src/lib/form/radio/tng-radio.js.map +1 -1
  36. package/src/lib/form/textarea/tng-textarea.d.ts +6 -14
  37. package/src/lib/form/textarea/tng-textarea.d.ts.map +1 -1
  38. package/src/lib/form/textarea/tng-textarea.js +42 -85
  39. package/src/lib/form/textarea/tng-textarea.js.map +1 -1
  40. package/src/lib/layout/accordion/tng-accordion.d.ts +9 -0
  41. package/src/lib/layout/accordion/tng-accordion.d.ts.map +1 -1
  42. package/src/lib/layout/accordion/tng-accordion.js +113 -0
  43. package/src/lib/layout/accordion/tng-accordion.js.map +1 -1
  44. package/src/lib/layout/tree/__tests__/tng-tree.test-harness.d.ts +20 -0
  45. package/src/lib/layout/tree/__tests__/tng-tree.test-harness.d.ts.map +1 -0
  46. package/src/lib/layout/tree/__tests__/tng-tree.test-harness.js +106 -0
  47. package/src/lib/layout/tree/__tests__/tng-tree.test-harness.js.map +1 -0
  48. package/src/lib/layout/tree/index.d.ts +5 -0
  49. package/src/lib/layout/tree/index.d.ts.map +1 -0
  50. package/src/lib/layout/tree/index.js +5 -0
  51. package/src/lib/layout/tree/index.js.map +1 -0
  52. package/src/lib/layout/tree/tng-tree-group.d.ts +10 -0
  53. package/src/lib/layout/tree/tng-tree-group.d.ts.map +1 -0
  54. package/src/lib/layout/tree/tng-tree-group.js +29 -0
  55. package/src/lib/layout/tree/tng-tree-group.js.map +1 -0
  56. package/src/lib/layout/tree/tng-tree-indicator.d.ts +8 -0
  57. package/src/lib/layout/tree/tng-tree-indicator.d.ts.map +1 -0
  58. package/src/lib/layout/tree/tng-tree-indicator.js +36 -0
  59. package/src/lib/layout/tree/tng-tree-indicator.js.map +1 -0
  60. package/src/lib/layout/tree/tng-tree-item.d.ts +36 -0
  61. package/src/lib/layout/tree/tng-tree-item.d.ts.map +1 -0
  62. package/src/lib/layout/tree/tng-tree-item.js +139 -0
  63. package/src/lib/layout/tree/tng-tree-item.js.map +1 -0
  64. package/src/lib/layout/tree/tng-tree.d.ts +64 -1
  65. package/src/lib/layout/tree/tng-tree.d.ts.map +1 -1
  66. package/src/lib/layout/tree/tng-tree.js +536 -1
  67. package/src/lib/layout/tree/tng-tree.js.map +1 -1
  68. package/src/lib/layout/tree/tng-tree.transforms.d.ts +10 -0
  69. package/src/lib/layout/tree/tng-tree.transforms.d.ts.map +1 -0
  70. package/src/lib/layout/tree/tng-tree.transforms.js +46 -0
  71. package/src/lib/layout/tree/tng-tree.transforms.js.map +1 -0
  72. package/src/lib/overlay/dialog/tng-dialog.d.ts +158 -0
  73. package/src/lib/overlay/dialog/tng-dialog.d.ts.map +1 -0
  74. package/src/lib/overlay/dialog/tng-dialog.js +854 -0
  75. package/src/lib/overlay/dialog/tng-dialog.js.map +1 -0
  76. package/src/lib/overlay/popover/tng-popover.d.ts +121 -0
  77. package/src/lib/overlay/popover/tng-popover.d.ts.map +1 -0
  78. package/src/lib/overlay/popover/tng-popover.js +614 -0
  79. package/src/lib/overlay/popover/tng-popover.js.map +1 -0
  80. package/src/lib/overlay/tng-overlay-runtime.d.ts +11 -0
  81. package/src/lib/overlay/tng-overlay-runtime.d.ts.map +1 -0
  82. package/src/lib/overlay/tng-overlay-runtime.js +6 -0
  83. package/src/lib/overlay/tng-overlay-runtime.js.map +1 -0
  84. package/src/lib/overlay/tooltip/tng-tooltip.d.ts +92 -3
  85. package/src/lib/overlay/tooltip/tng-tooltip.d.ts.map +1 -1
  86. package/src/lib/overlay/tooltip/tng-tooltip.js +474 -3
  87. package/src/lib/overlay/tooltip/tng-tooltip.js.map +1 -1
  88. package/src/lib/utility/badge/tng-badge.d.ts +6 -0
  89. package/src/lib/utility/badge/tng-badge.d.ts.map +1 -1
  90. package/src/lib/utility/badge/tng-badge.js +65 -0
  91. package/src/lib/utility/badge/tng-badge.js.map +1 -1
  92. package/src/lib/utility/copy/tng-copy.d.ts +21 -10
  93. package/src/lib/utility/copy/tng-copy.d.ts.map +1 -1
  94. package/src/lib/utility/copy/tng-copy.js +117 -88
  95. package/src/lib/utility/copy/tng-copy.js.map +1 -1
  96. package/src/lib/utility/tag/tng-tag.d.ts +37 -0
  97. package/src/lib/utility/tag/tng-tag.d.ts.map +1 -1
  98. package/src/lib/utility/tag/tng-tag.js +195 -1
  99. package/src/lib/utility/tag/tng-tag.js.map +1 -1
@@ -0,0 +1,854 @@
1
+ import { __decorate } from "tslib";
2
+ import { Directive, ElementRef, HostBinding, HostListener, afterNextRender, booleanAttribute, effect, inject, Injector, input, output, signal, } from '@angular/core';
3
+ import { createOverlayFocusHandoffController, createModalIsolationManager, createOverlayScrollLockManager, resolveFirstFocusableElement, resolveFocusableElements, createTngIdFactory, } from '@tailng-ui/cdk';
4
+ import { tngPrimitiveOverlayRuntime } from '../tng-overlay-runtime';
5
+ const createDialogId = createTngIdFactory('tng-dialog');
6
+ const createDialogPanelId = createTngIdFactory('tng-dialog-panel');
7
+ const createDialogTitleId = createTngIdFactory('tng-dialog-title');
8
+ const createDialogDescriptionId = createTngIdFactory('tng-dialog-description');
9
+ const createDialogFocusableId = createTngIdFactory('tng-dialog-focusable');
10
+ function normalizeOptionalBooleanInput(value) {
11
+ if (value === null || value === undefined) {
12
+ return undefined;
13
+ }
14
+ return booleanAttribute(value);
15
+ }
16
+ function toScrollLockDocument(documentRef) {
17
+ if (!(documentRef instanceof Document)) {
18
+ return null;
19
+ }
20
+ return documentRef;
21
+ }
22
+ function toModalIsolationDocument(documentRef) {
23
+ if (!(documentRef instanceof Document)) {
24
+ return null;
25
+ }
26
+ return documentRef;
27
+ }
28
+ function toModalIsolationElement(elementRef) {
29
+ if (!(elementRef instanceof HTMLElement)) {
30
+ return null;
31
+ }
32
+ return elementRef;
33
+ }
34
+ function mapOverlayDismissReason(reason) {
35
+ if (reason === 'escape-key') {
36
+ return 'escape';
37
+ }
38
+ if (reason === 'outside-pointer') {
39
+ return 'backdrop';
40
+ }
41
+ return 'programmatic';
42
+ }
43
+ const dialogGlobalDocument = typeof document === 'undefined' ? null : document;
44
+ const dialogModalIsolation = createModalIsolationManager({
45
+ documentRef: toModalIsolationDocument(dialogGlobalDocument),
46
+ });
47
+ const dialogFocusHandoff = createOverlayFocusHandoffController();
48
+ export function resolveTngDialogActiveElement(documentRef) {
49
+ if (!(documentRef instanceof Document)) {
50
+ return null;
51
+ }
52
+ const activeElement = documentRef.activeElement;
53
+ return activeElement instanceof HTMLElement ? activeElement : null;
54
+ }
55
+ export function resolveTngDialogFocusableElements(container) {
56
+ return resolveFocusableElements(container);
57
+ }
58
+ export function resolveTngDialogMarkedInitialElement(container) {
59
+ if (!(container instanceof HTMLElement)) {
60
+ return null;
61
+ }
62
+ const markedInitial = container.querySelector('[data-tng-dialog-initial-focus]');
63
+ if (markedInitial === null) {
64
+ return null;
65
+ }
66
+ if (resolveTngDialogFocusableElements(container).includes(markedInitial)) {
67
+ return markedInitial;
68
+ }
69
+ return resolveTngDialogFocusableElements(markedInitial)[0] ?? null;
70
+ }
71
+ function readKeyboardEvent(event) {
72
+ return event instanceof KeyboardEvent ? event : null;
73
+ }
74
+ function resolveFirstFocusableWithin(container) {
75
+ return resolveFirstFocusableElement(container);
76
+ }
77
+ function isDialogCloseReason(value) {
78
+ return value === 'backdrop' || value === 'close-button' || value === 'escape' || value === 'programmatic';
79
+ }
80
+ let TngDialog = class TngDialog {
81
+ openInput = input(undefined, {
82
+ alias: 'open',
83
+ transform: normalizeOptionalBooleanInput,
84
+ });
85
+ defaultOpen = input(false, {
86
+ transform: booleanAttribute,
87
+ });
88
+ disabled = input(false, {
89
+ transform: booleanAttribute,
90
+ });
91
+ dismissible = input(true, {
92
+ transform: booleanAttribute,
93
+ });
94
+ closeOnBackdropClick = input(true, {
95
+ transform: booleanAttribute,
96
+ });
97
+ closeOnEscape = input(true, {
98
+ transform: booleanAttribute,
99
+ });
100
+ restoreFocus = input(true, {
101
+ transform: booleanAttribute,
102
+ });
103
+ autoFocus = input('first-focusable', {
104
+ transform: (value) => {
105
+ return value === 'none' || value === 'dialog' || value === 'first-focusable'
106
+ ? value
107
+ : 'first-focusable';
108
+ },
109
+ });
110
+ trapFocus = input(true, {
111
+ transform: booleanAttribute,
112
+ });
113
+ lockScroll = input(true, {
114
+ transform: booleanAttribute,
115
+ });
116
+ ariaLabel = input(null);
117
+ ariaLabelledby = input(null);
118
+ ariaDescribedby = input(null);
119
+ size = input('md');
120
+ openChange = output();
121
+ closed = output();
122
+ hostRef = inject(ElementRef);
123
+ documentRef = dialogGlobalDocument;
124
+ injector = inject(Injector);
125
+ instanceId = createDialogId();
126
+ scrollLock = createOverlayScrollLockManager({
127
+ documentRef: toScrollLockDocument(this.documentRef),
128
+ });
129
+ uncontrolledOpen = signal(false);
130
+ openStateEffect = effect(() => {
131
+ if (!this.initialized) {
132
+ return;
133
+ }
134
+ if (this.isOpen()) {
135
+ this.activateDialog();
136
+ return;
137
+ }
138
+ this.deactivateDialog();
139
+ });
140
+ initialized = false;
141
+ isActive = false;
142
+ isFocusLayerRegistered = false;
143
+ isOverlayLayerRegistered = false;
144
+ panelElement = null;
145
+ panelElementId = null;
146
+ registeredTitleId = null;
147
+ registeredDescriptionId = null;
148
+ dataSlot = 'dialog';
149
+ get dataOpenAttr() {
150
+ return this.isOpen() ? 'true' : 'false';
151
+ }
152
+ get dataStateAttr() {
153
+ return this.isOpen() ? 'open' : 'closed';
154
+ }
155
+ get dataDisabledAttr() {
156
+ return this.disabled() ? '' : null;
157
+ }
158
+ get dataSizeAttr() {
159
+ return this.size();
160
+ }
161
+ ngOnInit() {
162
+ if (!this.isControlled()) {
163
+ this.uncontrolledOpen.set(this.defaultOpen());
164
+ }
165
+ this.initialized = true;
166
+ if (this.isOpen()) {
167
+ this.activateDialog();
168
+ }
169
+ }
170
+ ngOnDestroy() {
171
+ this.openStateEffect.destroy();
172
+ this.deactivateDialog();
173
+ this.unregisterFocusLayer();
174
+ }
175
+ isOpen() {
176
+ return this.openInput() ?? this.uncontrolledOpen();
177
+ }
178
+ openDialog() {
179
+ if (this.disabled()) {
180
+ return;
181
+ }
182
+ this.setOpenState(true);
183
+ }
184
+ closeDialog(reason = 'programmatic') {
185
+ this.requestClose(reason);
186
+ }
187
+ toggleDialog(force) {
188
+ if (force !== undefined) {
189
+ if (force) {
190
+ this.openDialog();
191
+ return;
192
+ }
193
+ this.closeDialog('programmatic');
194
+ return;
195
+ }
196
+ if (this.isOpen()) {
197
+ this.closeDialog('programmatic');
198
+ return;
199
+ }
200
+ this.openDialog();
201
+ }
202
+ requestClose(reason) {
203
+ if (!this.isOpen()) {
204
+ return;
205
+ }
206
+ this.closed.emit(reason);
207
+ this.setOpenState(false);
208
+ }
209
+ registerPanel(panel, id) {
210
+ this.panelElement = panel;
211
+ this.panelElementId = id;
212
+ }
213
+ unregisterPanel(panel) {
214
+ if (this.panelElement === panel) {
215
+ this.panelElement = null;
216
+ this.panelElementId = null;
217
+ }
218
+ }
219
+ registerTitle(id) {
220
+ this.registeredTitleId = id;
221
+ }
222
+ unregisterTitle(id) {
223
+ if (this.registeredTitleId === id) {
224
+ this.registeredTitleId = null;
225
+ }
226
+ }
227
+ registerDescription(id) {
228
+ this.registeredDescriptionId = id;
229
+ }
230
+ unregisterDescription(id) {
231
+ if (this.registeredDescriptionId === id) {
232
+ this.registeredDescriptionId = null;
233
+ }
234
+ }
235
+ getPanelId() {
236
+ return this.panelElementId;
237
+ }
238
+ resolveAriaLabelledby() {
239
+ const explicit = this.ariaLabelledby()?.trim() ?? '';
240
+ if (explicit.length > 0) {
241
+ return explicit;
242
+ }
243
+ return this.registeredTitleId;
244
+ }
245
+ resolveAriaDescribedby() {
246
+ const explicit = this.ariaDescribedby()?.trim() ?? '';
247
+ if (explicit.length > 0) {
248
+ return explicit;
249
+ }
250
+ return this.registeredDescriptionId;
251
+ }
252
+ onBackdropPointerDown(event) {
253
+ if (!this.shouldCloseFromBackdrop()) {
254
+ return;
255
+ }
256
+ if (!(event.currentTarget instanceof HTMLElement)) {
257
+ return;
258
+ }
259
+ if (event.target !== event.currentTarget) {
260
+ return;
261
+ }
262
+ event.preventDefault();
263
+ this.requestClose('backdrop');
264
+ }
265
+ trapTabNavigation(event) {
266
+ if (!this.trapFocus()) {
267
+ return;
268
+ }
269
+ const keyboardEvent = readKeyboardEvent(event);
270
+ if (keyboardEvent === null || keyboardEvent.key !== 'Tab') {
271
+ return;
272
+ }
273
+ const panel = this.panelElement;
274
+ if (panel === null) {
275
+ return;
276
+ }
277
+ if (!dialogFocusHandoff.isTrapActive(this.instanceId)) {
278
+ return;
279
+ }
280
+ const focusableMemberIds = this.resolveFocusableMemberIds(panel);
281
+ const firstMemberId = focusableMemberIds[0];
282
+ if (firstMemberId === undefined) {
283
+ return;
284
+ }
285
+ const lastMemberId = focusableMemberIds[focusableMemberIds.length - 1] ?? firstMemberId;
286
+ const activeElement = resolveTngDialogActiveElement(this.documentRef);
287
+ const activeElementId = activeElement === null ? null : this.ensureElementId(activeElement);
288
+ let candidateId = null;
289
+ if (activeElement === null || !panel.contains(activeElement)) {
290
+ candidateId = keyboardEvent.shiftKey ? lastMemberId : firstMemberId;
291
+ }
292
+ else if (activeElementId === firstMemberId && keyboardEvent.shiftKey) {
293
+ candidateId = lastMemberId;
294
+ }
295
+ else if (activeElementId === lastMemberId && !keyboardEvent.shiftKey) {
296
+ candidateId = firstMemberId;
297
+ }
298
+ else {
299
+ if (activeElementId !== null) {
300
+ dialogFocusHandoff.recordFocus(this.instanceId, activeElementId);
301
+ }
302
+ return;
303
+ }
304
+ const resolvedId = dialogFocusHandoff.resolveFocusCandidate(this.instanceId, candidateId);
305
+ if (resolvedId === null) {
306
+ return;
307
+ }
308
+ const nextFocusTarget = this.resolveElementById(resolvedId);
309
+ if (nextFocusTarget === null) {
310
+ return;
311
+ }
312
+ keyboardEvent.preventDefault();
313
+ nextFocusTarget.focus();
314
+ dialogFocusHandoff.recordFocus(this.instanceId, resolvedId);
315
+ }
316
+ recordFocusedElement(target) {
317
+ if (!(target instanceof HTMLElement) || !this.isOpen()) {
318
+ return;
319
+ }
320
+ const panel = this.panelElement;
321
+ if (panel === null || !panel.contains(target)) {
322
+ return;
323
+ }
324
+ const targetId = this.ensureElementId(target);
325
+ dialogFocusHandoff.recordFocus(this.instanceId, targetId);
326
+ }
327
+ activateDialog() {
328
+ if (this.isActive) {
329
+ return;
330
+ }
331
+ this.isActive = true;
332
+ if (this.lockScroll()) {
333
+ this.scrollLock.acquire(this.instanceId);
334
+ }
335
+ this.registerFocusLayer();
336
+ this.activateFocusLayer();
337
+ this.registerOverlayLayer();
338
+ this.activateModalIsolation();
339
+ afterNextRender(() => {
340
+ this.focusInitialElement();
341
+ }, { injector: this.injector });
342
+ }
343
+ deactivateDialog() {
344
+ if (!this.isActive) {
345
+ return;
346
+ }
347
+ this.isActive = false;
348
+ if (this.lockScroll()) {
349
+ this.scrollLock.release(this.instanceId);
350
+ }
351
+ this.deactivateFocusLayer();
352
+ this.unregisterFocusLayer();
353
+ this.unregisterOverlayLayer();
354
+ this.deactivateModalIsolation();
355
+ }
356
+ focusInitialElement() {
357
+ if (!this.isOpen()) {
358
+ return;
359
+ }
360
+ const panel = this.panelElement;
361
+ if (panel === null) {
362
+ return;
363
+ }
364
+ if (this.autoFocus() === 'none') {
365
+ return;
366
+ }
367
+ if (this.autoFocus() === 'dialog') {
368
+ panel.focus();
369
+ return;
370
+ }
371
+ const markedInitial = resolveTngDialogMarkedInitialElement(panel);
372
+ if (markedInitial !== null) {
373
+ markedInitial.focus();
374
+ return;
375
+ }
376
+ const firstFocusable = resolveFirstFocusableWithin(panel);
377
+ if (firstFocusable !== null) {
378
+ firstFocusable.focus();
379
+ return;
380
+ }
381
+ panel.focus();
382
+ }
383
+ isControlled() {
384
+ return this.openInput() !== undefined;
385
+ }
386
+ setOpenState(nextOpen) {
387
+ if (nextOpen && this.disabled()) {
388
+ return;
389
+ }
390
+ if (this.isOpen() === nextOpen) {
391
+ return;
392
+ }
393
+ if (!this.isControlled()) {
394
+ this.uncontrolledOpen.set(nextOpen);
395
+ }
396
+ this.openChange.emit(nextOpen);
397
+ }
398
+ shouldCloseFromBackdrop() {
399
+ return this.isOpen() && this.dismissible() && this.closeOnBackdropClick();
400
+ }
401
+ shouldCloseFromEscape() {
402
+ return this.isOpen() && this.dismissible() && this.closeOnEscape();
403
+ }
404
+ activateModalIsolation() {
405
+ const hostElement = toModalIsolationElement(this.hostRef.nativeElement);
406
+ if (hostElement === null) {
407
+ return;
408
+ }
409
+ dialogModalIsolation.activate(this.instanceId, hostElement);
410
+ }
411
+ deactivateModalIsolation() {
412
+ dialogModalIsolation.deactivate(this.instanceId);
413
+ }
414
+ registerOverlayLayer() {
415
+ const hostElement = this.hostRef.nativeElement;
416
+ tngPrimitiveOverlayRuntime.registerLayer({
417
+ containsTarget: (target, path) => {
418
+ if (target instanceof Node && hostElement.contains(target)) {
419
+ return true;
420
+ }
421
+ return path.includes(hostElement);
422
+ },
423
+ dismissOnEscape: this.shouldCloseFromEscape(),
424
+ dismissOnOutsidePointer: false,
425
+ id: this.instanceId,
426
+ modal: true,
427
+ onDismiss: (reason) => {
428
+ if (reason === 'escape-key' && !this.shouldCloseFromEscape()) {
429
+ return;
430
+ }
431
+ if (reason === 'outside-pointer' && !this.shouldCloseFromBackdrop()) {
432
+ return;
433
+ }
434
+ this.requestClose(mapOverlayDismissReason(reason));
435
+ },
436
+ });
437
+ this.isOverlayLayerRegistered = true;
438
+ }
439
+ unregisterOverlayLayer() {
440
+ if (!this.isOverlayLayerRegistered) {
441
+ return;
442
+ }
443
+ tngPrimitiveOverlayRuntime.unregisterLayer(this.instanceId);
444
+ this.isOverlayLayerRegistered = false;
445
+ }
446
+ activateFocusLayer() {
447
+ const activeElement = resolveTngDialogActiveElement(this.documentRef);
448
+ const restoreFocusTargetId = activeElement === null ? null : this.ensureElementId(activeElement);
449
+ dialogFocusHandoff.activateLayer(this.instanceId, restoreFocusTargetId);
450
+ }
451
+ deactivateFocusLayer() {
452
+ const restoreFocusTargetId = dialogFocusHandoff.deactivateLayer(this.instanceId);
453
+ if (!this.restoreFocus() || restoreFocusTargetId === null) {
454
+ return;
455
+ }
456
+ const restoreFocusTarget = this.resolveElementById(restoreFocusTargetId);
457
+ restoreFocusTarget?.focus();
458
+ }
459
+ registerFocusLayer() {
460
+ if (this.isFocusLayerRegistered) {
461
+ return;
462
+ }
463
+ dialogFocusHandoff.registerLayer({
464
+ layerId: this.instanceId,
465
+ members: () => {
466
+ const panel = this.panelElement;
467
+ if (panel === null) {
468
+ return [];
469
+ }
470
+ return this.resolveFocusableMemberIds(panel);
471
+ },
472
+ restoreFocus: this.restoreFocus(),
473
+ trapFocus: this.trapFocus(),
474
+ });
475
+ this.isFocusLayerRegistered = true;
476
+ }
477
+ unregisterFocusLayer() {
478
+ if (!this.isFocusLayerRegistered) {
479
+ return;
480
+ }
481
+ dialogFocusHandoff.unregisterLayer(this.instanceId);
482
+ this.isFocusLayerRegistered = false;
483
+ }
484
+ resolveFocusableMemberIds(panel) {
485
+ const focusableElements = resolveTngDialogFocusableElements(panel);
486
+ if (focusableElements.length === 0) {
487
+ return [];
488
+ }
489
+ return focusableElements.map((element) => this.ensureElementId(element));
490
+ }
491
+ ensureElementId(element) {
492
+ const existingId = element.id.trim();
493
+ if (existingId.length > 0) {
494
+ return existingId;
495
+ }
496
+ const generatedId = createDialogFocusableId();
497
+ element.id = generatedId;
498
+ return generatedId;
499
+ }
500
+ resolveElementById(id) {
501
+ if (this.documentRef === null) {
502
+ return null;
503
+ }
504
+ const element = this.documentRef.getElementById(id);
505
+ return element instanceof HTMLElement ? element : null;
506
+ }
507
+ };
508
+ __decorate([
509
+ HostBinding('attr.data-slot')
510
+ ], TngDialog.prototype, "dataSlot", void 0);
511
+ __decorate([
512
+ HostBinding('attr.data-open')
513
+ ], TngDialog.prototype, "dataOpenAttr", null);
514
+ __decorate([
515
+ HostBinding('attr.data-state')
516
+ ], TngDialog.prototype, "dataStateAttr", null);
517
+ __decorate([
518
+ HostBinding('attr.data-disabled')
519
+ ], TngDialog.prototype, "dataDisabledAttr", null);
520
+ __decorate([
521
+ HostBinding('attr.data-size')
522
+ ], TngDialog.prototype, "dataSizeAttr", null);
523
+ TngDialog = __decorate([
524
+ Directive({
525
+ selector: '[tngDialog]',
526
+ exportAs: 'tngDialog',
527
+ standalone: true,
528
+ })
529
+ ], TngDialog);
530
+ export { TngDialog };
531
+ let TngDialogPanel = class TngDialogPanel {
532
+ dialog = inject(TngDialog);
533
+ hostRef = inject(ElementRef);
534
+ resolvedId = this.hostRef.nativeElement.id.trim().length > 0
535
+ ? this.hostRef.nativeElement.id
536
+ : createDialogPanelId();
537
+ dataSlot = 'dialog-panel';
538
+ get dataOpenAttr() {
539
+ return this.dialog.isOpen() ? 'true' : 'false';
540
+ }
541
+ get dataSizeAttr() {
542
+ return this.dialog.size();
543
+ }
544
+ get dataStateAttr() {
545
+ return this.dialog.isOpen() ? 'open' : 'closed';
546
+ }
547
+ get hiddenAttr() {
548
+ return this.dialog.isOpen() ? null : '';
549
+ }
550
+ get idAttr() {
551
+ return this.resolvedId;
552
+ }
553
+ roleAttr = 'dialog';
554
+ tabindexAttr = '-1';
555
+ get ariaDescribedByAttr() {
556
+ return this.dialog.resolveAriaDescribedby();
557
+ }
558
+ get ariaLabelAttr() {
559
+ const label = this.dialog.ariaLabel()?.trim() ?? '';
560
+ return label.length > 0 ? label : null;
561
+ }
562
+ get ariaLabelledByAttr() {
563
+ return this.dialog.resolveAriaLabelledby();
564
+ }
565
+ get ariaModalAttr() {
566
+ return this.dialog.isOpen() ? 'true' : null;
567
+ }
568
+ ngOnInit() {
569
+ this.dialog.registerPanel(this.hostRef.nativeElement, this.resolvedId);
570
+ }
571
+ ngOnDestroy() {
572
+ this.dialog.unregisterPanel(this.hostRef.nativeElement);
573
+ }
574
+ onKeydown(event) {
575
+ if (event.key === 'Tab') {
576
+ this.dialog.trapTabNavigation(event);
577
+ }
578
+ }
579
+ onFocusIn(target) {
580
+ this.dialog.recordFocusedElement(target);
581
+ }
582
+ };
583
+ __decorate([
584
+ HostBinding('attr.data-slot')
585
+ ], TngDialogPanel.prototype, "dataSlot", void 0);
586
+ __decorate([
587
+ HostBinding('attr.data-open')
588
+ ], TngDialogPanel.prototype, "dataOpenAttr", null);
589
+ __decorate([
590
+ HostBinding('attr.data-size')
591
+ ], TngDialogPanel.prototype, "dataSizeAttr", null);
592
+ __decorate([
593
+ HostBinding('attr.data-state')
594
+ ], TngDialogPanel.prototype, "dataStateAttr", null);
595
+ __decorate([
596
+ HostBinding('attr.hidden')
597
+ ], TngDialogPanel.prototype, "hiddenAttr", null);
598
+ __decorate([
599
+ HostBinding('attr.id')
600
+ ], TngDialogPanel.prototype, "idAttr", null);
601
+ __decorate([
602
+ HostBinding('attr.role')
603
+ ], TngDialogPanel.prototype, "roleAttr", void 0);
604
+ __decorate([
605
+ HostBinding('attr.tabindex')
606
+ ], TngDialogPanel.prototype, "tabindexAttr", void 0);
607
+ __decorate([
608
+ HostBinding('attr.aria-describedby')
609
+ ], TngDialogPanel.prototype, "ariaDescribedByAttr", null);
610
+ __decorate([
611
+ HostBinding('attr.aria-label')
612
+ ], TngDialogPanel.prototype, "ariaLabelAttr", null);
613
+ __decorate([
614
+ HostBinding('attr.aria-labelledby')
615
+ ], TngDialogPanel.prototype, "ariaLabelledByAttr", null);
616
+ __decorate([
617
+ HostBinding('attr.aria-modal')
618
+ ], TngDialogPanel.prototype, "ariaModalAttr", null);
619
+ __decorate([
620
+ HostListener('keydown', ['$event'])
621
+ ], TngDialogPanel.prototype, "onKeydown", null);
622
+ __decorate([
623
+ HostListener('focusin', ['$event.target'])
624
+ ], TngDialogPanel.prototype, "onFocusIn", null);
625
+ TngDialogPanel = __decorate([
626
+ Directive({
627
+ selector: '[tngDialogPanel]',
628
+ exportAs: 'tngDialogPanel',
629
+ standalone: true,
630
+ })
631
+ ], TngDialogPanel);
632
+ export { TngDialogPanel };
633
+ let TngDialogBackdrop = class TngDialogBackdrop {
634
+ dialog = inject(TngDialog);
635
+ dataSlot = 'dialog-backdrop';
636
+ get dataOpenAttr() {
637
+ return this.dialog.isOpen() ? 'true' : 'false';
638
+ }
639
+ get dataStateAttr() {
640
+ return this.dialog.isOpen() ? 'open' : 'closed';
641
+ }
642
+ get hiddenAttr() {
643
+ return this.dialog.isOpen() ? null : '';
644
+ }
645
+ onPointerDown(event) {
646
+ this.dialog.onBackdropPointerDown(event);
647
+ }
648
+ };
649
+ __decorate([
650
+ HostBinding('attr.data-slot')
651
+ ], TngDialogBackdrop.prototype, "dataSlot", void 0);
652
+ __decorate([
653
+ HostBinding('attr.data-open')
654
+ ], TngDialogBackdrop.prototype, "dataOpenAttr", null);
655
+ __decorate([
656
+ HostBinding('attr.data-state')
657
+ ], TngDialogBackdrop.prototype, "dataStateAttr", null);
658
+ __decorate([
659
+ HostBinding('attr.hidden')
660
+ ], TngDialogBackdrop.prototype, "hiddenAttr", null);
661
+ __decorate([
662
+ HostListener('pointerdown', ['$event'])
663
+ ], TngDialogBackdrop.prototype, "onPointerDown", null);
664
+ TngDialogBackdrop = __decorate([
665
+ Directive({
666
+ selector: '[tngDialogBackdrop]',
667
+ exportAs: 'tngDialogBackdrop',
668
+ standalone: true,
669
+ })
670
+ ], TngDialogBackdrop);
671
+ export { TngDialogBackdrop };
672
+ let TngDialogTrigger = class TngDialogTrigger {
673
+ parentDialog = inject(TngDialog, { optional: true });
674
+ dialogFor = input(null, {
675
+ alias: 'tngDialogTrigger',
676
+ });
677
+ ariaHasPopupAttr = 'dialog';
678
+ get ariaControlsAttr() {
679
+ return this.resolveDialog()?.getPanelId() ?? null;
680
+ }
681
+ get ariaExpandedAttr() {
682
+ return this.resolveDialog()?.isOpen() ? 'true' : 'false';
683
+ }
684
+ dataSlot = 'dialog-trigger';
685
+ get dataOpenAttr() {
686
+ return this.resolveDialog()?.isOpen() ? 'true' : 'false';
687
+ }
688
+ get dataStateAttr() {
689
+ return this.resolveDialog()?.isOpen() ? 'open' : 'closed';
690
+ }
691
+ get dataDisabledAttr() {
692
+ return this.resolveDialog()?.disabled() ? '' : null;
693
+ }
694
+ onClick() {
695
+ this.resolveDialog()?.openDialog();
696
+ }
697
+ resolveDialog() {
698
+ return this.dialogFor() ?? this.parentDialog;
699
+ }
700
+ };
701
+ __decorate([
702
+ HostBinding('attr.aria-haspopup')
703
+ ], TngDialogTrigger.prototype, "ariaHasPopupAttr", void 0);
704
+ __decorate([
705
+ HostBinding('attr.aria-controls')
706
+ ], TngDialogTrigger.prototype, "ariaControlsAttr", null);
707
+ __decorate([
708
+ HostBinding('attr.aria-expanded')
709
+ ], TngDialogTrigger.prototype, "ariaExpandedAttr", null);
710
+ __decorate([
711
+ HostBinding('attr.data-slot')
712
+ ], TngDialogTrigger.prototype, "dataSlot", void 0);
713
+ __decorate([
714
+ HostBinding('attr.data-open')
715
+ ], TngDialogTrigger.prototype, "dataOpenAttr", null);
716
+ __decorate([
717
+ HostBinding('attr.data-state')
718
+ ], TngDialogTrigger.prototype, "dataStateAttr", null);
719
+ __decorate([
720
+ HostBinding('attr.data-disabled')
721
+ ], TngDialogTrigger.prototype, "dataDisabledAttr", null);
722
+ __decorate([
723
+ HostListener('click')
724
+ ], TngDialogTrigger.prototype, "onClick", null);
725
+ TngDialogTrigger = __decorate([
726
+ Directive({
727
+ selector: '[tngDialogTrigger]',
728
+ exportAs: 'tngDialogTrigger',
729
+ standalone: true,
730
+ })
731
+ ], TngDialogTrigger);
732
+ export { TngDialogTrigger };
733
+ let TngDialogTitle = class TngDialogTitle {
734
+ dialog = inject(TngDialog);
735
+ hostRef = inject(ElementRef);
736
+ resolvedId = this.hostRef.nativeElement.id.trim().length > 0
737
+ ? this.hostRef.nativeElement.id
738
+ : createDialogTitleId();
739
+ dataSlot = 'dialog-title';
740
+ get idAttr() {
741
+ return this.resolvedId;
742
+ }
743
+ ngOnInit() {
744
+ this.dialog.registerTitle(this.resolvedId);
745
+ }
746
+ ngOnDestroy() {
747
+ this.dialog.unregisterTitle(this.resolvedId);
748
+ }
749
+ };
750
+ __decorate([
751
+ HostBinding('attr.data-slot')
752
+ ], TngDialogTitle.prototype, "dataSlot", void 0);
753
+ __decorate([
754
+ HostBinding('attr.id')
755
+ ], TngDialogTitle.prototype, "idAttr", null);
756
+ TngDialogTitle = __decorate([
757
+ Directive({
758
+ selector: '[tngDialogTitle]',
759
+ exportAs: 'tngDialogTitle',
760
+ standalone: true,
761
+ })
762
+ ], TngDialogTitle);
763
+ export { TngDialogTitle };
764
+ let TngDialogDescription = class TngDialogDescription {
765
+ dialog = inject(TngDialog);
766
+ hostRef = inject(ElementRef);
767
+ resolvedId = this.hostRef.nativeElement.id.trim().length > 0
768
+ ? this.hostRef.nativeElement.id
769
+ : createDialogDescriptionId();
770
+ dataSlot = 'dialog-description';
771
+ get idAttr() {
772
+ return this.resolvedId;
773
+ }
774
+ ngOnInit() {
775
+ this.dialog.registerDescription(this.resolvedId);
776
+ }
777
+ ngOnDestroy() {
778
+ this.dialog.unregisterDescription(this.resolvedId);
779
+ }
780
+ };
781
+ __decorate([
782
+ HostBinding('attr.data-slot')
783
+ ], TngDialogDescription.prototype, "dataSlot", void 0);
784
+ __decorate([
785
+ HostBinding('attr.id')
786
+ ], TngDialogDescription.prototype, "idAttr", null);
787
+ TngDialogDescription = __decorate([
788
+ Directive({
789
+ selector: '[tngDialogDescription]',
790
+ exportAs: 'tngDialogDescription',
791
+ standalone: true,
792
+ })
793
+ ], TngDialogDescription);
794
+ export { TngDialogDescription };
795
+ let TngDialogContent = class TngDialogContent {
796
+ dataSlot = 'dialog-content';
797
+ };
798
+ __decorate([
799
+ HostBinding('attr.data-slot')
800
+ ], TngDialogContent.prototype, "dataSlot", void 0);
801
+ TngDialogContent = __decorate([
802
+ Directive({
803
+ selector: '[tngDialogContent]',
804
+ exportAs: 'tngDialogContent',
805
+ standalone: true,
806
+ })
807
+ ], TngDialogContent);
808
+ export { TngDialogContent };
809
+ let TngDialogActions = class TngDialogActions {
810
+ dataSlot = 'dialog-actions';
811
+ };
812
+ __decorate([
813
+ HostBinding('attr.data-slot')
814
+ ], TngDialogActions.prototype, "dataSlot", void 0);
815
+ TngDialogActions = __decorate([
816
+ Directive({
817
+ selector: '[tngDialogActions]',
818
+ exportAs: 'tngDialogActions',
819
+ standalone: true,
820
+ })
821
+ ], TngDialogActions);
822
+ export { TngDialogActions };
823
+ let TngDialogClose = class TngDialogClose {
824
+ dialog = inject(TngDialog);
825
+ closeReason = input(null, {
826
+ alias: 'tngDialogClose',
827
+ });
828
+ dataSlot = 'dialog-close';
829
+ onClick() {
830
+ this.dialog.requestClose(this.resolveCloseReason());
831
+ }
832
+ resolveCloseReason() {
833
+ const raw = this.closeReason()?.trim();
834
+ if (raw === undefined || raw.length === 0) {
835
+ return 'close-button';
836
+ }
837
+ return isDialogCloseReason(raw) ? raw : 'close-button';
838
+ }
839
+ };
840
+ __decorate([
841
+ HostBinding('attr.data-slot')
842
+ ], TngDialogClose.prototype, "dataSlot", void 0);
843
+ __decorate([
844
+ HostListener('click')
845
+ ], TngDialogClose.prototype, "onClick", null);
846
+ TngDialogClose = __decorate([
847
+ Directive({
848
+ selector: '[tngDialogClose]',
849
+ exportAs: 'tngDialogClose',
850
+ standalone: true,
851
+ })
852
+ ], TngDialogClose);
853
+ export { TngDialogClose };
854
+ //# sourceMappingURL=tng-dialog.js.map