@praxisui/dialog 0.0.1

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.
@@ -0,0 +1,981 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Directive, EventEmitter, inject, HostBinding, ContentChild, ViewChild, Output, Input, ViewEncapsulation, ChangeDetectionStrategy, Component, Injectable, ApplicationRef, Injector, TemplateRef } from '@angular/core';
3
+ import { Subject } from 'rxjs';
4
+ import * as i1 from '@angular/common';
5
+ import { CommonModule } from '@angular/common';
6
+ import * as i2 from '@angular/cdk/portal';
7
+ import { PortalModule, CdkPortalOutlet, ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
8
+ import * as i3 from '@angular/cdk/a11y';
9
+ import { A11yModule } from '@angular/cdk/a11y';
10
+ import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
11
+ import * as i2$1 from '@angular/forms';
12
+ import { FormsModule } from '@angular/forms';
13
+ import { DomSanitizer } from '@angular/platform-browser';
14
+ import { GlobalConfigService } from '@praxisui/core';
15
+
16
+ const PRAXIS_DIALOG_DATA = new InjectionToken('PRAXIS_DIALOG_DATA');
17
+ const PRAXIS_DIALOG_I18N = new InjectionToken('PRAXIS_DIALOG_I18N', {
18
+ providedIn: 'root',
19
+ factory: () => ({ ok: 'OK', cancel: 'Cancelar', yes: 'Sim', no: 'Não' }),
20
+ });
21
+ const PRAXIS_DIALOG_DEFAULTS = new InjectionToken('PRAXIS_DIALOG_DEFAULTS', {
22
+ providedIn: 'root',
23
+ factory: () => ({ maxWidth: '90vw', maxHeight: '80vh' }),
24
+ });
25
+ const PRAXIS_DIALOG_CONTENT_REGISTRY = new InjectionToken('PRAXIS_DIALOG_CONTENT_REGISTRY', {
26
+ providedIn: 'root',
27
+ factory: () => ({}),
28
+ });
29
+ const PRAXIS_DIALOG_TEMPLATE_REGISTRY = new InjectionToken('PRAXIS_DIALOG_TEMPLATE_REGISTRY', {
30
+ providedIn: 'root',
31
+ factory: () => ({}),
32
+ });
33
+ const PRAXIS_DIALOG_GLOBAL_PRESETS = new InjectionToken('PRAXIS_DIALOG_GLOBAL_PRESETS', {
34
+ providedIn: 'root',
35
+ factory: () => ({}),
36
+ });
37
+
38
+ class PraxisDialogRef {
39
+ overlayRef;
40
+ id;
41
+ _afterOpened = new Subject();
42
+ _beforeClosed = new Subject();
43
+ _afterClosed = new Subject();
44
+ _backdropClick = new Subject();
45
+ _keydownEvents = new Subject();
46
+ componentInstance;
47
+ constructor(overlayRef) {
48
+ this.overlayRef = overlayRef;
49
+ }
50
+ beforeCloseHandler;
51
+ setBeforeClose(handler) {
52
+ this.beforeCloseHandler = handler;
53
+ }
54
+ emitOpened() {
55
+ this._afterOpened.next();
56
+ this._afterOpened.complete();
57
+ }
58
+ backdropClick() {
59
+ return this._backdropClick.asObservable();
60
+ }
61
+ keydownEvents() {
62
+ return this._keydownEvents.asObservable();
63
+ }
64
+ afterOpened() {
65
+ return this._afterOpened.asObservable();
66
+ }
67
+ beforeClosed() {
68
+ return this._beforeClosed.asObservable();
69
+ }
70
+ afterClosed() {
71
+ return this._afterClosed.asObservable();
72
+ }
73
+ notifyBackdropClick(ev) {
74
+ this._backdropClick.next(ev);
75
+ }
76
+ notifyKeydown(ev) {
77
+ this._keydownEvents.next(ev);
78
+ }
79
+ async close(result) {
80
+ this._beforeClosed.next(result);
81
+ this._beforeClosed.complete();
82
+ if (this.beforeCloseHandler) {
83
+ try {
84
+ await this.beforeCloseHandler(result);
85
+ }
86
+ catch { }
87
+ }
88
+ this.overlayRef.dispose();
89
+ this._afterClosed.next(result);
90
+ this._afterClosed.complete();
91
+ this._backdropClick.complete();
92
+ this._keydownEvents.complete();
93
+ }
94
+ updateSize(width, height) {
95
+ this.overlayRef.updateSize({
96
+ width: coerceCssUnit$1(width),
97
+ height: coerceCssUnit$1(height),
98
+ });
99
+ return this;
100
+ }
101
+ updatePosition(position) {
102
+ this.overlayRef.updatePositionStrategy(this.overlayRef.getConfig().positionStrategy);
103
+ if (position) {
104
+ const ps = this.overlayRef.getConfig().positionStrategy;
105
+ if (ps?.top !== undefined && position.top != null)
106
+ ps.top(position.top);
107
+ if (ps?.bottom !== undefined && position.bottom != null)
108
+ ps.bottom(position.bottom);
109
+ if (ps?.left !== undefined && position.left != null)
110
+ ps.left(position.left);
111
+ if (ps?.right !== undefined && position.right != null)
112
+ ps.right(position.right);
113
+ this.overlayRef.updatePosition();
114
+ }
115
+ return this;
116
+ }
117
+ }
118
+ function coerceCssUnit$1(v) {
119
+ if (v == null)
120
+ return undefined;
121
+ return typeof v === 'number' ? `${v}px` : v;
122
+ }
123
+
124
+ class PraxisDialogTitleDirective {
125
+ templateRef;
126
+ constructor(templateRef) {
127
+ this.templateRef = templateRef;
128
+ }
129
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogTitleDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
130
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.4", type: PraxisDialogTitleDirective, isStandalone: true, selector: "ng-template[praxisDialogTitle]", ngImport: i0 });
131
+ }
132
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogTitleDirective, decorators: [{
133
+ type: Directive,
134
+ args: [{ selector: 'ng-template[praxisDialogTitle]', standalone: true }]
135
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
136
+
137
+ class PraxisDialogActionsDirective {
138
+ templateRef;
139
+ constructor(templateRef) {
140
+ this.templateRef = templateRef;
141
+ }
142
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogActionsDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
143
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.4", type: PraxisDialogActionsDirective, isStandalone: true, selector: "ng-template[praxisDialogActions]", ngImport: i0 });
144
+ }
145
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogActionsDirective, decorators: [{
146
+ type: Directive,
147
+ args: [{ selector: 'ng-template[praxisDialogActions]', standalone: true }]
148
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
149
+
150
+ class PraxisDialogContentDirective {
151
+ templateRef;
152
+ constructor(templateRef) {
153
+ this.templateRef = templateRef;
154
+ }
155
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogContentDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
156
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.4", type: PraxisDialogContentDirective, isStandalone: true, selector: "ng-template[praxisDialogContent]", ngImport: i0 });
157
+ }
158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogContentDirective, decorators: [{
159
+ type: Directive,
160
+ args: [{ selector: 'ng-template[praxisDialogContent]', standalone: true }]
161
+ }], ctorParameters: () => [{ type: i0.TemplateRef }] });
162
+
163
+ class PraxisDialogComponent {
164
+ title;
165
+ actions = [];
166
+ actionsLayout = 'stretched';
167
+ animation = true;
168
+ open = true;
169
+ width;
170
+ height;
171
+ minWidth;
172
+ maxWidth;
173
+ minHeight;
174
+ maxHeight;
175
+ themeColor = 'light';
176
+ disableClose = false;
177
+ hasBackdrop = true;
178
+ overlayMode = false; // true when opened via service/CDK overlay
179
+ zIndex;
180
+ panelClass;
181
+ backdropClass;
182
+ position;
183
+ autoFocusedElement;
184
+ autoFocus;
185
+ restoreFocus = true;
186
+ id;
187
+ ariaRole = 'dialog';
188
+ ariaLabel;
189
+ ariaLabelledBy;
190
+ ariaDescribedBy;
191
+ styles;
192
+ titleIcon;
193
+ action = new EventEmitter();
194
+ close = new EventEmitter();
195
+ opened = new EventEmitter();
196
+ afterClosed = new EventEmitter();
197
+ contentHost;
198
+ panelEl;
199
+ titleTpl;
200
+ actionsTpl;
201
+ contentTpl;
202
+ hostClass = 'pdx-dialog-host';
203
+ tabindex = '-1';
204
+ data = inject(PRAXIS_DIALOG_DATA, { optional: true });
205
+ get titleId() { return (this.id ?? 'praxis-dialog') + '-title'; }
206
+ isDisplayed = false; // controls DOM visibility for animation in tag mode
207
+ ngOnInit() {
208
+ if (this.open) {
209
+ this.isDisplayed = true;
210
+ this.applyAnimationVars();
211
+ this.applyStyleVars();
212
+ this.beginOpenAnimation().then(() => {
213
+ if (!this.overlayMode)
214
+ this.opened.emit();
215
+ });
216
+ this.scheduleInitialFocus();
217
+ }
218
+ if (this.ariaRole === 'alertdialog') {
219
+ const hasLabel = !!(this.title || this.ariaLabel || this.ariaLabelledBy);
220
+ try {
221
+ const isDev = typeof globalThis.ngDevMode !== 'undefined' ? !!globalThis.ngDevMode : true;
222
+ if (isDev && !hasLabel) {
223
+ console.warn('[PraxisDialog] alertdialog requires a title or aria label.');
224
+ }
225
+ }
226
+ catch { }
227
+ }
228
+ }
229
+ ngOnChanges(changes) {
230
+ if (changes['open'] && this.open) {
231
+ this.isDisplayed = true;
232
+ this.applyAnimationVars();
233
+ this.applyStyleVars();
234
+ this.beginOpenAnimation().then(() => {
235
+ if (!this.overlayMode)
236
+ this.opened.emit();
237
+ });
238
+ this.scheduleInitialFocus();
239
+ }
240
+ if (changes['open'] && !this.open && !this.overlayMode && this.isDisplayed) {
241
+ // animate close then hide
242
+ this.beginCloseAnimation().then(() => {
243
+ this.isDisplayed = false;
244
+ this.afterClosed.emit();
245
+ if (this.restoreFocus && this._previouslyFocused && typeof this._previouslyFocused.focus === 'function') {
246
+ try {
247
+ this._previouslyFocused.focus();
248
+ }
249
+ catch { }
250
+ }
251
+ });
252
+ }
253
+ if (changes['animation']) {
254
+ this.applyAnimationVars();
255
+ }
256
+ if (changes['styles']) {
257
+ this.applyStyleVars();
258
+ }
259
+ }
260
+ onKeydown(ev) {
261
+ if (ev.key === 'Escape' && !this.disableClose) {
262
+ ev.stopPropagation();
263
+ this.close.emit();
264
+ }
265
+ if ((ev.key === 'Enter' || ev.key === ' ') && !this.disableClose) {
266
+ const target = ev.target;
267
+ const isInteractive = !!target && /^(BUTTON|A|INPUT|TEXTAREA|SELECT)$/.test(target.tagName);
268
+ if (!isInteractive) {
269
+ const primary = this.actions.find((a) => a.role === 'primary') || this.actions[0];
270
+ if (primary) {
271
+ ev.preventDefault();
272
+ this.onActionClick(primary);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ onActionClick(a) {
278
+ this.action.emit(a);
279
+ if (a.close) {
280
+ this.close.emit(a.payload);
281
+ }
282
+ }
283
+ onBackdrop(ev) {
284
+ if (!this.hasBackdrop)
285
+ return;
286
+ if (this.disableClose)
287
+ return;
288
+ // Avoid closing when clicking inside the panel area
289
+ this.close.emit();
290
+ }
291
+ focus() {
292
+ try {
293
+ this.panelEl?.nativeElement?.focus?.();
294
+ }
295
+ catch { }
296
+ }
297
+ scheduleInitialFocus() {
298
+ const panel = this.panelEl?.nativeElement;
299
+ if (!panel)
300
+ return;
301
+ const prev = document.activeElement;
302
+ if (!this._previouslyFocused && this.restoreFocus)
303
+ this._previouslyFocused = prev;
304
+ queueMicrotask(() => {
305
+ try {
306
+ if (this.autoFocus && this.ariaRole === 'alertdialog') {
307
+ // In alertdialog, focus the primary action if present
308
+ const focused = this.focusPrimaryAction();
309
+ if (focused)
310
+ return;
311
+ }
312
+ if (this.autoFocusedElement) {
313
+ const el = panel.querySelector(this.autoFocusedElement);
314
+ el?.focus?.();
315
+ return;
316
+ }
317
+ // focus first focusable
318
+ const focusable = panel.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
319
+ (focusable || panel).focus?.();
320
+ }
321
+ catch { }
322
+ });
323
+ }
324
+ _previouslyFocused;
325
+ ngOnDestroy() {
326
+ if (this.restoreFocus && this._previouslyFocused && typeof this._previouslyFocused.focus === 'function') {
327
+ try {
328
+ this._previouslyFocused.focus();
329
+ }
330
+ catch { }
331
+ }
332
+ }
333
+ get animationClasses() {
334
+ const anim = this.animation;
335
+ if (!anim)
336
+ return [];
337
+ const conf = typeof anim === 'boolean' ? { type: 'translate', duration: 300 } : anim;
338
+ const type = conf.type || 'translate';
339
+ const dir = conf.direction || 'up';
340
+ const map = {
341
+ translate: `pdx-anim-translate-${dir}`,
342
+ slide: `pdx-anim-translate-${dir}`,
343
+ fade: 'pdx-anim-fade',
344
+ expand: 'pdx-anim-zoom',
345
+ zoom: 'pdx-anim-zoom',
346
+ };
347
+ return [map[type] || ''];
348
+ }
349
+ get panelNgClass() {
350
+ const anim = this.animationClasses || [];
351
+ const extra = this.panelClass;
352
+ if (extra && typeof extra === 'object' && !Array.isArray(extra)) {
353
+ const obj = { ...extra };
354
+ for (const c of anim)
355
+ obj[c] = true;
356
+ return obj;
357
+ }
358
+ if (typeof extra === 'string')
359
+ return [...anim, extra];
360
+ if (Array.isArray(extra))
361
+ return [...anim, ...extra];
362
+ return anim;
363
+ }
364
+ applyAnimationVars() {
365
+ if (!this.panelEl)
366
+ return;
367
+ const anim = this.animation;
368
+ const conf = anim === true || anim == null ? { type: 'translate', duration: 300 } : anim;
369
+ const dur = (conf.duration ?? 300);
370
+ try {
371
+ this.panelEl.nativeElement.style.setProperty('--pdx-dialog-anim-duration', `${dur}ms`);
372
+ this.panelEl.nativeElement.style.setProperty('--pdx-dialog-anim-ease', conf.easing || 'cubic-bezier(0.2, 0, 0, 1)');
373
+ }
374
+ catch { }
375
+ }
376
+ applyStyleVars() {
377
+ if (!this.panelEl)
378
+ return;
379
+ const s = this.styles || {};
380
+ const set = (name, value) => {
381
+ if (value == null)
382
+ return;
383
+ try {
384
+ this.panelEl.nativeElement.style.setProperty(name, String(value));
385
+ }
386
+ catch { }
387
+ };
388
+ // Base
389
+ set('--pdx-dialog-actions-padding', s.actionsPadding);
390
+ set('--pdx-dialog-elevation-shadow', s.containerElevationShadow);
391
+ set('--pdx-dialog-max-width', s.containerMaxWidth);
392
+ set('--pdx-dialog-min-width', s.containerMinWidth);
393
+ set('--pdx-dialog-shape', s.containerShape);
394
+ set('--pdx-dialog-small-max-width', s.containerSmallMaxWidth);
395
+ set('--pdx-dialog-content-padding', s.contentPadding);
396
+ set('--pdx-dialog-headline-padding', s.headlinePadding);
397
+ set('--pdx-dialog-with-actions-content-padding', s.withActionsContentPadding);
398
+ // Color
399
+ set('--pdx-dialog-container-color', s.containerColor);
400
+ set('--pdx-dialog-subhead-color', s.subheadColor);
401
+ set('--pdx-dialog-supporting-text-color', s.supportingTextColor);
402
+ // Typography - title
403
+ set('--pdx-dialog-subhead-font', s.subheadFont);
404
+ set('--pdx-dialog-subhead-line-height', s.subheadLineHeight);
405
+ set('--pdx-dialog-subhead-size', s.subheadSize);
406
+ set('--pdx-dialog-subhead-tracking', s.subheadTracking);
407
+ set('--pdx-dialog-subhead-weight', s.subheadWeight);
408
+ // Typography - content
409
+ set('--pdx-dialog-supporting-text-font', s.supportingTextFont);
410
+ set('--pdx-dialog-supporting-text-line-height', s.supportingTextLineHeight);
411
+ set('--pdx-dialog-supporting-text-size', s.supportingTextSize);
412
+ set('--pdx-dialog-supporting-text-tracking', s.supportingTextTracking);
413
+ set('--pdx-dialog-supporting-text-weight', s.supportingTextWeight);
414
+ // Alignment alias
415
+ if (s.actionsAlignment)
416
+ this.actionsLayout = s.actionsAlignment;
417
+ }
418
+ beginOpenAnimation() {
419
+ if (!this.animation)
420
+ return Promise.resolve();
421
+ const panel = this.panelEl?.nativeElement;
422
+ if (!panel)
423
+ return Promise.resolve();
424
+ panel.classList.add('pdx-state-opening');
425
+ return new Promise((resolve) => {
426
+ requestAnimationFrame(() => {
427
+ panel.classList.remove('pdx-state-opening');
428
+ panel.classList.add('pdx-state-open');
429
+ // resolve after transition duration
430
+ const dur = parseFloat(getComputedStyle(panel).getPropertyValue('--pdx-dialog-anim-duration')) || 300;
431
+ setTimeout(() => resolve(), dur);
432
+ });
433
+ });
434
+ }
435
+ beginCloseAnimation() {
436
+ if (!this.animation)
437
+ return Promise.resolve();
438
+ const panel = this.panelEl?.nativeElement;
439
+ if (!panel)
440
+ return Promise.resolve();
441
+ panel.classList.add('pdx-state-closing');
442
+ return new Promise((resolve) => {
443
+ let done = false;
444
+ const finish = () => { if (done)
445
+ return; done = true; panel.removeEventListener('transitionend', onEnd); resolve(); };
446
+ const onEnd = (ev) => {
447
+ if (ev.propertyName === 'opacity' || ev.propertyName === 'transform')
448
+ finish();
449
+ };
450
+ panel.addEventListener('transitionend', onEnd);
451
+ const durVar = getComputedStyle(panel).getPropertyValue('--pdx-dialog-anim-duration');
452
+ const durMs = parseFloat(durVar) || 300;
453
+ setTimeout(finish, durMs + 20);
454
+ });
455
+ }
456
+ focusPrimaryAction() {
457
+ try {
458
+ const panel = this.panelEl?.nativeElement;
459
+ if (!panel)
460
+ return false;
461
+ const buttons = Array.from(panel.querySelectorAll('.pdx-dialog__actions .pdx-dialog__action-btn'));
462
+ if (buttons.length === 0)
463
+ return false;
464
+ // Prefer the first button corresponding to a primary role
465
+ const primaryIndex = this.actions.findIndex((a) => a?.role === 'primary');
466
+ if (primaryIndex >= 0 && buttons[primaryIndex]) {
467
+ buttons[primaryIndex].focus();
468
+ return true;
469
+ }
470
+ // Fallback to last button
471
+ buttons[buttons.length - 1].focus();
472
+ return true;
473
+ }
474
+ catch {
475
+ return false;
476
+ }
477
+ }
478
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
479
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: PraxisDialogComponent, isStandalone: true, selector: "praxis-dialog", inputs: { title: "title", actions: "actions", actionsLayout: "actionsLayout", animation: "animation", open: "open", width: "width", height: "height", minWidth: "minWidth", maxWidth: "maxWidth", minHeight: "minHeight", maxHeight: "maxHeight", themeColor: "themeColor", disableClose: "disableClose", hasBackdrop: "hasBackdrop", overlayMode: "overlayMode", zIndex: "zIndex", panelClass: "panelClass", backdropClass: "backdropClass", position: "position", autoFocusedElement: "autoFocusedElement", autoFocus: "autoFocus", restoreFocus: "restoreFocus", id: "id", ariaRole: "ariaRole", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", ariaDescribedBy: "ariaDescribedBy", styles: "styles", titleIcon: "titleIcon" }, outputs: { action: "action", close: "close", opened: "opened", afterClosed: "afterClosed" }, host: { properties: { "class": "this.hostClass", "attr.tabindex": "this.tabindex" } }, queries: [{ propertyName: "titleTpl", first: true, predicate: PraxisDialogTitleDirective, descendants: true, read: PraxisDialogTitleDirective }, { propertyName: "actionsTpl", first: true, predicate: PraxisDialogActionsDirective, descendants: true, read: PraxisDialogActionsDirective }, { propertyName: "contentTpl", first: true, predicate: PraxisDialogContentDirective, descendants: true, read: PraxisDialogContentDirective }], viewQueries: [{ propertyName: "contentHost", first: true, predicate: CdkPortalOutlet, descendants: true }, { propertyName: "panelEl", first: true, predicate: ["panel"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "@let _titleId = (id ?? 'praxis-dialog') + '-title';\n\n<ng-template #panelHeader>\n <div class=\"pdx-dialog__title\" @if (titleTpl?.templateRef || title || titleIcon)>\n @if (titleTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"titleTpl!.templateRef\"></ng-container>\n } @else {\n <div class=\"pdx-dialog__title-row\">\n <span *ngIf=\"titleIcon\" class=\"material-icons pdx-dialog__title-icon\" aria-hidden=\"true\">{{ titleIcon }}</span>\n <h2 class=\"pdx-dialog__title-text\" [attr.id]=\"_titleId\">{{ title }}</h2>\n </div>\n }\n </div>\n </ng-template>\n\n<ng-template #panelActions>\n <div class=\"pdx-dialog__actions\" [class.is-stretched]=\"actionsLayout==='stretched'\">\n @if (actionsTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"actionsTpl!.templateRef\"></ng-container>\n } @else {\n @for (a of actions; track a?.id ?? a) {\n <button\n type=\"button\"\n class=\"pdx-dialog__action-btn\"\n [attr.data-testid]=\"a.id || null\"\n [ngClass]=\"a.cssClass\"\n (click)=\"onActionClick(a)\"\n [attr.cdkFocusInitial]=\"a.role==='primary' && ariaRole==='alertdialog' ? '' : null\"\n >\n <span *ngIf=\"a.icon\" class=\"material-icons\">{{ a.icon }}</span>\n {{ a.text }}\n </button>\n }\n }\n </div>\n</ng-template>\n\n@if (overlayMode) {\n <div\n #panel\n class=\"pdx-dialog pdx-dialog--{{ themeColor }} pdx-dialog--layout-{{ actionsLayout }}\"\n [attr.role]=\"ariaRole\"\n [attr.id]=\"id || null\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"(ariaLabelledBy || (title ? _titleId : null))\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n (keydown)=\"onKeydown($event)\"\n cdkTrapFocus\n [ngClass]=\"panelNgClass\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n [style.min-width]=\"minWidth\"\n [style.max-width]=\"maxWidth\"\n [style.min-height]=\"minHeight\"\n [style.max-height]=\"maxHeight\"\n [class.has-actions]=\"(actions.length||0)>0 || !!actionsTpl?.templateRef\"\n >\n <ng-container [ngTemplateOutlet]=\"panelHeader\"></ng-container>\n <div class=\"pdx-dialog__content\">\n @if (contentTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"contentTpl!.templateRef\"></ng-container>\n } @else {\n <ng-template cdkPortalOutlet></ng-template>\n }\n </div>\n <ng-container [ngTemplateOutlet]=\"panelActions\"></ng-container>\n </div>\n} @else {\n <div class=\"pdx-dialog-overlay\" [class.has-backdrop]=\"hasBackdrop\" [hidden]=\"!isDisplayed\" (click)=\"onBackdrop($event)\" [ngClass]=\"backdropClass\" [style.z-index]=\"zIndex\">\n <div class=\"pdx-dialog-shell\" (click)=\"$event.stopPropagation()\">\n <div\n #panel\n class=\"pdx-dialog pdx-dialog--{{ themeColor }} pdx-dialog--layout-{{ actionsLayout }}\"\n [attr.role]=\"ariaRole\"\n [attr.id]=\"id || null\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"(ariaLabelledBy || (title ? _titleId : null))\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n (keydown)=\"onKeydown($event)\"\n cdkTrapFocus\n [ngClass]=\"panelNgClass\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n [style.min-width]=\"minWidth\"\n [style.max-width]=\"maxWidth\"\n [style.min-height]=\"minHeight\"\n [style.max-height]=\"maxHeight\"\n [style.top]=\"position?.top || null\"\n [style.bottom]=\"position?.bottom || null\"\n [style.left]=\"position?.left || null\"\n [style.right]=\"position?.right || null\"\n [class.pdx-has-position]=\"position\"\n [class.has-actions]=\"(actions.length||0)>0 || !!actionsTpl?.templateRef\"\n >\n <ng-container [ngTemplateOutlet]=\"panelHeader\"></ng-container>\n <div class=\"pdx-dialog__content\">\n @if (contentTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"contentTpl!.templateRef\"></ng-container>\n } @else {\n <ng-template cdkPortalOutlet></ng-template>\n }\n </div>\n <ng-container [ngTemplateOutlet]=\"panelActions\"></ng-container>\n </div>\n </div>\n </div>\n}\n", styles: [".pdx-dialog-host{--pdx-dialog-bg: var(--pdx-dialog-bg, #fff);--pdx-dialog-surface: var(--pdx-dialog-surface, #fff);--pdx-dialog-border: var(--pdx-dialog-border, rgba(0, 0, 0, .12));--pdx-dialog-title-fg: var(--pdx-dialog-title-fg, #111);--pdx-dialog-elevation: var(--pdx-dialog-elevation, 24)}.pdx-dialog-overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:1000;background:transparent}.pdx-dialog-overlay.has-backdrop{background:#00000059}.pdx-dialog-shell{display:contents}.pdx-dialog.pdx-has-position{position:absolute}.pdx-dialog{background:var(--pdx-dialog-container-color, var(--pdx-dialog-surface));color:#111;border:1px solid var(--pdx-dialog-border);border-radius:var(--pdx-dialog-shape, 8px);box-shadow:var(--pdx-dialog-elevation-shadow, 0 2px 8px rgba(0, 0, 0, .2));max-width:var(--pdx-dialog-max-width, 90vw);max-height:var(--pdx-dialog-max-height, 80vh);display:flex;flex-direction:column;min-width:var(--pdx-dialog-min-width, 280px);outline:0}.pdx-dialog__title{padding:var(--pdx-dialog-headline-padding, 12px 16px);border-bottom:1px solid var(--pdx-dialog-border);color:var(--pdx-dialog-subhead-color, var(--pdx-dialog-title-fg))}.pdx-dialog__title-row{display:flex;align-items:center;gap:8px}.pdx-dialog__title-icon{font-size:20px;line-height:1;opacity:.87}.pdx-dialog__title-text{margin:0;font-family:var(--pdx-dialog-subhead-font, inherit);line-height:var(--pdx-dialog-subhead-line-height, 1.4);font-size:var(--pdx-dialog-subhead-size, 16px);letter-spacing:var(--pdx-dialog-subhead-tracking, normal);font-weight:var(--pdx-dialog-subhead-weight, 600)}.pdx-dialog__content{padding:var(--pdx-dialog-content-padding, 16px);overflow:auto;color:var(--pdx-dialog-supporting-text-color, inherit);font-family:var(--pdx-dialog-supporting-text-font, inherit);line-height:var(--pdx-dialog-supporting-text-line-height, 1.4);font-size:var(--pdx-dialog-supporting-text-size, 14px);letter-spacing:var(--pdx-dialog-supporting-text-tracking, normal);font-weight:var(--pdx-dialog-supporting-text-weight, 400)}.pdx-dialog.has-actions .pdx-dialog__content{padding-bottom:var(--pdx-dialog-with-actions-content-padding, var(--pdx-dialog-content-padding, 16px))}.pdx-dialog__actions{display:flex;gap:8px;padding:var(--pdx-dialog-actions-padding, 12px 16px);border-top:1px solid var(--pdx-dialog-border);justify-content:flex-end}.pdx-dialog__actions.is-stretched{justify-content:stretch}.pdx-dialog__action-btn{appearance:none;border:1px solid var(--pdx-dialog-border);background:#f7f7f7;border-radius:4px;padding:6px 12px;cursor:pointer}.pdx-dialog--primary .pdx-dialog__title{background:#1976d2;color:#fff;border-bottom-color:#0003}.pdx-dialog--dark{background:#222;color:#f2f2f2}.pdx-dialog{transform-origin:center;transition-property:transform,opacity;transition-duration:var(--pdx-dialog-anim-duration, .3s);transition-timing-function:var(--pdx-dialog-anim-ease, cubic-bezier(.2, 0, 0, 1))}.pdx-dialog.pdx-anim-fade{opacity:1}.pdx-dialog.pdx-anim-zoom{transform:scale(1)}.pdx-dialog.pdx-anim-translate-up,.pdx-dialog.pdx-anim-translate-down{transform:translateY(0)}.pdx-dialog.pdx-anim-translate-left,.pdx-dialog.pdx-anim-translate-right{transform:translate(0)}.pdx-dialog.pdx-state-opening.pdx-anim-fade{opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-zoom{transform:scale(.96);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-up{transform:translateY(-16px);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-down{transform:translateY(16px);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-left{transform:translate(-16px);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-right{transform:translate(16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-fade{opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-zoom{transform:scale(.96);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-up{transform:translateY(-16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-down{transform:translateY(16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-left{transform:translate(-16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-right{transform:translate(16px);opacity:0}@media (prefers-reduced-motion: reduce){.pdx-dialog{transition:none!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i3.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
480
+ }
481
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialogComponent, decorators: [{
482
+ type: Component,
483
+ args: [{ selector: 'praxis-dialog', standalone: true, imports: [CommonModule, PortalModule, A11yModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "@let _titleId = (id ?? 'praxis-dialog') + '-title';\n\n<ng-template #panelHeader>\n <div class=\"pdx-dialog__title\" @if (titleTpl?.templateRef || title || titleIcon)>\n @if (titleTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"titleTpl!.templateRef\"></ng-container>\n } @else {\n <div class=\"pdx-dialog__title-row\">\n <span *ngIf=\"titleIcon\" class=\"material-icons pdx-dialog__title-icon\" aria-hidden=\"true\">{{ titleIcon }}</span>\n <h2 class=\"pdx-dialog__title-text\" [attr.id]=\"_titleId\">{{ title }}</h2>\n </div>\n }\n </div>\n </ng-template>\n\n<ng-template #panelActions>\n <div class=\"pdx-dialog__actions\" [class.is-stretched]=\"actionsLayout==='stretched'\">\n @if (actionsTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"actionsTpl!.templateRef\"></ng-container>\n } @else {\n @for (a of actions; track a?.id ?? a) {\n <button\n type=\"button\"\n class=\"pdx-dialog__action-btn\"\n [attr.data-testid]=\"a.id || null\"\n [ngClass]=\"a.cssClass\"\n (click)=\"onActionClick(a)\"\n [attr.cdkFocusInitial]=\"a.role==='primary' && ariaRole==='alertdialog' ? '' : null\"\n >\n <span *ngIf=\"a.icon\" class=\"material-icons\">{{ a.icon }}</span>\n {{ a.text }}\n </button>\n }\n }\n </div>\n</ng-template>\n\n@if (overlayMode) {\n <div\n #panel\n class=\"pdx-dialog pdx-dialog--{{ themeColor }} pdx-dialog--layout-{{ actionsLayout }}\"\n [attr.role]=\"ariaRole\"\n [attr.id]=\"id || null\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"(ariaLabelledBy || (title ? _titleId : null))\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n (keydown)=\"onKeydown($event)\"\n cdkTrapFocus\n [ngClass]=\"panelNgClass\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n [style.min-width]=\"minWidth\"\n [style.max-width]=\"maxWidth\"\n [style.min-height]=\"minHeight\"\n [style.max-height]=\"maxHeight\"\n [class.has-actions]=\"(actions.length||0)>0 || !!actionsTpl?.templateRef\"\n >\n <ng-container [ngTemplateOutlet]=\"panelHeader\"></ng-container>\n <div class=\"pdx-dialog__content\">\n @if (contentTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"contentTpl!.templateRef\"></ng-container>\n } @else {\n <ng-template cdkPortalOutlet></ng-template>\n }\n </div>\n <ng-container [ngTemplateOutlet]=\"panelActions\"></ng-container>\n </div>\n} @else {\n <div class=\"pdx-dialog-overlay\" [class.has-backdrop]=\"hasBackdrop\" [hidden]=\"!isDisplayed\" (click)=\"onBackdrop($event)\" [ngClass]=\"backdropClass\" [style.z-index]=\"zIndex\">\n <div class=\"pdx-dialog-shell\" (click)=\"$event.stopPropagation()\">\n <div\n #panel\n class=\"pdx-dialog pdx-dialog--{{ themeColor }} pdx-dialog--layout-{{ actionsLayout }}\"\n [attr.role]=\"ariaRole\"\n [attr.id]=\"id || null\"\n [attr.aria-modal]=\"true\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-labelledby]=\"(ariaLabelledBy || (title ? _titleId : null))\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n (keydown)=\"onKeydown($event)\"\n cdkTrapFocus\n [ngClass]=\"panelNgClass\"\n [style.width]=\"width\"\n [style.height]=\"height\"\n [style.min-width]=\"minWidth\"\n [style.max-width]=\"maxWidth\"\n [style.min-height]=\"minHeight\"\n [style.max-height]=\"maxHeight\"\n [style.top]=\"position?.top || null\"\n [style.bottom]=\"position?.bottom || null\"\n [style.left]=\"position?.left || null\"\n [style.right]=\"position?.right || null\"\n [class.pdx-has-position]=\"position\"\n [class.has-actions]=\"(actions.length||0)>0 || !!actionsTpl?.templateRef\"\n >\n <ng-container [ngTemplateOutlet]=\"panelHeader\"></ng-container>\n <div class=\"pdx-dialog__content\">\n @if (contentTpl?.templateRef) {\n <ng-container [ngTemplateOutlet]=\"contentTpl!.templateRef\"></ng-container>\n } @else {\n <ng-template cdkPortalOutlet></ng-template>\n }\n </div>\n <ng-container [ngTemplateOutlet]=\"panelActions\"></ng-container>\n </div>\n </div>\n </div>\n}\n", styles: [".pdx-dialog-host{--pdx-dialog-bg: var(--pdx-dialog-bg, #fff);--pdx-dialog-surface: var(--pdx-dialog-surface, #fff);--pdx-dialog-border: var(--pdx-dialog-border, rgba(0, 0, 0, .12));--pdx-dialog-title-fg: var(--pdx-dialog-title-fg, #111);--pdx-dialog-elevation: var(--pdx-dialog-elevation, 24)}.pdx-dialog-overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:1000;background:transparent}.pdx-dialog-overlay.has-backdrop{background:#00000059}.pdx-dialog-shell{display:contents}.pdx-dialog.pdx-has-position{position:absolute}.pdx-dialog{background:var(--pdx-dialog-container-color, var(--pdx-dialog-surface));color:#111;border:1px solid var(--pdx-dialog-border);border-radius:var(--pdx-dialog-shape, 8px);box-shadow:var(--pdx-dialog-elevation-shadow, 0 2px 8px rgba(0, 0, 0, .2));max-width:var(--pdx-dialog-max-width, 90vw);max-height:var(--pdx-dialog-max-height, 80vh);display:flex;flex-direction:column;min-width:var(--pdx-dialog-min-width, 280px);outline:0}.pdx-dialog__title{padding:var(--pdx-dialog-headline-padding, 12px 16px);border-bottom:1px solid var(--pdx-dialog-border);color:var(--pdx-dialog-subhead-color, var(--pdx-dialog-title-fg))}.pdx-dialog__title-row{display:flex;align-items:center;gap:8px}.pdx-dialog__title-icon{font-size:20px;line-height:1;opacity:.87}.pdx-dialog__title-text{margin:0;font-family:var(--pdx-dialog-subhead-font, inherit);line-height:var(--pdx-dialog-subhead-line-height, 1.4);font-size:var(--pdx-dialog-subhead-size, 16px);letter-spacing:var(--pdx-dialog-subhead-tracking, normal);font-weight:var(--pdx-dialog-subhead-weight, 600)}.pdx-dialog__content{padding:var(--pdx-dialog-content-padding, 16px);overflow:auto;color:var(--pdx-dialog-supporting-text-color, inherit);font-family:var(--pdx-dialog-supporting-text-font, inherit);line-height:var(--pdx-dialog-supporting-text-line-height, 1.4);font-size:var(--pdx-dialog-supporting-text-size, 14px);letter-spacing:var(--pdx-dialog-supporting-text-tracking, normal);font-weight:var(--pdx-dialog-supporting-text-weight, 400)}.pdx-dialog.has-actions .pdx-dialog__content{padding-bottom:var(--pdx-dialog-with-actions-content-padding, var(--pdx-dialog-content-padding, 16px))}.pdx-dialog__actions{display:flex;gap:8px;padding:var(--pdx-dialog-actions-padding, 12px 16px);border-top:1px solid var(--pdx-dialog-border);justify-content:flex-end}.pdx-dialog__actions.is-stretched{justify-content:stretch}.pdx-dialog__action-btn{appearance:none;border:1px solid var(--pdx-dialog-border);background:#f7f7f7;border-radius:4px;padding:6px 12px;cursor:pointer}.pdx-dialog--primary .pdx-dialog__title{background:#1976d2;color:#fff;border-bottom-color:#0003}.pdx-dialog--dark{background:#222;color:#f2f2f2}.pdx-dialog{transform-origin:center;transition-property:transform,opacity;transition-duration:var(--pdx-dialog-anim-duration, .3s);transition-timing-function:var(--pdx-dialog-anim-ease, cubic-bezier(.2, 0, 0, 1))}.pdx-dialog.pdx-anim-fade{opacity:1}.pdx-dialog.pdx-anim-zoom{transform:scale(1)}.pdx-dialog.pdx-anim-translate-up,.pdx-dialog.pdx-anim-translate-down{transform:translateY(0)}.pdx-dialog.pdx-anim-translate-left,.pdx-dialog.pdx-anim-translate-right{transform:translate(0)}.pdx-dialog.pdx-state-opening.pdx-anim-fade{opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-zoom{transform:scale(.96);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-up{transform:translateY(-16px);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-down{transform:translateY(16px);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-left{transform:translate(-16px);opacity:0}.pdx-dialog.pdx-state-opening.pdx-anim-translate-right{transform:translate(16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-fade{opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-zoom{transform:scale(.96);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-up{transform:translateY(-16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-down{transform:translateY(16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-left{transform:translate(-16px);opacity:0}.pdx-dialog.pdx-state-closing.pdx-anim-translate-right{transform:translate(16px);opacity:0}@media (prefers-reduced-motion: reduce){.pdx-dialog{transition:none!important}}\n"] }]
484
+ }], propDecorators: { title: [{
485
+ type: Input
486
+ }], actions: [{
487
+ type: Input
488
+ }], actionsLayout: [{
489
+ type: Input
490
+ }], animation: [{
491
+ type: Input
492
+ }], open: [{
493
+ type: Input
494
+ }], width: [{
495
+ type: Input
496
+ }], height: [{
497
+ type: Input
498
+ }], minWidth: [{
499
+ type: Input
500
+ }], maxWidth: [{
501
+ type: Input
502
+ }], minHeight: [{
503
+ type: Input
504
+ }], maxHeight: [{
505
+ type: Input
506
+ }], themeColor: [{
507
+ type: Input
508
+ }], disableClose: [{
509
+ type: Input
510
+ }], hasBackdrop: [{
511
+ type: Input
512
+ }], overlayMode: [{
513
+ type: Input
514
+ }], zIndex: [{
515
+ type: Input
516
+ }], panelClass: [{
517
+ type: Input
518
+ }], backdropClass: [{
519
+ type: Input
520
+ }], position: [{
521
+ type: Input
522
+ }], autoFocusedElement: [{
523
+ type: Input
524
+ }], autoFocus: [{
525
+ type: Input
526
+ }], restoreFocus: [{
527
+ type: Input
528
+ }], id: [{
529
+ type: Input
530
+ }], ariaRole: [{
531
+ type: Input
532
+ }], ariaLabel: [{
533
+ type: Input
534
+ }], ariaLabelledBy: [{
535
+ type: Input
536
+ }], ariaDescribedBy: [{
537
+ type: Input
538
+ }], styles: [{
539
+ type: Input
540
+ }], titleIcon: [{
541
+ type: Input
542
+ }], action: [{
543
+ type: Output
544
+ }], close: [{
545
+ type: Output
546
+ }], opened: [{
547
+ type: Output
548
+ }], afterClosed: [{
549
+ type: Output
550
+ }], contentHost: [{
551
+ type: ViewChild,
552
+ args: [CdkPortalOutlet, { static: false }]
553
+ }], panelEl: [{
554
+ type: ViewChild,
555
+ args: ['panel', { static: true }]
556
+ }], titleTpl: [{
557
+ type: ContentChild,
558
+ args: [PraxisDialogTitleDirective, { read: PraxisDialogTitleDirective }]
559
+ }], actionsTpl: [{
560
+ type: ContentChild,
561
+ args: [PraxisDialogActionsDirective, { read: PraxisDialogActionsDirective }]
562
+ }], contentTpl: [{
563
+ type: ContentChild,
564
+ args: [PraxisDialogContentDirective, { read: PraxisDialogContentDirective }]
565
+ }], hostClass: [{
566
+ type: HostBinding,
567
+ args: ['class']
568
+ }], tabindex: [{
569
+ type: HostBinding,
570
+ args: ['attr.tabindex']
571
+ }] } });
572
+ const PraxisDialogDirectives = [];
573
+
574
+ class DialogOverlayService {
575
+ overlay = inject(Overlay);
576
+ createOverlay(cfg, injector) {
577
+ const overlayConfig = this.buildOverlayConfig(cfg);
578
+ return this.overlay.create(overlayConfig);
579
+ }
580
+ attachContainer(ref, injector) {
581
+ const portal = new ComponentPortal(PraxisDialogComponent, null, injector);
582
+ const compRef = ref.attach(portal);
583
+ return compRef;
584
+ }
585
+ buildOverlayConfig(cfg) {
586
+ const positionStrategy = this.overlay
587
+ .position()
588
+ .global()
589
+ .centerHorizontally()
590
+ .centerVertically();
591
+ if (cfg?.position) {
592
+ if (cfg.position.top != null)
593
+ positionStrategy.top(String(cfg.position.top));
594
+ if (cfg.position.bottom != null)
595
+ positionStrategy.bottom(String(cfg.position.bottom));
596
+ if (cfg.position.left != null)
597
+ positionStrategy.left(String(cfg.position.left));
598
+ if (cfg.position.right != null)
599
+ positionStrategy.right(String(cfg.position.right));
600
+ }
601
+ const scrollStrategy = (() => {
602
+ switch (cfg?.scrollStrategy) {
603
+ case 'noop':
604
+ return this.overlay.scrollStrategies.noop();
605
+ case 'reposition':
606
+ return this.overlay.scrollStrategies.reposition();
607
+ case 'block':
608
+ default:
609
+ return this.overlay.scrollStrategies.block();
610
+ }
611
+ })();
612
+ const hasBackdrop = cfg?.hasBackdrop ?? true;
613
+ const overlayConfig = new OverlayConfig({
614
+ hasBackdrop,
615
+ disposeOnNavigation: cfg?.closeOnNavigation ?? true,
616
+ panelClass: cfg?.panelClass,
617
+ backdropClass: cfg?.backdropClass,
618
+ positionStrategy,
619
+ scrollStrategy,
620
+ width: coerceCssUnit(cfg?.width),
621
+ height: coerceCssUnit(cfg?.height),
622
+ });
623
+ if (cfg?.zIndex != null) {
624
+ overlayConfig.zIndex = cfg.zIndex;
625
+ }
626
+ return overlayConfig;
627
+ }
628
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: DialogOverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
629
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: DialogOverlayService, providedIn: 'root' });
630
+ }
631
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: DialogOverlayService, decorators: [{
632
+ type: Injectable,
633
+ args: [{ providedIn: 'root' }]
634
+ }] });
635
+ function coerceCssUnit(v) {
636
+ if (v == null)
637
+ return undefined;
638
+ return typeof v === 'number' ? `${v}px` : v;
639
+ }
640
+ const DialogOverlayImports = [OverlayModule, PortalModule];
641
+
642
+ class PraxisDialog {
643
+ overlay = inject(DialogOverlayService);
644
+ appRef = inject(ApplicationRef);
645
+ registry = inject(PRAXIS_DIALOG_CONTENT_REGISTRY);
646
+ i18n = inject(PRAXIS_DIALOG_I18N);
647
+ templateRegistry = inject(PRAXIS_DIALOG_TEMPLATE_REGISTRY);
648
+ sanitizer = inject(DomSanitizer);
649
+ globalPresets = inject(PRAXIS_DIALOG_GLOBAL_PRESETS);
650
+ defaults = inject(PRAXIS_DIALOG_DEFAULTS);
651
+ afterAllClosed = new Subject();
652
+ afterOpened = new Subject();
653
+ _openDialogs = [];
654
+ get openDialogs() { return this._openDialogs.slice(); }
655
+ _seq = 0;
656
+ open(content, config) {
657
+ const injector = Injector.create({
658
+ providers: [{ provide: PRAXIS_DIALOG_DATA, useValue: config?.data }],
659
+ });
660
+ const overlayRef = this.overlay.createOverlay(config ?? {}, injector);
661
+ const ref = new PraxisDialogRef(overlayRef);
662
+ // Auto-id fallback if not provided
663
+ let assignedId = config?.id ?? `praxis-dialog-${++this._seq}`;
664
+ while (this.getDialogById(assignedId)) {
665
+ assignedId = `praxis-dialog-${++this._seq}`;
666
+ }
667
+ ref.id = assignedId;
668
+ const compRef = this.overlay.attachContainer(overlayRef, injector);
669
+ const container = compRef.instance;
670
+ // Set inputs before detectChanges so the correct branch/materialization occurs
671
+ container.overlayMode = true;
672
+ container.title = config?.title;
673
+ container.titleIcon = config?.icon ?? config?.titleIcon;
674
+ container.actions = config?.actions ?? [];
675
+ container.actionsLayout = config?.actionsLayout ?? 'stretched';
676
+ container.animation = config?.animation ?? { type: 'translate', duration: 300 };
677
+ container.width = config?.width;
678
+ container.height = config?.height;
679
+ container.minWidth = config?.minWidth;
680
+ container.maxWidth = config?.maxWidth ?? this.defaults.maxWidth;
681
+ container.minHeight = config?.minHeight;
682
+ container.maxHeight = config?.maxHeight ?? this.defaults.maxHeight;
683
+ container.themeColor = config?.themeColor ?? 'light';
684
+ container.styles = config?.styles;
685
+ container.disableClose = !!config?.disableClose;
686
+ container.hasBackdrop = config?.hasBackdrop ?? true;
687
+ container.panelClass = config?.panelClass;
688
+ container.backdropClass = config?.backdropClass;
689
+ container.position = config?.position;
690
+ container.autoFocusedElement = config?.autoFocusedElement;
691
+ container.autoFocus = config?.autoFocus ?? container.autoFocus;
692
+ container.restoreFocus = config?.restoreFocus ?? true;
693
+ container.id = assignedId;
694
+ container.ariaRole = config?.ariaRole ?? 'dialog';
695
+ container.ariaLabel = config?.ariaLabel;
696
+ container.ariaLabelledBy = config?.ariaLabelledBy;
697
+ container.ariaDescribedBy = config?.ariaDescribedBy;
698
+ try {
699
+ compRef.changeDetectorRef.detectChanges();
700
+ }
701
+ catch { }
702
+ const subAction = container.action.subscribe((a) => {
703
+ if (a.close && !config?.disableClose) {
704
+ ref.close(a.payload);
705
+ }
706
+ });
707
+ const subClose = container.close.subscribe((result) => {
708
+ if (!config?.disableClose) {
709
+ ref.close(result);
710
+ }
711
+ });
712
+ const backdropSub = overlayRef.backdropClick().subscribe((ev) => {
713
+ ref.notifyBackdropClick(ev);
714
+ if (!config?.disableClose && (config?.closeOnBackdropClick ?? true)) {
715
+ ref.close();
716
+ }
717
+ });
718
+ const keydownSub = overlayRef.keydownEvents().subscribe((ev) => {
719
+ ref.notifyKeydown(ev);
720
+ });
721
+ // Attach portal content now that the outlet exists
722
+ const doAttach = () => {
723
+ if (content instanceof TemplateRef) {
724
+ const origin = config?.viewContainerRef ?? null;
725
+ const portal = new TemplatePortal(content, origin, { $implicit: config?.data });
726
+ container.contentHost.attachTemplatePortal(portal);
727
+ }
728
+ else {
729
+ const portal = new ComponentPortal(content);
730
+ const innerRef = container.contentHost.attachComponentPortal(portal);
731
+ ref.componentInstance = innerRef.instance;
732
+ }
733
+ };
734
+ if (container.contentHost) {
735
+ doAttach();
736
+ }
737
+ else {
738
+ try {
739
+ compRef.changeDetectorRef.detectChanges();
740
+ }
741
+ catch { }
742
+ queueMicrotask(() => {
743
+ if (container.contentHost)
744
+ doAttach();
745
+ });
746
+ }
747
+ // Animate open then emit afterOpened
748
+ container.beginOpenAnimation().then(() => {
749
+ ref.emitOpened();
750
+ this.afterOpened.next(ref);
751
+ });
752
+ overlayRef.detachments().subscribe(() => {
753
+ subAction.unsubscribe();
754
+ subClose.unsubscribe();
755
+ backdropSub.unsubscribe();
756
+ keydownSub.unsubscribe();
757
+ });
758
+ // Close animation hook
759
+ ref.setBeforeClose(async () => {
760
+ await container.beginCloseAnimation();
761
+ });
762
+ // Track open dialogs and notify when all are closed
763
+ this._openDialogs.push(ref);
764
+ ref.afterClosed().subscribe(() => {
765
+ this._openDialogs = this._openDialogs.filter((r) => r !== ref);
766
+ if (this._openDialogs.length === 0)
767
+ this.afterAllClosed.next();
768
+ });
769
+ return ref;
770
+ }
771
+ confirm(cfg, variant) {
772
+ const merged = this.mergePresets('confirm', variant, cfg);
773
+ const actions = [
774
+ { text: merged.cancelLabel ?? this.i18n.cancel, role: 'secondary', close: true, id: 'cancel', payload: false },
775
+ { text: merged.confirmLabel ?? this.i18n.ok, role: 'primary', close: true, id: 'confirm', payload: true },
776
+ ];
777
+ const Comp = SimpleDialogContentComponent;
778
+ return this.open(Comp, {
779
+ ...merged,
780
+ actions,
781
+ data: { message: merged.message, mode: 'confirm' },
782
+ ariaRole: merged.ariaRole ?? 'alertdialog',
783
+ });
784
+ }
785
+ alert(cfg) {
786
+ const merged = this.mergePresets('alert', undefined, cfg);
787
+ const actions = [
788
+ { text: merged.okLabel ?? this.i18n.ok, role: 'primary', close: true, id: 'ok' },
789
+ ];
790
+ const Comp = SimpleDialogContentComponent;
791
+ return this.open(Comp, { ...merged, actions, data: { message: merged.message, mode: 'alert' }, ariaRole: merged.ariaRole ?? 'alertdialog' });
792
+ }
793
+ prompt(cfg) {
794
+ const merged = this.mergePresets('prompt', undefined, cfg);
795
+ const actions = [
796
+ { text: merged.cancelLabel ?? this.i18n.cancel, role: 'secondary', close: true, id: 'cancel', payload: null },
797
+ { text: merged.okLabel ?? this.i18n.ok, role: 'primary', close: false, id: 'ok' },
798
+ ];
799
+ const Comp = SimpleDialogContentComponent;
800
+ const ref = this.open(Comp, { ...merged, actions, data: { message: merged.message, mode: 'prompt', placeholder: merged.placeholder, defaultValue: merged.defaultValue }, ariaRole: merged.ariaRole ?? 'dialog' });
801
+ ref.afterOpened().subscribe(() => {
802
+ const inst = ref.componentInstance;
803
+ inst.submit = () => ref.close(inst.value ?? '');
804
+ });
805
+ return ref;
806
+ }
807
+ openByRegistry(id, config) {
808
+ const comp = this.registry[id];
809
+ if (!comp) {
810
+ throw new Error(`Dialog registry: component '${id}' not found`);
811
+ }
812
+ return this.open(comp, config);
813
+ }
814
+ openTemplateById(templateId, config) {
815
+ const tpl = this.templateRegistry[templateId];
816
+ if (!tpl)
817
+ throw new Error(`Dialog template '${templateId}' not found`);
818
+ return this.open(tpl, config);
819
+ }
820
+ openSafeHtml(html, config) {
821
+ const safe = this.sanitizer.bypassSecurityTrustHtml(html);
822
+ const Comp = SimpleHtmlDialogContentComponent;
823
+ return this.open(Comp, { ...config, data: { safeHtml: safe } });
824
+ }
825
+ mergePresets(type, variant, cfg) {
826
+ const base = this.globalPresets?.[type] ?? {};
827
+ const variantCfg = variant ? (this.globalPresets?.variants?.[variant] ?? {}) : {};
828
+ return mergeDialogConfig(mergeDialogConfig(base, variantCfg), cfg);
829
+ }
830
+ closeAll() {
831
+ const list = this._openDialogs.slice();
832
+ list.forEach((r) => r.close());
833
+ }
834
+ getDialogById(id) {
835
+ return this._openDialogs.find((r) => r.id === id);
836
+ }
837
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialog, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
838
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialog, providedIn: 'root' });
839
+ }
840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: PraxisDialog, decorators: [{
841
+ type: Injectable,
842
+ args: [{ providedIn: 'root' }]
843
+ }] });
844
+ class SimpleDialogContentComponent {
845
+ message;
846
+ mode = 'alert';
847
+ placeholder;
848
+ value;
849
+ submit;
850
+ dialogData = inject(PRAXIS_DIALOG_DATA, { optional: true });
851
+ constructor() {
852
+ const data = this.dialogData;
853
+ try {
854
+ this.message = data?.message;
855
+ this.mode = data?.mode || 'alert';
856
+ this.placeholder = data?.placeholder;
857
+ this.value = data?.defaultValue ?? null;
858
+ }
859
+ catch { }
860
+ }
861
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SimpleDialogContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
862
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: SimpleDialogContentComponent, isStandalone: true, selector: "praxis-simple-dialog-content", ngImport: i0, template: `
863
+ <div class="pdx-simple-dialog">
864
+ <p *ngIf="message" class="pdx-simple-dialog__message">{{ message }}</p>
865
+ <input
866
+ *ngIf="mode === 'prompt'"
867
+ class="pdx-simple-dialog__input"
868
+ [attr.placeholder]="placeholder || ''"
869
+ [(ngModel)]="value"
870
+ />
871
+ </div>
872
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
873
+ }
874
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SimpleDialogContentComponent, decorators: [{
875
+ type: Component,
876
+ args: [{
877
+ selector: 'praxis-simple-dialog-content',
878
+ standalone: true,
879
+ imports: [CommonModule, FormsModule],
880
+ template: `
881
+ <div class="pdx-simple-dialog">
882
+ <p *ngIf="message" class="pdx-simple-dialog__message">{{ message }}</p>
883
+ <input
884
+ *ngIf="mode === 'prompt'"
885
+ class="pdx-simple-dialog__input"
886
+ [attr.placeholder]="placeholder || ''"
887
+ [(ngModel)]="value"
888
+ />
889
+ </div>
890
+ `,
891
+ }]
892
+ }], ctorParameters: () => [] });
893
+ class SimpleHtmlDialogContentComponent {
894
+ safeHtml;
895
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SimpleHtmlDialogContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
896
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: SimpleHtmlDialogContentComponent, isStandalone: true, selector: "praxis-simple-html-dialog-content", ngImport: i0, template: `<div class="pdx-simple-dialog" [innerHTML]="safeHtml"></div>`, isInline: true });
897
+ }
898
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SimpleHtmlDialogContentComponent, decorators: [{
899
+ type: Component,
900
+ args: [{
901
+ selector: 'praxis-simple-html-dialog-content',
902
+ standalone: true,
903
+ imports: [],
904
+ template: `<div class="pdx-simple-dialog" [innerHTML]="safeHtml"></div>`,
905
+ }]
906
+ }] });
907
+ function mergeDialogConfig(a, b) {
908
+ const out = { ...a };
909
+ for (const [k, v] of Object.entries(b || {})) {
910
+ if (v == null)
911
+ continue;
912
+ if (k === 'styles' && typeof v === 'object') {
913
+ out.styles = { ...(out.styles || {}), ...v };
914
+ }
915
+ else if (k === 'actions' && Array.isArray(v)) {
916
+ out.actions = v.slice();
917
+ }
918
+ else if (typeof v === 'object' && !Array.isArray(v)) {
919
+ out[k] = { ...(out[k] || {}), ...v };
920
+ }
921
+ else {
922
+ out[k] = v;
923
+ }
924
+ }
925
+ return out;
926
+ }
927
+
928
+ /**
929
+ * Provides PRAXIS_DIALOG_GLOBAL_PRESETS by transforming GlobalConfig.dialog
930
+ * into the structure expected by @praxisui/dialog.
931
+ */
932
+ function provideDialogGlobalPresetsFromGlobalConfig() {
933
+ return {
934
+ provide: PRAXIS_DIALOG_GLOBAL_PRESETS,
935
+ deps: [GlobalConfigService],
936
+ useFactory: (gc) => {
937
+ const d = gc.get('dialog') || undefined;
938
+ const coerce = (entry) => {
939
+ if (!entry || typeof entry !== 'object')
940
+ return entry;
941
+ const out = { ...entry };
942
+ // Best-effort JSON parse for text inputs saved via editor
943
+ const parseIfString = (v) => {
944
+ if (typeof v === 'string') {
945
+ try {
946
+ return JSON.parse(v);
947
+ }
948
+ catch {
949
+ return v;
950
+ }
951
+ }
952
+ return v;
953
+ };
954
+ if (typeof out.actions === 'string')
955
+ out.actions = parseIfString(out.actions);
956
+ if (typeof out.styles === 'string')
957
+ out.styles = parseIfString(out.styles);
958
+ if (typeof out.animation === 'string')
959
+ out.animation = parseIfString(out.animation);
960
+ return out;
961
+ };
962
+ const variants = {};
963
+ for (const key of Object.keys(d?.variants || {})) {
964
+ variants[key] = coerce(d.variants[key]);
965
+ }
966
+ return {
967
+ confirm: coerce(d?.defaults?.confirm),
968
+ alert: coerce(d?.defaults?.alert),
969
+ prompt: coerce(d?.defaults?.prompt),
970
+ variants,
971
+ };
972
+ },
973
+ };
974
+ }
975
+
976
+ /**
977
+ * Generated bundle index. Do not edit.
978
+ */
979
+
980
+ export { PRAXIS_DIALOG_CONTENT_REGISTRY, PRAXIS_DIALOG_DATA, PRAXIS_DIALOG_DEFAULTS, PRAXIS_DIALOG_GLOBAL_PRESETS, PRAXIS_DIALOG_I18N, PRAXIS_DIALOG_TEMPLATE_REGISTRY, PraxisDialog, PraxisDialogActionsDirective, PraxisDialogComponent, PraxisDialogContentDirective, PraxisDialogDirectives, PraxisDialogRef, PraxisDialogTitleDirective, provideDialogGlobalPresetsFromGlobalConfig };
981
+ //# sourceMappingURL=praxisui-dialog.mjs.map