@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,1757 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, ComponentRef, TemplateRef, EmbeddedViewRef, inject, ElementRef, Component, RendererFactory2, EnvironmentInjector, createComponent, Injectable, DOCUMENT as DOCUMENT$1 } from '@angular/core';
3
+ import { DOCUMENT } from '@angular/common';
4
+ import { Subject } from 'rxjs';
5
+
6
+ /**
7
+ * Injection token for the overlay container element
8
+ */
9
+ const FUI_OVERLAY_CONTAINER = new InjectionToken('FUI_OVERLAY_CONTAINER');
10
+ /**
11
+ * Injection token for the overlay scroll strategy
12
+ */
13
+ const FUI_OVERLAY_SCROLL_STRATEGY = new InjectionToken('FUI_OVERLAY_SCROLL_STRATEGY');
14
+
15
+ /**
16
+ * # FuiOverlayRefImpl
17
+ *
18
+ * Implementation of the FuiOverlayRef interface. This class manages the lifecycle
19
+ * of an individual overlay instance, including content attachment, positioning,
20
+ * and cleanup.
21
+ *
22
+ * ## Features
23
+ * - Content attachment and detachment
24
+ * - Event handling (backdrop clicks, keyboard events)
25
+ * - Position and size updates
26
+ * - CSS class management
27
+ * - Proper cleanup and disposal
28
+ */
29
+ class FuiOverlayRefImpl {
30
+ _id;
31
+ _overlayElement;
32
+ _renderer;
33
+ _zIndex;
34
+ _backdropElement = null;
35
+ _attachedContent = null;
36
+ _config;
37
+ _hasAttached = false;
38
+ // Event subjects
39
+ _backdropClick = new Subject();
40
+ _keydownEvents = new Subject();
41
+ _attachments = new Subject();
42
+ _detachments = new Subject();
43
+ // Event listeners
44
+ _backdropClickListener;
45
+ _keydownListener;
46
+ constructor(overlayElement, config, renderer, id, zIndex = 1000) {
47
+ this._id = id ?? this._generateId();
48
+ this._overlayElement = overlayElement;
49
+ this._config = { ...config };
50
+ this._renderer = renderer;
51
+ this._zIndex = zIndex;
52
+ requestAnimationFrame(() => {
53
+ this._setupOverlayElement();
54
+ this._setupBackdrop();
55
+ this._setupEventListeners();
56
+ });
57
+ }
58
+ /** Unique identifier for this overlay */
59
+ get id() {
60
+ return this._id;
61
+ }
62
+ /** The overlay pane element */
63
+ get overlayElement() {
64
+ return this._overlayElement;
65
+ }
66
+ /** The backdrop element (if any) */
67
+ get backdropElement() {
68
+ return this._backdropElement;
69
+ }
70
+ /** Observable that emits when the backdrop is clicked */
71
+ get backdropClick() {
72
+ return this._backdropClick.asObservable();
73
+ }
74
+ /** Observable that emits when a key is pressed while the overlay has focus */
75
+ get keydownEvents() {
76
+ return this._keydownEvents.asObservable();
77
+ }
78
+ /** Observable that emits when the overlay is attached to the DOM */
79
+ get attachments() {
80
+ return this._attachments.asObservable();
81
+ }
82
+ /** Observable that emits when the overlay is detached from the DOM */
83
+ get detachments() {
84
+ return this._detachments.asObservable();
85
+ }
86
+ /** Whether the overlay is currently attached */
87
+ get hasAttached() {
88
+ return this._hasAttached;
89
+ }
90
+ /**
91
+ * Attaches content to the overlay
92
+ */
93
+ attach(content, injector) {
94
+ if (this._hasAttached) {
95
+ throw new Error('Overlay already has content attached');
96
+ }
97
+ // Hide overlay initially to prevent flash at (0,0) before positioning
98
+ this._renderer.setStyle(this._overlayElement, 'opacity', '0');
99
+ let attachedContent = null;
100
+ if (content instanceof ComponentRef) {
101
+ // Component content
102
+ attachedContent = content;
103
+ this._overlayElement.appendChild(content.location.nativeElement);
104
+ }
105
+ else if (content instanceof TemplateRef) {
106
+ // Template content
107
+ const viewRef = content.createEmbeddedView({}, injector);
108
+ attachedContent = viewRef;
109
+ viewRef.rootNodes.forEach((node) => {
110
+ this._overlayElement.appendChild(node);
111
+ });
112
+ }
113
+ else if (content instanceof HTMLElement) {
114
+ // DOM element content
115
+ this._overlayElement.appendChild(content);
116
+ }
117
+ else if (typeof content === 'string') {
118
+ // String content
119
+ this._renderer.setProperty(this._overlayElement, 'textContent', content);
120
+ }
121
+ this._attachedContent = attachedContent;
122
+ this._hasAttached = true;
123
+ this._attachments.next();
124
+ // Apply position strategy after DOM has settled
125
+ // Use single RAF to ensure layout is complete, then make visible
126
+ if (this._config.positionStrategy) {
127
+ requestAnimationFrame(() => {
128
+ if (this._config.positionStrategy && this._hasAttached) {
129
+ this._config.positionStrategy.apply();
130
+ // Make overlay visible after positioning
131
+ this._renderer.setStyle(this._overlayElement, 'opacity', '1');
132
+ }
133
+ });
134
+ }
135
+ else {
136
+ // No position strategy, make visible immediately
137
+ this._renderer.setStyle(this._overlayElement, 'opacity', '1');
138
+ }
139
+ // Enable scroll strategy if available
140
+ if (this._config.scrollStrategy) {
141
+ this._config.scrollStrategy.enable();
142
+ }
143
+ return attachedContent;
144
+ }
145
+ /**
146
+ * Detaches the content from the overlay
147
+ */
148
+ detach() {
149
+ if (!this._hasAttached) {
150
+ return;
151
+ }
152
+ // Disable scroll strategy
153
+ if (this._config.scrollStrategy) {
154
+ this._config.scrollStrategy.disable();
155
+ }
156
+ // Clean up attached content
157
+ if (this._attachedContent) {
158
+ if (this._attachedContent instanceof ComponentRef) {
159
+ this._attachedContent.destroy();
160
+ }
161
+ else if (this._attachedContent instanceof EmbeddedViewRef) {
162
+ this._attachedContent.destroy();
163
+ }
164
+ this._attachedContent = null;
165
+ }
166
+ // Clear overlay element content
167
+ while (this._overlayElement.firstChild) {
168
+ this._overlayElement.removeChild(this._overlayElement.firstChild);
169
+ }
170
+ this._hasAttached = false;
171
+ this._detachments.next();
172
+ }
173
+ /**
174
+ * Disposes the overlay and cleans up resources
175
+ */
176
+ dispose() {
177
+ // Detach content first
178
+ this.detach();
179
+ // Dispose position strategy
180
+ if (this._config.positionStrategy) {
181
+ this._config.positionStrategy.dispose();
182
+ }
183
+ // Dispose scroll strategy
184
+ if (this._config.scrollStrategy) {
185
+ this._config.scrollStrategy.detach();
186
+ }
187
+ // Remove event listeners
188
+ this._removeEventListeners();
189
+ // Remove backdrop
190
+ if (this._backdropElement?.parentElement) {
191
+ this._backdropElement.parentElement.removeChild(this._backdropElement);
192
+ }
193
+ // Remove overlay element
194
+ if (this._overlayElement.parentElement) {
195
+ this._overlayElement.parentElement.removeChild(this._overlayElement);
196
+ }
197
+ // Complete subjects
198
+ this._backdropClick.complete();
199
+ this._keydownEvents.complete();
200
+ this._attachments.complete();
201
+ this._detachments.complete();
202
+ }
203
+ /**
204
+ * Updates the position of the overlay
205
+ */
206
+ updatePosition() {
207
+ if (this._config.positionStrategy) {
208
+ this._config.positionStrategy.apply();
209
+ }
210
+ }
211
+ /**
212
+ * Updates the size of the overlay
213
+ */
214
+ updateSize(config) {
215
+ if (config) {
216
+ Object.assign(this._config, config);
217
+ }
218
+ this._applySizeConfig();
219
+ }
220
+ /**
221
+ * Adds CSS classes to the overlay pane
222
+ */
223
+ addPanelClass(classes) {
224
+ const classArray = Array.isArray(classes) ? classes : [classes];
225
+ classArray.forEach((className) => {
226
+ if (className) {
227
+ this._renderer.addClass(this._overlayElement, className);
228
+ }
229
+ });
230
+ }
231
+ /**
232
+ * Removes CSS classes from the overlay pane
233
+ */
234
+ removePanelClass(classes) {
235
+ const classArray = Array.isArray(classes) ? classes : [classes];
236
+ classArray.forEach((className) => {
237
+ if (className) {
238
+ this._renderer.removeClass(this._overlayElement, className);
239
+ }
240
+ });
241
+ }
242
+ /**
243
+ * Gets the current configuration of the overlay
244
+ */
245
+ getConfig() {
246
+ return { ...this._config };
247
+ }
248
+ /**
249
+ * Updates the configuration of the overlay
250
+ */
251
+ updateConfig(config) {
252
+ const oldConfig = { ...this._config };
253
+ Object.assign(this._config, config);
254
+ // Update backdrop if needed
255
+ if (config.hasBackdrop !== undefined && config.hasBackdrop !== oldConfig.hasBackdrop) {
256
+ if (config.hasBackdrop) {
257
+ this._createBackdrop();
258
+ }
259
+ else {
260
+ this._removeBackdrop();
261
+ }
262
+ }
263
+ // Update backdrop classes
264
+ const backdrop = this._backdropElement;
265
+ if (config.backdropClass && backdrop) {
266
+ // Remove old classes
267
+ if (oldConfig.backdropClass) {
268
+ const oldClasses = Array.isArray(oldConfig.backdropClass) ? oldConfig.backdropClass : [oldConfig.backdropClass];
269
+ oldClasses.forEach((className) => {
270
+ if (className) {
271
+ this._renderer.removeClass(backdrop, className);
272
+ }
273
+ });
274
+ }
275
+ // Add new classes
276
+ const newClasses = Array.isArray(config.backdropClass) ? config.backdropClass : [config.backdropClass];
277
+ newClasses.forEach((className) => {
278
+ if (className) {
279
+ this._renderer.addClass(backdrop, className);
280
+ }
281
+ });
282
+ }
283
+ // Update panel classes
284
+ if (config.panelClass) {
285
+ // Remove old classes
286
+ if (oldConfig.panelClass) {
287
+ this.removePanelClass(oldConfig.panelClass);
288
+ }
289
+ // Add new classes
290
+ this.addPanelClass(config.panelClass);
291
+ }
292
+ // Update size
293
+ this._applySizeConfig();
294
+ // Update position strategy
295
+ if (config.positionStrategy && config.positionStrategy !== oldConfig.positionStrategy) {
296
+ if (oldConfig.positionStrategy) {
297
+ oldConfig.positionStrategy.dispose();
298
+ }
299
+ config.positionStrategy.setOverlayElement(this._overlayElement);
300
+ }
301
+ // Update scroll strategy
302
+ if (config.scrollStrategy && config.scrollStrategy !== oldConfig.scrollStrategy) {
303
+ if (oldConfig.scrollStrategy) {
304
+ oldConfig.scrollStrategy.detach();
305
+ }
306
+ config.scrollStrategy.attach(this);
307
+ }
308
+ }
309
+ _setupOverlayElement() {
310
+ // Add base classes
311
+ this._renderer.addClass(this._overlayElement, 'fui-overlay-pane');
312
+ // Add custom panel classes
313
+ if (this._config.panelClass) {
314
+ this.addPanelClass(this._config.panelClass);
315
+ }
316
+ // Apply z-index for proper stacking of nested overlays
317
+ this._renderer.setStyle(this._overlayElement, 'z-index', String(this._zIndex));
318
+ // Apply size configuration
319
+ this._applySizeConfig();
320
+ // Set direction
321
+ if (this._config.direction) {
322
+ this._renderer.setAttribute(this._overlayElement, 'dir', this._config.direction);
323
+ }
324
+ // Set position strategy
325
+ if (this._config.positionStrategy) {
326
+ this._config.positionStrategy.setOverlayElement(this._overlayElement);
327
+ }
328
+ }
329
+ _setupBackdrop() {
330
+ if (this._config.hasBackdrop) {
331
+ this._createBackdrop();
332
+ }
333
+ }
334
+ _createBackdrop() {
335
+ if (this._backdropElement) {
336
+ return;
337
+ }
338
+ this._backdropElement = this._renderer.createElement('div');
339
+ this._renderer.addClass(this._backdropElement, 'fui-overlay-backdrop');
340
+ // Apply z-index for backdrop (slightly below the overlay panel)
341
+ this._renderer.setStyle(this._backdropElement, 'z-index', String(this._zIndex - 1));
342
+ // Add custom backdrop classes
343
+ const backdropEl = this._backdropElement;
344
+ if (this._config.backdropClass && backdropEl) {
345
+ const classes = Array.isArray(this._config.backdropClass)
346
+ ? this._config.backdropClass
347
+ : [this._config.backdropClass];
348
+ classes.forEach((className) => {
349
+ if (className) {
350
+ this._renderer.addClass(backdropEl, className);
351
+ }
352
+ });
353
+ }
354
+ // Insert backdrop before overlay element
355
+ const parent = this._overlayElement.parentElement;
356
+ if (parent) {
357
+ this._renderer.insertBefore(parent, this._backdropElement, this._overlayElement);
358
+ }
359
+ // Setup backdrop click listener
360
+ this._backdropClickListener = (event) => {
361
+ if (event.target === this._backdropElement) {
362
+ this._backdropClick.next(event);
363
+ }
364
+ };
365
+ if (this._backdropElement) {
366
+ this._backdropElement.addEventListener('click', this._backdropClickListener);
367
+ }
368
+ }
369
+ _removeBackdrop() {
370
+ if (this._backdropElement) {
371
+ if (this._backdropClickListener) {
372
+ this._backdropElement.removeEventListener('click', this._backdropClickListener);
373
+ this._backdropClickListener = undefined;
374
+ }
375
+ if (this._backdropElement.parentElement) {
376
+ this._backdropElement.parentElement.removeChild(this._backdropElement);
377
+ }
378
+ this._backdropElement = null;
379
+ }
380
+ }
381
+ _setupEventListeners() {
382
+ // Keydown events
383
+ this._keydownListener = (event) => {
384
+ this._keydownEvents.next(event);
385
+ };
386
+ this._overlayElement.addEventListener('keydown', this._keydownListener);
387
+ }
388
+ _removeEventListeners() {
389
+ if (this._keydownListener) {
390
+ this._overlayElement.removeEventListener('keydown', this._keydownListener);
391
+ this._keydownListener = undefined;
392
+ }
393
+ if (this._backdropClickListener && this._backdropElement) {
394
+ this._backdropElement.removeEventListener('click', this._backdropClickListener);
395
+ this._backdropClickListener = undefined;
396
+ }
397
+ }
398
+ _applySizeConfig() {
399
+ const config = this._config;
400
+ if (config.width !== undefined) {
401
+ this._renderer.setStyle(this._overlayElement, 'width', this._coerceCssPixelValue(config.width));
402
+ }
403
+ if (config.height !== undefined) {
404
+ this._renderer.setStyle(this._overlayElement, 'height', this._coerceCssPixelValue(config.height));
405
+ }
406
+ if (config.minWidth !== undefined) {
407
+ this._renderer.setStyle(this._overlayElement, 'min-width', this._coerceCssPixelValue(config.minWidth));
408
+ }
409
+ if (config.minHeight !== undefined) {
410
+ this._renderer.setStyle(this._overlayElement, 'min-height', this._coerceCssPixelValue(config.minHeight));
411
+ }
412
+ if (config.maxWidth !== undefined) {
413
+ this._renderer.setStyle(this._overlayElement, 'max-width', this._coerceCssPixelValue(config.maxWidth));
414
+ }
415
+ if (config.maxHeight !== undefined) {
416
+ this._renderer.setStyle(this._overlayElement, 'max-height', this._coerceCssPixelValue(config.maxHeight));
417
+ }
418
+ }
419
+ _coerceCssPixelValue(value) {
420
+ return typeof value === 'number' ? `${value}px` : value;
421
+ }
422
+ _generateId() {
423
+ return `fui-overlay-${Math.random().toString(36).substr(2, 9)}`;
424
+ }
425
+ }
426
+
427
+ /**
428
+ * # FuiOverlayContainer Component
429
+ *
430
+ * Container component that holds all overlay content. This component is automatically
431
+ * created and attached to the document body when the overlay service is first used.
432
+ *
433
+ * ## Features
434
+ * - Provides a dedicated container for all overlay content
435
+ * - Manages z-index stacking for multiple overlays
436
+ * - Handles cleanup when destroyed
437
+ * - Applies proper CSS classes for styling
438
+ *
439
+ * ## Usage
440
+ *
441
+ * This component is used internally by the overlay service and should not be
442
+ * instantiated directly by application code.
443
+ */
444
+ class FuiOverlayContainerComponent {
445
+ _document = inject(DOCUMENT);
446
+ _elementRef = inject((ElementRef));
447
+ ngOnInit() {
448
+ // Ensure the container is attached to the document body
449
+ if (this._elementRef.nativeElement.parentElement !== this._document.body) {
450
+ this._document.body.appendChild(this._elementRef.nativeElement);
451
+ }
452
+ }
453
+ ngOnDestroy() {
454
+ // Clean up the container element
455
+ const element = this._elementRef.nativeElement;
456
+ if (element.parentElement) {
457
+ element.parentElement.removeChild(element);
458
+ }
459
+ }
460
+ /**
461
+ * Gets the container element
462
+ */
463
+ getContainerElement() {
464
+ return this._elementRef.nativeElement;
465
+ }
466
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiOverlayContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
467
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.6", type: FuiOverlayContainerComponent, isStandalone: true, selector: "fui-overlay-container", host: { properties: { "attr.aria-live": "\"polite\"", "attr.aria-atomic": "\"true\"" }, classAttribute: "fui-overlay-container" }, ngImport: i0, template: '', isInline: true, styles: [":host{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:var(--fui-z-popover)}\n"] });
468
+ }
469
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiOverlayContainerComponent, decorators: [{
470
+ type: Component,
471
+ args: [{ selector: 'fui-overlay-container', standalone: true, template: '', host: {
472
+ class: 'fui-overlay-container',
473
+ '[attr.aria-live]': '"polite"',
474
+ '[attr.aria-atomic]': '"true"',
475
+ }, styles: [":host{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:var(--fui-z-popover)}\n"] }]
476
+ }] });
477
+
478
+ /**
479
+ * # FuiGlobalPositionStrategy
480
+ *
481
+ * A position strategy that positions the overlay at a fixed position on the screen.
482
+ * This strategy is useful for modals, dialogs, and other overlays that should be
483
+ * positioned relative to the viewport rather than a specific element.
484
+ *
485
+ * ## Features
486
+ * - Fixed positioning relative to the viewport
487
+ * - Support for all CSS position properties (top, bottom, left, right)
488
+ * - Centering options (horizontal and vertical)
489
+ * - Responsive positioning
490
+ * - Size constraints (width, height, min/max dimensions)
491
+ */
492
+ class FuiGlobalPositionStrategy {
493
+ _renderer;
494
+ _options;
495
+ _overlayElement;
496
+ _positionChanges = new Subject();
497
+ _isDisposed = false;
498
+ constructor(_renderer, _options = {}) {
499
+ this._renderer = _renderer;
500
+ this._options = _options;
501
+ }
502
+ /** Observable that emits when the position changes */
503
+ get positionChanges() {
504
+ return this._positionChanges.asObservable();
505
+ }
506
+ /**
507
+ * Sets the overlay element reference
508
+ */
509
+ setOverlayElement(element) {
510
+ this._overlayElement = element;
511
+ }
512
+ /**
513
+ * Applies the positioning strategy
514
+ */
515
+ apply() {
516
+ if (!this._overlayElement || this._isDisposed) {
517
+ return;
518
+ }
519
+ // Set position to fixed for global positioning
520
+ this._renderer.setStyle(this._overlayElement, 'position', 'fixed');
521
+ // Apply positioning
522
+ this._applyPosition();
523
+ // Apply centering if requested
524
+ this._applyCentering();
525
+ // Apply size constraints
526
+ this._applySizeConstraints();
527
+ }
528
+ /**
529
+ * Sets the top position
530
+ */
531
+ top(value = '0') {
532
+ this._options.top = value;
533
+ this._options.bottom = undefined;
534
+ return this;
535
+ }
536
+ /**
537
+ * Sets the bottom position
538
+ */
539
+ bottom(value = '0') {
540
+ this._options.bottom = value;
541
+ this._options.top = undefined;
542
+ return this;
543
+ }
544
+ /**
545
+ * Sets the left position
546
+ */
547
+ left(value = '0') {
548
+ this._options.left = value;
549
+ this._options.right = undefined;
550
+ return this;
551
+ }
552
+ /**
553
+ * Sets the right position
554
+ */
555
+ right(value = '0') {
556
+ this._options.right = value;
557
+ this._options.left = undefined;
558
+ return this;
559
+ }
560
+ /**
561
+ * Centers the overlay horizontally
562
+ */
563
+ centerHorizontally() {
564
+ this._options.centerHorizontally = true;
565
+ this._options.left = undefined;
566
+ this._options.right = undefined;
567
+ return this;
568
+ }
569
+ /**
570
+ * Centers the overlay vertically
571
+ */
572
+ centerVertically() {
573
+ this._options.centerVertically = true;
574
+ this._options.top = undefined;
575
+ this._options.bottom = undefined;
576
+ return this;
577
+ }
578
+ /**
579
+ * Sets the width of the overlay
580
+ */
581
+ width(value) {
582
+ this._options.width = value;
583
+ return this;
584
+ }
585
+ /**
586
+ * Sets the height of the overlay
587
+ */
588
+ height(value) {
589
+ this._options.height = value;
590
+ return this;
591
+ }
592
+ /**
593
+ * Sets the minimum width of the overlay
594
+ */
595
+ minWidth(value) {
596
+ this._options.minWidth = value;
597
+ return this;
598
+ }
599
+ /**
600
+ * Sets the minimum height of the overlay
601
+ */
602
+ minHeight(value) {
603
+ this._options.minHeight = value;
604
+ return this;
605
+ }
606
+ /**
607
+ * Sets the maximum width of the overlay
608
+ */
609
+ maxWidth(value) {
610
+ this._options.maxWidth = value;
611
+ return this;
612
+ }
613
+ /**
614
+ * Sets the maximum height of the overlay
615
+ */
616
+ maxHeight(value) {
617
+ this._options.maxHeight = value;
618
+ return this;
619
+ }
620
+ /**
621
+ * Disposes the strategy and cleans up resources
622
+ */
623
+ dispose() {
624
+ if (this._isDisposed) {
625
+ return;
626
+ }
627
+ this._isDisposed = true;
628
+ this._positionChanges.complete();
629
+ }
630
+ /**
631
+ * Detaches the strategy from the overlay
632
+ */
633
+ detach() {
634
+ // Global position strategy doesn't need special detach logic
635
+ }
636
+ _applyPosition() {
637
+ if (!this._overlayElement) {
638
+ return;
639
+ }
640
+ const options = this._options;
641
+ // Apply top/bottom positioning
642
+ if (options.top !== undefined) {
643
+ this._renderer.setStyle(this._overlayElement, 'top', this._coerceCssPixelValue(options.top));
644
+ this._renderer.removeStyle(this._overlayElement, 'bottom');
645
+ }
646
+ else if (options.bottom !== undefined) {
647
+ this._renderer.setStyle(this._overlayElement, 'bottom', this._coerceCssPixelValue(options.bottom));
648
+ this._renderer.removeStyle(this._overlayElement, 'top');
649
+ }
650
+ // Apply left/right positioning
651
+ if (options.left !== undefined) {
652
+ this._renderer.setStyle(this._overlayElement, 'left', this._coerceCssPixelValue(options.left));
653
+ this._renderer.removeStyle(this._overlayElement, 'right');
654
+ }
655
+ else if (options.right !== undefined) {
656
+ this._renderer.setStyle(this._overlayElement, 'right', this._coerceCssPixelValue(options.right));
657
+ this._renderer.removeStyle(this._overlayElement, 'left');
658
+ }
659
+ }
660
+ _applyCentering() {
661
+ if (!this._overlayElement) {
662
+ return;
663
+ }
664
+ const options = this._options;
665
+ if (options.centerHorizontally) {
666
+ this._renderer.setStyle(this._overlayElement, 'left', '50%');
667
+ this._renderer.setStyle(this._overlayElement, 'transform', 'translateX(-50%)');
668
+ this._renderer.removeStyle(this._overlayElement, 'right');
669
+ }
670
+ if (options.centerVertically) {
671
+ this._renderer.setStyle(this._overlayElement, 'top', '50%');
672
+ const existingTransform = this._overlayElement.style.transform;
673
+ if (existingTransform?.includes('translateX')) {
674
+ this._renderer.setStyle(this._overlayElement, 'transform', 'translate(-50%, -50%)');
675
+ }
676
+ else {
677
+ this._renderer.setStyle(this._overlayElement, 'transform', 'translateY(-50%)');
678
+ }
679
+ this._renderer.removeStyle(this._overlayElement, 'bottom');
680
+ }
681
+ }
682
+ _applySizeConstraints() {
683
+ if (!this._overlayElement) {
684
+ return;
685
+ }
686
+ const options = this._options;
687
+ if (options.width !== undefined) {
688
+ this._renderer.setStyle(this._overlayElement, 'width', this._coerceCssPixelValue(options.width));
689
+ }
690
+ if (options.height !== undefined) {
691
+ this._renderer.setStyle(this._overlayElement, 'height', this._coerceCssPixelValue(options.height));
692
+ }
693
+ if (options.minWidth !== undefined) {
694
+ this._renderer.setStyle(this._overlayElement, 'min-width', this._coerceCssPixelValue(options.minWidth));
695
+ }
696
+ if (options.minHeight !== undefined) {
697
+ this._renderer.setStyle(this._overlayElement, 'min-height', this._coerceCssPixelValue(options.minHeight));
698
+ }
699
+ if (options.maxWidth !== undefined) {
700
+ this._renderer.setStyle(this._overlayElement, 'max-width', this._coerceCssPixelValue(options.maxWidth));
701
+ }
702
+ if (options.maxHeight !== undefined) {
703
+ this._renderer.setStyle(this._overlayElement, 'max-height', this._coerceCssPixelValue(options.maxHeight));
704
+ }
705
+ }
706
+ _coerceCssPixelValue(value) {
707
+ return typeof value === 'number' ? `${value}px` : value;
708
+ }
709
+ }
710
+
711
+ /**
712
+ * # FuiConnectedPositionStrategy
713
+ *
714
+ * A position strategy that positions the overlay relative to a connected element.
715
+ * This strategy is ideal for tooltips, dropdowns, menus, and other overlays that
716
+ * should be positioned relative to a trigger element.
717
+ *
718
+ * ## Features
719
+ * - Positions overlay relative to an origin element
720
+ * - Multiple fallback positions with automatic selection
721
+ * - Collision detection and viewport boundary handling
722
+ * - Push and grow behaviors for constrained spaces
723
+ * - Scroll and resize event handling
724
+ * - Transform origin support for animations
725
+ */
726
+ class FuiConnectedPositionStrategy {
727
+ _renderer;
728
+ _overlayElement;
729
+ _originElement;
730
+ _positions;
731
+ _appliedPosition;
732
+ _isDisposed = false;
733
+ _resizeSubscription;
734
+ _scrollSubscription;
735
+ _positionChanges = new Subject();
736
+ // Configuration options
737
+ _withPush = true;
738
+ _withGrowAfterOpen = false;
739
+ _withLockedPosition = false;
740
+ _defaultOffsetX = 0;
741
+ _defaultOffsetY = 0;
742
+ _transformOriginElement;
743
+ _viewportMargin = 0;
744
+ _withFlexibleDimensions = false;
745
+ constructor(_renderer, options) {
746
+ this._renderer = _renderer;
747
+ this._originElement = options.origin instanceof ElementRef ? options.origin.nativeElement : options.origin;
748
+ this._positions = [...options.positions];
749
+ // Apply configuration options
750
+ if (options.withPush !== undefined) {
751
+ this._withPush = options.withPush;
752
+ }
753
+ if (options.withGrowAfterOpen !== undefined) {
754
+ this._withGrowAfterOpen = options.withGrowAfterOpen;
755
+ }
756
+ if (options.withLockedPosition !== undefined) {
757
+ this._withLockedPosition = options.withLockedPosition;
758
+ }
759
+ if (options.withDefaultOffsetX !== undefined) {
760
+ this._defaultOffsetX = options.withDefaultOffsetX;
761
+ }
762
+ if (options.withDefaultOffsetY !== undefined) {
763
+ this._defaultOffsetY = options.withDefaultOffsetY;
764
+ }
765
+ if (options.withTransformOriginOn) {
766
+ this._transformOriginElement =
767
+ options.withTransformOriginOn instanceof ElementRef
768
+ ? options.withTransformOriginOn.nativeElement
769
+ : options.withTransformOriginOn;
770
+ }
771
+ if (options.viewportMargin !== undefined) {
772
+ this._viewportMargin = options.viewportMargin;
773
+ }
774
+ if (options.withFlexibleDimensions !== undefined) {
775
+ this._withFlexibleDimensions = options.withFlexibleDimensions;
776
+ }
777
+ }
778
+ /** Observable that emits when the position changes */
779
+ get positionChanges() {
780
+ return this._positionChanges.asObservable();
781
+ }
782
+ /**
783
+ * Sets the overlay element reference
784
+ */
785
+ setOverlayElement(element) {
786
+ this._overlayElement = element;
787
+ }
788
+ /**
789
+ * Applies the positioning strategy
790
+ */
791
+ apply() {
792
+ if (!this._overlayElement || this._isDisposed) {
793
+ return;
794
+ }
795
+ // Set position to fixed for precise positioning
796
+ this._renderer.setStyle(this._overlayElement, 'position', 'fixed');
797
+ // Force a layout recalculation to ensure accurate dimensions
798
+ // This is necessary to get the correct bounding rect
799
+ void this._overlayElement.offsetHeight;
800
+ // Find the best position
801
+ const bestPosition = this._getBestPosition();
802
+ if (bestPosition) {
803
+ this._applyPosition(bestPosition);
804
+ this._appliedPosition = bestPosition;
805
+ // Set up event listeners for repositioning
806
+ this._setupEventListeners();
807
+ // Emit position change
808
+ this._positionChanges.next({
809
+ connectionPair: bestPosition,
810
+ scrollableViewProperties: this._getScrollableViewProperties(),
811
+ });
812
+ }
813
+ }
814
+ /**
815
+ * Updates the positions array
816
+ */
817
+ withPositions(positions) {
818
+ this._positions = [...positions];
819
+ return this;
820
+ }
821
+ /**
822
+ * Enables pushing the overlay on-screen if it would otherwise be clipped
823
+ */
824
+ withPush(enabled = true) {
825
+ this._withPush = enabled;
826
+ return this;
827
+ }
828
+ /**
829
+ * Enables growing the overlay after it reaches the edge of the viewport
830
+ */
831
+ withGrowAfterOpen(enabled = true) {
832
+ this._withGrowAfterOpen = enabled;
833
+ return this;
834
+ }
835
+ /**
836
+ * Locks the position after the first time it is positioned
837
+ */
838
+ withLockedPosition(enabled = true) {
839
+ this._withLockedPosition = enabled;
840
+ return this;
841
+ }
842
+ /**
843
+ * Sets the default offset for the x-axis
844
+ */
845
+ withDefaultOffsetX(offset) {
846
+ this._defaultOffsetX = offset;
847
+ return this;
848
+ }
849
+ /**
850
+ * Sets the default offset for the y-axis
851
+ */
852
+ withDefaultOffsetY(offset) {
853
+ this._defaultOffsetY = offset;
854
+ return this;
855
+ }
856
+ /**
857
+ * Sets the element to use for transform origin
858
+ */
859
+ withTransformOriginOn(element) {
860
+ this._transformOriginElement = element instanceof ElementRef ? element.nativeElement : element;
861
+ return this;
862
+ }
863
+ /**
864
+ * Sets the margin between the overlay and the viewport edge
865
+ */
866
+ withViewportMargin(margin) {
867
+ this._viewportMargin = margin;
868
+ return this;
869
+ }
870
+ /**
871
+ * Enables flexible dimensions
872
+ */
873
+ withFlexibleDimensions(enabled = true) {
874
+ this._withFlexibleDimensions = enabled;
875
+ return this;
876
+ }
877
+ /**
878
+ * Disposes the strategy and cleans up resources
879
+ */
880
+ dispose() {
881
+ if (this._isDisposed) {
882
+ return;
883
+ }
884
+ this._isDisposed = true;
885
+ this._removeEventListeners();
886
+ this._positionChanges.complete();
887
+ }
888
+ /**
889
+ * Detaches the strategy from the overlay
890
+ */
891
+ detach() {
892
+ this._removeEventListeners();
893
+ }
894
+ _getBestPosition() {
895
+ if (!this._overlayElement || !this._originElement) {
896
+ return null;
897
+ }
898
+ // If position is locked and we have an applied position, use it
899
+ if (this._withLockedPosition && this._appliedPosition) {
900
+ return this._appliedPosition;
901
+ }
902
+ // Force layout recalculation to ensure overlay has correct dimensions
903
+ // This is critical for center-aligned positions (right, left, top, bottom)
904
+ void this._overlayElement.offsetHeight;
905
+ void this._overlayElement.offsetWidth;
906
+ const originRect = this._originElement.getBoundingClientRect();
907
+ const overlayRect = this._overlayElement.getBoundingClientRect();
908
+ const viewportSize = this._getViewportSize();
909
+ let bestPosition = null;
910
+ let bestScore = -1;
911
+ for (const position of this._positions) {
912
+ const positionedRect = this._calculatePositionedRect(position, originRect, overlayRect);
913
+ const score = this._calculatePositionScore(positionedRect, viewportSize);
914
+ if (score > bestScore) {
915
+ bestScore = score;
916
+ bestPosition = position;
917
+ }
918
+ // If we found a perfect position (fully visible), use it
919
+ if (score === 1) {
920
+ break;
921
+ }
922
+ }
923
+ return bestPosition;
924
+ }
925
+ _applyPosition(position) {
926
+ if (!this._overlayElement || !this._originElement) {
927
+ return;
928
+ }
929
+ // Force layout recalculation again to ensure dimensions are current
930
+ void this._overlayElement.offsetHeight;
931
+ void this._overlayElement.offsetWidth;
932
+ const originRect = this._originElement.getBoundingClientRect();
933
+ const overlayRect = this._overlayElement.getBoundingClientRect();
934
+ const positionedRect = this._calculatePositionedRect(position, originRect, overlayRect);
935
+ let { x, y } = positionedRect;
936
+ // Apply push behavior if enabled
937
+ if (this._withPush) {
938
+ const pushedPosition = this._pushWithinViewport({ x, y, width: overlayRect.width, height: overlayRect.height });
939
+ x = pushedPosition.x;
940
+ y = pushedPosition.y;
941
+ }
942
+ // Apply the position
943
+ this._renderer.setStyle(this._overlayElement, 'left', `${x}px`);
944
+ this._renderer.setStyle(this._overlayElement, 'top', `${y}px`);
945
+ // Apply transform origin if specified
946
+ if (this._transformOriginElement) {
947
+ this._setTransformOrigin(position);
948
+ }
949
+ // Apply flexible dimensions if enabled
950
+ if (this._withFlexibleDimensions) {
951
+ this._applyFlexibleDimensions(position, { x, y, width: overlayRect.width, height: overlayRect.height });
952
+ }
953
+ }
954
+ _calculatePositionedRect(position, originRect, overlayRect) {
955
+ const offsetX = (position.offsetX ?? 0) + this._defaultOffsetX;
956
+ const offsetY = (position.offsetY ?? 0) + this._defaultOffsetY;
957
+ let x;
958
+ let y;
959
+ // Calculate x position
960
+ switch (position.originX) {
961
+ case 'start':
962
+ x = originRect.left;
963
+ break;
964
+ case 'center':
965
+ x = originRect.left + originRect.width / 2;
966
+ break;
967
+ case 'end':
968
+ x = originRect.right;
969
+ break;
970
+ }
971
+ switch (position.overlayX) {
972
+ case 'start':
973
+ // x remains as is
974
+ break;
975
+ case 'center':
976
+ x -= overlayRect.width / 2;
977
+ break;
978
+ case 'end':
979
+ x -= overlayRect.width;
980
+ break;
981
+ }
982
+ // Calculate y position
983
+ switch (position.originY) {
984
+ case 'top':
985
+ y = originRect.top;
986
+ break;
987
+ case 'center':
988
+ y = originRect.top + originRect.height / 2;
989
+ break;
990
+ case 'bottom':
991
+ y = originRect.bottom;
992
+ break;
993
+ }
994
+ switch (position.overlayY) {
995
+ case 'top':
996
+ // y remains as is
997
+ break;
998
+ case 'center':
999
+ y -= overlayRect.height / 2;
1000
+ break;
1001
+ case 'bottom':
1002
+ y -= overlayRect.height;
1003
+ break;
1004
+ }
1005
+ return {
1006
+ x: x + offsetX,
1007
+ y: y + offsetY,
1008
+ width: overlayRect.width,
1009
+ height: overlayRect.height,
1010
+ };
1011
+ }
1012
+ _calculatePositionScore(rect, viewportSize) {
1013
+ const margin = this._viewportMargin;
1014
+ const visibleArea = Math.max(0, Math.min(rect.x + rect.width, viewportSize.width - margin) - Math.max(rect.x, margin)) *
1015
+ Math.max(0, Math.min(rect.y + rect.height, viewportSize.height - margin) - Math.max(rect.y, margin));
1016
+ const totalArea = rect.width * rect.height;
1017
+ return totalArea > 0 ? visibleArea / totalArea : 0;
1018
+ }
1019
+ _pushWithinViewport(rect) {
1020
+ const viewportSize = this._getViewportSize();
1021
+ const margin = this._viewportMargin;
1022
+ let { x, y } = rect;
1023
+ // Push horizontally
1024
+ if (x < margin) {
1025
+ x = margin;
1026
+ }
1027
+ else if (x + rect.width > viewportSize.width - margin) {
1028
+ x = viewportSize.width - rect.width - margin;
1029
+ }
1030
+ // Push vertically
1031
+ if (y < margin) {
1032
+ y = margin;
1033
+ }
1034
+ else if (y + rect.height > viewportSize.height - margin) {
1035
+ y = viewportSize.height - rect.height - margin;
1036
+ }
1037
+ return { x, y };
1038
+ }
1039
+ _setTransformOrigin(position) {
1040
+ if (!this._transformOriginElement || !this._overlayElement) {
1041
+ return;
1042
+ }
1043
+ let transformOrigin = '';
1044
+ // Set horizontal transform origin
1045
+ switch (position.overlayX) {
1046
+ case 'start':
1047
+ transformOrigin += 'left';
1048
+ break;
1049
+ case 'center':
1050
+ transformOrigin += 'center';
1051
+ break;
1052
+ case 'end':
1053
+ transformOrigin += 'right';
1054
+ break;
1055
+ }
1056
+ transformOrigin += ' ';
1057
+ // Set vertical transform origin
1058
+ switch (position.overlayY) {
1059
+ case 'top':
1060
+ transformOrigin += 'top';
1061
+ break;
1062
+ case 'center':
1063
+ transformOrigin += 'center';
1064
+ break;
1065
+ case 'bottom':
1066
+ transformOrigin += 'bottom';
1067
+ break;
1068
+ }
1069
+ this._renderer.setStyle(this._transformOriginElement, 'transform-origin', transformOrigin);
1070
+ }
1071
+ _applyFlexibleDimensions(position, rect) {
1072
+ if (!this._overlayElement) {
1073
+ return;
1074
+ }
1075
+ const viewportSize = this._getViewportSize();
1076
+ const margin = this._viewportMargin;
1077
+ // Calculate available space
1078
+ const availableWidth = viewportSize.width - rect.x - margin;
1079
+ const availableHeight = viewportSize.height - rect.y - margin;
1080
+ // Apply flexible width if needed
1081
+ if (rect.width > availableWidth) {
1082
+ this._renderer.setStyle(this._overlayElement, 'max-width', `${availableWidth}px`);
1083
+ }
1084
+ // Apply flexible height if needed
1085
+ if (rect.height > availableHeight) {
1086
+ this._renderer.setStyle(this._overlayElement, 'max-height', `${availableHeight}px`);
1087
+ }
1088
+ }
1089
+ _setupEventListeners() {
1090
+ if (this._isDisposed) {
1091
+ return;
1092
+ }
1093
+ // Remove existing listeners
1094
+ this._removeEventListeners();
1095
+ // Set up resize listener
1096
+ this._resizeSubscription = () => {
1097
+ if (!this._isDisposed) {
1098
+ this.apply();
1099
+ }
1100
+ };
1101
+ window.addEventListener('resize', this._resizeSubscription);
1102
+ // Set up scroll listener
1103
+ this._scrollSubscription = () => {
1104
+ if (!this._isDisposed) {
1105
+ this.apply();
1106
+ }
1107
+ };
1108
+ window.addEventListener('scroll', this._scrollSubscription, true);
1109
+ }
1110
+ _removeEventListeners() {
1111
+ if (this._resizeSubscription) {
1112
+ window.removeEventListener('resize', this._resizeSubscription);
1113
+ this._resizeSubscription = undefined;
1114
+ }
1115
+ if (this._scrollSubscription) {
1116
+ window.removeEventListener('scroll', this._scrollSubscription, true);
1117
+ this._scrollSubscription = undefined;
1118
+ }
1119
+ }
1120
+ _getViewportSize() {
1121
+ return {
1122
+ width: window.innerWidth,
1123
+ height: window.innerHeight,
1124
+ };
1125
+ }
1126
+ _getScrollableViewProperties() {
1127
+ const viewportSize = this._getViewportSize();
1128
+ return {
1129
+ size: viewportSize,
1130
+ scrollPosition: {
1131
+ top: window.pageYOffset || document.documentElement.scrollTop,
1132
+ left: window.pageXOffset || document.documentElement.scrollLeft,
1133
+ },
1134
+ };
1135
+ }
1136
+ }
1137
+
1138
+ /**
1139
+ * # FuiNoopScrollStrategy
1140
+ *
1141
+ * A scroll strategy that does nothing when the user scrolls.
1142
+ * This is useful for overlays that should remain in their position
1143
+ * regardless of scrolling.
1144
+ */
1145
+ class FuiNoopScrollStrategy {
1146
+ enable() {
1147
+ // Intentionally empty
1148
+ }
1149
+ disable() {
1150
+ // Intentionally empty
1151
+ }
1152
+ attach(_overlayRef) {
1153
+ // Intentionally empty
1154
+ }
1155
+ detach() {
1156
+ // Intentionally empty
1157
+ }
1158
+ }
1159
+ /**
1160
+ * # FuiCloseScrollStrategy
1161
+ *
1162
+ * A scroll strategy that closes the overlay when the user scrolls.
1163
+ * This is useful for overlays like tooltips or dropdowns that should
1164
+ * disappear when the user scrolls away from the trigger element.
1165
+ */
1166
+ class FuiCloseScrollStrategy {
1167
+ _threshold;
1168
+ _overlayRef;
1169
+ _scrollListener;
1170
+ _isEnabled = false;
1171
+ constructor(_threshold = 0) {
1172
+ this._threshold = _threshold;
1173
+ }
1174
+ enable() {
1175
+ if (this._isEnabled || !this._overlayRef) {
1176
+ return;
1177
+ }
1178
+ this._isEnabled = true;
1179
+ this._scrollListener = () => {
1180
+ if (this._overlayRef) {
1181
+ this._overlayRef.dispose();
1182
+ }
1183
+ };
1184
+ window.addEventListener('scroll', this._scrollListener, true);
1185
+ }
1186
+ disable() {
1187
+ if (!this._isEnabled) {
1188
+ return;
1189
+ }
1190
+ this._isEnabled = false;
1191
+ this._removeScrollListener();
1192
+ }
1193
+ attach(overlayRef) {
1194
+ this._overlayRef = overlayRef;
1195
+ }
1196
+ detach() {
1197
+ this.disable();
1198
+ this._overlayRef = undefined;
1199
+ }
1200
+ _removeScrollListener() {
1201
+ if (this._scrollListener) {
1202
+ window.removeEventListener('scroll', this._scrollListener, true);
1203
+ this._scrollListener = undefined;
1204
+ }
1205
+ }
1206
+ }
1207
+ /**
1208
+ * # FuiRepositionScrollStrategy
1209
+ *
1210
+ * A scroll strategy that repositions the overlay when the user scrolls.
1211
+ * This is useful for overlays that should follow their trigger element
1212
+ * as it moves due to scrolling.
1213
+ */
1214
+ class FuiRepositionScrollStrategy {
1215
+ _scrollThrottle;
1216
+ _autoClose;
1217
+ _overlayRef;
1218
+ _scrollListener;
1219
+ _isEnabled = false;
1220
+ constructor(_scrollThrottle = 20, _autoClose = false) {
1221
+ this._scrollThrottle = _scrollThrottle;
1222
+ this._autoClose = _autoClose;
1223
+ }
1224
+ enable() {
1225
+ if (this._isEnabled || !this._overlayRef) {
1226
+ return;
1227
+ }
1228
+ this._isEnabled = true;
1229
+ let timeoutId = null;
1230
+ this._scrollListener = () => {
1231
+ if (timeoutId) {
1232
+ clearTimeout(timeoutId);
1233
+ }
1234
+ timeoutId = window.setTimeout(() => {
1235
+ if (this._overlayRef) {
1236
+ if (this._autoClose && this._isScrolledOutOfView()) {
1237
+ this._overlayRef.dispose();
1238
+ }
1239
+ else {
1240
+ this._overlayRef.updatePosition();
1241
+ }
1242
+ }
1243
+ }, this._scrollThrottle);
1244
+ };
1245
+ window.addEventListener('scroll', this._scrollListener, true);
1246
+ }
1247
+ disable() {
1248
+ if (!this._isEnabled) {
1249
+ return;
1250
+ }
1251
+ this._isEnabled = false;
1252
+ this._removeScrollListener();
1253
+ }
1254
+ attach(overlayRef) {
1255
+ this._overlayRef = overlayRef;
1256
+ }
1257
+ detach() {
1258
+ this.disable();
1259
+ this._overlayRef = undefined;
1260
+ }
1261
+ _removeScrollListener() {
1262
+ if (this._scrollListener) {
1263
+ window.removeEventListener('scroll', this._scrollListener, true);
1264
+ this._scrollListener = undefined;
1265
+ }
1266
+ }
1267
+ _isScrolledOutOfView() {
1268
+ // This is a simplified implementation
1269
+ // In a real implementation, you would check if the origin element
1270
+ // is still visible in the viewport
1271
+ return false;
1272
+ }
1273
+ }
1274
+ /**
1275
+ * # FuiBlockScrollStrategy
1276
+ *
1277
+ * A scroll strategy that prevents scrolling while the overlay is open.
1278
+ * This is useful for modal dialogs and other overlays that should
1279
+ * prevent interaction with the underlying content.
1280
+ */
1281
+ class FuiBlockScrollStrategy {
1282
+ _document;
1283
+ _previousHTMLStyles = {};
1284
+ _previousBodyStyles = {};
1285
+ _isEnabled = false;
1286
+ constructor(_document) {
1287
+ this._document = _document;
1288
+ }
1289
+ enable() {
1290
+ if (this._isEnabled) {
1291
+ return;
1292
+ }
1293
+ this._isEnabled = true;
1294
+ const doc = this._document ?? document;
1295
+ const html = doc.documentElement;
1296
+ const body = doc.body;
1297
+ if (!html || !body) {
1298
+ return;
1299
+ }
1300
+ // Store previous styles
1301
+ this._previousHTMLStyles = {
1302
+ overflow: html.style.overflow || '',
1303
+ paddingRight: html.style.paddingRight || '',
1304
+ };
1305
+ this._previousBodyStyles = {
1306
+ overflow: body.style.overflow || '',
1307
+ paddingRight: body.style.paddingRight || '',
1308
+ };
1309
+ // Calculate scrollbar width
1310
+ const scrollbarWidth = this._getScrollbarWidth();
1311
+ // Apply blocking styles
1312
+ html.style.overflow = 'hidden';
1313
+ body.style.overflow = 'hidden';
1314
+ // Compensate for scrollbar width to prevent layout shift
1315
+ if (scrollbarWidth > 0) {
1316
+ html.style.paddingRight = `${scrollbarWidth}px`;
1317
+ body.style.paddingRight = `${scrollbarWidth}px`;
1318
+ }
1319
+ }
1320
+ disable() {
1321
+ if (!this._isEnabled) {
1322
+ return;
1323
+ }
1324
+ this._isEnabled = false;
1325
+ const doc = this._document ?? document;
1326
+ const html = doc.documentElement;
1327
+ const body = doc.body;
1328
+ if (!html || !body) {
1329
+ return;
1330
+ }
1331
+ // Restore previous styles
1332
+ html.style.overflow = this._previousHTMLStyles['overflow'];
1333
+ html.style.paddingRight = this._previousHTMLStyles['paddingRight'];
1334
+ body.style.overflow = this._previousBodyStyles['overflow'];
1335
+ body.style.paddingRight = this._previousBodyStyles['paddingRight'];
1336
+ // Clear stored styles
1337
+ this._previousHTMLStyles = {};
1338
+ this._previousBodyStyles = {};
1339
+ }
1340
+ attach(_overlayRef) {
1341
+ // Block scroll strategy doesn't need the overlay reference
1342
+ }
1343
+ detach() {
1344
+ this.disable();
1345
+ }
1346
+ _getScrollbarWidth() {
1347
+ const doc = this._document ?? document;
1348
+ // Create a temporary div to measure scrollbar width
1349
+ const outer = doc.createElement('div');
1350
+ outer.style.visibility = 'hidden';
1351
+ outer.style.overflow = 'scroll';
1352
+ // outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps
1353
+ doc.body.appendChild(outer);
1354
+ const inner = doc.createElement('div');
1355
+ outer.appendChild(inner);
1356
+ const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
1357
+ // Clean up
1358
+ outer.parentNode?.removeChild(outer);
1359
+ return scrollbarWidth;
1360
+ }
1361
+ }
1362
+ /**
1363
+ * Factory functions for creating scroll strategies
1364
+ */
1365
+ const FUI_SCROLL_STRATEGIES = {
1366
+ /**
1367
+ * Creates a noop scroll strategy
1368
+ */
1369
+ noop: () => new FuiNoopScrollStrategy(),
1370
+ /**
1371
+ * Creates a close scroll strategy
1372
+ */
1373
+ close: (threshold = 0) => new FuiCloseScrollStrategy(threshold),
1374
+ /**
1375
+ * Creates a reposition scroll strategy
1376
+ */
1377
+ reposition: (scrollThrottle = 20, autoClose = false) => new FuiRepositionScrollStrategy(scrollThrottle, autoClose),
1378
+ /**
1379
+ * Creates a block scroll strategy
1380
+ * @param doc Optional Document reference for SSR compatibility
1381
+ */
1382
+ block: (doc) => new FuiBlockScrollStrategy(doc),
1383
+ };
1384
+
1385
+ /**
1386
+ * # FuiOverlayService
1387
+ *
1388
+ * Core service for creating and managing overlays. This service provides a
1389
+ * comprehensive overlay system similar to Angular Material's overlay service,
1390
+ * with support for positioning strategies, scroll strategies, backdrop handling,
1391
+ * and focus management.
1392
+ *
1393
+ * ## Features
1394
+ * - Create overlays with flexible positioning strategies
1395
+ * - Global and connected positioning support
1396
+ * - Multiple scroll strategies (reposition, close, block, noop)
1397
+ * - Backdrop support with click detection
1398
+ * - Z-index management for stacking overlays
1399
+ * - Focus management and restoration
1400
+ * - Keyboard event handling
1401
+ * - Responsive positioning with collision detection
1402
+ *
1403
+ * ## Usage
1404
+ *
1405
+ * ### Basic Overlay
1406
+ * ```typescript
1407
+ * const overlayRef = this.overlay.create({
1408
+ * positionStrategy: this.overlay.position()
1409
+ * .global()
1410
+ * .centerHorizontally()
1411
+ * .centerVertically(),
1412
+ * hasBackdrop: true
1413
+ * });
1414
+ *
1415
+ * overlayRef.attach(myComponent);
1416
+ * ```
1417
+ *
1418
+ * ### Connected Overlay (Tooltip/Dropdown)
1419
+ * ```typescript
1420
+ * const overlayRef = this.overlay.create({
1421
+ * positionStrategy: this.overlay.position()
1422
+ * .connectedTo(this.triggerElement)
1423
+ * .withPositions([{
1424
+ * originX: 'center',
1425
+ * originY: 'bottom',
1426
+ * overlayX: 'center',
1427
+ * overlayY: 'top',
1428
+ * offsetY: 8
1429
+ * }]),
1430
+ * scrollStrategy: this.overlay.scrollStrategies.reposition()
1431
+ * });
1432
+ * ```
1433
+ */
1434
+ /** Base z-index for overlays */
1435
+ const OVERLAY_BASE_Z_INDEX = 1000;
1436
+ /** Z-index increment for each new overlay */
1437
+ const OVERLAY_Z_INDEX_INCREMENT = 10;
1438
+ class FuiOverlayService {
1439
+ _document = inject(DOCUMENT);
1440
+ _rendererFactory = inject(RendererFactory2);
1441
+ _environmentInjector = inject(EnvironmentInjector);
1442
+ _renderer;
1443
+ _overlayContainer;
1444
+ _nextUniqueId = 0;
1445
+ _activeOverlays = new Set();
1446
+ _currentZIndex = OVERLAY_BASE_Z_INDEX;
1447
+ constructor() {
1448
+ this._renderer = this._rendererFactory.createRenderer(null, null);
1449
+ }
1450
+ /**
1451
+ * Creates a new overlay with the given configuration
1452
+ */
1453
+ create(config = {}) {
1454
+ const overlayElement = this._createOverlayElement();
1455
+ // Calculate z-index for this overlay (increments for each new overlay)
1456
+ const zIndex = this._getNextZIndex();
1457
+ const overlayRef = new FuiOverlayRefImpl(overlayElement, this._applyConfigDefaults(config), this._renderer, `fui-overlay-${this._nextUniqueId++}`, zIndex);
1458
+ // Attach to container
1459
+ this._attachToContainer(overlayElement);
1460
+ // Track active overlay
1461
+ this._activeOverlays.add(overlayRef);
1462
+ // Set up disposal cleanup
1463
+ overlayRef.detachments.subscribe(() => {
1464
+ this._activeOverlays.delete(overlayRef);
1465
+ this._recalculateBaseZIndex();
1466
+ });
1467
+ return overlayRef;
1468
+ }
1469
+ /**
1470
+ * Gets the next available z-index for a new overlay
1471
+ */
1472
+ _getNextZIndex() {
1473
+ this._currentZIndex += OVERLAY_Z_INDEX_INCREMENT;
1474
+ return this._currentZIndex;
1475
+ }
1476
+ /**
1477
+ * Recalculates the base z-index when overlays are disposed
1478
+ * Resets to base if no overlays are active
1479
+ */
1480
+ _recalculateBaseZIndex() {
1481
+ if (this._activeOverlays.size === 0) {
1482
+ this._currentZIndex = OVERLAY_BASE_Z_INDEX;
1483
+ }
1484
+ }
1485
+ /**
1486
+ * Gets the position builder for creating position strategies
1487
+ */
1488
+ position() {
1489
+ return new FuiOverlayPositionBuilder(this._renderer);
1490
+ }
1491
+ /**
1492
+ * Gets the scroll strategies factory
1493
+ */
1494
+ get scrollStrategies() {
1495
+ return FUI_SCROLL_STRATEGIES;
1496
+ }
1497
+ /**
1498
+ * Gets all currently active overlays
1499
+ */
1500
+ getActiveOverlays() {
1501
+ return Array.from(this._activeOverlays);
1502
+ }
1503
+ /**
1504
+ * Disposes all active overlays
1505
+ */
1506
+ disposeAll() {
1507
+ const overlays = Array.from(this._activeOverlays);
1508
+ overlays.forEach((overlay) => {
1509
+ overlay.dispose();
1510
+ });
1511
+ }
1512
+ _createOverlayElement() {
1513
+ const overlayElement = this._renderer.createElement('div');
1514
+ this._renderer.addClass(overlayElement, 'fui-overlay-pane');
1515
+ this._renderer.setStyle(overlayElement, 'pointer-events', 'auto');
1516
+ return overlayElement;
1517
+ }
1518
+ _attachToContainer(overlayElement) {
1519
+ const container = this._getOverlayContainer();
1520
+ this._renderer.appendChild(container, overlayElement);
1521
+ }
1522
+ _getOverlayContainer() {
1523
+ if (!this._overlayContainer) {
1524
+ this._overlayContainer = createComponent(FuiOverlayContainerComponent, {
1525
+ environmentInjector: this._environmentInjector,
1526
+ });
1527
+ // Manually trigger ngOnInit to ensure container is attached to body
1528
+ this._overlayContainer.instance.ngOnInit();
1529
+ }
1530
+ return this._overlayContainer.instance.getContainerElement();
1531
+ }
1532
+ _applyConfigDefaults(config) {
1533
+ return {
1534
+ hasBackdrop: false,
1535
+ backdropClass: 'fui-overlay-backdrop',
1536
+ backdropClickBehavior: 'close',
1537
+ panelClass: '',
1538
+ scrollStrategy: this.scrollStrategies.noop(),
1539
+ direction: 'ltr',
1540
+ disposeOnNavigation: true,
1541
+ ...config,
1542
+ };
1543
+ }
1544
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiOverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1545
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiOverlayService, providedIn: 'root' });
1546
+ }
1547
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiOverlayService, decorators: [{
1548
+ type: Injectable,
1549
+ args: [{
1550
+ providedIn: 'root',
1551
+ }]
1552
+ }], ctorParameters: () => [] });
1553
+ /**
1554
+ * # FuiOverlayPositionBuilder
1555
+ *
1556
+ * Builder class for creating position strategies. Provides a fluent API
1557
+ * for configuring overlay positioning.
1558
+ */
1559
+ class FuiOverlayPositionBuilder {
1560
+ _renderer;
1561
+ constructor(_renderer) {
1562
+ this._renderer = _renderer;
1563
+ }
1564
+ /**
1565
+ * Creates a global position strategy
1566
+ */
1567
+ global(options = {}) {
1568
+ return new FuiGlobalPositionStrategy(this._renderer, options);
1569
+ }
1570
+ /**
1571
+ * Creates a connected position strategy
1572
+ */
1573
+ connectedTo(origin, positions) {
1574
+ const defaultPositions = [
1575
+ {
1576
+ originX: 'start',
1577
+ originY: 'bottom',
1578
+ overlayX: 'start',
1579
+ overlayY: 'top',
1580
+ },
1581
+ {
1582
+ originX: 'start',
1583
+ originY: 'top',
1584
+ overlayX: 'start',
1585
+ overlayY: 'bottom',
1586
+ },
1587
+ ];
1588
+ const options = {
1589
+ origin,
1590
+ positions: positions ?? defaultPositions,
1591
+ };
1592
+ return new FuiConnectedPositionStrategy(this._renderer, options);
1593
+ }
1594
+ /**
1595
+ * Creates a flexible connected position strategy with common tooltip positions
1596
+ */
1597
+ flexibleConnectedTo(origin, preferredPosition = 'top') {
1598
+ const positions = this._getFlexiblePositions(preferredPosition);
1599
+ const options = {
1600
+ origin,
1601
+ positions,
1602
+ withPush: true,
1603
+ withFlexibleDimensions: true,
1604
+ viewportMargin: 8,
1605
+ };
1606
+ return new FuiConnectedPositionStrategy(this._renderer, options);
1607
+ }
1608
+ _getFlexiblePositions(preferredPosition) {
1609
+ const positions = [];
1610
+ switch (preferredPosition) {
1611
+ case 'top':
1612
+ positions.push({ originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 }, { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }, { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: 8 }, { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -8 });
1613
+ break;
1614
+ case 'bottom':
1615
+ positions.push({ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 }, { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 }, { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: 8 }, { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -8 });
1616
+ break;
1617
+ case 'left':
1618
+ positions.push({ originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -8 }, { originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: 8 }, { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 }, { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 });
1619
+ break;
1620
+ case 'right':
1621
+ positions.push({ originX: 'end', originY: 'center', overlayX: 'start', overlayY: 'center', offsetX: 8 }, { originX: 'start', originY: 'center', overlayX: 'end', overlayY: 'center', offsetX: -8 }, { originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -8 }, { originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 8 });
1622
+ break;
1623
+ }
1624
+ return positions;
1625
+ }
1626
+ }
1627
+
1628
+ /**
1629
+ * # Portal Service
1630
+ *
1631
+ * A service for dynamically moving DOM elements to different locations in the DOM tree,
1632
+ * particularly useful for moving elements to the document body to avoid clipping issues
1633
+ * with parent containers that have overflow: hidden.
1634
+ *
1635
+ * ## Features
1636
+ * - Move elements to document body or any other container
1637
+ * - Maintain element references and event listeners
1638
+ * - Proper cleanup when detaching
1639
+ * - Support for multiple portal outlets
1640
+ *
1641
+ * ## Usage
1642
+ *
1643
+ * ```typescript
1644
+ * constructor(private portalService: PortalService) {}
1645
+ *
1646
+ * openMenu() {
1647
+ * const menuElement = this.menuElementRef.nativeElement;
1648
+ * this.portalAttachment = this.portalService.attachToBody(menuElement);
1649
+ * }
1650
+ *
1651
+ * closeMenu() {
1652
+ * if (this.portalAttachment) {
1653
+ * this.portalAttachment.detach();
1654
+ * this.portalAttachment = null;
1655
+ * }
1656
+ * }
1657
+ * ```
1658
+ */
1659
+ class PortalService {
1660
+ _document = inject(DOCUMENT$1);
1661
+ _renderer;
1662
+ _rendererFactory = inject(RendererFactory2);
1663
+ constructor() {
1664
+ this._renderer = this._rendererFactory.createRenderer(null, null);
1665
+ }
1666
+ /**
1667
+ * Attaches an element to the document body
1668
+ * @param element The element to attach
1669
+ * @returns PortalAttachment with detach function
1670
+ */
1671
+ attachToBody(element) {
1672
+ return this.attachToContainer(element, this._document.body);
1673
+ }
1674
+ /**
1675
+ * Attaches an element to a specific container
1676
+ * @param element The element to attach
1677
+ * @param container The container to attach to
1678
+ * @returns PortalAttachment with detach function
1679
+ */
1680
+ attachToContainer(element, container) {
1681
+ // Store the original parent and next sibling for restoration
1682
+ const originalParent = element.parentElement;
1683
+ const originalNextSibling = element.nextSibling;
1684
+ // Move the element to the container
1685
+ this._renderer.appendChild(container, element);
1686
+ // Return attachment object with detach function
1687
+ return {
1688
+ element,
1689
+ detach: () => {
1690
+ // Restore the element to its original position
1691
+ if (originalParent) {
1692
+ if (originalNextSibling) {
1693
+ this._renderer.insertBefore(originalParent, element, originalNextSibling);
1694
+ }
1695
+ else {
1696
+ this._renderer.appendChild(originalParent, element);
1697
+ }
1698
+ }
1699
+ },
1700
+ };
1701
+ }
1702
+ /**
1703
+ * Creates a portal outlet element and attaches it to the document body
1704
+ * @param className Optional CSS class for the outlet
1705
+ * @returns The created outlet element
1706
+ */
1707
+ createBodyOutlet(className) {
1708
+ const outlet = this._renderer.createElement('div');
1709
+ if (className) {
1710
+ this._renderer.addClass(outlet, className);
1711
+ }
1712
+ // Add some default styles to ensure proper positioning
1713
+ this._renderer.setStyle(outlet, 'position', 'absolute');
1714
+ this._renderer.setStyle(outlet, 'top', '0');
1715
+ this._renderer.setStyle(outlet, 'left', '0');
1716
+ this._renderer.setStyle(outlet, 'pointer-events', 'none');
1717
+ this._renderer.setStyle(outlet, 'z-index', '1000');
1718
+ this._renderer.appendChild(this._document.body, outlet);
1719
+ return outlet;
1720
+ }
1721
+ /**
1722
+ * Removes a portal outlet from the document
1723
+ * @param outlet The outlet element to remove
1724
+ */
1725
+ removeOutlet(outlet) {
1726
+ if (outlet.parentElement) {
1727
+ this._renderer.removeChild(outlet.parentElement, outlet);
1728
+ }
1729
+ }
1730
+ /**
1731
+ * Checks if an element is currently attached to a portal
1732
+ * @param element The element to check
1733
+ * @returns True if the element is attached to the body or not in its original position
1734
+ */
1735
+ isAttachedToPortal(element) {
1736
+ // Simple check: if the element's parent is the body, it's likely in a portal
1737
+ // This is a heuristic and might need refinement based on specific use cases
1738
+ return element.parentElement === this._document.body;
1739
+ }
1740
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: PortalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1741
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: PortalService, providedIn: 'root' });
1742
+ }
1743
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: PortalService, decorators: [{
1744
+ type: Injectable,
1745
+ args: [{
1746
+ providedIn: 'root',
1747
+ }]
1748
+ }], ctorParameters: () => [] });
1749
+
1750
+ // Core overlay exports
1751
+
1752
+ /**
1753
+ * Generated bundle index. Do not edit.
1754
+ */
1755
+
1756
+ export { FUI_OVERLAY_CONTAINER, FUI_OVERLAY_SCROLL_STRATEGY, FUI_SCROLL_STRATEGIES, FuiBlockScrollStrategy, FuiCloseScrollStrategy, FuiConnectedPositionStrategy, FuiGlobalPositionStrategy, FuiNoopScrollStrategy, FuiOverlayContainerComponent, FuiOverlayPositionBuilder, FuiOverlayRefImpl, FuiOverlayService, FuiRepositionScrollStrategy, PortalService };
1757
+ //# sourceMappingURL=raintonic-formaui-cdk-overlay.mjs.map