ngx-com 0.0.19 → 0.0.21

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 (56) hide show
  1. package/fesm2022/ngx-com-components-alert.mjs +346 -0
  2. package/fesm2022/ngx-com-components-alert.mjs.map +1 -0
  3. package/fesm2022/ngx-com-components-button.mjs +1 -1
  4. package/fesm2022/ngx-com-components-button.mjs.map +1 -1
  5. package/fesm2022/ngx-com-components-calendar.mjs +29 -36
  6. package/fesm2022/ngx-com-components-calendar.mjs.map +1 -1
  7. package/fesm2022/ngx-com-components-card.mjs +1 -1
  8. package/fesm2022/ngx-com-components-card.mjs.map +1 -1
  9. package/fesm2022/ngx-com-components-carousel.mjs +708 -0
  10. package/fesm2022/ngx-com-components-carousel.mjs.map +1 -0
  11. package/fesm2022/ngx-com-components-checkbox.mjs +17 -8
  12. package/fesm2022/ngx-com-components-checkbox.mjs.map +1 -1
  13. package/fesm2022/ngx-com-components-code-block.mjs +158 -0
  14. package/fesm2022/ngx-com-components-code-block.mjs.map +1 -0
  15. package/fesm2022/ngx-com-components-collapsible.mjs +1 -1
  16. package/fesm2022/ngx-com-components-collapsible.mjs.map +1 -1
  17. package/fesm2022/ngx-com-components-confirm.mjs +3 -3
  18. package/fesm2022/ngx-com-components-confirm.mjs.map +1 -1
  19. package/fesm2022/ngx-com-components-dialog.mjs +703 -0
  20. package/fesm2022/ngx-com-components-dialog.mjs.map +1 -0
  21. package/fesm2022/ngx-com-components-dropdown.mjs +36 -31
  22. package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -1
  23. package/fesm2022/ngx-com-components-form-field.mjs +48 -8
  24. package/fesm2022/ngx-com-components-form-field.mjs.map +1 -1
  25. package/fesm2022/ngx-com-components-item.mjs +1 -1
  26. package/fesm2022/ngx-com-components-item.mjs.map +1 -1
  27. package/fesm2022/ngx-com-components-paginator.mjs +3 -3
  28. package/fesm2022/ngx-com-components-paginator.mjs.map +1 -1
  29. package/fesm2022/ngx-com-components-radio.mjs +16 -9
  30. package/fesm2022/ngx-com-components-radio.mjs.map +1 -1
  31. package/fesm2022/ngx-com-components-segmented-control.mjs +1 -1
  32. package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -1
  33. package/fesm2022/ngx-com-components-separator.mjs +102 -0
  34. package/fesm2022/ngx-com-components-separator.mjs.map +1 -0
  35. package/fesm2022/ngx-com-components-switch.mjs +258 -0
  36. package/fesm2022/ngx-com-components-switch.mjs.map +1 -0
  37. package/fesm2022/ngx-com-components-table.mjs +631 -0
  38. package/fesm2022/ngx-com-components-table.mjs.map +1 -0
  39. package/fesm2022/ngx-com-components-tabs.mjs +2 -2
  40. package/fesm2022/ngx-com-components-tabs.mjs.map +1 -1
  41. package/fesm2022/ngx-com-components-toast.mjs +783 -0
  42. package/fesm2022/ngx-com-components-toast.mjs.map +1 -0
  43. package/package.json +33 -1
  44. package/types/ngx-com-components-alert.d.ts +166 -0
  45. package/types/ngx-com-components-carousel.d.ts +281 -0
  46. package/types/ngx-com-components-checkbox.d.ts +7 -2
  47. package/types/ngx-com-components-code-block.d.ts +66 -0
  48. package/types/ngx-com-components-confirm.d.ts +2 -2
  49. package/types/ngx-com-components-dialog.d.ts +264 -0
  50. package/types/ngx-com-components-dropdown.d.ts +8 -5
  51. package/types/ngx-com-components-form-field.d.ts +19 -3
  52. package/types/ngx-com-components-radio.d.ts +5 -3
  53. package/types/ngx-com-components-separator.d.ts +75 -0
  54. package/types/ngx-com-components-switch.d.ts +110 -0
  55. package/types/ngx-com-components-table.d.ts +377 -0
  56. package/types/ngx-com-components-toast.d.ts +217 -0
@@ -0,0 +1,703 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, viewChild, signal, computed, ChangeDetectionStrategy, Component, Injector, DestroyRef, PLATFORM_ID, TemplateRef, Injectable, Directive, input } from '@angular/core';
3
+ import { NgTemplateOutlet, NgComponentOutlet, DOCUMENT, isPlatformBrowser } from '@angular/common';
4
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
+ import { Overlay } from '@angular/cdk/overlay';
6
+ import { ComponentPortal } from '@angular/cdk/portal';
7
+ import { filter } from 'rxjs/operators';
8
+ import { Subject, Observable } from 'rxjs';
9
+ import { FocusTrapFactory } from '@angular/cdk/a11y';
10
+ import { cva } from 'class-variance-authority';
11
+ import { mergeClasses } from 'ngx-com/utils';
12
+
13
+ /**
14
+ * Reference to an open dialog instance.
15
+ * Returned by `ComDialog.open()` for programmatic control.
16
+ */
17
+ class ComDialogRef {
18
+ afterClosedSubject = new Subject();
19
+ backdropClickSubject = new Subject();
20
+ closed = false;
21
+ /** @internal */
22
+ _overlayRef = null;
23
+ /** @internal */
24
+ _closeFn = null;
25
+ /**
26
+ * Close the dialog, optionally passing a result value.
27
+ */
28
+ close(result) {
29
+ if (this.closed)
30
+ return;
31
+ this._closeFn?.(result);
32
+ }
33
+ /**
34
+ * Emits the result once after the dialog is fully closed and disposed.
35
+ */
36
+ afterClosed() {
37
+ return this.afterClosedSubject.asObservable();
38
+ }
39
+ /**
40
+ * Emits each time the backdrop is clicked.
41
+ */
42
+ backdropClick() {
43
+ return this.backdropClickSubject.asObservable();
44
+ }
45
+ /**
46
+ * Proxies keydown events from the overlay.
47
+ */
48
+ keydownEvents() {
49
+ return this._overlayRef?.keydownEvents() ?? new Observable();
50
+ }
51
+ /** @internal Called by the service after exit animation completes. */
52
+ _notifyClosed(result) {
53
+ if (this.closed)
54
+ return;
55
+ this.closed = true;
56
+ this.afterClosedSubject.next(result);
57
+ this.afterClosedSubject.complete();
58
+ this.backdropClickSubject.complete();
59
+ }
60
+ /** @internal Forward backdrop click from overlay. */
61
+ _notifyBackdropClick(event) {
62
+ this.backdropClickSubject.next(event);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Injection token for data passed to a dialog component.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * readonly data = inject<MyData>(COM_DIALOG_DATA);
72
+ * ```
73
+ */
74
+ const COM_DIALOG_DATA = new InjectionToken('COM_DIALOG_DATA');
75
+ /**
76
+ * Injection token for global dialog configuration defaults.
77
+ * @internal
78
+ */
79
+ const COM_DIALOG_CONFIG = new InjectionToken('COM_DIALOG_CONFIG');
80
+ /**
81
+ * Provides global dialog configuration defaults.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * bootstrapApplication(AppComponent, {
86
+ * providers: [
87
+ * provideComDialogConfig({ size: 'lg', hasBackdrop: true }),
88
+ * ],
89
+ * });
90
+ * ```
91
+ */
92
+ function provideComDialogConfig(config) {
93
+ return { provide: COM_DIALOG_CONFIG, useValue: config };
94
+ }
95
+
96
+ /**
97
+ * Injection token allowing dialog directives to register their element IDs
98
+ * with the container for ARIA binding.
99
+ * @internal
100
+ */
101
+ const COM_DIALOG_CONTAINER_REF = new InjectionToken('COM_DIALOG_CONTAINER_REF');
102
+
103
+ /**
104
+ * CVA variants for the dialog backdrop.
105
+ *
106
+ * @tokens `--color-backdrop`
107
+ */
108
+ const dialogBackdropVariants = cva([
109
+ 'fixed',
110
+ 'inset-0',
111
+ 'z-50',
112
+ 'bg-backdrop',
113
+ 'backdrop-blur-sm',
114
+ ], {
115
+ variants: {
116
+ visible: {
117
+ true: 'animate-in fade-in-0',
118
+ false: 'animate-out fade-out-0',
119
+ },
120
+ },
121
+ defaultVariants: {
122
+ visible: true,
123
+ },
124
+ });
125
+ /**
126
+ * CVA variants for the dialog panel container.
127
+ *
128
+ * @tokens `--color-popover`, `--color-popover-foreground`, `--color-border`,
129
+ * `--shadow-xl`, `--radius-overlay`
130
+ */
131
+ const dialogPanelVariants = cva([
132
+ 'com-dialog-panel',
133
+ 'fixed',
134
+ 'left-1/2',
135
+ 'top-1/2',
136
+ '-translate-x-1/2',
137
+ '-translate-y-1/2',
138
+ 'z-50',
139
+ 'flex',
140
+ 'flex-col',
141
+ 'border',
142
+ 'border-border',
143
+ 'bg-popover',
144
+ 'text-popover-foreground',
145
+ 'shadow-xl',
146
+ 'rounded-overlay',
147
+ 'outline-none',
148
+ ], {
149
+ variants: {
150
+ size: {
151
+ sm: 'w-full max-w-sm max-h-[85vh] p-6',
152
+ md: 'w-full max-w-lg max-h-[85vh] p-6',
153
+ lg: 'w-full max-w-2xl max-h-[85vh] p-6',
154
+ xl: 'w-full max-w-4xl max-h-[85vh] p-6',
155
+ full: 'w-screen h-screen max-w-none max-h-none rounded-none border-none p-6',
156
+ },
157
+ visible: {
158
+ true: 'animate-in fade-in-0 zoom-in-95',
159
+ false: 'animate-out fade-out-0 zoom-out-95',
160
+ },
161
+ },
162
+ defaultVariants: {
163
+ size: 'md',
164
+ visible: true,
165
+ },
166
+ });
167
+ /**
168
+ * CVA variants for the dialog title.
169
+ *
170
+ * @tokens `--color-foreground`
171
+ */
172
+ const dialogTitleVariants = cva([
173
+ 'font-heading',
174
+ 'text-lg',
175
+ 'font-semibold',
176
+ 'tracking-tight',
177
+ 'text-foreground',
178
+ ]);
179
+ /**
180
+ * CVA variants for the dialog content area.
181
+ *
182
+ * @tokens `--color-muted-foreground`
183
+ */
184
+ const dialogContentVariants = cva([
185
+ 'flex-1',
186
+ 'overflow-y-auto',
187
+ 'py-4',
188
+ 'text-sm',
189
+ 'text-muted-foreground',
190
+ ]);
191
+ /**
192
+ * CVA variants for the dialog actions area.
193
+ */
194
+ const dialogActionsVariants = cva([
195
+ 'flex',
196
+ 'flex-col-reverse',
197
+ 'sm:flex-row',
198
+ 'sm:justify-end',
199
+ 'gap-2',
200
+ 'pt-4',
201
+ ]);
202
+
203
+ let dialogIdCounter = 0;
204
+ /** Generate a unique ID for a dialog title element. */
205
+ function generateDialogTitleId() {
206
+ return `com-dialog-title-${++dialogIdCounter}`;
207
+ }
208
+ /** Generate a unique ID for a dialog content element. */
209
+ function generateDialogContentId() {
210
+ return `com-dialog-content-${++dialogIdCounter}`;
211
+ }
212
+
213
+ /** Fallback timeout for exit animation when animationend doesn't fire. */
214
+ const ANIMATION_FALLBACK_MS = 200;
215
+ /**
216
+ * Internal dialog container component rendered inside the CDK overlay.
217
+ * Hosts the user's component or template and manages focus trap, animation, and ARIA.
218
+ *
219
+ * @internal Not exported in public API
220
+ *
221
+ * @tokens `--color-popover`, `--color-popover-foreground`, `--color-border`,
222
+ * `--color-foreground`, `--color-muted-foreground`, `--color-backdrop`,
223
+ * `--shadow-xl`, `--radius-overlay`, `--color-ring`
224
+ */
225
+ class ComDialogContainerComponent {
226
+ focusTrapFactory = inject(FocusTrapFactory);
227
+ focusTrap = null;
228
+ animationFallback = null;
229
+ closing = false;
230
+ pendingResult = undefined;
231
+ panelElement = viewChild('panelElement', ...(ngDevMode ? [{ debugName: "panelElement" }] : []));
232
+ /** Resolved dialog configuration, set by the service. */
233
+ config = signal(null, ...(ngDevMode ? [{ debugName: "config" }] : []));
234
+ /** Whether the panel is visible (for animation state). */
235
+ visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
236
+ /** The dialog ref for this instance, set by the service. */
237
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
238
+ dialogRef = signal(null, ...(ngDevMode ? [{ debugName: "dialogRef" }] : []));
239
+ /** Injector for content component, set by the service. */
240
+ contentInjector = signal(null, ...(ngDevMode ? [{ debugName: "contentInjector" }] : []));
241
+ // ─── Content signals (set by service) ───
242
+ /** Whether the content is a TemplateRef. */
243
+ isTemplate = signal(false, ...(ngDevMode ? [{ debugName: "isTemplate" }] : []));
244
+ /** Template ref content (when isTemplate is true). */
245
+ templateContent = signal(null, ...(ngDevMode ? [{ debugName: "templateContent" }] : []));
246
+ /** Component type content (when isTemplate is false). */
247
+ componentContent = signal(null, ...(ngDevMode ? [{ debugName: "componentContent" }] : []));
248
+ // ─── ARIA ID registration ───
249
+ /** ID of the title directive for aria-labelledby. */
250
+ titleId = signal('', ...(ngDevMode ? [{ debugName: "titleId" }] : []));
251
+ /** ID of the content directive for aria-describedby. */
252
+ contentId = signal('', ...(ngDevMode ? [{ debugName: "contentId" }] : []));
253
+ /** Register a title element ID (called by ComDialogTitle directive). */
254
+ registerTitleId(id) {
255
+ this.titleId.set(id);
256
+ }
257
+ /** Register a content element ID (called by ComDialogContent directive). */
258
+ registerContentId(id) {
259
+ this.contentId.set(id);
260
+ }
261
+ // ─── Computed classes ───
262
+ backdropClasses = computed(() => mergeClasses(dialogBackdropVariants({ visible: this.visible() })), ...(ngDevMode ? [{ debugName: "backdropClasses" }] : []));
263
+ panelClasses = computed(() => mergeClasses(dialogPanelVariants({
264
+ size: this.config()?.size ?? 'md',
265
+ visible: this.visible(),
266
+ }), this.config()?.panelClass ?? ''), ...(ngDevMode ? [{ debugName: "panelClasses" }] : []));
267
+ /** Template context for TemplateRef-based dialogs. */
268
+ templateContext = computed(() => ({
269
+ $implicit: this.dialogRef(),
270
+ data: this.config()?.data,
271
+ }), ...(ngDevMode ? [{ debugName: "templateContext" }] : []));
272
+ ngAfterViewInit() {
273
+ this.setupFocusTrap();
274
+ }
275
+ /** Handle backdrop click. */
276
+ onBackdropClick(event) {
277
+ const ref = this.dialogRef();
278
+ if (ref) {
279
+ ref._notifyBackdropClick(event);
280
+ }
281
+ if (!this.config()?.disableClose) {
282
+ this.dialogRef()?.close();
283
+ }
284
+ }
285
+ /** Start the close animation sequence. */
286
+ startClose(result) {
287
+ if (this.closing)
288
+ return;
289
+ this.closing = true;
290
+ this.pendingResult = result;
291
+ this.visible.set(false);
292
+ // Fallback in case animationend doesn't fire
293
+ this.animationFallback = setTimeout(() => this.finishClose(), ANIMATION_FALLBACK_MS);
294
+ }
295
+ /** Handle animation end on the panel. */
296
+ onAnimationEnd() {
297
+ if (this.closing) {
298
+ this.finishClose();
299
+ }
300
+ }
301
+ /** Clean up focus trap. */
302
+ destroyFocusTrap() {
303
+ this.focusTrap?.destroy();
304
+ this.focusTrap = null;
305
+ }
306
+ finishClose() {
307
+ if (this.animationFallback !== null) {
308
+ clearTimeout(this.animationFallback);
309
+ this.animationFallback = null;
310
+ }
311
+ this.destroyFocusTrap();
312
+ this.dialogRef()?._notifyClosed(this.pendingResult);
313
+ }
314
+ setupFocusTrap() {
315
+ const panelEl = this.panelElement()?.nativeElement;
316
+ if (!panelEl)
317
+ return;
318
+ this.focusTrap = this.focusTrapFactory.create(panelEl);
319
+ const autoFocus = this.config()?.autoFocus ?? 'first-tabbable';
320
+ if (autoFocus === 'first-tabbable') {
321
+ this.focusTrap.focusInitialElementWhenReady();
322
+ }
323
+ else if (autoFocus === 'dialog') {
324
+ panelEl.focus();
325
+ }
326
+ // autoFocus === false: don't move focus
327
+ }
328
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
329
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: ComDialogContainerComponent, isStandalone: true, selector: "com-dialog-container", viewQueries: [{ propertyName: "panelElement", first: true, predicate: ["panelElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
330
+ @if (config()?.hasBackdrop) {
331
+ <div
332
+ [class]="backdropClasses()"
333
+ [attr.data-state]="visible() ? 'open' : 'closed'"
334
+ aria-hidden="true"
335
+ (click)="onBackdropClick($event)"
336
+ ></div>
337
+ }
338
+ <div
339
+ #panelElement
340
+ [class]="panelClasses()"
341
+ role="dialog"
342
+ aria-modal="true"
343
+ [attr.aria-labelledby]="titleId() || null"
344
+ [attr.aria-describedby]="contentId() || null"
345
+ [attr.aria-label]="!titleId() && config()?.ariaLabel ? config()!.ariaLabel : null"
346
+ [attr.data-state]="visible() ? 'open' : 'closed'"
347
+ tabindex="-1"
348
+ (animationend)="onAnimationEnd()"
349
+ >
350
+ @if (isTemplate()) {
351
+ <ng-container
352
+ [ngTemplateOutlet]="templateContent()!"
353
+ [ngTemplateOutletContext]="templateContext()"
354
+ />
355
+ } @else if (componentContent(); as comp) {
356
+ @if (contentInjector(); as inj) {
357
+ <ng-container
358
+ *ngComponentOutlet="comp; injector: inj"
359
+ />
360
+ }
361
+ }
362
+ </div>
363
+ `, isInline: true, styles: [":host{display:contents}[data-state=open]{--tw-enter-opacity: 0;--tw-enter-scale: .95}[data-state=closed]{--tw-exit-opacity: 0;--tw-exit-scale: .95}@media(prefers-reduced-motion:reduce){[data-state=open],[data-state=closed]{animation:none}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
364
+ }
365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogContainerComponent, decorators: [{
366
+ type: Component,
367
+ args: [{ selector: 'com-dialog-container', template: `
368
+ @if (config()?.hasBackdrop) {
369
+ <div
370
+ [class]="backdropClasses()"
371
+ [attr.data-state]="visible() ? 'open' : 'closed'"
372
+ aria-hidden="true"
373
+ (click)="onBackdropClick($event)"
374
+ ></div>
375
+ }
376
+ <div
377
+ #panelElement
378
+ [class]="panelClasses()"
379
+ role="dialog"
380
+ aria-modal="true"
381
+ [attr.aria-labelledby]="titleId() || null"
382
+ [attr.aria-describedby]="contentId() || null"
383
+ [attr.aria-label]="!titleId() && config()?.ariaLabel ? config()!.ariaLabel : null"
384
+ [attr.data-state]="visible() ? 'open' : 'closed'"
385
+ tabindex="-1"
386
+ (animationend)="onAnimationEnd()"
387
+ >
388
+ @if (isTemplate()) {
389
+ <ng-container
390
+ [ngTemplateOutlet]="templateContent()!"
391
+ [ngTemplateOutletContext]="templateContext()"
392
+ />
393
+ } @else if (componentContent(); as comp) {
394
+ @if (contentInjector(); as inj) {
395
+ <ng-container
396
+ *ngComponentOutlet="comp; injector: inj"
397
+ />
398
+ }
399
+ }
400
+ </div>
401
+ `, imports: [NgTemplateOutlet, NgComponentOutlet], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:contents}[data-state=open]{--tw-enter-opacity: 0;--tw-enter-scale: .95}[data-state=closed]{--tw-exit-opacity: 0;--tw-exit-scale: .95}@media(prefers-reduced-motion:reduce){[data-state=open],[data-state=closed]{animation:none}}\n"] }]
402
+ }], propDecorators: { panelElement: [{ type: i0.ViewChild, args: ['panelElement', { isSignal: true }] }] } });
403
+
404
+ /** Default dialog configuration values. */
405
+ const COM_DIALOG_DEFAULTS = {
406
+ data: undefined,
407
+ size: 'md',
408
+ disableClose: false,
409
+ hasBackdrop: true,
410
+ backdropClass: '',
411
+ panelClass: '',
412
+ autoFocus: 'first-tabbable',
413
+ restoreFocus: true,
414
+ ariaLabel: '',
415
+ };
416
+
417
+ /**
418
+ * Service for opening dialog modals imperatively.
419
+ *
420
+ * @example
421
+ * ```typescript
422
+ * const dialog = inject(ComDialog);
423
+ *
424
+ * // Open a component
425
+ * const ref = dialog.open<boolean>(ConfirmComponent, { data: { id: 123 } });
426
+ * ref.afterClosed().subscribe(result => {
427
+ * if (result) performAction();
428
+ * });
429
+ *
430
+ * // Open a template
431
+ * dialog.open(templateRef, { size: 'sm' });
432
+ * ```
433
+ */
434
+ class ComDialog {
435
+ overlay = inject(Overlay);
436
+ injector = inject(Injector);
437
+ destroyRef = inject(DestroyRef);
438
+ platformId = inject(PLATFORM_ID);
439
+ document = inject(DOCUMENT);
440
+ globalConfig = inject(COM_DIALOG_CONFIG, { optional: true });
441
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
442
+ openDialogs = [];
443
+ /**
444
+ * Open a dialog with the given component or template.
445
+ *
446
+ * @param content - The component class or TemplateRef to render inside the dialog.
447
+ * @param config - Optional dialog configuration.
448
+ * @returns A reference to the opened dialog.
449
+ */
450
+ open(content, config) {
451
+ if (!isPlatformBrowser(this.platformId)) {
452
+ return new ComDialogRef();
453
+ }
454
+ const resolvedConfig = this.resolveConfig(config);
455
+ const previouslyFocused = this.document.activeElement;
456
+ // Create the dialog ref
457
+ const dialogRef = new ComDialogRef();
458
+ // Create a child injector with dialog-specific providers
459
+ const isTemplate = content instanceof TemplateRef;
460
+ const containerRef = {
461
+ registerTitleId: () => { },
462
+ registerContentId: () => { },
463
+ };
464
+ const contentInjector = Injector.create({
465
+ providers: [
466
+ { provide: ComDialogRef, useValue: dialogRef },
467
+ { provide: COM_DIALOG_DATA, useValue: resolvedConfig.data },
468
+ { provide: COM_DIALOG_CONTAINER_REF, useValue: containerRef },
469
+ ],
470
+ parent: this.injector,
471
+ });
472
+ // Create CDK overlay
473
+ const positionStrategy = this.overlay
474
+ .position()
475
+ .global()
476
+ .centerHorizontally()
477
+ .centerVertically();
478
+ const overlayRef = this.overlay.create({
479
+ positionStrategy,
480
+ scrollStrategy: this.overlay.scrollStrategies.block(),
481
+ hasBackdrop: false, // We handle backdrop ourselves for animation control
482
+ panelClass: 'com-dialog-overlay',
483
+ disposeOnNavigation: false,
484
+ });
485
+ dialogRef._overlayRef = overlayRef;
486
+ // Attach the container component
487
+ const portal = new ComponentPortal(ComDialogContainerComponent, null, contentInjector);
488
+ const containerComponentRef = overlayRef.attach(portal);
489
+ const container = containerComponentRef.instance;
490
+ // Wire up the container ref for ARIA registration
491
+ containerRef.registerTitleId = (id) => container.registerTitleId(id);
492
+ containerRef.registerContentId = (id) => container.registerContentId(id);
493
+ // Configure the container
494
+ container.config.set(resolvedConfig);
495
+ container.dialogRef.set(dialogRef);
496
+ container.isTemplate.set(isTemplate);
497
+ if (isTemplate) {
498
+ container.templateContent.set(content);
499
+ }
500
+ else {
501
+ container.componentContent.set(content);
502
+ container.contentInjector.set(contentInjector);
503
+ }
504
+ // Wire up the close function
505
+ dialogRef._closeFn = (result) => {
506
+ container.startClose(result);
507
+ };
508
+ // Subscribe to Escape key
509
+ overlayRef
510
+ .keydownEvents()
511
+ .pipe(filter((event) => event.key === 'Escape'), takeUntilDestroyed(this.destroyRef))
512
+ .subscribe((event) => {
513
+ if (!resolvedConfig.disableClose) {
514
+ event.preventDefault();
515
+ event.stopPropagation();
516
+ dialogRef.close();
517
+ }
518
+ });
519
+ // Track open dialog
520
+ this.openDialogs.push(dialogRef);
521
+ // Clean up after close
522
+ dialogRef.afterClosed().subscribe(() => {
523
+ const idx = this.openDialogs.indexOf(dialogRef);
524
+ if (idx !== -1) {
525
+ this.openDialogs.splice(idx, 1);
526
+ }
527
+ overlayRef.dispose();
528
+ // Restore focus
529
+ if (resolvedConfig.restoreFocus && previouslyFocused) {
530
+ previouslyFocused.focus();
531
+ }
532
+ });
533
+ // Show panel after a microtask to allow initial styles to apply
534
+ requestAnimationFrame(() => {
535
+ container.visible.set(true);
536
+ });
537
+ return dialogRef;
538
+ }
539
+ /** Close all open dialogs. */
540
+ closeAll() {
541
+ for (const ref of [...this.openDialogs]) {
542
+ ref.close();
543
+ }
544
+ }
545
+ /** Number of currently open dialogs. */
546
+ get openDialogCount() {
547
+ return this.openDialogs.length;
548
+ }
549
+ resolveConfig(config) {
550
+ return {
551
+ ...COM_DIALOG_DEFAULTS,
552
+ ...this.globalConfig,
553
+ ...config,
554
+ };
555
+ }
556
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialog, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
557
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialog, providedIn: 'root' });
558
+ }
559
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialog, decorators: [{
560
+ type: Injectable,
561
+ args: [{ providedIn: 'root' }]
562
+ }] });
563
+
564
+ /**
565
+ * Marks an element as the dialog title. Sets up aria-labelledby
566
+ * binding on the dialog container.
567
+ *
568
+ * @example
569
+ * ```html
570
+ * <h2 comDialogTitle>Delete item</h2>
571
+ * ```
572
+ *
573
+ * @tokens `--color-foreground`
574
+ */
575
+ class ComDialogTitle {
576
+ containerRef = inject(COM_DIALOG_CONTAINER_REF, { optional: true });
577
+ /** Unique ID for aria-labelledby binding. */
578
+ id = signal(generateDialogTitleId(), ...(ngDevMode ? [{ debugName: "id" }] : []));
579
+ /** Computed CSS classes. */
580
+ classes = signal(mergeClasses(dialogTitleVariants()), ...(ngDevMode ? [{ debugName: "classes" }] : []));
581
+ ngOnInit() {
582
+ this.containerRef?.registerTitleId(this.id());
583
+ }
584
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogTitle, deps: [], target: i0.ɵɵFactoryTarget.Directive });
585
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: ComDialogTitle, isStandalone: true, selector: "[comDialogTitle]", host: { properties: { "id": "id()", "class": "classes()" } }, exportAs: ["comDialogTitle"], ngImport: i0 });
586
+ }
587
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogTitle, decorators: [{
588
+ type: Directive,
589
+ args: [{
590
+ selector: '[comDialogTitle]',
591
+ exportAs: 'comDialogTitle',
592
+ host: {
593
+ '[id]': 'id()',
594
+ '[class]': 'classes()',
595
+ },
596
+ }]
597
+ }] });
598
+
599
+ /**
600
+ * Marks an element as the dialog content area. Sets up aria-describedby
601
+ * binding on the dialog container.
602
+ *
603
+ * @example
604
+ * ```html
605
+ * <div comDialogContent>
606
+ * <p>Are you sure you want to delete this item?</p>
607
+ * </div>
608
+ * ```
609
+ *
610
+ * @tokens `--color-muted-foreground`
611
+ */
612
+ class ComDialogContent {
613
+ containerRef = inject(COM_DIALOG_CONTAINER_REF, { optional: true });
614
+ /** Unique ID for aria-describedby binding. */
615
+ id = signal(generateDialogContentId(), ...(ngDevMode ? [{ debugName: "id" }] : []));
616
+ /** Computed CSS classes. */
617
+ classes = signal(mergeClasses(dialogContentVariants()), ...(ngDevMode ? [{ debugName: "classes" }] : []));
618
+ ngOnInit() {
619
+ this.containerRef?.registerContentId(this.id());
620
+ }
621
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
622
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: ComDialogContent, isStandalone: true, selector: "[comDialogContent]", host: { properties: { "id": "id()", "class": "classes()" } }, exportAs: ["comDialogContent"], ngImport: i0 });
623
+ }
624
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogContent, decorators: [{
625
+ type: Directive,
626
+ args: [{
627
+ selector: '[comDialogContent]',
628
+ exportAs: 'comDialogContent',
629
+ host: {
630
+ '[id]': 'id()',
631
+ '[class]': 'classes()',
632
+ },
633
+ }]
634
+ }] });
635
+
636
+ /**
637
+ * Marks an element as the dialog actions area (footer with buttons).
638
+ *
639
+ * @example
640
+ * ```html
641
+ * <div comDialogActions>
642
+ * <button comButton variant="outline" [comDialogClose]="false">Cancel</button>
643
+ * <button comButton [comDialogClose]="true">Confirm</button>
644
+ * </div>
645
+ * ```
646
+ */
647
+ class ComDialogActions {
648
+ /** Computed CSS classes. */
649
+ classes = signal(mergeClasses(dialogActionsVariants()), ...(ngDevMode ? [{ debugName: "classes" }] : []));
650
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogActions, deps: [], target: i0.ɵɵFactoryTarget.Directive });
651
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: ComDialogActions, isStandalone: true, selector: "[comDialogActions]", host: { properties: { "class": "classes()" } }, exportAs: ["comDialogActions"], ngImport: i0 });
652
+ }
653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogActions, decorators: [{
654
+ type: Directive,
655
+ args: [{
656
+ selector: '[comDialogActions]',
657
+ exportAs: 'comDialogActions',
658
+ host: {
659
+ '[class]': 'classes()',
660
+ },
661
+ }]
662
+ }] });
663
+
664
+ /**
665
+ * Closes the nearest dialog when the host element is clicked.
666
+ * Optionally passes a result value.
667
+ *
668
+ * @example
669
+ * ```html
670
+ * <button comButton [comDialogClose]="false">Cancel</button>
671
+ * <button comButton [comDialogClose]="true">Confirm</button>
672
+ * ```
673
+ */
674
+ class ComDialogClose {
675
+ dialogRef = inject(ComDialogRef);
676
+ /** The result value to pass when closing the dialog. */
677
+ comDialogClose = input(undefined, ...(ngDevMode ? [{ debugName: "comDialogClose" }] : []));
678
+ onClick() {
679
+ this.dialogRef.close(this.comDialogClose());
680
+ }
681
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogClose, deps: [], target: i0.ɵɵFactoryTarget.Directive });
682
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.0", type: ComDialogClose, isStandalone: true, selector: "[comDialogClose]", inputs: { comDialogClose: { classPropertyName: "comDialogClose", publicName: "comDialogClose", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()" } }, exportAs: ["comDialogClose"], ngImport: i0 });
683
+ }
684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComDialogClose, decorators: [{
685
+ type: Directive,
686
+ args: [{
687
+ selector: '[comDialogClose]',
688
+ exportAs: 'comDialogClose',
689
+ host: {
690
+ '(click)': 'onClick()',
691
+ },
692
+ }]
693
+ }], propDecorators: { comDialogClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "comDialogClose", required: false }] }] } });
694
+
695
+ // Public API for the dialog system
696
+ // Service
697
+
698
+ /**
699
+ * Generated bundle index. Do not edit.
700
+ */
701
+
702
+ export { COM_DIALOG_CONFIG, COM_DIALOG_DATA, ComDialog, ComDialogActions, ComDialogClose, ComDialogContent, ComDialogRef, ComDialogTitle, dialogActionsVariants, dialogBackdropVariants, dialogContentVariants, dialogPanelVariants, dialogTitleVariants, provideComDialogConfig };
703
+ //# sourceMappingURL=ngx-com-components-dialog.mjs.map