@tailng-ui/components 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 (59) hide show
  1. package/package.json +9 -4
  2. package/src/lib/feedback/toast/tng-toast.component.d.ts +55 -6
  3. package/src/lib/feedback/toast/tng-toast.component.d.ts.map +1 -1
  4. package/src/lib/feedback/toast/tng-toast.component.js +234 -23
  5. package/src/lib/feedback/toast/tng-toast.component.js.map +1 -1
  6. package/src/lib/form/autocomplete/tng-autocomplete.component.js +1 -1
  7. package/src/lib/form/autocomplete/tng-autocomplete.component.js.map +1 -1
  8. package/src/lib/form/chips/tng-chips.component.d.ts +9 -1
  9. package/src/lib/form/chips/tng-chips.component.d.ts.map +1 -1
  10. package/src/lib/form/chips/tng-chips.component.js +39 -3
  11. package/src/lib/form/chips/tng-chips.component.js.map +1 -1
  12. package/src/lib/form/index.d.ts +2 -2
  13. package/src/lib/form/index.d.ts.map +1 -1
  14. package/src/lib/form/index.js +2 -2
  15. package/src/lib/form/index.js.map +1 -1
  16. package/src/lib/form/input-otp/index.d.ts +3 -0
  17. package/src/lib/form/input-otp/index.d.ts.map +1 -0
  18. package/src/lib/form/input-otp/index.js +3 -0
  19. package/src/lib/form/input-otp/index.js.map +1 -0
  20. package/src/lib/form/input-otp/tng-input-otp.component.d.ts +79 -7
  21. package/src/lib/form/input-otp/tng-input-otp.component.d.ts.map +1 -1
  22. package/src/lib/form/input-otp/tng-input-otp.component.js +441 -81
  23. package/src/lib/form/input-otp/tng-input-otp.component.js.map +1 -1
  24. package/src/lib/form/radio/tng-radio.component.d.ts +4 -0
  25. package/src/lib/form/radio/tng-radio.component.d.ts.map +1 -1
  26. package/src/lib/form/radio/tng-radio.component.js +15 -0
  27. package/src/lib/form/radio/tng-radio.component.js.map +1 -1
  28. package/src/lib/form/textarea/tng-textarea.component.d.ts +2 -0
  29. package/src/lib/form/textarea/tng-textarea.component.d.ts.map +1 -1
  30. package/src/lib/form/textarea/tng-textarea.component.js +7 -4
  31. package/src/lib/form/textarea/tng-textarea.component.js.map +1 -1
  32. package/src/lib/navigation/breadcrumb/tng-breadcrumb-item.component.d.ts +10 -0
  33. package/src/lib/navigation/breadcrumb/tng-breadcrumb-item.component.d.ts.map +1 -1
  34. package/src/lib/navigation/breadcrumb/tng-breadcrumb-item.component.js +52 -1
  35. package/src/lib/navigation/breadcrumb/tng-breadcrumb-item.component.js.map +1 -1
  36. package/src/lib/navigation/context-menu/tng-context-menu.component.d.ts +1 -0
  37. package/src/lib/navigation/context-menu/tng-context-menu.component.d.ts.map +1 -1
  38. package/src/lib/navigation/context-menu/tng-context-menu.component.js +20 -4
  39. package/src/lib/navigation/context-menu/tng-context-menu.component.js.map +1 -1
  40. package/src/lib/overlay/popover/tng-popover.component.d.ts +14 -19
  41. package/src/lib/overlay/popover/tng-popover.component.d.ts.map +1 -1
  42. package/src/lib/overlay/popover/tng-popover.component.js +24 -105
  43. package/src/lib/overlay/popover/tng-popover.component.js.map +1 -1
  44. package/src/lib/overlay/tng-overlay-focus-handoff.d.ts +10 -0
  45. package/src/lib/overlay/tng-overlay-focus-handoff.d.ts.map +1 -0
  46. package/src/lib/overlay/tng-overlay-focus-handoff.js +3 -0
  47. package/src/lib/overlay/tng-overlay-focus-handoff.js.map +1 -0
  48. package/src/lib/overlay/tng-overlay-runtime.d.ts +2 -2
  49. package/src/lib/overlay/tooltip/tng-tooltip.component.d.ts +1 -18
  50. package/src/lib/overlay/tooltip/tng-tooltip.component.d.ts.map +1 -1
  51. package/src/lib/overlay/tooltip/tng-tooltip.component.js +5 -85
  52. package/src/lib/overlay/tooltip/tng-tooltip.component.js.map +1 -1
  53. package/src/lib/utility/avatar/tng-avatar.component.d.ts.map +1 -1
  54. package/src/lib/utility/avatar/tng-avatar.component.js +8 -1
  55. package/src/lib/utility/avatar/tng-avatar.component.js.map +1 -1
  56. package/src/lib/utility/tag/tng-tag.component.d.ts +8 -0
  57. package/src/lib/utility/tag/tng-tag.component.d.ts.map +1 -1
  58. package/src/lib/utility/tag/tng-tag.component.js +16 -3
  59. package/src/lib/utility/tag/tng-tag.component.js.map +1 -1
package/package.json CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "name": "@tailng-ui/components",
3
- "version": "0.1.0",
3
+ "version": "0.11.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/tailng/tailng-ui"
7
+ },
8
+ "dependencies": {
9
+ "@tailng-ui/primitives": "^0.11.0"
10
+ },
4
11
  "peerDependencies": {
5
12
  "@angular/core": "^21.1.0",
6
- "@tailng-ui/cdk": "workspace:*",
7
- "@tailng-ui/primitives": "workspace:*",
8
13
  "tslib": "^2.3.0"
9
14
  },
10
15
  "sideEffects": false,
11
16
  "types": "./src/index.d.ts",
12
17
  "main": "./src/index.js",
13
18
  "type": "commonjs"
14
- }
19
+ }
@@ -1,39 +1,88 @@
1
1
  import type { OnDestroy } from '@angular/core';
2
+ import type { ElementRef } from '@angular/core';
2
3
  import type { TngToastTone } from '@tailng-ui/primitives';
3
4
  export type TngToastMode = 'snackbar' | 'toast';
4
5
  export type TngToastPosition = 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
6
+ export type TngToastDismissReason = 'escape' | 'manual' | 'timeout';
7
+ export type TngToastDismissEvent = Readonly<{
8
+ id: string;
9
+ reason: TngToastDismissReason;
10
+ }>;
11
+ export type TngToastAction = Readonly<{
12
+ dismissOnSelect: boolean;
13
+ label: string;
14
+ onSelect?: (id: string) => void;
15
+ }>;
16
+ export type TngToastActionOptions = Readonly<{
17
+ dismissOnSelect?: boolean;
18
+ label: string;
19
+ onSelect?: (id: string) => void;
20
+ }>;
5
21
  export type TngToastOptions = Readonly<Partial<{
22
+ action: TngToastActionOptions | null;
6
23
  duration: number;
7
24
  title: string | null;
8
25
  tone: TngToastTone;
9
26
  }>>;
10
- type TngToastKeyboardEvent = Readonly<Pick<KeyboardEvent, 'key'>> & Readonly<{
11
- preventDefault: () => void;
27
+ type TngToastRecord = Readonly<{
28
+ action: TngToastAction | null;
29
+ duration: number;
30
+ id: string;
31
+ message: string;
32
+ title: string | null;
33
+ tone: TngToastTone;
12
34
  }>;
13
35
  export declare function normalizeTngToastDuration(value: number, fallback: number): number;
14
36
  export declare function normalizeTngToastMaxVisible(value: number): number;
15
37
  export declare function resolveTngToastNextSlice<TValue>(values: readonly TValue[], maxVisible: number): readonly TValue[];
16
38
  export declare function shouldDismissTngToastForKey(key: string): boolean;
17
39
  export declare class TngToastComponent implements OnDestroy {
18
- private sequence;
19
- private readonly timeoutByToastId;
40
+ private readonly createToastId;
41
+ private readonly dismissTimers;
42
+ private readonly documentRef;
43
+ private readonly overlayLayerId;
44
+ private readonly createToastFocusId;
45
+ private isFocusLayerActive;
46
+ private isFocusLayerRegistered;
47
+ private isOverlayLayerRegistered;
20
48
  readonly duration: import("@angular/core").InputSignal<number>;
21
49
  readonly maxVisible: import("@angular/core").InputSignal<number>;
22
50
  readonly mode: import("@angular/core").InputSignal<TngToastMode>;
23
51
  readonly position: import("@angular/core").InputSignal<TngToastPosition>;
24
52
  readonly dismissed: import("@angular/core").OutputEmitterRef<string>;
53
+ readonly dismissedWithReason: import("@angular/core").OutputEmitterRef<Readonly<{
54
+ id: string;
55
+ reason: TngToastDismissReason;
56
+ }>>;
25
57
  protected readonly toasts: import("@angular/core").WritableSignal<readonly Readonly<{
58
+ action: TngToastAction | null;
26
59
  duration: number;
27
60
  id: string;
28
61
  message: string;
29
62
  title: string | null;
30
63
  tone: TngToastTone;
31
64
  }>[]>;
32
- dismiss(id: string): void;
65
+ protected readonly viewportRef: import("@angular/core").Signal<ElementRef<HTMLElement> | undefined>;
66
+ private readonly overlayLayerEffect;
67
+ dismiss(id: string, reason?: TngToastDismissReason): void;
33
68
  ngOnDestroy(): void;
34
69
  show(message: string, options?: TngToastOptions): string;
35
- protected onToastKeydown(id: string, event: TngToastKeyboardEvent): void;
70
+ protected onActionClick(toast: TngToastRecord): void;
71
+ protected onViewportFocusin(event: FocusEvent): void;
36
72
  private clearDismissTimer;
73
+ private handleOverlayDismiss;
74
+ private registerOverlayLayer;
75
+ private resolveEscapeDismissToastId;
76
+ private activateFocusLayer;
77
+ private deactivateFocusLayer;
78
+ private ensureElementId;
79
+ private registerFocusLayer;
80
+ private resolveActiveElementId;
81
+ private resolveElementById;
82
+ private resolveFocusableMemberIds;
83
+ private shouldRestoreFocus;
84
+ private unregisterFocusLayer;
85
+ private unregisterOverlayLayer;
37
86
  private scheduleDismiss;
38
87
  }
39
88
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"tng-toast.component.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/feedback/toast/tng-toast.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAM1D,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAChD,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEzF,MAAM,MAAM,eAAe,GAAG,QAAQ,CACpC,OAAO,CAAC;IACN,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC,CACH,CAAC;AAEF,KAAK,qBAAqB,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,GAC/D,QAAQ,CAAC;IAAE,cAAc,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC,CAAC;AAU3C,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMjF;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAC7C,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,UAAU,EAAE,MAAM,GACjB,SAAS,MAAM,EAAE,CAMnB;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,qBAMa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAoD;IAErF,SAAgB,QAAQ,8CAAuB;IAC/C,SAAgB,UAAU,8CAAoB;IAC9C,SAAgB,IAAI,oDAAgC;IACpD,SAAgB,QAAQ,wDAA2C;IAEnE,SAAgB,SAAS,mDAAoB;IAC7C,SAAS,CAAC,QAAQ,CAAC,MAAM;kBAtDf,MAAM;YACZ,MAAM;iBACD,MAAM;eACR,MAAM,GAAG,IAAI;cACd,YAAY;UAkDgD;IAE3D,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAYzB,WAAW,IAAI,IAAI;IAQnB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM;IAgCnE,SAAS,CAAC,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,IAAI;IASxE,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"tng-toast.component.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/feedback/toast/tng-toast.component.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  import { __decorate } from "tslib";
2
- import { Component, input, output, signal } from '@angular/core';
2
+ import { Component, effect, input, output, signal, viewChild } from '@angular/core';
3
+ import { createTngIdFactory, createTngKeyedTimerController, resolveFocusableElements, } from '@tailng-ui/cdk';
3
4
  import { TngToastItem as TngToastItemPrimitive, TngToastViewport as TngToastViewportPrimitive, } from '@tailng-ui/primitives';
5
+ import { tngOverlayFocusHandoff } from '../../overlay/tng-overlay-focus-handoff';
6
+ import { tngOverlayRuntime } from '../../overlay/tng-overlay-runtime';
7
+ const createToastLayerId = createTngIdFactory('tng-toast-layer');
4
8
  export function normalizeTngToastDuration(value, fallback) {
5
9
  if (!Number.isFinite(value) || value < 0) {
6
10
  return fallback;
@@ -22,16 +26,53 @@ export function resolveTngToastNextSlice(values, maxVisible) {
22
26
  export function shouldDismissTngToastForKey(key) {
23
27
  return key === 'Escape';
24
28
  }
29
+ function mapOverlayDismissReasonToToastDismissReason(reason) {
30
+ if (reason === 'escape-key') {
31
+ return 'escape';
32
+ }
33
+ return null;
34
+ }
35
+ function normalizeTngToastAction(action) {
36
+ if (action === null || action === undefined) {
37
+ return null;
38
+ }
39
+ const normalizedLabel = action.label.trim();
40
+ if (normalizedLabel.length === 0) {
41
+ return null;
42
+ }
43
+ return {
44
+ dismissOnSelect: action.dismissOnSelect ?? true,
45
+ label: normalizedLabel,
46
+ onSelect: action.onSelect,
47
+ };
48
+ }
25
49
  let TngToastComponent = class TngToastComponent {
26
- sequence = 0;
27
- timeoutByToastId = new Map();
50
+ createToastId = createTngIdFactory('tng-toast');
51
+ dismissTimers = createTngKeyedTimerController();
52
+ documentRef = typeof document === 'undefined' ? null : document;
53
+ overlayLayerId = createToastLayerId();
54
+ createToastFocusId = createTngIdFactory('tng-toast-focus', this.overlayLayerId);
55
+ isFocusLayerActive = false;
56
+ isFocusLayerRegistered = false;
57
+ isOverlayLayerRegistered = false;
28
58
  duration = input(4000);
29
59
  maxVisible = input(4);
30
60
  mode = input('toast');
31
61
  position = input('bottom-right');
32
62
  dismissed = output();
63
+ dismissedWithReason = output();
33
64
  toasts = signal([]);
34
- dismiss(id) {
65
+ viewportRef = viewChild('viewportRef');
66
+ overlayLayerEffect = effect(() => {
67
+ if (this.toasts().length > 0) {
68
+ this.registerOverlayLayer();
69
+ this.activateFocusLayer();
70
+ return;
71
+ }
72
+ this.deactivateFocusLayer();
73
+ this.unregisterOverlayLayer();
74
+ });
75
+ dismiss(id, reason = 'manual') {
35
76
  const currentToasts = this.toasts();
36
77
  const nextToasts = currentToasts.filter((toast) => toast.id !== id);
37
78
  if (nextToasts.length === currentToasts.length) {
@@ -39,20 +80,26 @@ let TngToastComponent = class TngToastComponent {
39
80
  }
40
81
  this.clearDismissTimer(id);
41
82
  this.toasts.set(nextToasts);
83
+ this.dismissedWithReason.emit({
84
+ id,
85
+ reason,
86
+ });
42
87
  this.dismissed.emit(id);
43
88
  }
44
89
  ngOnDestroy() {
45
- for (const timeoutId of this.timeoutByToastId.values()) {
46
- clearTimeout(timeoutId);
47
- }
48
- this.timeoutByToastId.clear();
90
+ this.overlayLayerEffect.destroy();
91
+ this.deactivateFocusLayer();
92
+ this.unregisterFocusLayer();
93
+ this.unregisterOverlayLayer();
94
+ this.dismissTimers.clearAll();
49
95
  }
50
96
  show(message, options = {}) {
51
- this.sequence += 1;
52
- const id = `tng-toast-${this.sequence}`;
97
+ const id = this.createToastId();
53
98
  const fallbackDuration = this.duration();
54
99
  const resolvedDuration = normalizeTngToastDuration(options.duration ?? fallbackDuration, fallbackDuration);
100
+ const resolvedAction = normalizeTngToastAction(options.action);
55
101
  const nextToast = {
102
+ action: resolvedAction,
56
103
  duration: resolvedDuration,
57
104
  id,
58
105
  message,
@@ -72,31 +119,195 @@ let TngToastComponent = class TngToastComponent {
72
119
  this.scheduleDismiss(nextToast);
73
120
  return id;
74
121
  }
75
- onToastKeydown(id, event) {
76
- if (!shouldDismissTngToastForKey(event.key)) {
122
+ onActionClick(toast) {
123
+ toast.action?.onSelect?.(toast.id);
124
+ if (toast.action?.dismissOnSelect ?? false) {
125
+ this.dismiss(toast.id, 'manual');
126
+ }
127
+ }
128
+ onViewportFocusin(event) {
129
+ const viewportElement = this.viewportRef()?.nativeElement;
130
+ if (viewportElement === undefined) {
77
131
  return;
78
132
  }
79
- event.preventDefault();
80
- this.dismiss(id);
133
+ const target = event.target;
134
+ if (!(target instanceof HTMLElement) || !viewportElement.contains(target)) {
135
+ return;
136
+ }
137
+ const targetId = this.ensureElementId(target);
138
+ tngOverlayFocusHandoff.recordFocus(this.overlayLayerId, targetId);
81
139
  }
82
140
  clearDismissTimer(id) {
83
- const timeoutId = this.timeoutByToastId.get(id);
84
- if (timeoutId === undefined) {
141
+ this.dismissTimers.cancel(id);
142
+ }
143
+ handleOverlayDismiss(reason) {
144
+ const dismissReason = mapOverlayDismissReasonToToastDismissReason(reason);
145
+ if (dismissReason === null) {
146
+ return;
147
+ }
148
+ const toastId = this.resolveEscapeDismissToastId();
149
+ if (toastId === null) {
150
+ return;
151
+ }
152
+ this.dismiss(toastId, dismissReason);
153
+ }
154
+ registerOverlayLayer() {
155
+ if (this.isOverlayLayerRegistered) {
156
+ return;
157
+ }
158
+ this.isOverlayLayerRegistered = true;
159
+ tngOverlayRuntime.registerLayer({
160
+ containsTarget: (target, path) => {
161
+ const viewportElement = this.viewportRef()?.nativeElement;
162
+ if (viewportElement === undefined) {
163
+ return false;
164
+ }
165
+ if (path.includes(viewportElement)) {
166
+ return true;
167
+ }
168
+ return target instanceof Node ? viewportElement.contains(target) : false;
169
+ },
170
+ dismissOnEscape: true,
171
+ dismissOnOutsidePointer: false,
172
+ id: this.overlayLayerId,
173
+ onDismiss: (reason) => {
174
+ this.handleOverlayDismiss(reason);
175
+ },
176
+ priority: -100,
177
+ });
178
+ }
179
+ resolveEscapeDismissToastId() {
180
+ const latestToast = this.toasts()[this.toasts().length - 1];
181
+ const fallbackToastId = latestToast?.id ?? null;
182
+ const viewportElement = this.viewportRef()?.nativeElement;
183
+ if (viewportElement === undefined || this.documentRef === null) {
184
+ return fallbackToastId;
185
+ }
186
+ const activeElement = this.documentRef.activeElement;
187
+ if (!(activeElement instanceof HTMLElement) || !viewportElement.contains(activeElement)) {
188
+ return fallbackToastId;
189
+ }
190
+ const focusedToast = activeElement.closest('[data-slot="toast-item"][data-toast-id]');
191
+ return focusedToast?.getAttribute('data-toast-id') ?? fallbackToastId;
192
+ }
193
+ activateFocusLayer() {
194
+ this.registerFocusLayer();
195
+ if (this.isFocusLayerActive) {
196
+ return;
197
+ }
198
+ this.isFocusLayerActive = true;
199
+ const activeElementId = this.resolveActiveElementId();
200
+ tngOverlayFocusHandoff.activateLayer(this.overlayLayerId, activeElementId);
201
+ }
202
+ deactivateFocusLayer() {
203
+ if (!this.isFocusLayerActive) {
204
+ return;
205
+ }
206
+ this.isFocusLayerActive = false;
207
+ const restoreFocusTargetId = tngOverlayFocusHandoff.deactivateLayer(this.overlayLayerId);
208
+ if (restoreFocusTargetId === null || !this.shouldRestoreFocus()) {
209
+ return;
210
+ }
211
+ const restoreElement = this.resolveElementById(restoreFocusTargetId);
212
+ restoreElement?.focus();
213
+ }
214
+ ensureElementId(element) {
215
+ const existingId = element.id.trim();
216
+ if (existingId.length > 0) {
217
+ return existingId;
218
+ }
219
+ const nextId = this.createToastFocusId();
220
+ element.id = nextId;
221
+ return nextId;
222
+ }
223
+ registerFocusLayer() {
224
+ if (this.isFocusLayerRegistered) {
225
+ return;
226
+ }
227
+ this.isFocusLayerRegistered = true;
228
+ tngOverlayFocusHandoff.registerLayer({
229
+ layerId: this.overlayLayerId,
230
+ members: () => {
231
+ const viewportElement = this.viewportRef()?.nativeElement;
232
+ if (viewportElement === undefined) {
233
+ return [];
234
+ }
235
+ return this.resolveFocusableMemberIds(viewportElement);
236
+ },
237
+ restoreFocus: true,
238
+ trapFocus: false,
239
+ });
240
+ }
241
+ resolveActiveElementId() {
242
+ if (this.documentRef === null) {
243
+ return null;
244
+ }
245
+ const activeElement = this.documentRef.activeElement;
246
+ if (!(activeElement instanceof HTMLElement)) {
247
+ return null;
248
+ }
249
+ return this.ensureElementId(activeElement);
250
+ }
251
+ resolveElementById(id) {
252
+ if (this.documentRef === null) {
253
+ return null;
254
+ }
255
+ const element = this.documentRef.getElementById(id);
256
+ return element instanceof HTMLElement ? element : null;
257
+ }
258
+ resolveFocusableMemberIds(viewportElement) {
259
+ const focusableMembers = resolveFocusableElements(viewportElement);
260
+ const ids = [];
261
+ const seenIds = new Set();
262
+ for (const member of focusableMembers) {
263
+ const id = this.ensureElementId(member);
264
+ if (seenIds.has(id)) {
265
+ continue;
266
+ }
267
+ seenIds.add(id);
268
+ ids.push(id);
269
+ }
270
+ return ids;
271
+ }
272
+ shouldRestoreFocus() {
273
+ if (this.documentRef === null) {
274
+ return false;
275
+ }
276
+ const viewportElement = this.viewportRef()?.nativeElement;
277
+ const activeElement = this.documentRef.activeElement;
278
+ if (!(activeElement instanceof HTMLElement)) {
279
+ return true;
280
+ }
281
+ if (activeElement === this.documentRef.body) {
282
+ return true;
283
+ }
284
+ if (viewportElement === undefined) {
285
+ return false;
286
+ }
287
+ return viewportElement.contains(activeElement);
288
+ }
289
+ unregisterFocusLayer() {
290
+ if (!this.isFocusLayerRegistered) {
291
+ return;
292
+ }
293
+ this.isFocusLayerRegistered = false;
294
+ tngOverlayFocusHandoff.unregisterLayer(this.overlayLayerId);
295
+ }
296
+ unregisterOverlayLayer() {
297
+ if (!this.isOverlayLayerRegistered) {
85
298
  return;
86
299
  }
87
- clearTimeout(timeoutId);
88
- this.timeoutByToastId.delete(id);
300
+ this.isOverlayLayerRegistered = false;
301
+ tngOverlayRuntime.unregisterLayer(this.overlayLayerId);
89
302
  }
90
303
  scheduleDismiss(toast) {
91
304
  this.clearDismissTimer(toast.id);
92
305
  if (toast.duration === 0) {
93
306
  return;
94
307
  }
95
- const timeoutId = setTimeout(() => {
96
- this.timeoutByToastId.delete(toast.id);
97
- this.dismiss(toast.id);
98
- }, toast.duration);
99
- this.timeoutByToastId.set(toast.id, timeoutId);
308
+ this.dismissTimers.schedule(toast.id, toast.duration, () => {
309
+ this.dismiss(toast.id, 'timeout');
310
+ });
100
311
  }
101
312
  };
102
313
  TngToastComponent = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"tng-toast.component.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/feedback/toast/tng-toast.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, input, output, signal } from '@angular/core';\nimport type { OnDestroy } from '@angular/core';\nimport type { TngToastTone } from '@tailng-ui/primitives';\nimport {\n TngToastItem as TngToastItemPrimitive,\n TngToastViewport as TngToastViewportPrimitive,\n} from '@tailng-ui/primitives';\n\nexport type TngToastMode = 'snackbar' | 'toast';\nexport type TngToastPosition = 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';\n\nexport type TngToastOptions = Readonly<\n Partial<{\n duration: number;\n title: string | null;\n tone: TngToastTone;\n }>\n>;\n\ntype TngToastKeyboardEvent = Readonly<Pick<KeyboardEvent, 'key'>> &\n Readonly<{ preventDefault: () => void }>;\n\ntype TngToastRecord = Readonly<{\n duration: number;\n id: string;\n message: string;\n title: string | null;\n tone: TngToastTone;\n}>;\n\nexport function normalizeTngToastDuration(value: number, fallback: number): number {\n if (!Number.isFinite(value) || value < 0) {\n return fallback;\n }\n\n return value;\n}\n\nexport function normalizeTngToastMaxVisible(value: number): number {\n if (!Number.isFinite(value) || value < 1) {\n return 1;\n }\n\n return Math.floor(value);\n}\n\nexport function resolveTngToastNextSlice<TValue>(\n values: readonly TValue[],\n maxVisible: number,\n): readonly TValue[] {\n if (values.length <= maxVisible) {\n return values;\n }\n\n return values.slice(values.length - maxVisible);\n}\n\nexport function shouldDismissTngToastForKey(key: string): boolean {\n return key === 'Escape';\n}\n\n@Component({\n selector: 'tng-toast',\n imports: [TngToastItemPrimitive, TngToastViewportPrimitive],\n templateUrl: './tng-toast.component.html',\n styleUrl: './tng-toast.component.css',\n})\nexport class TngToastComponent implements OnDestroy {\n private sequence = 0;\n private readonly timeoutByToastId = new Map<string, ReturnType<typeof setTimeout>>();\n\n public readonly duration = input<number>(4000);\n public readonly maxVisible = input<number>(4);\n public readonly mode = input<TngToastMode>('toast');\n public readonly position = input<TngToastPosition>('bottom-right');\n\n public readonly dismissed = output<string>();\n protected readonly toasts = signal<readonly TngToastRecord[]>([]);\n\n public dismiss(id: string): void {\n const currentToasts = this.toasts();\n const nextToasts = currentToasts.filter((toast) => toast.id !== id);\n if (nextToasts.length === currentToasts.length) {\n return;\n }\n\n this.clearDismissTimer(id);\n this.toasts.set(nextToasts);\n this.dismissed.emit(id);\n }\n\n public ngOnDestroy(): void {\n for (const timeoutId of this.timeoutByToastId.values()) {\n clearTimeout(timeoutId);\n }\n\n this.timeoutByToastId.clear();\n }\n\n public show(message: string, options: TngToastOptions = {}): string {\n this.sequence += 1;\n const id = `tng-toast-${this.sequence}`;\n const fallbackDuration = this.duration();\n const resolvedDuration = normalizeTngToastDuration(\n options.duration ?? fallbackDuration,\n fallbackDuration,\n );\n const nextToast: TngToastRecord = {\n duration: resolvedDuration,\n id,\n message,\n title: options.title ?? null,\n tone: options.tone ?? 'neutral',\n };\n\n const previousToasts = this.toasts();\n const visibleLimit = normalizeTngToastMaxVisible(this.maxVisible());\n const nextToasts = resolveTngToastNextSlice([...previousToasts, nextToast], visibleLimit);\n const nextIds = new Set(nextToasts.map((toast) => toast.id));\n\n for (const previousToast of previousToasts) {\n if (!nextIds.has(previousToast.id)) {\n this.clearDismissTimer(previousToast.id);\n }\n }\n\n this.toasts.set(nextToasts);\n this.scheduleDismiss(nextToast);\n return id;\n }\n\n protected onToastKeydown(id: string, event: TngToastKeyboardEvent): void {\n if (!shouldDismissTngToastForKey(event.key)) {\n return;\n }\n\n event.preventDefault();\n this.dismiss(id);\n }\n\n private clearDismissTimer(id: string): void {\n const timeoutId = this.timeoutByToastId.get(id);\n if (timeoutId === undefined) {\n return;\n }\n\n clearTimeout(timeoutId);\n this.timeoutByToastId.delete(id);\n }\n\n private scheduleDismiss(toast: TngToastRecord): void {\n this.clearDismissTimer(toast.id);\n if (toast.duration === 0) {\n return;\n }\n\n const timeoutId = setTimeout(() => {\n this.timeoutByToastId.delete(toast.id);\n this.dismiss(toast.id);\n }, toast.duration);\n\n this.timeoutByToastId.set(toast.id, timeoutId);\n }\n}\n"]}
1
+ {"version":3,"file":"tng-toast.component.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/feedback/toast/tng-toast.component.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,EACL,kBAAkB,EAClB,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EACL,YAAY,IAAI,qBAAqB,EACrC,gBAAgB,IAAI,yBAAyB,GAC9C,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAyCtE,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;AAEjE,MAAM,UAAU,yBAAyB,CAAC,KAAa,EAAE,QAAgB;IACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,KAAa;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACzC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,MAAyB,EACzB,UAAkB;IAElB,IAAI,MAAM,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;QAChC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAW;IACrD,OAAO,GAAG,KAAK,QAAQ,CAAC;AAC1B,CAAC;AAED,SAAS,2CAA2C,CAClD,MAA+B;IAE/B,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,uBAAuB,CAC9B,MAAgD;IAEhD,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,IAAI;QAC/C,KAAK,EAAE,eAAe;QACtB,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC;AACJ,CAAC;AAQM,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACX,aAAa,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAChD,aAAa,GAAG,6BAA6B,EAAU,CAAC;IACxD,WAAW,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChE,cAAc,GAAG,kBAAkB,EAAE,CAAC;IACtC,kBAAkB,GAAG,kBAAkB,CAAC,iBAAiB,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IACzF,kBAAkB,GAAG,KAAK,CAAC;IAC3B,sBAAsB,GAAG,KAAK,CAAC;IAC/B,wBAAwB,GAAG,KAAK,CAAC;IAEzB,QAAQ,GAAG,KAAK,CAAS,IAAI,CAAC,CAAC;IAC/B,UAAU,GAAG,KAAK,CAAS,CAAC,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,CAAe,OAAO,CAAC,CAAC;IACpC,QAAQ,GAAG,KAAK,CAAmB,cAAc,CAAC,CAAC;IAEnD,SAAS,GAAG,MAAM,EAAU,CAAC;IAC7B,mBAAmB,GAAG,MAAM,EAAwB,CAAC;IAClD,MAAM,GAAG,MAAM,CAA4B,EAAE,CAAC,CAAC;IAC/C,WAAW,GAAG,SAAS,CAA0B,aAAa,CAAC,CAAC;IAElE,kBAAkB,GAAG,MAAM,CAAC,GAAS,EAAE;QACtD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC5B,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEI,OAAO,CAAC,EAAU,EAAE,SAAgC,QAAQ;QACjE,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACpE,IAAI,UAAU,CAAC,MAAM,KAAK,aAAa,CAAC,MAAM,EAAE,CAAC;YAC/C,OAAO;QACT,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAC5B,EAAE;YACF,MAAM;SACP,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IAEM,WAAW;QAChB,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;IAChC,CAAC;IAEM,IAAI,CAAC,OAAe,EAAE,UAA2B,EAAE;QACxD,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,gBAAgB,GAAG,yBAAyB,CAChD,OAAO,CAAC,QAAQ,IAAI,gBAAgB,EACpC,gBAAgB,CACjB,CAAC;QACF,MAAM,cAAc,GAAG,uBAAuB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/D,MAAM,SAAS,GAAmB;YAChC,MAAM,EAAE,cAAc;YACtB,QAAQ,EAAE,gBAAgB;YAC1B,EAAE;YACF,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;YAC5B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,SAAS;SAChC,CAAC;QAEF,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,YAAY,GAAG,2BAA2B,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,wBAAwB,CAAC,CAAC,GAAG,cAAc,EAAE,SAAS,CAAC,EAAE,YAAY,CAAC,CAAC;QAC1F,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAE7D,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5B,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,EAAE,CAAC;IACZ,CAAC;IAES,aAAa,CAAC,KAAqB;QAC3C,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,MAAM,EAAE,eAAe,IAAI,KAAK,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAES,iBAAiB,CAAC,KAAiB;QAC3C,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC;QAC1D,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,CAAC,MAAM,YAAY,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1E,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC9C,sBAAsB,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IACpE,CAAC;IAEO,iBAAiB,CAAC,EAAU;QAClC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAEO,oBAAoB,CAAC,MAA+B;QAC1D,MAAM,aAAa,GAAG,2CAA2C,CAAC,MAAM,CAAC,CAAC;QAC1E,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,2BAA2B,EAAE,CAAC;QACnD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACvC,CAAC;IAEO,oBAAoB;QAC1B,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAClC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC;QACrC,iBAAiB,CAAC,aAAa,CAAC;YAC9B,cAAc,EAAE,CAAC,MAAe,EAAE,IAAwB,EAAW,EAAE;gBACrE,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC;gBAC1D,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;oBAClC,OAAO,KAAK,CAAC;gBACf,CAAC;gBAED,IAAI,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;oBACnC,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,OAAO,MAAM,YAAY,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAC3E,CAAC;YACD,eAAe,EAAE,IAAI;YACrB,uBAAuB,EAAE,KAAK;YAC9B,EAAE,EAAE,IAAI,CAAC,cAAc;YACvB,SAAS,EAAE,CAAC,MAA+B,EAAQ,EAAE;gBACnD,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;YACpC,CAAC;YACD,QAAQ,EAAE,CAAC,GAAG;SACf,CAAC,CAAC;IACL,CAAC;IAEO,2BAA2B;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5D,MAAM,eAAe,GAAG,WAAW,EAAE,EAAE,IAAI,IAAI,CAAC;QAEhD,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC;QAC1D,IAAI,eAAe,KAAK,SAAS,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC/D,OAAO,eAAe,CAAC;QACzB,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;QACrD,IAAI,CAAC,CAAC,aAAa,YAAY,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACxF,OAAO,eAAe,CAAC;QACzB,CAAC;QAED,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,CAAc,yCAAyC,CAAC,CAAC;QACnG,OAAO,YAAY,EAAE,YAAY,CAAC,eAAe,CAAC,IAAI,eAAe,CAAC;IACxE,CAAC;IAEO,kBAAkB;QACxB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QAED,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAC;QACtD,sBAAsB,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IAC7E,CAAC;IAEO,oBAAoB;QAC1B,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACzF,IAAI,oBAAoB,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAChE,OAAO;QACT,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,CAAC;QACrE,cAAc,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAEO,eAAe,CAAC,OAAoB;QAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,UAAU,CAAC;QACpB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACzC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;QACnC,sBAAsB,CAAC,aAAa,CAAC;YACnC,OAAO,EAAE,IAAI,CAAC,cAAc;YAC5B,OAAO,EAAE,GAAsB,EAAE;gBAC/B,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC;gBAC1D,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;oBAClC,OAAO,EAAE,CAAC;gBACZ,CAAC;gBAED,OAAO,IAAI,CAAC,yBAAyB,CAAC,eAAe,CAAC,CAAC;YACzD,CAAC;YACD,YAAY,EAAE,IAAI;YAClB,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;IACL,CAAC;IAEO,sBAAsB;QAC5B,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;QACrD,IAAI,CAAC,CAAC,aAAa,YAAY,WAAW,CAAC,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC;IAEO,kBAAkB,CAAC,EAAU;QACnC,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QACpD,OAAO,OAAO,YAAY,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,CAAC;IAEO,yBAAyB,CAAC,eAA4B;QAC5D,MAAM,gBAAgB,GAAG,wBAAwB,CAAC,eAAe,CAAC,CAAC;QACnE,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAElC,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;YACtC,MAAM,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACpB,SAAS;YACX,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;QAED,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,aAAa,CAAC;QAC1D,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;QACrD,IAAI,CAAC,CAAC,aAAa,YAAY,WAAW,CAAC,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,aAAa,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACjD,CAAC;IAEO,oBAAoB;QAC1B,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC;QACpC,sBAAsB,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9D,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC;YACnC,OAAO;QACT,CAAC;QAED,IAAI,CAAC,wBAAwB,GAAG,KAAK,CAAC;QACtC,iBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACzD,CAAC;IAEO,eAAe,CAAC,KAAqB;QAC3C,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjC,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE;YACzD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAnUY,iBAAiB;IAN7B,SAAS,CAAC;QACT,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,CAAC,qBAAqB,EAAE,yBAAyB,CAAC;QAC3D,WAAW,EAAE,4BAA4B;QACzC,QAAQ,EAAE,2BAA2B;KACtC,CAAC;GACW,iBAAiB,CAmU7B","sourcesContent":["import { Component, effect, input, output, signal, viewChild } from '@angular/core';\nimport type { OnDestroy } from '@angular/core';\nimport {\n createTngIdFactory,\n createTngKeyedTimerController,\n resolveFocusableElements,\n} from '@tailng-ui/cdk';\nimport type { ElementRef } from '@angular/core';\nimport type { TngOverlayDismissReason } from '@tailng-ui/cdk';\nimport type { TngToastTone } from '@tailng-ui/primitives';\nimport {\n TngToastItem as TngToastItemPrimitive,\n TngToastViewport as TngToastViewportPrimitive,\n} from '@tailng-ui/primitives';\nimport { tngOverlayFocusHandoff } from '../../overlay/tng-overlay-focus-handoff';\nimport { tngOverlayRuntime } from '../../overlay/tng-overlay-runtime';\n\nexport type TngToastMode = 'snackbar' | 'toast';\nexport type TngToastPosition = 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';\nexport type TngToastDismissReason = 'escape' | 'manual' | 'timeout';\n\nexport type TngToastDismissEvent = Readonly<{\n id: string;\n reason: TngToastDismissReason;\n}>;\n\nexport type TngToastAction = Readonly<{\n dismissOnSelect: boolean;\n label: string;\n onSelect?: (id: string) => void;\n}>;\n\nexport type TngToastActionOptions = Readonly<{\n dismissOnSelect?: boolean;\n label: string;\n onSelect?: (id: string) => void;\n}>;\n\nexport type TngToastOptions = Readonly<\n Partial<{\n action: TngToastActionOptions | null;\n duration: number;\n title: string | null;\n tone: TngToastTone;\n }>\n>;\n\ntype TngToastRecord = Readonly<{\n action: TngToastAction | null;\n duration: number;\n id: string;\n message: string;\n title: string | null;\n tone: TngToastTone;\n}>;\n\nconst createToastLayerId = createTngIdFactory('tng-toast-layer');\n\nexport function normalizeTngToastDuration(value: number, fallback: number): number {\n if (!Number.isFinite(value) || value < 0) {\n return fallback;\n }\n\n return value;\n}\n\nexport function normalizeTngToastMaxVisible(value: number): number {\n if (!Number.isFinite(value) || value < 1) {\n return 1;\n }\n\n return Math.floor(value);\n}\n\nexport function resolveTngToastNextSlice<TValue>(\n values: readonly TValue[],\n maxVisible: number,\n): readonly TValue[] {\n if (values.length <= maxVisible) {\n return values;\n }\n\n return values.slice(values.length - maxVisible);\n}\n\nexport function shouldDismissTngToastForKey(key: string): boolean {\n return key === 'Escape';\n}\n\nfunction mapOverlayDismissReasonToToastDismissReason(\n reason: TngOverlayDismissReason,\n): TngToastDismissReason | null {\n if (reason === 'escape-key') {\n return 'escape';\n }\n\n return null;\n}\n\nfunction normalizeTngToastAction(\n action: TngToastActionOptions | null | undefined,\n): TngToastAction | null {\n if (action === null || action === undefined) {\n return null;\n }\n\n const normalizedLabel = action.label.trim();\n if (normalizedLabel.length === 0) {\n return null;\n }\n\n return {\n dismissOnSelect: action.dismissOnSelect ?? true,\n label: normalizedLabel,\n onSelect: action.onSelect,\n };\n}\n\n@Component({\n selector: 'tng-toast',\n imports: [TngToastItemPrimitive, TngToastViewportPrimitive],\n templateUrl: './tng-toast.component.html',\n styleUrl: './tng-toast.component.css',\n})\nexport class TngToastComponent implements OnDestroy {\n private readonly createToastId = createTngIdFactory('tng-toast');\n private readonly dismissTimers = createTngKeyedTimerController<string>();\n private readonly documentRef = typeof document === 'undefined' ? null : document;\n private readonly overlayLayerId = createToastLayerId();\n private readonly createToastFocusId = createTngIdFactory('tng-toast-focus', this.overlayLayerId);\n private isFocusLayerActive = false;\n private isFocusLayerRegistered = false;\n private isOverlayLayerRegistered = false;\n\n public readonly duration = input<number>(4000);\n public readonly maxVisible = input<number>(4);\n public readonly mode = input<TngToastMode>('toast');\n public readonly position = input<TngToastPosition>('bottom-right');\n\n public readonly dismissed = output<string>();\n public readonly dismissedWithReason = output<TngToastDismissEvent>();\n protected readonly toasts = signal<readonly TngToastRecord[]>([]);\n protected readonly viewportRef = viewChild<ElementRef<HTMLElement>>('viewportRef');\n\n private readonly overlayLayerEffect = effect((): void => {\n if (this.toasts().length > 0) {\n this.registerOverlayLayer();\n this.activateFocusLayer();\n return;\n }\n\n this.deactivateFocusLayer();\n this.unregisterOverlayLayer();\n });\n\n public dismiss(id: string, reason: TngToastDismissReason = 'manual'): void {\n const currentToasts = this.toasts();\n const nextToasts = currentToasts.filter((toast) => toast.id !== id);\n if (nextToasts.length === currentToasts.length) {\n return;\n }\n\n this.clearDismissTimer(id);\n this.toasts.set(nextToasts);\n this.dismissedWithReason.emit({\n id,\n reason,\n });\n this.dismissed.emit(id);\n }\n\n public ngOnDestroy(): void {\n this.overlayLayerEffect.destroy();\n this.deactivateFocusLayer();\n this.unregisterFocusLayer();\n this.unregisterOverlayLayer();\n this.dismissTimers.clearAll();\n }\n\n public show(message: string, options: TngToastOptions = {}): string {\n const id = this.createToastId();\n const fallbackDuration = this.duration();\n const resolvedDuration = normalizeTngToastDuration(\n options.duration ?? fallbackDuration,\n fallbackDuration,\n );\n const resolvedAction = normalizeTngToastAction(options.action);\n const nextToast: TngToastRecord = {\n action: resolvedAction,\n duration: resolvedDuration,\n id,\n message,\n title: options.title ?? null,\n tone: options.tone ?? 'neutral',\n };\n\n const previousToasts = this.toasts();\n const visibleLimit = normalizeTngToastMaxVisible(this.maxVisible());\n const nextToasts = resolveTngToastNextSlice([...previousToasts, nextToast], visibleLimit);\n const nextIds = new Set(nextToasts.map((toast) => toast.id));\n\n for (const previousToast of previousToasts) {\n if (!nextIds.has(previousToast.id)) {\n this.clearDismissTimer(previousToast.id);\n }\n }\n\n this.toasts.set(nextToasts);\n this.scheduleDismiss(nextToast);\n return id;\n }\n\n protected onActionClick(toast: TngToastRecord): void {\n toast.action?.onSelect?.(toast.id);\n if (toast.action?.dismissOnSelect ?? false) {\n this.dismiss(toast.id, 'manual');\n }\n }\n\n protected onViewportFocusin(event: FocusEvent): void {\n const viewportElement = this.viewportRef()?.nativeElement;\n if (viewportElement === undefined) {\n return;\n }\n\n const target = event.target;\n if (!(target instanceof HTMLElement) || !viewportElement.contains(target)) {\n return;\n }\n\n const targetId = this.ensureElementId(target);\n tngOverlayFocusHandoff.recordFocus(this.overlayLayerId, targetId);\n }\n\n private clearDismissTimer(id: string): void {\n this.dismissTimers.cancel(id);\n }\n\n private handleOverlayDismiss(reason: TngOverlayDismissReason): void {\n const dismissReason = mapOverlayDismissReasonToToastDismissReason(reason);\n if (dismissReason === null) {\n return;\n }\n\n const toastId = this.resolveEscapeDismissToastId();\n if (toastId === null) {\n return;\n }\n\n this.dismiss(toastId, dismissReason);\n }\n\n private registerOverlayLayer(): void {\n if (this.isOverlayLayerRegistered) {\n return;\n }\n\n this.isOverlayLayerRegistered = true;\n tngOverlayRuntime.registerLayer({\n containsTarget: (target: unknown, path: readonly unknown[]): boolean => {\n const viewportElement = this.viewportRef()?.nativeElement;\n if (viewportElement === undefined) {\n return false;\n }\n\n if (path.includes(viewportElement)) {\n return true;\n }\n\n return target instanceof Node ? viewportElement.contains(target) : false;\n },\n dismissOnEscape: true,\n dismissOnOutsidePointer: false,\n id: this.overlayLayerId,\n onDismiss: (reason: TngOverlayDismissReason): void => {\n this.handleOverlayDismiss(reason);\n },\n priority: -100,\n });\n }\n\n private resolveEscapeDismissToastId(): string | null {\n const latestToast = this.toasts()[this.toasts().length - 1];\n const fallbackToastId = latestToast?.id ?? null;\n\n const viewportElement = this.viewportRef()?.nativeElement;\n if (viewportElement === undefined || this.documentRef === null) {\n return fallbackToastId;\n }\n\n const activeElement = this.documentRef.activeElement;\n if (!(activeElement instanceof HTMLElement) || !viewportElement.contains(activeElement)) {\n return fallbackToastId;\n }\n\n const focusedToast = activeElement.closest<HTMLElement>('[data-slot=\"toast-item\"][data-toast-id]');\n return focusedToast?.getAttribute('data-toast-id') ?? fallbackToastId;\n }\n\n private activateFocusLayer(): void {\n this.registerFocusLayer();\n if (this.isFocusLayerActive) {\n return;\n }\n\n this.isFocusLayerActive = true;\n const activeElementId = this.resolveActiveElementId();\n tngOverlayFocusHandoff.activateLayer(this.overlayLayerId, activeElementId);\n }\n\n private deactivateFocusLayer(): void {\n if (!this.isFocusLayerActive) {\n return;\n }\n\n this.isFocusLayerActive = false;\n const restoreFocusTargetId = tngOverlayFocusHandoff.deactivateLayer(this.overlayLayerId);\n if (restoreFocusTargetId === null || !this.shouldRestoreFocus()) {\n return;\n }\n\n const restoreElement = this.resolveElementById(restoreFocusTargetId);\n restoreElement?.focus();\n }\n\n private ensureElementId(element: HTMLElement): string {\n const existingId = element.id.trim();\n if (existingId.length > 0) {\n return existingId;\n }\n\n const nextId = this.createToastFocusId();\n element.id = nextId;\n return nextId;\n }\n\n private registerFocusLayer(): void {\n if (this.isFocusLayerRegistered) {\n return;\n }\n\n this.isFocusLayerRegistered = true;\n tngOverlayFocusHandoff.registerLayer({\n layerId: this.overlayLayerId,\n members: (): readonly string[] => {\n const viewportElement = this.viewportRef()?.nativeElement;\n if (viewportElement === undefined) {\n return [];\n }\n\n return this.resolveFocusableMemberIds(viewportElement);\n },\n restoreFocus: true,\n trapFocus: false,\n });\n }\n\n private resolveActiveElementId(): string | null {\n if (this.documentRef === null) {\n return null;\n }\n\n const activeElement = this.documentRef.activeElement;\n if (!(activeElement instanceof HTMLElement)) {\n return null;\n }\n\n return this.ensureElementId(activeElement);\n }\n\n private resolveElementById(id: string): HTMLElement | null {\n if (this.documentRef === null) {\n return null;\n }\n\n const element = this.documentRef.getElementById(id);\n return element instanceof HTMLElement ? element : null;\n }\n\n private resolveFocusableMemberIds(viewportElement: HTMLElement): readonly string[] {\n const focusableMembers = resolveFocusableElements(viewportElement);\n const ids: string[] = [];\n const seenIds = new Set<string>();\n\n for (const member of focusableMembers) {\n const id = this.ensureElementId(member);\n if (seenIds.has(id)) {\n continue;\n }\n\n seenIds.add(id);\n ids.push(id);\n }\n\n return ids;\n }\n\n private shouldRestoreFocus(): boolean {\n if (this.documentRef === null) {\n return false;\n }\n\n const viewportElement = this.viewportRef()?.nativeElement;\n const activeElement = this.documentRef.activeElement;\n if (!(activeElement instanceof HTMLElement)) {\n return true;\n }\n\n if (activeElement === this.documentRef.body) {\n return true;\n }\n\n if (viewportElement === undefined) {\n return false;\n }\n\n return viewportElement.contains(activeElement);\n }\n\n private unregisterFocusLayer(): void {\n if (!this.isFocusLayerRegistered) {\n return;\n }\n\n this.isFocusLayerRegistered = false;\n tngOverlayFocusHandoff.unregisterLayer(this.overlayLayerId);\n }\n\n private unregisterOverlayLayer(): void {\n if (!this.isOverlayLayerRegistered) {\n return;\n }\n\n this.isOverlayLayerRegistered = false;\n tngOverlayRuntime.unregisterLayer(this.overlayLayerId);\n }\n\n private scheduleDismiss(toast: TngToastRecord): void {\n this.clearDismissTimer(toast.id);\n if (toast.duration === 0) {\n return;\n }\n\n this.dismissTimers.schedule(toast.id, toast.duration, () => {\n this.dismiss(toast.id, 'timeout');\n });\n }\n}\n"]}
@@ -41,7 +41,7 @@ let TngAutocompleteComponent = class TngAutocompleteComponent {
41
41
  });
42
42
  selectedLabel = computed(() => {
43
43
  const opt = this.selectedOption();
44
- return opt ? this.getOptionLabel()(opt) : this.placeholder();
44
+ return opt ? this.getOptionLabel()(opt) : '';
45
45
  });
46
46
  filteredOptions = computed(() => {
47
47
  const q = this.query().toLowerCase().trim();
@@ -1 +1 @@
1
- {"version":3,"file":"tng-autocomplete.component.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/form/autocomplete/tng-autocomplete.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n computed,\n effect,\n inject,\n input,\n signal,\n} from '@angular/core';\n\nimport {\n TngAutocomplete,\n TngAutocompleteTrigger,\n TngAutocompleteTriggerContainer,\n TngAutocompleteIcon,\n TngAutocompleteContent,\n TngAutocompleteOverlay,\n TngAutocompleteListbox,\n TngAutocompleteOption,\n} from '@tailng-ui/primitives';\n\nexport type TngAutocompleteGetValue<O, V> = (opt: O) => V;\nexport type TngAutocompleteGetLabel<O> = (opt: O) => string;\nexport type TngAutocompleteIsDisabled<O> = (opt: O) => boolean;\nexport type TngAutocompleteTrackBy<O> = (index: number, opt: O) => unknown;\n\n@Component({\n selector: 'tng-autocomplete',\n standalone: true,\n imports: [\n TngAutocompleteTrigger,\n TngAutocompleteTriggerContainer,\n TngAutocompleteIcon,\n TngAutocompleteContent,\n TngAutocompleteOverlay,\n TngAutocompleteListbox,\n TngAutocompleteOption,\n ],\n hostDirectives: [\n {\n directive: TngAutocomplete,\n inputs: [\n 'open',\n 'value',\n 'disabled',\n 'loading',\n 'invalid',\n 'labelId',\n 'descriptionId',\n 'errorId',\n ],\n outputs: ['openChange', 'valueChange'],\n },\n ],\n templateUrl: './tng-autocomplete.component.html',\n styleUrl: './tng-autocomplete.component.css',\n})\nexport class TngAutocompleteComponent<O = unknown, V = unknown> {\n protected readonly primitive = inject<TngAutocomplete<V>>(TngAutocomplete);\n\n readonly options = input<readonly O[]>([]);\n readonly placeholder = input<string>('Type to search…');\n\n readonly getOptionValue = input<TngAutocompleteGetValue<O, V>>(\n ((opt: unknown) => (opt as { value?: V })?.value) as TngAutocompleteGetValue<O, V>,\n );\n readonly getOptionLabel = input<TngAutocompleteGetLabel<O>>(\n ((opt: unknown) =>\n String(\n (opt as { label?: string; value?: unknown })?.label ??\n (opt as { value?: unknown })?.value ??\n opt,\n )) as TngAutocompleteGetLabel<O>,\n );\n readonly isOptionDisabled = input<TngAutocompleteIsDisabled<O>>(\n ((opt: unknown) => !!(opt as { disabled?: boolean })?.disabled) as TngAutocompleteIsDisabled<O>,\n );\n /** Default: track by value, id, or option (stable identity for async/replaced options). */\n readonly trackBy = input<TngAutocompleteTrackBy<O>>((_, opt) => {\n const o = opt as Record<string, unknown> | null | undefined;\n return o?.['value'] ?? o?.['id'] ?? opt;\n });\n\n readonly iconText = input<string>('▾');\n\n readonly ariaLabel = input<string>('Autocomplete');\n\n protected readonly query = signal('');\n\n constructor() {\n effect(() => {\n const v = this.primitive.value();\n const open = this.primitive.open();\n if (!open) {\n const opt = this.findOption(v);\n this.query.set(opt ? this.getOptionLabel()(opt) : '');\n }\n });\n }\n\n protected readonly selectedOption = computed<O | null>(() => {\n const v = this.primitive.value();\n if (v === null) return null;\n const getV = this.getOptionValue();\n for (const opt of this.options()) {\n if (Object.is(getV(opt), v)) return opt;\n }\n return null;\n });\n\n protected readonly selectedLabel = computed<string>(() => {\n const opt = this.selectedOption();\n return opt ? this.getOptionLabel()(opt) : this.placeholder();\n });\n\n protected readonly filteredOptions = computed<readonly O[]>(() => {\n const q = this.query().toLowerCase().trim();\n const list = this.options();\n if (!q) return list;\n const getLabel = this.getOptionLabel();\n return list.filter((opt) =>\n getLabel(opt).toLowerCase().includes(q),\n );\n });\n\n protected readonly displayText = computed<string>(() =>\n this.primitive.open() ? this.query() : this.selectedLabel(),\n );\n\n protected onInput(ev: Event): void {\n const val = (ev.target as HTMLInputElement).value;\n this.query.set(val);\n }\n\n private findOption(value: V | null): O | null {\n if (value === null) return null;\n const getV = this.getOptionValue();\n for (const opt of this.options()) {\n if (Object.is(getV(opt), value)) return opt;\n }\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"tng-autocomplete.component.js","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/form/autocomplete/tng-autocomplete.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n computed,\n effect,\n inject,\n input,\n signal,\n} from '@angular/core';\n\nimport {\n TngAutocomplete,\n TngAutocompleteTrigger,\n TngAutocompleteTriggerContainer,\n TngAutocompleteIcon,\n TngAutocompleteContent,\n TngAutocompleteOverlay,\n TngAutocompleteListbox,\n TngAutocompleteOption,\n} from '@tailng-ui/primitives';\n\nexport type TngAutocompleteGetValue<O, V> = (opt: O) => V;\nexport type TngAutocompleteGetLabel<O> = (opt: O) => string;\nexport type TngAutocompleteIsDisabled<O> = (opt: O) => boolean;\nexport type TngAutocompleteTrackBy<O> = (index: number, opt: O) => unknown;\n\n@Component({\n selector: 'tng-autocomplete',\n standalone: true,\n imports: [\n TngAutocompleteTrigger,\n TngAutocompleteTriggerContainer,\n TngAutocompleteIcon,\n TngAutocompleteContent,\n TngAutocompleteOverlay,\n TngAutocompleteListbox,\n TngAutocompleteOption,\n ],\n hostDirectives: [\n {\n directive: TngAutocomplete,\n inputs: [\n 'open',\n 'value',\n 'disabled',\n 'loading',\n 'invalid',\n 'labelId',\n 'descriptionId',\n 'errorId',\n ],\n outputs: ['openChange', 'valueChange'],\n },\n ],\n templateUrl: './tng-autocomplete.component.html',\n styleUrl: './tng-autocomplete.component.css',\n})\nexport class TngAutocompleteComponent<O = unknown, V = unknown> {\n protected readonly primitive = inject<TngAutocomplete<V>>(TngAutocomplete);\n\n readonly options = input<readonly O[]>([]);\n readonly placeholder = input<string>('Type to search…');\n\n readonly getOptionValue = input<TngAutocompleteGetValue<O, V>>(\n ((opt: unknown) => (opt as { value?: V })?.value) as TngAutocompleteGetValue<O, V>,\n );\n readonly getOptionLabel = input<TngAutocompleteGetLabel<O>>(\n ((opt: unknown) =>\n String(\n (opt as { label?: string; value?: unknown })?.label ??\n (opt as { value?: unknown })?.value ??\n opt,\n )) as TngAutocompleteGetLabel<O>,\n );\n readonly isOptionDisabled = input<TngAutocompleteIsDisabled<O>>(\n ((opt: unknown) => !!(opt as { disabled?: boolean })?.disabled) as TngAutocompleteIsDisabled<O>,\n );\n /** Default: track by value, id, or option (stable identity for async/replaced options). */\n readonly trackBy = input<TngAutocompleteTrackBy<O>>((_, opt) => {\n const o = opt as Record<string, unknown> | null | undefined;\n return o?.['value'] ?? o?.['id'] ?? opt;\n });\n\n readonly iconText = input<string>('▾');\n\n readonly ariaLabel = input<string>('Autocomplete');\n\n protected readonly query = signal('');\n\n constructor() {\n effect(() => {\n const v = this.primitive.value();\n const open = this.primitive.open();\n if (!open) {\n const opt = this.findOption(v);\n this.query.set(opt ? this.getOptionLabel()(opt) : '');\n }\n });\n }\n\n protected readonly selectedOption = computed<O | null>(() => {\n const v = this.primitive.value();\n if (v === null) return null;\n const getV = this.getOptionValue();\n for (const opt of this.options()) {\n if (Object.is(getV(opt), v)) return opt;\n }\n return null;\n });\n\n protected readonly selectedLabel = computed<string>(() => {\n const opt = this.selectedOption();\n return opt ? this.getOptionLabel()(opt) : '';\n });\n\n protected readonly filteredOptions = computed<readonly O[]>(() => {\n const q = this.query().toLowerCase().trim();\n const list = this.options();\n if (!q) return list;\n const getLabel = this.getOptionLabel();\n return list.filter((opt) =>\n getLabel(opt).toLowerCase().includes(q),\n );\n });\n\n protected readonly displayText = computed<string>(() =>\n this.primitive.open() ? this.query() : this.selectedLabel(),\n );\n\n protected onInput(ev: Event): void {\n const val = (ev.target as HTMLInputElement).value;\n this.query.set(val);\n }\n\n private findOption(value: V | null): O | null {\n if (value === null) return null;\n const getV = this.getOptionValue();\n for (const opt of this.options()) {\n if (Object.is(getV(opt), value)) return opt;\n }\n return null;\n }\n}\n"]}
@@ -1,4 +1,12 @@
1
1
  export declare class TngChipsComponent {
2
- readonly ariaLabel: import("@angular/core").InputSignal<string>;
2
+ private readonly projectedChips;
3
+ readonly ariaLabel: import("@angular/core").InputSignal<string | null>;
4
+ readonly disabled: import("@angular/core").InputSignalWithTransform<boolean, string | boolean>;
5
+ readonly values: import("@angular/core").InputSignal<readonly unknown[] | undefined>;
6
+ readonly defaultValues: import("@angular/core").InputSignal<readonly unknown[]>;
7
+ readonly chipRemove: import("@angular/core").OutputEmitterRef<unknown>;
8
+ readonly valuesChange: import("@angular/core").OutputEmitterRef<readonly unknown[]>;
9
+ constructor();
10
+ private onProjectedChipRemove;
3
11
  }
4
12
  //# sourceMappingURL=tng-chips.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tng-chips.component.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/form/chips/tng-chips.component.ts"],"names":[],"mappings":"AAGA,qBAMa,iBAAiB;IAC5B,SAAgB,SAAS,8CAA0B;CACpD"}
1
+ {"version":3,"file":"tng-chips.component.d.ts","sourceRoot":"","sources":["../../../../../../../../libs/tailng-ui/components/src/lib/form/chips/tng-chips.component.ts"],"names":[],"mappings":"AAGA,qBAMa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAmD;IAElF,SAAgB,SAAS,qDAA0C;IACnE,SAAgB,QAAQ,8EAErB;IACH,SAAgB,MAAM,sEAAoD;IAC1E,SAAgB,aAAa,0DAAiC;IAE9D,SAAgB,UAAU,oDAAqB;IAC/C,SAAgB,YAAY,+DAAgC;;IAkB5D,OAAO,CAAC,qBAAqB;CAoB9B"}
@@ -1,8 +1,44 @@
1
1
  import { __decorate } from "tslib";
2
- import { Component, input } from '@angular/core';
3
- import { TngChips as TngChipsPrimitive } from '@tailng-ui/primitives';
2
+ import { Component, booleanAttribute, contentChildren, effect, input, output } from '@angular/core';
3
+ import { TngChip, TngChips as TngChipsPrimitive } from '@tailng-ui/primitives';
4
4
  let TngChipsComponent = class TngChipsComponent {
5
- ariaLabel = input('Chips');
5
+ projectedChips = contentChildren(TngChip, { descendants: true });
6
+ ariaLabel = input('Selected items');
7
+ disabled = input(false, {
8
+ transform: booleanAttribute,
9
+ });
10
+ values = input(undefined);
11
+ defaultValues = input([]);
12
+ chipRemove = output();
13
+ valuesChange = output();
14
+ constructor() {
15
+ effect((onCleanup) => {
16
+ const subscriptions = this.projectedChips().map((chip) => chip.chipRemove.subscribe((value) => {
17
+ this.onProjectedChipRemove(value);
18
+ }));
19
+ onCleanup(() => {
20
+ for (const subscription of subscriptions) {
21
+ subscription.unsubscribe();
22
+ }
23
+ });
24
+ });
25
+ }
26
+ onProjectedChipRemove(value) {
27
+ if (this.disabled()) {
28
+ return;
29
+ }
30
+ this.chipRemove.emit(value);
31
+ const currentValues = this.values();
32
+ if (currentValues === undefined) {
33
+ return;
34
+ }
35
+ const index = currentValues.findIndex((item) => Object.is(item, value));
36
+ if (index < 0) {
37
+ return;
38
+ }
39
+ const nextValues = [...currentValues.slice(0, index), ...currentValues.slice(index + 1)];
40
+ this.valuesChange.emit(nextValues);
41
+ }
6
42
  };
7
43
  TngChipsComponent = __decorate([
8
44
  Component({