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.
- package/fesm2022/ngx-com-components-alert.mjs +346 -0
- package/fesm2022/ngx-com-components-alert.mjs.map +1 -0
- package/fesm2022/ngx-com-components-button.mjs +1 -1
- package/fesm2022/ngx-com-components-button.mjs.map +1 -1
- package/fesm2022/ngx-com-components-calendar.mjs +29 -36
- package/fesm2022/ngx-com-components-calendar.mjs.map +1 -1
- package/fesm2022/ngx-com-components-card.mjs +1 -1
- package/fesm2022/ngx-com-components-card.mjs.map +1 -1
- package/fesm2022/ngx-com-components-carousel.mjs +708 -0
- package/fesm2022/ngx-com-components-carousel.mjs.map +1 -0
- package/fesm2022/ngx-com-components-checkbox.mjs +17 -8
- package/fesm2022/ngx-com-components-checkbox.mjs.map +1 -1
- package/fesm2022/ngx-com-components-code-block.mjs +158 -0
- package/fesm2022/ngx-com-components-code-block.mjs.map +1 -0
- package/fesm2022/ngx-com-components-collapsible.mjs +1 -1
- package/fesm2022/ngx-com-components-collapsible.mjs.map +1 -1
- package/fesm2022/ngx-com-components-confirm.mjs +3 -3
- package/fesm2022/ngx-com-components-confirm.mjs.map +1 -1
- package/fesm2022/ngx-com-components-dialog.mjs +703 -0
- package/fesm2022/ngx-com-components-dialog.mjs.map +1 -0
- package/fesm2022/ngx-com-components-dropdown.mjs +36 -31
- package/fesm2022/ngx-com-components-dropdown.mjs.map +1 -1
- package/fesm2022/ngx-com-components-form-field.mjs +48 -8
- package/fesm2022/ngx-com-components-form-field.mjs.map +1 -1
- package/fesm2022/ngx-com-components-item.mjs +1 -1
- package/fesm2022/ngx-com-components-item.mjs.map +1 -1
- package/fesm2022/ngx-com-components-paginator.mjs +3 -3
- package/fesm2022/ngx-com-components-paginator.mjs.map +1 -1
- package/fesm2022/ngx-com-components-radio.mjs +16 -9
- package/fesm2022/ngx-com-components-radio.mjs.map +1 -1
- package/fesm2022/ngx-com-components-segmented-control.mjs +1 -1
- package/fesm2022/ngx-com-components-segmented-control.mjs.map +1 -1
- package/fesm2022/ngx-com-components-separator.mjs +102 -0
- package/fesm2022/ngx-com-components-separator.mjs.map +1 -0
- package/fesm2022/ngx-com-components-switch.mjs +258 -0
- package/fesm2022/ngx-com-components-switch.mjs.map +1 -0
- package/fesm2022/ngx-com-components-table.mjs +631 -0
- package/fesm2022/ngx-com-components-table.mjs.map +1 -0
- package/fesm2022/ngx-com-components-tabs.mjs +2 -2
- package/fesm2022/ngx-com-components-tabs.mjs.map +1 -1
- package/fesm2022/ngx-com-components-toast.mjs +783 -0
- package/fesm2022/ngx-com-components-toast.mjs.map +1 -0
- package/package.json +33 -1
- package/types/ngx-com-components-alert.d.ts +166 -0
- package/types/ngx-com-components-carousel.d.ts +281 -0
- package/types/ngx-com-components-checkbox.d.ts +7 -2
- package/types/ngx-com-components-code-block.d.ts +66 -0
- package/types/ngx-com-components-confirm.d.ts +2 -2
- package/types/ngx-com-components-dialog.d.ts +264 -0
- package/types/ngx-com-components-dropdown.d.ts +8 -5
- package/types/ngx-com-components-form-field.d.ts +19 -3
- package/types/ngx-com-components-radio.d.ts +5 -3
- package/types/ngx-com-components-separator.d.ts +75 -0
- package/types/ngx-com-components-switch.d.ts +110 -0
- package/types/ngx-com-components-table.d.ts +377 -0
- 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
|