@raintonic/formaui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +145 -0
- package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +806 -0
- package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-cdk-form-field.mjs +86 -0
- package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-cdk-overlay.mjs +1757 -0
- package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +287 -0
- package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-accordion.mjs +217 -0
- package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-alert.mjs +161 -0
- package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-autocomplete.mjs +726 -0
- package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-avatar.mjs +92 -0
- package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-badge.mjs +107 -0
- package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-big-menu.mjs +68 -0
- package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +55 -0
- package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-button-group.mjs +103 -0
- package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-button.mjs +241 -0
- package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-card.mjs +270 -0
- package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-checkbox.mjs +295 -0
- package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-data-table.mjs +631 -0
- package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-date-picker.mjs +1331 -0
- package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-divider.mjs +41 -0
- package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-drawer.mjs +190 -0
- package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-dynamic-form.mjs +266 -0
- package/fesm2022/raintonic-formaui-components-dynamic-form.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-empty-state.mjs +33 -0
- package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-file-upload.mjs +246 -0
- package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-form-field.mjs +482 -0
- package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-icon.mjs +117 -0
- package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-input.mjs +327 -0
- package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-list.mjs +149 -0
- package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-menu.mjs +896 -0
- package/fesm2022/raintonic-formaui-components-menu.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-number-input.mjs +345 -0
- package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-paginator.mjs +139 -0
- package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-password-input.mjs +306 -0
- package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-popover.mjs +451 -0
- package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-progressbar.mjs +148 -0
- package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-radio.mjs +260 -0
- package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-select.mjs +1011 -0
- package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-side-panel.mjs +150 -0
- package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-sidebar.mjs +257 -0
- package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-skeleton.mjs +50 -0
- package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-slider.mjs +347 -0
- package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-spinner.mjs +63 -0
- package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-stepper.mjs +317 -0
- package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tab.mjs +197 -0
- package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tag.mjs +78 -0
- package/fesm2022/raintonic-formaui-components-tag.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-time-picker.mjs +644 -0
- package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-toggle.mjs +171 -0
- package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-toolbar.mjs +140 -0
- package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tooltip.mjs +555 -0
- package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tree-select.mjs +314 -0
- package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tree-table.mjs +103 -0
- package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-components-tree.mjs +430 -0
- package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-core.mjs +62 -0
- package/fesm2022/raintonic-formaui-core.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-services-dialog.mjs +798 -0
- package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-services-notification.mjs +391 -0
- package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-services-theme.mjs +248 -0
- package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -0
- package/fesm2022/raintonic-formaui-test-utils.mjs +66 -0
- package/fesm2022/raintonic-formaui-test-utils.mjs.map +1 -0
- package/fesm2022/raintonic-formaui.mjs +15 -0
- package/fesm2022/raintonic-formaui.mjs.map +1 -0
- package/llms-full.txt +1627 -0
- package/llms.txt +60 -0
- package/package.json +251 -0
- package/styles/_fonts-entry.scss +3 -0
- package/styles/fonts/dm-mono-400-latin.woff2 +0 -0
- package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
- package/styles/fonts/inter-tight-latin.woff2 +0 -0
- package/styles/index.scss +127 -0
- package/styles/partials/_constants.scss +29 -0
- package/styles/partials/_fonts.scss +36 -0
- package/styles/partials/_grid.scss +171 -0
- package/styles/partials/_mixins.scss +145 -0
- package/styles/partials/_motion.scss +252 -0
- package/styles/partials/_theme.scss +275 -0
- package/styles/partials/_typography.scss +112 -0
- package/styles/partials/_utilities.scss +480 -0
- package/styles/partials/themes/_dark.scss +254 -0
- package/styles/partials/themes/_light.scss +254 -0
- package/types/raintonic-formaui-cdk-drag-drop.d.ts +196 -0
- package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -0
- package/types/raintonic-formaui-cdk-form-field.d.ts +62 -0
- package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -0
- package/types/raintonic-formaui-cdk-overlay.d.ts +843 -0
- package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -0
- package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +112 -0
- package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -0
- package/types/raintonic-formaui-components-accordion.d.ts +124 -0
- package/types/raintonic-formaui-components-accordion.d.ts.map +1 -0
- package/types/raintonic-formaui-components-alert.d.ts +143 -0
- package/types/raintonic-formaui-components-alert.d.ts.map +1 -0
- package/types/raintonic-formaui-components-autocomplete.d.ts +193 -0
- package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -0
- package/types/raintonic-formaui-components-avatar.d.ts +52 -0
- package/types/raintonic-formaui-components-avatar.d.ts.map +1 -0
- package/types/raintonic-formaui-components-badge.d.ts +47 -0
- package/types/raintonic-formaui-components-badge.d.ts.map +1 -0
- package/types/raintonic-formaui-components-big-menu.d.ts +62 -0
- package/types/raintonic-formaui-components-big-menu.d.ts.map +1 -0
- package/types/raintonic-formaui-components-breadcrumb.d.ts +26 -0
- package/types/raintonic-formaui-components-breadcrumb.d.ts.map +1 -0
- package/types/raintonic-formaui-components-button-group.d.ts +61 -0
- package/types/raintonic-formaui-components-button-group.d.ts.map +1 -0
- package/types/raintonic-formaui-components-button.d.ts +116 -0
- package/types/raintonic-formaui-components-button.d.ts.map +1 -0
- package/types/raintonic-formaui-components-card.d.ts +191 -0
- package/types/raintonic-formaui-components-card.d.ts.map +1 -0
- package/types/raintonic-formaui-components-checkbox.d.ts +132 -0
- package/types/raintonic-formaui-components-checkbox.d.ts.map +1 -0
- package/types/raintonic-formaui-components-data-table.d.ts +368 -0
- package/types/raintonic-formaui-components-data-table.d.ts.map +1 -0
- package/types/raintonic-formaui-components-date-picker.d.ts +341 -0
- package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -0
- package/types/raintonic-formaui-components-divider.d.ts +21 -0
- package/types/raintonic-formaui-components-divider.d.ts.map +1 -0
- package/types/raintonic-formaui-components-drawer.d.ts +48 -0
- package/types/raintonic-formaui-components-drawer.d.ts.map +1 -0
- package/types/raintonic-formaui-components-dynamic-form.d.ts +412 -0
- package/types/raintonic-formaui-components-dynamic-form.d.ts.map +1 -0
- package/types/raintonic-formaui-components-empty-state.d.ts +14 -0
- package/types/raintonic-formaui-components-empty-state.d.ts.map +1 -0
- package/types/raintonic-formaui-components-file-upload.d.ts +77 -0
- package/types/raintonic-formaui-components-file-upload.d.ts.map +1 -0
- package/types/raintonic-formaui-components-form-field.d.ts +271 -0
- package/types/raintonic-formaui-components-form-field.d.ts.map +1 -0
- package/types/raintonic-formaui-components-icon.d.ts +61 -0
- package/types/raintonic-formaui-components-icon.d.ts.map +1 -0
- package/types/raintonic-formaui-components-input.d.ts +149 -0
- package/types/raintonic-formaui-components-input.d.ts.map +1 -0
- package/types/raintonic-formaui-components-list.d.ts +48 -0
- package/types/raintonic-formaui-components-list.d.ts.map +1 -0
- package/types/raintonic-formaui-components-menu.d.ts +403 -0
- package/types/raintonic-formaui-components-menu.d.ts.map +1 -0
- package/types/raintonic-formaui-components-number-input.d.ts +127 -0
- package/types/raintonic-formaui-components-number-input.d.ts.map +1 -0
- package/types/raintonic-formaui-components-paginator.d.ts +37 -0
- package/types/raintonic-formaui-components-paginator.d.ts.map +1 -0
- package/types/raintonic-formaui-components-password-input.d.ts +111 -0
- package/types/raintonic-formaui-components-password-input.d.ts.map +1 -0
- package/types/raintonic-formaui-components-popover.d.ts +131 -0
- package/types/raintonic-formaui-components-popover.d.ts.map +1 -0
- package/types/raintonic-formaui-components-progressbar.d.ts +111 -0
- package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -0
- package/types/raintonic-formaui-components-radio.d.ts +95 -0
- package/types/raintonic-formaui-components-radio.d.ts.map +1 -0
- package/types/raintonic-formaui-components-select.d.ts +307 -0
- package/types/raintonic-formaui-components-select.d.ts.map +1 -0
- package/types/raintonic-formaui-components-side-panel.d.ts +51 -0
- package/types/raintonic-formaui-components-side-panel.d.ts.map +1 -0
- package/types/raintonic-formaui-components-sidebar.d.ts +174 -0
- package/types/raintonic-formaui-components-sidebar.d.ts.map +1 -0
- package/types/raintonic-formaui-components-skeleton.d.ts +20 -0
- package/types/raintonic-formaui-components-skeleton.d.ts.map +1 -0
- package/types/raintonic-formaui-components-slider.d.ts +108 -0
- package/types/raintonic-formaui-components-slider.d.ts.map +1 -0
- package/types/raintonic-formaui-components-spinner.d.ts +42 -0
- package/types/raintonic-formaui-components-spinner.d.ts.map +1 -0
- package/types/raintonic-formaui-components-stepper.d.ts +126 -0
- package/types/raintonic-formaui-components-stepper.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tab.d.ts +96 -0
- package/types/raintonic-formaui-components-tab.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tag.d.ts +34 -0
- package/types/raintonic-formaui-components-tag.d.ts.map +1 -0
- package/types/raintonic-formaui-components-time-picker.d.ts +172 -0
- package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -0
- package/types/raintonic-formaui-components-toggle.d.ts +70 -0
- package/types/raintonic-formaui-components-toggle.d.ts.map +1 -0
- package/types/raintonic-formaui-components-toolbar.d.ts +128 -0
- package/types/raintonic-formaui-components-toolbar.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tooltip.d.ts +268 -0
- package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tree-select.d.ts +80 -0
- package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tree-table.d.ts +90 -0
- package/types/raintonic-formaui-components-tree-table.d.ts.map +1 -0
- package/types/raintonic-formaui-components-tree.d.ts +104 -0
- package/types/raintonic-formaui-components-tree.d.ts.map +1 -0
- package/types/raintonic-formaui-core.d.ts +115 -0
- package/types/raintonic-formaui-core.d.ts.map +1 -0
- package/types/raintonic-formaui-services-dialog.d.ts +451 -0
- package/types/raintonic-formaui-services-dialog.d.ts.map +1 -0
- package/types/raintonic-formaui-services-notification.d.ts +221 -0
- package/types/raintonic-formaui-services-notification.d.ts.map +1 -0
- package/types/raintonic-formaui-services-theme.d.ts +126 -0
- package/types/raintonic-formaui-services-theme.d.ts.map +1 -0
- package/types/raintonic-formaui-test-utils.d.ts +24 -0
- package/types/raintonic-formaui-test-utils.d.ts.map +1 -0
- package/types/raintonic-formaui.d.ts +4 -0
- package/types/raintonic-formaui.d.ts.map +1 -0
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, ElementRef, signal, computed, HostListener, ViewChild, ChangeDetectionStrategy, Component, ViewEncapsulation, EnvironmentInjector, Injector, ApplicationRef, TemplateRef, createComponent, Injectable } from '@angular/core';
|
|
3
|
+
import { Subject, defer } from 'rxjs';
|
|
4
|
+
import { filter, startWith, map } from 'rxjs/operators';
|
|
5
|
+
import { FuiOverlayService } from '@raintonic/formaui/cdk/overlay';
|
|
6
|
+
import { DOCUMENT } from '@angular/common';
|
|
7
|
+
import { FuiIconComponent } from '@raintonic/formaui/components/icon';
|
|
8
|
+
import { FuiButtonDirective } from '@raintonic/formaui/components/button';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Injection token for dialog data passed to the dialog component
|
|
12
|
+
*/
|
|
13
|
+
const FUI_DIALOG_DATA = new InjectionToken('FuiDialogData');
|
|
14
|
+
/**
|
|
15
|
+
* Injection token for the dialog scroll strategy
|
|
16
|
+
*/
|
|
17
|
+
const FUI_DIALOG_DEFAULT_OPTIONS = new InjectionToken('FuiDialogDefaultOptions');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* # FuiDialogRefImpl
|
|
21
|
+
*
|
|
22
|
+
* Implementation of the FuiDialogRef interface. This class manages the lifecycle
|
|
23
|
+
* of an individual dialog instance, providing methods to close, update position/size,
|
|
24
|
+
* and observe dialog events.
|
|
25
|
+
*
|
|
26
|
+
* ## Features
|
|
27
|
+
* - Dialog result handling
|
|
28
|
+
* - Position and size updates
|
|
29
|
+
* - Event observables (backdrop click, keydown, lifecycle)
|
|
30
|
+
* - CSS class management
|
|
31
|
+
* - Focus management integration
|
|
32
|
+
*/
|
|
33
|
+
class FuiDialogRef {
|
|
34
|
+
_overlayRef;
|
|
35
|
+
_config;
|
|
36
|
+
_id;
|
|
37
|
+
_componentInstance = null;
|
|
38
|
+
_result;
|
|
39
|
+
_state = 'enter';
|
|
40
|
+
// Event subjects
|
|
41
|
+
_afterOpened = new Subject();
|
|
42
|
+
_beforeClosed = new Subject();
|
|
43
|
+
_afterClosed = new Subject();
|
|
44
|
+
// Configuration
|
|
45
|
+
disableClose;
|
|
46
|
+
constructor(_overlayRef, _config, id) {
|
|
47
|
+
this._overlayRef = _overlayRef;
|
|
48
|
+
this._config = _config;
|
|
49
|
+
this._id = id ?? this._generateId();
|
|
50
|
+
this.disableClose = _config.disableClose ?? false;
|
|
51
|
+
this._setupEventHandlers();
|
|
52
|
+
}
|
|
53
|
+
/** Unique ID for this dialog instance */
|
|
54
|
+
get id() {
|
|
55
|
+
return this._id;
|
|
56
|
+
}
|
|
57
|
+
/** The component instance if the dialog was created with a component */
|
|
58
|
+
get componentInstance() {
|
|
59
|
+
return this._componentInstance;
|
|
60
|
+
}
|
|
61
|
+
/** Sets the component instance (internal use) */
|
|
62
|
+
set componentInstance(instance) {
|
|
63
|
+
this._componentInstance = instance;
|
|
64
|
+
}
|
|
65
|
+
/** Observable that emits when the dialog has been opened */
|
|
66
|
+
get afterOpened() {
|
|
67
|
+
return this._afterOpened.asObservable();
|
|
68
|
+
}
|
|
69
|
+
/** Observable that emits when the dialog has started closing */
|
|
70
|
+
get beforeClosed() {
|
|
71
|
+
return this._beforeClosed.asObservable();
|
|
72
|
+
}
|
|
73
|
+
/** Observable that emits when the dialog has finished closing */
|
|
74
|
+
get afterClosed() {
|
|
75
|
+
return this._afterClosed.asObservable();
|
|
76
|
+
}
|
|
77
|
+
/** Observable that emits when the backdrop is clicked */
|
|
78
|
+
get backdropClick() {
|
|
79
|
+
return this._overlayRef.backdropClick;
|
|
80
|
+
}
|
|
81
|
+
/** Observable that emits when a keydown event occurs on the overlay */
|
|
82
|
+
get keydownEvents() {
|
|
83
|
+
return this._overlayRef.keydownEvents;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Closes the dialog with an optional result
|
|
87
|
+
*/
|
|
88
|
+
close(dialogResult) {
|
|
89
|
+
if (this._state === 'exit') {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this._result = dialogResult;
|
|
93
|
+
this._state = 'exit';
|
|
94
|
+
// Emit before closed event
|
|
95
|
+
this._beforeClosed.next(dialogResult);
|
|
96
|
+
this._beforeClosed.complete();
|
|
97
|
+
// Get animation duration from config
|
|
98
|
+
const exitDuration = this._parseAnimationDuration(this._config.exitAnimationDuration);
|
|
99
|
+
if (exitDuration > 0) {
|
|
100
|
+
// Add exit animation class
|
|
101
|
+
this._overlayRef.addPanelClass('fui-dialog-exit');
|
|
102
|
+
// Wait for animation then dispose
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
this._finishDialogClose();
|
|
105
|
+
}, exitDuration);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this._finishDialogClose();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Updates the dialog's position
|
|
113
|
+
*/
|
|
114
|
+
updatePosition(position) {
|
|
115
|
+
const strategy = this._overlayRef.getConfig().positionStrategy;
|
|
116
|
+
if (strategy) {
|
|
117
|
+
if (position?.left || position?.right) {
|
|
118
|
+
if (position?.left) {
|
|
119
|
+
strategy.left(position.left);
|
|
120
|
+
}
|
|
121
|
+
else if (position?.right) {
|
|
122
|
+
strategy.right(position.right);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
strategy.centerHorizontally();
|
|
127
|
+
}
|
|
128
|
+
if (position?.top || position?.bottom) {
|
|
129
|
+
if (position?.top) {
|
|
130
|
+
strategy.top(position.top);
|
|
131
|
+
}
|
|
132
|
+
else if (position?.bottom) {
|
|
133
|
+
strategy.bottom(position.bottom);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
strategy.centerVertically();
|
|
138
|
+
}
|
|
139
|
+
this._overlayRef.updatePosition();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Updates the dialog's dimensions
|
|
144
|
+
*/
|
|
145
|
+
updateSize(width, height) {
|
|
146
|
+
this._overlayRef.updateSize({
|
|
147
|
+
width: width ?? undefined,
|
|
148
|
+
height: height ?? undefined,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Adds CSS classes to the dialog panel
|
|
153
|
+
*/
|
|
154
|
+
addPanelClass(classes) {
|
|
155
|
+
this._overlayRef.addPanelClass(classes);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Removes CSS classes from the dialog panel
|
|
159
|
+
*/
|
|
160
|
+
removePanelClass(classes) {
|
|
161
|
+
this._overlayRef.removePanelClass(classes);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Gets the current state of the dialog's lifecycle
|
|
165
|
+
*/
|
|
166
|
+
getState() {
|
|
167
|
+
return this._state;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Notifies that the dialog has been opened (internal use)
|
|
171
|
+
*/
|
|
172
|
+
_notifyOpened() {
|
|
173
|
+
this._afterOpened.next();
|
|
174
|
+
this._afterOpened.complete();
|
|
175
|
+
}
|
|
176
|
+
_setupEventHandlers() {
|
|
177
|
+
// Handle backdrop clicks
|
|
178
|
+
this._overlayRef.backdropClick.subscribe(() => {
|
|
179
|
+
if (!this.disableClose) {
|
|
180
|
+
this.close();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
// Handle escape key
|
|
184
|
+
this._overlayRef.keydownEvents
|
|
185
|
+
.pipe(filter((event) => event.key === 'Escape' && !this.disableClose))
|
|
186
|
+
.subscribe((event) => {
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
this.close();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
_finishDialogClose() {
|
|
192
|
+
// Dispose the overlay
|
|
193
|
+
this._overlayRef.dispose();
|
|
194
|
+
// Emit after closed event
|
|
195
|
+
this._afterClosed.next(this._result);
|
|
196
|
+
this._afterClosed.complete();
|
|
197
|
+
}
|
|
198
|
+
_parseAnimationDuration(duration) {
|
|
199
|
+
if (duration === undefined) {
|
|
200
|
+
return 200; // Default 200ms
|
|
201
|
+
}
|
|
202
|
+
if (typeof duration === 'number') {
|
|
203
|
+
return duration;
|
|
204
|
+
}
|
|
205
|
+
// Parse CSS duration string (e.g., '200ms', '0.2s')
|
|
206
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(duration);
|
|
207
|
+
if (match) {
|
|
208
|
+
const value = parseFloat(match[1]);
|
|
209
|
+
const unit = match[2];
|
|
210
|
+
return unit === 's' ? value * 1000 : value;
|
|
211
|
+
}
|
|
212
|
+
return 200;
|
|
213
|
+
}
|
|
214
|
+
_generateId() {
|
|
215
|
+
return `fui-dialog-${Math.random().toString(36).substr(2, 9)}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* # FuiDialogContainerComponent
|
|
221
|
+
*
|
|
222
|
+
* Internal container component that hosts the dialog content. This component
|
|
223
|
+
* handles focus trapping, animations, and accessibility attributes.
|
|
224
|
+
*
|
|
225
|
+
* ## Features
|
|
226
|
+
* - Focus trapping within the dialog
|
|
227
|
+
* - Configurable ARIA attributes
|
|
228
|
+
* - Enter/exit animations
|
|
229
|
+
* - Focus restoration on close
|
|
230
|
+
*/
|
|
231
|
+
class FuiDialogContainerComponent {
|
|
232
|
+
_document = inject(DOCUMENT);
|
|
233
|
+
_elementRef = inject((ElementRef));
|
|
234
|
+
_dialogContainer;
|
|
235
|
+
/** Configuration for the dialog */
|
|
236
|
+
config;
|
|
237
|
+
/** The previously focused element before the dialog was opened */
|
|
238
|
+
_elementFocusedBeforeDialogWasOpened = null;
|
|
239
|
+
/** Animation state of the dialog */
|
|
240
|
+
animationState = signal('void', ...(ngDevMode ? [{ debugName: "animationState" }] : /* istanbul ignore next */ []));
|
|
241
|
+
/** Whether the dialog is currently animating */
|
|
242
|
+
isAnimating = computed(() => this.animationState() !== 'void', ...(ngDevMode ? [{ debugName: "isAnimating" }] : /* istanbul ignore next */ []));
|
|
243
|
+
ngAfterViewInit() {
|
|
244
|
+
this._trapFocus();
|
|
245
|
+
}
|
|
246
|
+
ngOnDestroy() {
|
|
247
|
+
this._restoreFocus();
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Handles keydown events for focus trap cycling within the dialog.
|
|
251
|
+
* Tab loops from last to first focusable element, Shift+Tab from first to last.
|
|
252
|
+
*/
|
|
253
|
+
_onKeydown(event) {
|
|
254
|
+
if (event.key === 'Tab') {
|
|
255
|
+
const focusableElements = this._getFocusableElements();
|
|
256
|
+
if (focusableElements.length === 0) {
|
|
257
|
+
event.preventDefault();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const firstFocusable = focusableElements[0];
|
|
261
|
+
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
262
|
+
const activeElement = this._document.activeElement;
|
|
263
|
+
if (event.shiftKey) {
|
|
264
|
+
// Shift+Tab: if on first element, wrap to last
|
|
265
|
+
if (activeElement === firstFocusable || activeElement === this._dialogContainer.nativeElement) {
|
|
266
|
+
event.preventDefault();
|
|
267
|
+
lastFocusable.focus();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
// Tab: if on last element, wrap to first
|
|
272
|
+
if (activeElement === lastFocusable) {
|
|
273
|
+
event.preventDefault();
|
|
274
|
+
firstFocusable.focus();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Initializes the dialog container with the given configuration
|
|
281
|
+
*/
|
|
282
|
+
_initializeWithConfig(config) {
|
|
283
|
+
this.config = config;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Starts the enter animation
|
|
287
|
+
*/
|
|
288
|
+
_startEnterAnimation() {
|
|
289
|
+
this.animationState.set('enter');
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Starts the exit animation
|
|
293
|
+
*/
|
|
294
|
+
_startExitAnimation() {
|
|
295
|
+
this.animationState.set('exit');
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Gets the native element of the container
|
|
299
|
+
*/
|
|
300
|
+
_getHostElement() {
|
|
301
|
+
return this._elementRef.nativeElement;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Saves the element that was focused before the dialog opened and traps focus
|
|
305
|
+
*/
|
|
306
|
+
_trapFocus() {
|
|
307
|
+
// Store the currently focused element
|
|
308
|
+
this._elementFocusedBeforeDialogWasOpened = this._document.activeElement;
|
|
309
|
+
// Focus the dialog container
|
|
310
|
+
const autoFocus = this.config?.autoFocus ?? 'first-tabbable';
|
|
311
|
+
if (autoFocus === false || autoFocus === 'dialog') {
|
|
312
|
+
// Focus the dialog container itself
|
|
313
|
+
this._dialogContainer.nativeElement.focus();
|
|
314
|
+
}
|
|
315
|
+
else if (autoFocus === 'first-tabbable') {
|
|
316
|
+
this._focusFirstTabbableElement();
|
|
317
|
+
}
|
|
318
|
+
else if (autoFocus === 'first-heading') {
|
|
319
|
+
this._focusFirstHeading();
|
|
320
|
+
}
|
|
321
|
+
else if (typeof autoFocus === 'string') {
|
|
322
|
+
// Focus a specific element by selector
|
|
323
|
+
this._focusBySelector(autoFocus);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// Default: focus first tabbable
|
|
327
|
+
this._focusFirstTabbableElement();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Focuses the first tabbable element within the dialog
|
|
332
|
+
*/
|
|
333
|
+
_focusFirstTabbableElement() {
|
|
334
|
+
const focusableElements = this._getFocusableElements();
|
|
335
|
+
if (focusableElements.length > 0) {
|
|
336
|
+
focusableElements[0].focus();
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// Fallback to container
|
|
340
|
+
this._dialogContainer.nativeElement.focus();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Focuses the first heading element within the dialog
|
|
345
|
+
*/
|
|
346
|
+
_focusFirstHeading() {
|
|
347
|
+
const heading = this._dialogContainer.nativeElement.querySelector('h1, h2, h3, h4, h5, h6, [role="heading"]');
|
|
348
|
+
if (heading) {
|
|
349
|
+
// Make heading focusable if it's not already
|
|
350
|
+
if (!heading.hasAttribute('tabindex')) {
|
|
351
|
+
heading.setAttribute('tabindex', '-1');
|
|
352
|
+
}
|
|
353
|
+
heading.focus();
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
this._focusFirstTabbableElement();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Focuses an element matching the given selector
|
|
361
|
+
*/
|
|
362
|
+
_focusBySelector(selector) {
|
|
363
|
+
const element = this._dialogContainer.nativeElement.querySelector(selector);
|
|
364
|
+
if (element) {
|
|
365
|
+
element.focus();
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
this._focusFirstTabbableElement();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Gets all focusable elements within the dialog
|
|
373
|
+
*/
|
|
374
|
+
_getFocusableElements() {
|
|
375
|
+
const focusableSelectors = [
|
|
376
|
+
'a[href]',
|
|
377
|
+
'button:not([disabled])',
|
|
378
|
+
'textarea:not([disabled])',
|
|
379
|
+
'input:not([disabled])',
|
|
380
|
+
'select:not([disabled])',
|
|
381
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
382
|
+
'[contenteditable="true"]',
|
|
383
|
+
].join(',');
|
|
384
|
+
return Array.from(this._dialogContainer.nativeElement.querySelectorAll(focusableSelectors)).filter((el) => {
|
|
385
|
+
// Filter out elements that are not visible
|
|
386
|
+
return el.offsetParent !== null;
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Restores focus to the element that was focused before the dialog opened
|
|
391
|
+
*/
|
|
392
|
+
_restoreFocus() {
|
|
393
|
+
const shouldRestoreFocus = this.config?.restoreFocus !== false;
|
|
394
|
+
if (shouldRestoreFocus && this._elementFocusedBeforeDialogWasOpened) {
|
|
395
|
+
// Check if the element is still in the DOM and can receive focus
|
|
396
|
+
if (typeof this._elementFocusedBeforeDialogWasOpened.focus === 'function') {
|
|
397
|
+
this._elementFocusedBeforeDialogWasOpened.focus();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
this._elementFocusedBeforeDialogWasOpened = null;
|
|
401
|
+
}
|
|
402
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiDialogContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
403
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.6", type: FuiDialogContainerComponent, isStandalone: true, selector: "fui-dialog-container", host: { listeners: { "keydown": "_onKeydown($event)" }, classAttribute: "fui-dialog-container-host" }, viewQueries: [{ propertyName: "_dialogContainer", first: true, predicate: ["dialogContainer"], descendants: true, static: true }], ngImport: i0, template: `
|
|
404
|
+
<div
|
|
405
|
+
#dialogContainer
|
|
406
|
+
class="fui-dialog-container"
|
|
407
|
+
[class.fui-dialog-enter]="animationState() === 'enter'"
|
|
408
|
+
[class.fui-dialog-exit]="animationState() === 'exit'"
|
|
409
|
+
[attr.role]="config.role || 'dialog'"
|
|
410
|
+
[attr.aria-modal]="true"
|
|
411
|
+
[attr.aria-label]="config.ariaLabel"
|
|
412
|
+
[attr.aria-labelledby]="config.ariaLabelledBy"
|
|
413
|
+
[attr.aria-describedby]="config.ariaDescribedBy"
|
|
414
|
+
tabindex="-1"
|
|
415
|
+
>
|
|
416
|
+
<div class="fui-dialog-content" aria-live="polite">
|
|
417
|
+
<ng-content></ng-content>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
`, isInline: true, styles: [":host{display:block;outline:0}.fui-dialog-container{display:flex;flex-direction:column;box-sizing:border-box;overflow:auto;outline:0;max-height:inherit;border:1px solid var(--fui-border-color);border-radius:var(--fui-border-radius-md);background:var(--fui-surface-00);box-shadow:var(--fui-dialog-box-shadow, var(--fui-shadow-05))}.fui-dialog-content{display:contents}.fui-dialog-enter{animation:fui-dialog-enter var(--fui-duration-moderate-02) var(--fui-ease-entrance)}.fui-dialog-exit{animation:fui-dialog-exit var(--fui-duration-moderate-01) var(--fui-ease-exit)}@keyframes fui-dialog-enter{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes fui-dialog-exit{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@media(prefers-reduced-motion:reduce){.fui-dialog-enter,.fui-dialog-exit{animation:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
421
|
+
}
|
|
422
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiDialogContainerComponent, decorators: [{
|
|
423
|
+
type: Component,
|
|
424
|
+
args: [{ selector: 'fui-dialog-container', standalone: true, imports: [], template: `
|
|
425
|
+
<div
|
|
426
|
+
#dialogContainer
|
|
427
|
+
class="fui-dialog-container"
|
|
428
|
+
[class.fui-dialog-enter]="animationState() === 'enter'"
|
|
429
|
+
[class.fui-dialog-exit]="animationState() === 'exit'"
|
|
430
|
+
[attr.role]="config.role || 'dialog'"
|
|
431
|
+
[attr.aria-modal]="true"
|
|
432
|
+
[attr.aria-label]="config.ariaLabel"
|
|
433
|
+
[attr.aria-labelledby]="config.ariaLabelledBy"
|
|
434
|
+
[attr.aria-describedby]="config.ariaDescribedBy"
|
|
435
|
+
tabindex="-1"
|
|
436
|
+
>
|
|
437
|
+
<div class="fui-dialog-content" aria-live="polite">
|
|
438
|
+
<ng-content></ng-content>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
442
|
+
class: 'fui-dialog-container-host',
|
|
443
|
+
}, styles: [":host{display:block;outline:0}.fui-dialog-container{display:flex;flex-direction:column;box-sizing:border-box;overflow:auto;outline:0;max-height:inherit;border:1px solid var(--fui-border-color);border-radius:var(--fui-border-radius-md);background:var(--fui-surface-00);box-shadow:var(--fui-dialog-box-shadow, var(--fui-shadow-05))}.fui-dialog-content{display:contents}.fui-dialog-enter{animation:fui-dialog-enter var(--fui-duration-moderate-02) var(--fui-ease-entrance)}.fui-dialog-exit{animation:fui-dialog-exit var(--fui-duration-moderate-01) var(--fui-ease-exit)}@keyframes fui-dialog-enter{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes fui-dialog-exit{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@media(prefers-reduced-motion:reduce){.fui-dialog-enter,.fui-dialog-exit{animation:none}}\n"] }]
|
|
444
|
+
}], propDecorators: { _dialogContainer: [{
|
|
445
|
+
type: ViewChild,
|
|
446
|
+
args: ['dialogContainer', { static: true }]
|
|
447
|
+
}], _onKeydown: [{
|
|
448
|
+
type: HostListener,
|
|
449
|
+
args: ['keydown', ['$event']]
|
|
450
|
+
}] } });
|
|
451
|
+
|
|
452
|
+
class FuiConfirmDialogComponent {
|
|
453
|
+
data = inject(FUI_DIALOG_DATA);
|
|
454
|
+
dialogRef = inject(FuiDialogRef);
|
|
455
|
+
title = this.data.title;
|
|
456
|
+
message = this.data.message;
|
|
457
|
+
confirmText = this.data.confirmText ?? 'Confirm';
|
|
458
|
+
cancelText = this.data.cancelText ?? 'Cancel';
|
|
459
|
+
variant = this.data.variant ?? 'info';
|
|
460
|
+
icon = computed(() => {
|
|
461
|
+
switch (this.variant) {
|
|
462
|
+
case 'warning':
|
|
463
|
+
return 'warning-diamond';
|
|
464
|
+
case 'danger':
|
|
465
|
+
return 'warning-octagon';
|
|
466
|
+
default:
|
|
467
|
+
return 'info';
|
|
468
|
+
}
|
|
469
|
+
}, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
470
|
+
confirmButtonVariant = computed(() => {
|
|
471
|
+
switch (this.variant) {
|
|
472
|
+
case 'danger':
|
|
473
|
+
return 'danger';
|
|
474
|
+
case 'warning':
|
|
475
|
+
return 'primary';
|
|
476
|
+
default:
|
|
477
|
+
return 'primary';
|
|
478
|
+
}
|
|
479
|
+
}, ...(ngDevMode ? [{ debugName: "confirmButtonVariant" }] : /* istanbul ignore next */ []));
|
|
480
|
+
onConfirm() {
|
|
481
|
+
this.dialogRef.close(true);
|
|
482
|
+
}
|
|
483
|
+
onCancel() {
|
|
484
|
+
this.dialogRef.close(false);
|
|
485
|
+
}
|
|
486
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiConfirmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
487
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.6", type: FuiConfirmDialogComponent, isStandalone: true, selector: "fui-confirm-dialog", host: { classAttribute: "fui-confirm-dialog" }, ngImport: i0, template: "<div class=\"fui-confirm-dialog__header\">\n <fui-icon class=\"fui-confirm-dialog__icon fui-confirm-dialog__icon--{{ variant }}\" [name]=\"icon()\" size=\"lg\" />\n <h2 class=\"fui-confirm-dialog__title\" id=\"confirm-dialog-title\">{{ title }}</h2>\n</div>\n\n<div class=\"fui-confirm-dialog__body\" id=\"confirm-dialog-description\">\n <p class=\"fui-confirm-dialog__message\">{{ message }}</p>\n</div>\n\n<div class=\"fui-confirm-dialog__actions\">\n <button fuiButton variant=\"ghost\" (click)=\"onCancel()\">{{ cancelText }}</button>\n <button fuiButton [variant]=\"confirmButtonVariant()\" (click)=\"onConfirm()\">{{ confirmText }}</button>\n</div>\n", styles: [".fui-confirm-dialog{display:flex;flex-direction:column;padding:var(--fui-padding-24, 1.5rem)}.fui-confirm-dialog__header{display:flex;align-items:center;gap:var(--fui-gap-12, .75rem);margin-bottom:var(--fui-gap-16, 1rem)}.fui-confirm-dialog__icon{flex-shrink:0}.fui-confirm-dialog__icon--info{color:var(--fui-info)}.fui-confirm-dialog__icon--warning{color:var(--fui-warning)}.fui-confirm-dialog__icon--danger{color:var(--fui-danger)}.fui-confirm-dialog__title{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-04);font-weight:var(--fui-font-weight-semibold, 600);color:var(--fui-text-primary);margin:0}.fui-confirm-dialog__body{margin-bottom:var(--fui-padding-24, 1.5rem)}.fui-confirm-dialog__message{font-size:var(--fui-font-size-02);color:var(--fui-text-secondary);line-height:1.5;margin:0}.fui-confirm-dialog__actions{display:flex;justify-content:flex-end;gap:var(--fui-gap-8, .5rem)}\n"], dependencies: [{ kind: "component", type: FuiIconComponent, selector: "fui-icon", inputs: ["name", "size", "weight", "color", "ariaLabel", "spin", "pulse"] }, { kind: "directive", type: FuiButtonDirective, selector: "button[fuiButton], a[fuiButton]", inputs: ["variant", "size", "disabled", "fullWidth", "loading", "iconOnly", "aria-label", "type"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
488
|
+
}
|
|
489
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiConfirmDialogComponent, decorators: [{
|
|
490
|
+
type: Component,
|
|
491
|
+
args: [{ selector: 'fui-confirm-dialog', standalone: true, imports: [FuiIconComponent, FuiButtonDirective], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'fui-confirm-dialog' }, template: "<div class=\"fui-confirm-dialog__header\">\n <fui-icon class=\"fui-confirm-dialog__icon fui-confirm-dialog__icon--{{ variant }}\" [name]=\"icon()\" size=\"lg\" />\n <h2 class=\"fui-confirm-dialog__title\" id=\"confirm-dialog-title\">{{ title }}</h2>\n</div>\n\n<div class=\"fui-confirm-dialog__body\" id=\"confirm-dialog-description\">\n <p class=\"fui-confirm-dialog__message\">{{ message }}</p>\n</div>\n\n<div class=\"fui-confirm-dialog__actions\">\n <button fuiButton variant=\"ghost\" (click)=\"onCancel()\">{{ cancelText }}</button>\n <button fuiButton [variant]=\"confirmButtonVariant()\" (click)=\"onConfirm()\">{{ confirmText }}</button>\n</div>\n", styles: [".fui-confirm-dialog{display:flex;flex-direction:column;padding:var(--fui-padding-24, 1.5rem)}.fui-confirm-dialog__header{display:flex;align-items:center;gap:var(--fui-gap-12, .75rem);margin-bottom:var(--fui-gap-16, 1rem)}.fui-confirm-dialog__icon{flex-shrink:0}.fui-confirm-dialog__icon--info{color:var(--fui-info)}.fui-confirm-dialog__icon--warning{color:var(--fui-warning)}.fui-confirm-dialog__icon--danger{color:var(--fui-danger)}.fui-confirm-dialog__title{font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-04);font-weight:var(--fui-font-weight-semibold, 600);color:var(--fui-text-primary);margin:0}.fui-confirm-dialog__body{margin-bottom:var(--fui-padding-24, 1.5rem)}.fui-confirm-dialog__message{font-size:var(--fui-font-size-02);color:var(--fui-text-secondary);line-height:1.5;margin:0}.fui-confirm-dialog__actions{display:flex;justify-content:flex-end;gap:var(--fui-gap-8, .5rem)}\n"] }]
|
|
492
|
+
}] });
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* # FuiDialogService
|
|
496
|
+
*
|
|
497
|
+
* Service for opening modal dialogs. This service provides a comprehensive
|
|
498
|
+
* dialog system similar to Angular Material's MatDialog, built on top of
|
|
499
|
+
* the FuiOverlayService.
|
|
500
|
+
*
|
|
501
|
+
* ## Features
|
|
502
|
+
* - Open dialogs with components or templates
|
|
503
|
+
* - Pass data to dialog components
|
|
504
|
+
* - Configure positioning, sizing, and behavior
|
|
505
|
+
* - Get results from dialogs via observables
|
|
506
|
+
* - Manage multiple open dialogs
|
|
507
|
+
* - Automatic focus management and accessibility
|
|
508
|
+
*
|
|
509
|
+
* ## Usage
|
|
510
|
+
*
|
|
511
|
+
* ### Opening a Component Dialog
|
|
512
|
+
* ```typescript
|
|
513
|
+
* const dialogRef = this.dialog.open(MyDialogComponent, {
|
|
514
|
+
* width: '400px',
|
|
515
|
+
* data: { name: 'John' }
|
|
516
|
+
* });
|
|
517
|
+
*
|
|
518
|
+
* dialogRef.afterClosed().subscribe(result => {
|
|
519
|
+
* console.log('Dialog result:', result);
|
|
520
|
+
* });
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* ### Opening a Template Dialog
|
|
524
|
+
* ```typescript
|
|
525
|
+
* const dialogRef = this.dialog.open(myTemplateRef, {
|
|
526
|
+
* width: '300px',
|
|
527
|
+
* hasBackdrop: true
|
|
528
|
+
* });
|
|
529
|
+
* ```
|
|
530
|
+
*
|
|
531
|
+
* ### Accessing Data in Dialog Component
|
|
532
|
+
* ```typescript
|
|
533
|
+
* export class MyDialogComponent {
|
|
534
|
+
* private readonly data = inject(FUI_DIALOG_DATA);
|
|
535
|
+
* private readonly dialogRef = inject(FuiDialogRef);
|
|
536
|
+
*
|
|
537
|
+
* onClose() {
|
|
538
|
+
* this.dialogRef.close('result');
|
|
539
|
+
* }
|
|
540
|
+
* }
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
class FuiDialogService {
|
|
544
|
+
_overlayService = inject(FuiOverlayService);
|
|
545
|
+
_environmentInjector = inject(EnvironmentInjector);
|
|
546
|
+
_injector = inject(Injector);
|
|
547
|
+
_appRef = inject(ApplicationRef);
|
|
548
|
+
_openDialogs = [];
|
|
549
|
+
_afterOpenedSubject = new Subject();
|
|
550
|
+
_afterAllClosedSubject = new Subject();
|
|
551
|
+
_nextUniqueId = 0;
|
|
552
|
+
/**
|
|
553
|
+
* Observable that emits when a dialog is opened
|
|
554
|
+
*/
|
|
555
|
+
afterOpened = this._afterOpenedSubject.asObservable();
|
|
556
|
+
/**
|
|
557
|
+
* Observable that emits when all dialogs are closed
|
|
558
|
+
*/
|
|
559
|
+
afterAllClosed = defer(() => this._openDialogs.length ? this._getAfterAllClosed() : this._getAfterAllClosed().pipe(startWith(undefined)));
|
|
560
|
+
/**
|
|
561
|
+
* Gets all currently open dialogs
|
|
562
|
+
*/
|
|
563
|
+
get openDialogs() {
|
|
564
|
+
return this._openDialogs.slice();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Opens a dialog containing the given component or template
|
|
568
|
+
* @param componentOrTemplateRef Component type or TemplateRef to display
|
|
569
|
+
* @param config Configuration options for the dialog
|
|
570
|
+
* @returns Reference to the opened dialog
|
|
571
|
+
*/
|
|
572
|
+
open(componentOrTemplateRef, config) {
|
|
573
|
+
const mergedConfig = this._applyConfigDefaults(config);
|
|
574
|
+
const overlayRef = this._createOverlay(mergedConfig);
|
|
575
|
+
const dialogContainer = this._attachDialogContainer(overlayRef, mergedConfig);
|
|
576
|
+
const dialogRef = this._createDialogRef(overlayRef, dialogContainer, mergedConfig);
|
|
577
|
+
if (componentOrTemplateRef instanceof TemplateRef) {
|
|
578
|
+
this._attachTemplateContent(dialogContainer, componentOrTemplateRef, dialogRef);
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
const componentRef = this._attachComponentContent(dialogContainer, componentOrTemplateRef, dialogRef, mergedConfig);
|
|
582
|
+
dialogRef.componentInstance = componentRef.instance;
|
|
583
|
+
}
|
|
584
|
+
// Track open dialogs
|
|
585
|
+
this._openDialogs.push(dialogRef);
|
|
586
|
+
// Handle dialog close
|
|
587
|
+
dialogRef.afterClosed.subscribe(() => {
|
|
588
|
+
this._removeOpenDialog(dialogRef);
|
|
589
|
+
});
|
|
590
|
+
// Start enter animation and notify opened
|
|
591
|
+
dialogContainer._startEnterAnimation();
|
|
592
|
+
// Notify that dialog has been opened
|
|
593
|
+
requestAnimationFrame(() => {
|
|
594
|
+
dialogRef._notifyOpened();
|
|
595
|
+
this._afterOpenedSubject.next(dialogRef);
|
|
596
|
+
});
|
|
597
|
+
return dialogRef;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Closes all open dialogs
|
|
601
|
+
*/
|
|
602
|
+
closeAll() {
|
|
603
|
+
const dialogs = this._openDialogs.slice();
|
|
604
|
+
dialogs.forEach((dialog) => {
|
|
605
|
+
dialog.close();
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Gets a dialog by its ID
|
|
610
|
+
* @param id Dialog ID
|
|
611
|
+
* @returns The dialog reference or undefined
|
|
612
|
+
*/
|
|
613
|
+
getDialogById(id) {
|
|
614
|
+
return this._openDialogs.find((dialog) => dialog.id === id);
|
|
615
|
+
}
|
|
616
|
+
_createOverlay(config) {
|
|
617
|
+
const overlayConfig = this._getOverlayConfig(config);
|
|
618
|
+
return this._overlayService.create(overlayConfig);
|
|
619
|
+
}
|
|
620
|
+
_getOverlayConfig(config) {
|
|
621
|
+
// Create position strategy
|
|
622
|
+
const positionStrategy = this._overlayService.position().global();
|
|
623
|
+
// Apply positioning
|
|
624
|
+
if (config.position?.left || config.position?.right) {
|
|
625
|
+
if (config.position.left) {
|
|
626
|
+
positionStrategy.left(config.position.left);
|
|
627
|
+
}
|
|
628
|
+
else if (config.position.right) {
|
|
629
|
+
positionStrategy.right(config.position.right);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
positionStrategy.centerHorizontally();
|
|
634
|
+
}
|
|
635
|
+
if (config.position?.top || config.position?.bottom) {
|
|
636
|
+
if (config.position.top) {
|
|
637
|
+
positionStrategy.top(config.position.top);
|
|
638
|
+
}
|
|
639
|
+
else if (config.position.bottom) {
|
|
640
|
+
positionStrategy.bottom(config.position.bottom);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
positionStrategy.centerVertically();
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
positionStrategy,
|
|
648
|
+
scrollStrategy: this._overlayService.scrollStrategies.block(),
|
|
649
|
+
hasBackdrop: config.hasBackdrop ?? true,
|
|
650
|
+
backdropClass: this._getBackdropClass(config),
|
|
651
|
+
backdropClickBehavior: config.disableClose ? 'ignore' : 'close',
|
|
652
|
+
panelClass: this._getPanelClass(config),
|
|
653
|
+
width: config.width,
|
|
654
|
+
height: config.height,
|
|
655
|
+
minWidth: config.minWidth,
|
|
656
|
+
minHeight: config.minHeight,
|
|
657
|
+
maxWidth: config.maxWidth,
|
|
658
|
+
maxHeight: config.maxHeight,
|
|
659
|
+
direction: config.direction,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
_getBackdropClass(config) {
|
|
663
|
+
const baseClass = 'fui-dialog-backdrop';
|
|
664
|
+
let customClasses = [];
|
|
665
|
+
if (config.backdropClass) {
|
|
666
|
+
customClasses = Array.isArray(config.backdropClass) ? config.backdropClass : [config.backdropClass];
|
|
667
|
+
}
|
|
668
|
+
return [baseClass, ...customClasses];
|
|
669
|
+
}
|
|
670
|
+
_getPanelClass(config) {
|
|
671
|
+
const baseClass = 'fui-dialog-panel';
|
|
672
|
+
let customPanelClasses = [];
|
|
673
|
+
if (config.panelClass) {
|
|
674
|
+
customPanelClasses = Array.isArray(config.panelClass) ? config.panelClass : [config.panelClass];
|
|
675
|
+
}
|
|
676
|
+
return [baseClass, ...customPanelClasses];
|
|
677
|
+
}
|
|
678
|
+
_attachDialogContainer(overlayRef, config) {
|
|
679
|
+
const containerRef = createComponent(FuiDialogContainerComponent, {
|
|
680
|
+
environmentInjector: this._environmentInjector,
|
|
681
|
+
});
|
|
682
|
+
// Initialize the container with config
|
|
683
|
+
containerRef.instance._initializeWithConfig(config);
|
|
684
|
+
// Trigger change detection
|
|
685
|
+
containerRef.changeDetectorRef.detectChanges();
|
|
686
|
+
// Attach to overlay
|
|
687
|
+
overlayRef.attach(containerRef);
|
|
688
|
+
return containerRef.instance;
|
|
689
|
+
}
|
|
690
|
+
_createDialogRef(overlayRef, container, config) {
|
|
691
|
+
const dialogId = config.id ?? `fui-dialog-${this._nextUniqueId++}`;
|
|
692
|
+
return new FuiDialogRef(overlayRef, config, dialogId);
|
|
693
|
+
}
|
|
694
|
+
_attachComponentContent(container, component, dialogRef, config) {
|
|
695
|
+
// Create custom injector with dialog data and ref
|
|
696
|
+
const providers = [
|
|
697
|
+
{ provide: FUI_DIALOG_DATA, useValue: config.data },
|
|
698
|
+
{ provide: FuiDialogRef, useValue: dialogRef },
|
|
699
|
+
];
|
|
700
|
+
const injector = Injector.create({
|
|
701
|
+
parent: config.injector ?? this._injector,
|
|
702
|
+
providers,
|
|
703
|
+
});
|
|
704
|
+
// Create the component
|
|
705
|
+
const componentRef = createComponent(component, {
|
|
706
|
+
environmentInjector: this._environmentInjector,
|
|
707
|
+
elementInjector: injector,
|
|
708
|
+
});
|
|
709
|
+
// Attach to ApplicationRef to connect it to Angular's change detection tree
|
|
710
|
+
// This is crucial for form controls and event bindings to work properly
|
|
711
|
+
this._appRef.attachView(componentRef.hostView);
|
|
712
|
+
// Append to container
|
|
713
|
+
const hostElement = container._getHostElement();
|
|
714
|
+
const containerElement = hostElement.querySelector('.fui-dialog-container');
|
|
715
|
+
if (containerElement) {
|
|
716
|
+
containerElement.appendChild(componentRef.location.nativeElement);
|
|
717
|
+
}
|
|
718
|
+
// Trigger change detection
|
|
719
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
720
|
+
return componentRef;
|
|
721
|
+
}
|
|
722
|
+
_attachTemplateContent(container, template, dialogRef) {
|
|
723
|
+
// Create the embedded view
|
|
724
|
+
const context = { $implicit: dialogRef };
|
|
725
|
+
const viewRef = template.createEmbeddedView(context);
|
|
726
|
+
// Attach to ApplicationRef to connect it to Angular's change detection tree
|
|
727
|
+
this._appRef.attachView(viewRef);
|
|
728
|
+
// Append to container
|
|
729
|
+
const hostElement = container._getHostElement();
|
|
730
|
+
const containerElement = hostElement.querySelector('.fui-dialog-container');
|
|
731
|
+
if (containerElement) {
|
|
732
|
+
viewRef.rootNodes.forEach((node) => {
|
|
733
|
+
containerElement.appendChild(node);
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
// Mark for check
|
|
737
|
+
viewRef.detectChanges();
|
|
738
|
+
return viewRef;
|
|
739
|
+
}
|
|
740
|
+
_removeOpenDialog(dialogRef) {
|
|
741
|
+
const index = this._openDialogs.indexOf(dialogRef);
|
|
742
|
+
if (index > -1) {
|
|
743
|
+
this._openDialogs.splice(index, 1);
|
|
744
|
+
// Emit if all dialogs are closed
|
|
745
|
+
if (this._openDialogs.length === 0) {
|
|
746
|
+
this._afterAllClosedSubject.next();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
_getAfterAllClosed() {
|
|
751
|
+
return this._afterAllClosedSubject.asObservable();
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Opens a confirmation dialog and returns an Observable<boolean>.
|
|
755
|
+
* Resolves to `true` when confirmed, `false` when cancelled, ESC, or backdrop click.
|
|
756
|
+
*/
|
|
757
|
+
confirm(config) {
|
|
758
|
+
const dialogRef = this.open(FuiConfirmDialogComponent, {
|
|
759
|
+
width: '28rem',
|
|
760
|
+
maxWidth: '90vw',
|
|
761
|
+
data: config,
|
|
762
|
+
role: config.variant === 'danger' ? 'alertdialog' : 'dialog',
|
|
763
|
+
ariaLabelledBy: 'confirm-dialog-title',
|
|
764
|
+
ariaDescribedBy: 'confirm-dialog-description',
|
|
765
|
+
disableClose: false,
|
|
766
|
+
});
|
|
767
|
+
return dialogRef.afterClosed.pipe(map((result) => result === true));
|
|
768
|
+
}
|
|
769
|
+
_applyConfigDefaults(config) {
|
|
770
|
+
return {
|
|
771
|
+
role: 'dialog',
|
|
772
|
+
hasBackdrop: true,
|
|
773
|
+
disableClose: false,
|
|
774
|
+
maxWidth: '80vw',
|
|
775
|
+
autoFocus: 'first-tabbable',
|
|
776
|
+
restoreFocus: true,
|
|
777
|
+
closeOnNavigation: true,
|
|
778
|
+
enterAnimationDuration: 200,
|
|
779
|
+
exitAnimationDuration: 150,
|
|
780
|
+
...config,
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
784
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiDialogService, providedIn: 'root' });
|
|
785
|
+
}
|
|
786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiDialogService, decorators: [{
|
|
787
|
+
type: Injectable,
|
|
788
|
+
args: [{
|
|
789
|
+
providedIn: 'root',
|
|
790
|
+
}]
|
|
791
|
+
}] });
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Generated bundle index. Do not edit.
|
|
795
|
+
*/
|
|
796
|
+
|
|
797
|
+
export { FUI_DIALOG_DATA, FUI_DIALOG_DEFAULT_OPTIONS, FuiConfirmDialogComponent, FuiDialogContainerComponent, FuiDialogRef, FuiDialogService };
|
|
798
|
+
//# sourceMappingURL=raintonic-formaui-services-dialog.mjs.map
|