@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.
Files changed (240) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +145 -0
  3. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +806 -0
  4. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -0
  5. package/fesm2022/raintonic-formaui-cdk-form-field.mjs +86 -0
  6. package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -0
  7. package/fesm2022/raintonic-formaui-cdk-overlay.mjs +1757 -0
  8. package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -0
  9. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +287 -0
  10. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -0
  11. package/fesm2022/raintonic-formaui-components-accordion.mjs +217 -0
  12. package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -0
  13. package/fesm2022/raintonic-formaui-components-alert.mjs +161 -0
  14. package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -0
  15. package/fesm2022/raintonic-formaui-components-autocomplete.mjs +726 -0
  16. package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -0
  17. package/fesm2022/raintonic-formaui-components-avatar.mjs +92 -0
  18. package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -0
  19. package/fesm2022/raintonic-formaui-components-badge.mjs +107 -0
  20. package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -0
  21. package/fesm2022/raintonic-formaui-components-big-menu.mjs +68 -0
  22. package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +1 -0
  23. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +55 -0
  24. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -0
  25. package/fesm2022/raintonic-formaui-components-button-group.mjs +103 -0
  26. package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -0
  27. package/fesm2022/raintonic-formaui-components-button.mjs +241 -0
  28. package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -0
  29. package/fesm2022/raintonic-formaui-components-card.mjs +270 -0
  30. package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -0
  31. package/fesm2022/raintonic-formaui-components-checkbox.mjs +295 -0
  32. package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -0
  33. package/fesm2022/raintonic-formaui-components-data-table.mjs +631 -0
  34. package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -0
  35. package/fesm2022/raintonic-formaui-components-date-picker.mjs +1331 -0
  36. package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -0
  37. package/fesm2022/raintonic-formaui-components-divider.mjs +41 -0
  38. package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -0
  39. package/fesm2022/raintonic-formaui-components-drawer.mjs +190 -0
  40. package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -0
  41. package/fesm2022/raintonic-formaui-components-dynamic-form.mjs +266 -0
  42. package/fesm2022/raintonic-formaui-components-dynamic-form.mjs.map +1 -0
  43. package/fesm2022/raintonic-formaui-components-empty-state.mjs +33 -0
  44. package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -0
  45. package/fesm2022/raintonic-formaui-components-file-upload.mjs +246 -0
  46. package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -0
  47. package/fesm2022/raintonic-formaui-components-form-field.mjs +482 -0
  48. package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -0
  49. package/fesm2022/raintonic-formaui-components-icon.mjs +117 -0
  50. package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -0
  51. package/fesm2022/raintonic-formaui-components-input.mjs +327 -0
  52. package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -0
  53. package/fesm2022/raintonic-formaui-components-list.mjs +149 -0
  54. package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -0
  55. package/fesm2022/raintonic-formaui-components-menu.mjs +896 -0
  56. package/fesm2022/raintonic-formaui-components-menu.mjs.map +1 -0
  57. package/fesm2022/raintonic-formaui-components-number-input.mjs +345 -0
  58. package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -0
  59. package/fesm2022/raintonic-formaui-components-paginator.mjs +139 -0
  60. package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -0
  61. package/fesm2022/raintonic-formaui-components-password-input.mjs +306 -0
  62. package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -0
  63. package/fesm2022/raintonic-formaui-components-popover.mjs +451 -0
  64. package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -0
  65. package/fesm2022/raintonic-formaui-components-progressbar.mjs +148 -0
  66. package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -0
  67. package/fesm2022/raintonic-formaui-components-radio.mjs +260 -0
  68. package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -0
  69. package/fesm2022/raintonic-formaui-components-select.mjs +1011 -0
  70. package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -0
  71. package/fesm2022/raintonic-formaui-components-side-panel.mjs +150 -0
  72. package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -0
  73. package/fesm2022/raintonic-formaui-components-sidebar.mjs +257 -0
  74. package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +1 -0
  75. package/fesm2022/raintonic-formaui-components-skeleton.mjs +50 -0
  76. package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -0
  77. package/fesm2022/raintonic-formaui-components-slider.mjs +347 -0
  78. package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -0
  79. package/fesm2022/raintonic-formaui-components-spinner.mjs +63 -0
  80. package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -0
  81. package/fesm2022/raintonic-formaui-components-stepper.mjs +317 -0
  82. package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -0
  83. package/fesm2022/raintonic-formaui-components-tab.mjs +197 -0
  84. package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -0
  85. package/fesm2022/raintonic-formaui-components-tag.mjs +78 -0
  86. package/fesm2022/raintonic-formaui-components-tag.mjs.map +1 -0
  87. package/fesm2022/raintonic-formaui-components-time-picker.mjs +644 -0
  88. package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -0
  89. package/fesm2022/raintonic-formaui-components-toggle.mjs +171 -0
  90. package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -0
  91. package/fesm2022/raintonic-formaui-components-toolbar.mjs +140 -0
  92. package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -0
  93. package/fesm2022/raintonic-formaui-components-tooltip.mjs +555 -0
  94. package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -0
  95. package/fesm2022/raintonic-formaui-components-tree-select.mjs +314 -0
  96. package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -0
  97. package/fesm2022/raintonic-formaui-components-tree-table.mjs +103 -0
  98. package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -0
  99. package/fesm2022/raintonic-formaui-components-tree.mjs +430 -0
  100. package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -0
  101. package/fesm2022/raintonic-formaui-core.mjs +62 -0
  102. package/fesm2022/raintonic-formaui-core.mjs.map +1 -0
  103. package/fesm2022/raintonic-formaui-services-dialog.mjs +798 -0
  104. package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -0
  105. package/fesm2022/raintonic-formaui-services-notification.mjs +391 -0
  106. package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -0
  107. package/fesm2022/raintonic-formaui-services-theme.mjs +248 -0
  108. package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -0
  109. package/fesm2022/raintonic-formaui-test-utils.mjs +66 -0
  110. package/fesm2022/raintonic-formaui-test-utils.mjs.map +1 -0
  111. package/fesm2022/raintonic-formaui.mjs +15 -0
  112. package/fesm2022/raintonic-formaui.mjs.map +1 -0
  113. package/llms-full.txt +1627 -0
  114. package/llms.txt +60 -0
  115. package/package.json +251 -0
  116. package/styles/_fonts-entry.scss +3 -0
  117. package/styles/fonts/dm-mono-400-latin.woff2 +0 -0
  118. package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
  119. package/styles/fonts/inter-tight-latin.woff2 +0 -0
  120. package/styles/index.scss +127 -0
  121. package/styles/partials/_constants.scss +29 -0
  122. package/styles/partials/_fonts.scss +36 -0
  123. package/styles/partials/_grid.scss +171 -0
  124. package/styles/partials/_mixins.scss +145 -0
  125. package/styles/partials/_motion.scss +252 -0
  126. package/styles/partials/_theme.scss +275 -0
  127. package/styles/partials/_typography.scss +112 -0
  128. package/styles/partials/_utilities.scss +480 -0
  129. package/styles/partials/themes/_dark.scss +254 -0
  130. package/styles/partials/themes/_light.scss +254 -0
  131. package/types/raintonic-formaui-cdk-drag-drop.d.ts +196 -0
  132. package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -0
  133. package/types/raintonic-formaui-cdk-form-field.d.ts +62 -0
  134. package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -0
  135. package/types/raintonic-formaui-cdk-overlay.d.ts +843 -0
  136. package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -0
  137. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +112 -0
  138. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -0
  139. package/types/raintonic-formaui-components-accordion.d.ts +124 -0
  140. package/types/raintonic-formaui-components-accordion.d.ts.map +1 -0
  141. package/types/raintonic-formaui-components-alert.d.ts +143 -0
  142. package/types/raintonic-formaui-components-alert.d.ts.map +1 -0
  143. package/types/raintonic-formaui-components-autocomplete.d.ts +193 -0
  144. package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -0
  145. package/types/raintonic-formaui-components-avatar.d.ts +52 -0
  146. package/types/raintonic-formaui-components-avatar.d.ts.map +1 -0
  147. package/types/raintonic-formaui-components-badge.d.ts +47 -0
  148. package/types/raintonic-formaui-components-badge.d.ts.map +1 -0
  149. package/types/raintonic-formaui-components-big-menu.d.ts +62 -0
  150. package/types/raintonic-formaui-components-big-menu.d.ts.map +1 -0
  151. package/types/raintonic-formaui-components-breadcrumb.d.ts +26 -0
  152. package/types/raintonic-formaui-components-breadcrumb.d.ts.map +1 -0
  153. package/types/raintonic-formaui-components-button-group.d.ts +61 -0
  154. package/types/raintonic-formaui-components-button-group.d.ts.map +1 -0
  155. package/types/raintonic-formaui-components-button.d.ts +116 -0
  156. package/types/raintonic-formaui-components-button.d.ts.map +1 -0
  157. package/types/raintonic-formaui-components-card.d.ts +191 -0
  158. package/types/raintonic-formaui-components-card.d.ts.map +1 -0
  159. package/types/raintonic-formaui-components-checkbox.d.ts +132 -0
  160. package/types/raintonic-formaui-components-checkbox.d.ts.map +1 -0
  161. package/types/raintonic-formaui-components-data-table.d.ts +368 -0
  162. package/types/raintonic-formaui-components-data-table.d.ts.map +1 -0
  163. package/types/raintonic-formaui-components-date-picker.d.ts +341 -0
  164. package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -0
  165. package/types/raintonic-formaui-components-divider.d.ts +21 -0
  166. package/types/raintonic-formaui-components-divider.d.ts.map +1 -0
  167. package/types/raintonic-formaui-components-drawer.d.ts +48 -0
  168. package/types/raintonic-formaui-components-drawer.d.ts.map +1 -0
  169. package/types/raintonic-formaui-components-dynamic-form.d.ts +412 -0
  170. package/types/raintonic-formaui-components-dynamic-form.d.ts.map +1 -0
  171. package/types/raintonic-formaui-components-empty-state.d.ts +14 -0
  172. package/types/raintonic-formaui-components-empty-state.d.ts.map +1 -0
  173. package/types/raintonic-formaui-components-file-upload.d.ts +77 -0
  174. package/types/raintonic-formaui-components-file-upload.d.ts.map +1 -0
  175. package/types/raintonic-formaui-components-form-field.d.ts +271 -0
  176. package/types/raintonic-formaui-components-form-field.d.ts.map +1 -0
  177. package/types/raintonic-formaui-components-icon.d.ts +61 -0
  178. package/types/raintonic-formaui-components-icon.d.ts.map +1 -0
  179. package/types/raintonic-formaui-components-input.d.ts +149 -0
  180. package/types/raintonic-formaui-components-input.d.ts.map +1 -0
  181. package/types/raintonic-formaui-components-list.d.ts +48 -0
  182. package/types/raintonic-formaui-components-list.d.ts.map +1 -0
  183. package/types/raintonic-formaui-components-menu.d.ts +403 -0
  184. package/types/raintonic-formaui-components-menu.d.ts.map +1 -0
  185. package/types/raintonic-formaui-components-number-input.d.ts +127 -0
  186. package/types/raintonic-formaui-components-number-input.d.ts.map +1 -0
  187. package/types/raintonic-formaui-components-paginator.d.ts +37 -0
  188. package/types/raintonic-formaui-components-paginator.d.ts.map +1 -0
  189. package/types/raintonic-formaui-components-password-input.d.ts +111 -0
  190. package/types/raintonic-formaui-components-password-input.d.ts.map +1 -0
  191. package/types/raintonic-formaui-components-popover.d.ts +131 -0
  192. package/types/raintonic-formaui-components-popover.d.ts.map +1 -0
  193. package/types/raintonic-formaui-components-progressbar.d.ts +111 -0
  194. package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -0
  195. package/types/raintonic-formaui-components-radio.d.ts +95 -0
  196. package/types/raintonic-formaui-components-radio.d.ts.map +1 -0
  197. package/types/raintonic-formaui-components-select.d.ts +307 -0
  198. package/types/raintonic-formaui-components-select.d.ts.map +1 -0
  199. package/types/raintonic-formaui-components-side-panel.d.ts +51 -0
  200. package/types/raintonic-formaui-components-side-panel.d.ts.map +1 -0
  201. package/types/raintonic-formaui-components-sidebar.d.ts +174 -0
  202. package/types/raintonic-formaui-components-sidebar.d.ts.map +1 -0
  203. package/types/raintonic-formaui-components-skeleton.d.ts +20 -0
  204. package/types/raintonic-formaui-components-skeleton.d.ts.map +1 -0
  205. package/types/raintonic-formaui-components-slider.d.ts +108 -0
  206. package/types/raintonic-formaui-components-slider.d.ts.map +1 -0
  207. package/types/raintonic-formaui-components-spinner.d.ts +42 -0
  208. package/types/raintonic-formaui-components-spinner.d.ts.map +1 -0
  209. package/types/raintonic-formaui-components-stepper.d.ts +126 -0
  210. package/types/raintonic-formaui-components-stepper.d.ts.map +1 -0
  211. package/types/raintonic-formaui-components-tab.d.ts +96 -0
  212. package/types/raintonic-formaui-components-tab.d.ts.map +1 -0
  213. package/types/raintonic-formaui-components-tag.d.ts +34 -0
  214. package/types/raintonic-formaui-components-tag.d.ts.map +1 -0
  215. package/types/raintonic-formaui-components-time-picker.d.ts +172 -0
  216. package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -0
  217. package/types/raintonic-formaui-components-toggle.d.ts +70 -0
  218. package/types/raintonic-formaui-components-toggle.d.ts.map +1 -0
  219. package/types/raintonic-formaui-components-toolbar.d.ts +128 -0
  220. package/types/raintonic-formaui-components-toolbar.d.ts.map +1 -0
  221. package/types/raintonic-formaui-components-tooltip.d.ts +268 -0
  222. package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -0
  223. package/types/raintonic-formaui-components-tree-select.d.ts +80 -0
  224. package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -0
  225. package/types/raintonic-formaui-components-tree-table.d.ts +90 -0
  226. package/types/raintonic-formaui-components-tree-table.d.ts.map +1 -0
  227. package/types/raintonic-formaui-components-tree.d.ts +104 -0
  228. package/types/raintonic-formaui-components-tree.d.ts.map +1 -0
  229. package/types/raintonic-formaui-core.d.ts +115 -0
  230. package/types/raintonic-formaui-core.d.ts.map +1 -0
  231. package/types/raintonic-formaui-services-dialog.d.ts +451 -0
  232. package/types/raintonic-formaui-services-dialog.d.ts.map +1 -0
  233. package/types/raintonic-formaui-services-notification.d.ts +221 -0
  234. package/types/raintonic-formaui-services-notification.d.ts.map +1 -0
  235. package/types/raintonic-formaui-services-theme.d.ts +126 -0
  236. package/types/raintonic-formaui-services-theme.d.ts.map +1 -0
  237. package/types/raintonic-formaui-test-utils.d.ts +24 -0
  238. package/types/raintonic-formaui-test-utils.d.ts.map +1 -0
  239. package/types/raintonic-formaui.d.ts +4 -0
  240. 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