@raintonic/formaui 0.3.1 → 0.9.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 (238) hide show
  1. package/CHANGELOG.md +80 -35
  2. package/README.md +22 -26
  3. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +39 -41
  4. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -1
  5. package/fesm2022/raintonic-formaui-cdk-form-field.mjs +207 -3
  6. package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -1
  7. package/fesm2022/raintonic-formaui-cdk-overlay.mjs +27 -2
  8. package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -1
  9. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +5 -12
  10. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -1
  11. package/fesm2022/raintonic-formaui-components-accordion.mjs +8 -5
  12. package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -1
  13. package/fesm2022/raintonic-formaui-components-alert.mjs +16 -2
  14. package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -1
  15. package/fesm2022/raintonic-formaui-components-autocomplete.mjs +255 -462
  16. package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -1
  17. package/fesm2022/raintonic-formaui-components-avatar.mjs +34 -59
  18. package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -1
  19. package/fesm2022/raintonic-formaui-components-badge.mjs +2 -2
  20. package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -1
  21. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +4 -4
  22. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -1
  23. package/fesm2022/raintonic-formaui-components-button-group.mjs +2 -2
  24. package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -1
  25. package/fesm2022/raintonic-formaui-components-button.mjs +15 -20
  26. package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -1
  27. package/fesm2022/raintonic-formaui-components-card.mjs +2 -2
  28. package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -1
  29. package/fesm2022/raintonic-formaui-components-checkbox.mjs +2 -2
  30. package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -1
  31. package/fesm2022/raintonic-formaui-components-chip.mjs +97 -0
  32. package/fesm2022/raintonic-formaui-components-chip.mjs.map +1 -0
  33. package/fesm2022/raintonic-formaui-components-data-table.mjs +69 -29
  34. package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -1
  35. package/fesm2022/raintonic-formaui-components-date-picker.mjs +223 -144
  36. package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -1
  37. package/fesm2022/raintonic-formaui-components-divider.mjs +2 -2
  38. package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -1
  39. package/fesm2022/raintonic-formaui-components-drawer.mjs +2 -2
  40. package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -1
  41. package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs +888 -0
  42. package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs.map +1 -0
  43. package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs +774 -0
  44. package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs.map +1 -0
  45. package/fesm2022/raintonic-formaui-components-empty-state.mjs +2 -2
  46. package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -1
  47. package/fesm2022/raintonic-formaui-components-file-upload.mjs +2 -2
  48. package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -1
  49. package/fesm2022/raintonic-formaui-components-form-field.mjs +81 -50
  50. package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -1
  51. package/fesm2022/raintonic-formaui-components-icon.mjs +2 -2
  52. package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -1
  53. package/fesm2022/raintonic-formaui-components-input.mjs +47 -12
  54. package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -1
  55. package/fesm2022/raintonic-formaui-components-list.mjs +4 -4
  56. package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -1
  57. package/fesm2022/raintonic-formaui-components-number-input.mjs +20 -12
  58. package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -1
  59. package/fesm2022/raintonic-formaui-components-paginator.mjs +2 -2
  60. package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -1
  61. package/fesm2022/raintonic-formaui-components-password-input.mjs +35 -110
  62. package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -1
  63. package/fesm2022/raintonic-formaui-components-popover.mjs +3 -2
  64. package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -1
  65. package/fesm2022/raintonic-formaui-components-progressbar.mjs +3 -2
  66. package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -1
  67. package/fesm2022/raintonic-formaui-components-radio.mjs +5 -6
  68. package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -1
  69. package/fesm2022/raintonic-formaui-components-select.mjs +257 -412
  70. package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -1
  71. package/fesm2022/raintonic-formaui-components-side-panel.mjs +2 -2
  72. package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -1
  73. package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs +525 -0
  74. package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs.map +1 -0
  75. package/fesm2022/raintonic-formaui-components-skeleton.mjs +2 -2
  76. package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -1
  77. package/fesm2022/raintonic-formaui-components-slider.mjs +2 -2
  78. package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -1
  79. package/fesm2022/raintonic-formaui-components-spinner.mjs +2 -2
  80. package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -1
  81. package/fesm2022/raintonic-formaui-components-stepper.mjs +50 -45
  82. package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -1
  83. package/fesm2022/raintonic-formaui-components-strength-meter.mjs +149 -0
  84. package/fesm2022/raintonic-formaui-components-strength-meter.mjs.map +1 -0
  85. package/fesm2022/raintonic-formaui-components-tab.mjs +2 -2
  86. package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -1
  87. package/fesm2022/raintonic-formaui-components-time-picker.mjs +194 -154
  88. package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -1
  89. package/fesm2022/raintonic-formaui-components-toggle-group.mjs +302 -0
  90. package/fesm2022/raintonic-formaui-components-toggle-group.mjs.map +1 -0
  91. package/fesm2022/raintonic-formaui-components-toggle.mjs +2 -2
  92. package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -1
  93. package/fesm2022/raintonic-formaui-components-toolbar.mjs +2 -2
  94. package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -1
  95. package/fesm2022/raintonic-formaui-components-tooltip.mjs +10 -4
  96. package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -1
  97. package/fesm2022/raintonic-formaui-components-topbar.mjs +60 -0
  98. package/fesm2022/raintonic-formaui-components-topbar.mjs.map +1 -0
  99. package/fesm2022/raintonic-formaui-components-tree-select.mjs +59 -69
  100. package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -1
  101. package/fesm2022/raintonic-formaui-components-tree-table.mjs +2 -2
  102. package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -1
  103. package/fesm2022/raintonic-formaui-components-tree.mjs +31 -5
  104. package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -1
  105. package/fesm2022/raintonic-formaui-core.mjs +279 -1
  106. package/fesm2022/raintonic-formaui-core.mjs.map +1 -1
  107. package/fesm2022/raintonic-formaui-services-breakpoint.mjs +93 -0
  108. package/fesm2022/raintonic-formaui-services-breakpoint.mjs.map +1 -0
  109. package/fesm2022/raintonic-formaui-services-dialog.mjs +314 -16
  110. package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -1
  111. package/fesm2022/raintonic-formaui-services-notification.mjs +93 -29
  112. package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -1
  113. package/fesm2022/raintonic-formaui-services-theme.mjs +46 -196
  114. package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -1
  115. package/fesm2022/raintonic-formaui.mjs +1 -1
  116. package/fesm2022/raintonic-formaui.mjs.map +1 -1
  117. package/llms-full.txt +2329 -450
  118. package/llms.txt +36 -33
  119. package/package.json +42 -19
  120. package/styles/fonts/Geist-Bold.woff2 +0 -0
  121. package/styles/fonts/Geist-Italic.woff2 +0 -0
  122. package/styles/fonts/Geist-Light.woff2 +0 -0
  123. package/styles/fonts/Geist-Medium.woff2 +0 -0
  124. package/styles/fonts/Geist-Regular.woff2 +0 -0
  125. package/styles/fonts/Geist-SemiBold.woff2 +0 -0
  126. package/styles/fonts/GeistMono-Regular.woff2 +0 -0
  127. package/styles/generated/_tokens.scss +906 -0
  128. package/styles/index.scss +11 -10
  129. package/styles/partials/_brand.scss +46 -0
  130. package/styles/partials/_constants.scss +22 -20
  131. package/styles/partials/_fonts.scss +54 -10
  132. package/styles/partials/_grid.scss +29 -18
  133. package/styles/partials/_mixins.scss +69 -27
  134. package/styles/partials/_motion.scss +28 -33
  135. package/styles/partials/_theme.scss +28 -255
  136. package/styles/partials/_type.scss +117 -0
  137. package/styles/partials/_typography.scss +45 -45
  138. package/styles/partials/_utilities.scss +198 -98
  139. package/styles/partials/components/_button.scss +144 -75
  140. package/styles/partials/components/_dialog.scss +181 -180
  141. package/styles/partials/components/_overlay.scss +87 -87
  142. package/styles/partials/themes/_dark.scss +3 -268
  143. package/styles/partials/themes/_light.scss +4 -268
  144. package/styles/styles.css +7744 -0
  145. package/styles/styles.entry.scss +3 -0
  146. package/styles/utilities.css +4802 -0
  147. package/styles/utilities.entry.scss +3 -0
  148. package/types/raintonic-formaui-cdk-drag-drop.d.ts +0 -1
  149. package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -1
  150. package/types/raintonic-formaui-cdk-form-field.d.ts +118 -2
  151. package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -1
  152. package/types/raintonic-formaui-cdk-overlay.d.ts +2 -0
  153. package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -1
  154. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +0 -1
  155. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -1
  156. package/types/raintonic-formaui-components-accordion.d.ts +1 -1
  157. package/types/raintonic-formaui-components-accordion.d.ts.map +1 -1
  158. package/types/raintonic-formaui-components-alert.d.ts +6 -1
  159. package/types/raintonic-formaui-components-alert.d.ts.map +1 -1
  160. package/types/raintonic-formaui-components-autocomplete.d.ts +73 -116
  161. package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -1
  162. package/types/raintonic-formaui-components-avatar.d.ts +13 -31
  163. package/types/raintonic-formaui-components-avatar.d.ts.map +1 -1
  164. package/types/raintonic-formaui-components-button.d.ts +4 -10
  165. package/types/raintonic-formaui-components-button.d.ts.map +1 -1
  166. package/types/raintonic-formaui-components-chip.d.ts +43 -0
  167. package/types/raintonic-formaui-components-chip.d.ts.map +1 -0
  168. package/types/raintonic-formaui-components-data-table.d.ts +48 -11
  169. package/types/raintonic-formaui-components-data-table.d.ts.map +1 -1
  170. package/types/raintonic-formaui-components-date-picker.d.ts +59 -23
  171. package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -1
  172. package/types/raintonic-formaui-components-dropdown-menu.d.ts +394 -0
  173. package/types/raintonic-formaui-components-dropdown-menu.d.ts.map +1 -0
  174. package/types/raintonic-formaui-components-dual-tier-navigation.d.ts +87 -0
  175. package/types/raintonic-formaui-components-dual-tier-navigation.d.ts.map +1 -0
  176. package/types/raintonic-formaui-components-form-field.d.ts +51 -21
  177. package/types/raintonic-formaui-components-form-field.d.ts.map +1 -1
  178. package/types/raintonic-formaui-components-input.d.ts +20 -11
  179. package/types/raintonic-formaui-components-input.d.ts.map +1 -1
  180. package/types/raintonic-formaui-components-number-input.d.ts +5 -3
  181. package/types/raintonic-formaui-components-number-input.d.ts.map +1 -1
  182. package/types/raintonic-formaui-components-password-input.d.ts +18 -32
  183. package/types/raintonic-formaui-components-password-input.d.ts.map +1 -1
  184. package/types/raintonic-formaui-components-popover.d.ts.map +1 -1
  185. package/types/raintonic-formaui-components-progressbar.d.ts +1 -1
  186. package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -1
  187. package/types/raintonic-formaui-components-radio.d.ts +1 -2
  188. package/types/raintonic-formaui-components-radio.d.ts.map +1 -1
  189. package/types/raintonic-formaui-components-select.d.ts +107 -76
  190. package/types/raintonic-formaui-components-select.d.ts.map +1 -1
  191. package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts +223 -0
  192. package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts.map +1 -0
  193. package/types/raintonic-formaui-components-stepper.d.ts +4 -2
  194. package/types/raintonic-formaui-components-stepper.d.ts.map +1 -1
  195. package/types/raintonic-formaui-components-strength-meter.d.ts +78 -0
  196. package/types/raintonic-formaui-components-strength-meter.d.ts.map +1 -0
  197. package/types/raintonic-formaui-components-time-picker.d.ts +44 -24
  198. package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -1
  199. package/types/raintonic-formaui-components-toggle-group.d.ts +100 -0
  200. package/types/raintonic-formaui-components-toggle-group.d.ts.map +1 -0
  201. package/types/raintonic-formaui-components-tooltip.d.ts +2 -1
  202. package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -1
  203. package/types/raintonic-formaui-components-topbar.d.ts +48 -0
  204. package/types/raintonic-formaui-components-topbar.d.ts.map +1 -0
  205. package/types/raintonic-formaui-components-tree-select.d.ts +25 -9
  206. package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -1
  207. package/types/raintonic-formaui-components-tree.d.ts +12 -1
  208. package/types/raintonic-formaui-components-tree.d.ts.map +1 -1
  209. package/types/raintonic-formaui-core.d.ts +243 -5
  210. package/types/raintonic-formaui-core.d.ts.map +1 -1
  211. package/types/raintonic-formaui-services-breakpoint.d.ts +44 -0
  212. package/types/raintonic-formaui-services-breakpoint.d.ts.map +1 -0
  213. package/types/raintonic-formaui-services-dialog.d.ts +141 -2
  214. package/types/raintonic-formaui-services-dialog.d.ts.map +1 -1
  215. package/types/raintonic-formaui-services-notification.d.ts +24 -2
  216. package/types/raintonic-formaui-services-notification.d.ts.map +1 -1
  217. package/types/raintonic-formaui-services-theme.d.ts +13 -103
  218. package/types/raintonic-formaui-services-theme.d.ts.map +1 -1
  219. package/types/raintonic-formaui.d.ts +1 -1
  220. package/fesm2022/raintonic-formaui-components-big-menu.mjs +0 -86
  221. package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +0 -1
  222. package/fesm2022/raintonic-formaui-components-menu.mjs +0 -896
  223. package/fesm2022/raintonic-formaui-components-menu.mjs.map +0 -1
  224. package/fesm2022/raintonic-formaui-components-sidebar.mjs +0 -275
  225. package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +0 -1
  226. package/fesm2022/raintonic-formaui-components-tag.mjs +0 -95
  227. package/fesm2022/raintonic-formaui-components-tag.mjs.map +0 -1
  228. package/styles/_fonts-entry.scss +0 -3
  229. package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
  230. package/styles/fonts/inter-tight-latin.woff2 +0 -0
  231. package/types/raintonic-formaui-components-big-menu.d.ts +0 -73
  232. package/types/raintonic-formaui-components-big-menu.d.ts.map +0 -1
  233. package/types/raintonic-formaui-components-menu.d.ts +0 -403
  234. package/types/raintonic-formaui-components-menu.d.ts.map +0 -1
  235. package/types/raintonic-formaui-components-sidebar.d.ts +0 -185
  236. package/types/raintonic-formaui-components-sidebar.d.ts.map +0 -1
  237. package/types/raintonic-formaui-components-tag.d.ts +0 -43
  238. package/types/raintonic-formaui-components-tag.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"raintonic-formaui-services-notification.mjs","sources":["../../../lib/services/notification/notification-ref.ts","../../../lib/services/notification/notification-container.component.ts","../../../lib/services/notification/notification-container.component.html","../../../lib/services/notification/notification.service.ts","../../../lib/services/notification/raintonic-formaui-services-notification.ts"],"sourcesContent":["import { Observable, Subject } from 'rxjs';\r\nimport { FuiNotificationConfig } from '@raintonic/formaui/components/alert';\r\n\r\n/**\r\n * Reference to a notification instance.\r\n * Provides control over the notification and observables for lifecycle events.\r\n */\r\nexport class FuiNotificationRef {\r\n private readonly _afterClosed = new Subject<void>();\r\n private readonly _afterDismissed = new Subject<void>();\r\n\r\n /**\r\n * Observable that emits when the notification is closed\r\n */\r\n readonly afterClosed: Observable<void> = this._afterClosed.asObservable();\r\n\r\n /**\r\n * Observable that emits when the notification is dismissed (after animation)\r\n */\r\n readonly afterDismissed: Observable<void> = this._afterDismissed.asObservable();\r\n\r\n constructor(\r\n public readonly id: string,\r\n public readonly config: FuiNotificationConfig,\r\n ) {}\r\n\r\n /**\r\n * Close the notification\r\n */\r\n close(): void {\r\n this._afterClosed.next();\r\n this._afterClosed.complete();\r\n }\r\n\r\n /**\r\n * Called internally when the notification is fully dismissed\r\n * @internal\r\n */\r\n _dismiss(): void {\r\n this._afterDismissed.next();\r\n this._afterDismissed.complete();\r\n }\r\n}\r\n","import { ChangeDetectionStrategy, Component, effect, signal, ViewEncapsulation } from '@angular/core';\r\nimport { FuiAlertComponent } from '@raintonic/formaui/components/alert';\r\nimport { FuiNotificationRef } from './notification-ref';\r\n\r\n/**\r\n * Container component for displaying stacked notifications.\r\n * This component is created by the notification service and managed internally.\r\n * @internal\r\n */\r\n@Component({\r\n selector: 'fui-notification-container',\r\n standalone: true,\r\n imports: [FuiAlertComponent],\r\n templateUrl: './notification-container.component.html',\r\n styleUrls: ['./notification-container.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n host: {\r\n class: 'fui-notification-container',\r\n role: 'log',\r\n 'aria-live': 'polite',\r\n 'aria-atomic': 'false',\r\n },\r\n})\r\nexport class FuiNotificationContainerComponent {\r\n /**\r\n * List of active notifications\r\n */\r\n readonly notifications = signal<FuiNotificationRef[]>([]);\r\n\r\n /**\r\n * Progress values for each notification (0-100)\r\n */\r\n readonly progressValues = signal(new Map());\r\n\r\n /**\r\n * Timers for auto-dismiss\r\n */\r\n private readonly timers = new Map<\r\n string,\r\n { interval: ReturnType<typeof setInterval>; timeout: ReturnType<typeof setTimeout> }\r\n >();\r\n\r\n constructor() {\r\n // Set up effect to manage timers when notifications change\r\n effect(() => {\r\n this.notifications();\r\n // This will react to changes in the notifications signal\r\n });\r\n }\r\n\r\n /**\r\n * Add a notification to the container\r\n */\r\n addNotification(notification: FuiNotificationRef): void {\r\n this.notifications.update((notifications) => [...notifications, notification]);\r\n\r\n // Set up auto-dismiss if duration is specified\r\n const duration = notification.config.duration ?? 5000;\r\n if (duration > 0) {\r\n this._setupAutoDismiss(notification, duration);\r\n }\r\n\r\n // Listen for manual close\r\n notification.afterClosed.subscribe(() => {\r\n this._removeNotification(notification);\r\n });\r\n }\r\n\r\n /**\r\n * Remove a notification from the container\r\n */\r\n removeNotification(notificationId: string): void {\r\n const notification = this.notifications().find((n) => n.id === notificationId);\r\n if (notification) {\r\n this._removeNotification(notification);\r\n }\r\n }\r\n\r\n /**\r\n * Handle notification close button click\r\n */\r\n onNotificationClose(notification: FuiNotificationRef): void {\r\n notification.close();\r\n }\r\n\r\n /**\r\n * Get the progress value for a notification\r\n */\r\n getProgress(notificationId: string): number {\r\n return this.progressValues().get(notificationId) ?? 100;\r\n }\r\n\r\n /**\r\n * Check if notification should show progress bar\r\n */\r\n shouldShowProgress(notification: FuiNotificationRef): boolean {\r\n const duration = notification.config.duration ?? 5000;\r\n return duration > 0;\r\n }\r\n\r\n private _setupAutoDismiss(notification: FuiNotificationRef, duration: number): void {\r\n const startTime = Date.now();\r\n const updateInterval = 50; // Update progress every 50ms for smooth animation\r\n\r\n // Update progress bar\r\n const interval = setInterval(() => {\r\n const elapsed = Date.now() - startTime;\r\n const progress = Math.max(0, 100 - (elapsed / duration) * 100);\r\n\r\n this.progressValues.update((values) => {\r\n const newValues = new Map(values);\r\n newValues.set(notification.id, progress);\r\n return newValues;\r\n });\r\n\r\n if (progress <= 0) {\r\n clearInterval(interval);\r\n }\r\n }, updateInterval);\r\n\r\n // Auto-dismiss after duration\r\n const timeout = setTimeout(() => {\r\n notification.close();\r\n }, duration);\r\n\r\n this.timers.set(notification.id, { interval, timeout });\r\n }\r\n\r\n private _removeNotification(notification: FuiNotificationRef): void {\r\n // Clear timers\r\n const timers = this.timers.get(notification.id);\r\n if (timers) {\r\n clearInterval(timers.interval);\r\n clearTimeout(timers.timeout);\r\n this.timers.delete(notification.id);\r\n }\r\n\r\n // Remove from progress map\r\n this.progressValues.update((values) => {\r\n const newValues = new Map(values);\r\n newValues.delete(notification.id);\r\n return newValues;\r\n });\r\n\r\n // Remove from notifications list\r\n this.notifications.update((notifications) => notifications.filter((n) => n.id !== notification.id));\r\n\r\n // Notify that notification is dismissed\r\n notification._dismiss();\r\n }\r\n}\r\n","<div class=\"fui-notification-container__stack\">\r\n @for (notification of notifications(); track notification.id) {\r\n <div class=\"fui-notification-container__item\" role=\"alert\" aria-atomic=\"true\">\r\n <fui-alert\r\n [variant]=\"notification.config.variant\"\r\n [title]=\"notification.config.title\"\r\n [description]=\"notification.config.description ?? null\"\r\n [closeable]=\"notification.config.closeable ?? true\"\r\n [icon]=\"notification.config.icon ?? null\"\r\n [progress]=\"shouldShowProgress(notification) ? getProgress(notification.id) : -1\"\r\n (closed)=\"onNotificationClose(notification)\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n","import {\r\n Injectable,\r\n Renderer2,\r\n RendererFactory2,\r\n ComponentRef,\r\n createComponent,\r\n EnvironmentInjector,\r\n inject,\r\n ApplicationRef,\r\n} from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { FuiNotificationConfig, FuiNotificationPosition } from '@raintonic/formaui/components/alert';\r\nimport { FuiNotificationRef } from './notification-ref';\r\nimport { FuiNotificationContainerComponent } from './notification-container.component';\r\n\r\n/**\r\n * # FuiNotificationService\r\n *\r\n * Service for displaying toast-style notifications using overlays.\r\n * Notifications can be displayed at various positions on the screen and will\r\n * auto-dismiss after a configurable duration with a visual countdown.\r\n *\r\n * ## Features\r\n * - Multiple notification variants (success, info, warning, error, primary, generic)\r\n * - Configurable positioning (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right)\r\n * - Auto-dismiss with progress bar countdown\r\n * - Manual dismiss via close button\r\n * - Stack multiple notifications\r\n * - Convenient shorthand methods for common variants\r\n *\r\n * ## Usage\r\n *\r\n * ### Success Notification\r\n * ```typescript\r\n * this.notificationService.success('Changes saved successfully!');\r\n * ```\r\n *\r\n * ### Error Notification with Description\r\n * ```typescript\r\n * this.notificationService.error('Failed to save', {\r\n * description: 'Please check your connection and try again.'\r\n * });\r\n * ```\r\n *\r\n * ### Custom Duration and Position\r\n * ```typescript\r\n * this.notificationService.info('New message received', {\r\n * description: 'Click to view',\r\n * duration: 10000,\r\n * position: 'bottom-right'\r\n * });\r\n * ```\r\n *\r\n * ### No Auto-Dismiss\r\n * ```typescript\r\n * this.notificationService.warning('Important notice', {\r\n * duration: 0, // Won't auto-dismiss\r\n * closeable: true\r\n * });\r\n * ```\r\n *\r\n * ### Full Configuration\r\n * ```typescript\r\n * const ref = this.notificationService.show({\r\n * variant: 'success',\r\n * title: 'Success!',\r\n * description: 'Your operation completed successfully.',\r\n * duration: 5000,\r\n * closeable: true,\r\n * icon: 'custom-icon',\r\n * position: 'top-center'\r\n * });\r\n *\r\n * // Programmatically close\r\n * ref.close();\r\n * ```\r\n */\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class FuiNotificationService {\r\n private readonly _document = inject(DOCUMENT);\r\n private readonly _rendererFactory = inject(RendererFactory2);\r\n private readonly _environmentInjector = inject(EnvironmentInjector);\r\n private readonly _applicationRef = inject(ApplicationRef);\r\n private readonly _renderer: Renderer2;\r\n\r\n /**\r\n * Map of container components by position\r\n */\r\n private readonly _containers = new Map<FuiNotificationPosition, ComponentRef<FuiNotificationContainerComponent>>();\r\n\r\n /**\r\n * Default position for notifications\r\n */\r\n private _defaultPosition: FuiNotificationPosition = 'bottom-right';\r\n\r\n /**\r\n * Next unique notification ID\r\n */\r\n private _nextId = 0;\r\n\r\n constructor() {\r\n this._renderer = this._rendererFactory.createRenderer(null, null);\r\n }\r\n\r\n /**\r\n * Set the default position for notifications\r\n */\r\n setDefaultPosition(position: FuiNotificationPosition): void {\r\n this._defaultPosition = position;\r\n }\r\n\r\n /**\r\n * Show a success notification\r\n */\r\n success(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'success',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show an info notification\r\n */\r\n info(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'info',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a warning notification\r\n */\r\n warning(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'warning',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show an error notification\r\n */\r\n error(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'error',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a primary notification\r\n */\r\n primary(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'primary',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a generic notification\r\n */\r\n generic(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'generic',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a notification with full configuration\r\n */\r\n show(config: FuiNotificationConfig & { position?: FuiNotificationPosition }): FuiNotificationRef {\r\n const position = config.position ?? this._defaultPosition;\r\n const container = this._getOrCreateContainer(position);\r\n\r\n // Create notification reference\r\n const notificationId = `fui-notification-${this._nextId++}`;\r\n const notificationRef = new FuiNotificationRef(notificationId, config);\r\n\r\n // Add to container\r\n container.instance.addNotification(notificationRef);\r\n\r\n return notificationRef;\r\n }\r\n\r\n /**\r\n * Clear all notifications at a specific position\r\n */\r\n clearPosition(position: FuiNotificationPosition): void {\r\n const container = this._containers.get(position);\r\n if (container) {\r\n // Clear all notifications\r\n const notifications = container.instance.notifications();\r\n notifications.forEach((notification) => {\r\n container.instance.removeNotification(notification.id);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Clear all notifications\r\n */\r\n clearAll(): void {\r\n this._containers.forEach((container) => {\r\n const notifications = container.instance.notifications();\r\n notifications.forEach((notification) => {\r\n container.instance.removeNotification(notification.id);\r\n });\r\n });\r\n }\r\n\r\n private _getOrCreateContainer(position: FuiNotificationPosition): ComponentRef<FuiNotificationContainerComponent> {\r\n let container = this._containers.get(position);\r\n\r\n if (!container) {\r\n container = this._createContainer(position);\r\n this._containers.set(position, container);\r\n }\r\n\r\n return container;\r\n }\r\n\r\n private _createContainer(position: FuiNotificationPosition): ComponentRef<FuiNotificationContainerComponent> {\r\n // Create the container component\r\n const containerRef = createComponent(FuiNotificationContainerComponent, {\r\n environmentInjector: this._environmentInjector,\r\n });\r\n\r\n // Add position class\r\n const positionClass = `fui-notification-container--${position}`;\r\n this._renderer.addClass(containerRef.location.nativeElement, positionClass);\r\n\r\n // Attach to the application\r\n this._applicationRef.attachView(containerRef.hostView);\r\n\r\n // Append to document body\r\n const containerElement = containerRef.location.nativeElement;\r\n this._renderer.appendChild(this._document.body, containerElement);\r\n\r\n return containerRef;\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"raintonic-formaui-services-notification.mjs","sources":["../../../lib/services/notification/notification-ref.ts","../../../lib/services/notification/notification-container.component.ts","../../../lib/services/notification/notification-container.component.html","../../../lib/services/notification/notification.service.ts","../../../lib/services/notification/raintonic-formaui-services-notification.ts"],"sourcesContent":["import { Observable, Subject } from 'rxjs';\r\nimport { FuiNotificationConfig } from '@raintonic/formaui/components/alert';\r\n\r\n/**\r\n * Reference to a notification instance.\r\n * Provides control over the notification and observables for lifecycle events.\r\n */\r\nexport class FuiNotificationRef {\r\n private readonly _afterClosed = new Subject<void>();\r\n private readonly _afterDismissed = new Subject<void>();\r\n\r\n /**\r\n * Observable that emits when the notification is closed\r\n */\r\n readonly afterClosed: Observable<void> = this._afterClosed.asObservable();\r\n\r\n /**\r\n * Observable that emits when the notification is dismissed (after animation)\r\n */\r\n readonly afterDismissed: Observable<void> = this._afterDismissed.asObservable();\r\n\r\n constructor(\r\n public readonly id: string,\r\n public readonly config: FuiNotificationConfig,\r\n ) {}\r\n\r\n /**\r\n * Close the notification\r\n */\r\n close(): void {\r\n this._afterClosed.next();\r\n this._afterClosed.complete();\r\n }\r\n\r\n /**\r\n * Called internally when the notification is fully dismissed\r\n * @internal\r\n */\r\n _dismiss(): void {\r\n this._afterDismissed.next();\r\n this._afterDismissed.complete();\r\n }\r\n}\r\n","import { ChangeDetectionStrategy, Component, effect, signal, ViewEncapsulation } from '@angular/core';\r\nimport { FuiAlertComponent } from '@raintonic/formaui/components/alert';\r\nimport { FuiNotificationRef } from './notification-ref';\r\n\r\n/**\r\n * Container component for displaying stacked notifications.\r\n * This component is created by the notification service and managed internally.\r\n * @internal\r\n */\r\n@Component({\r\n selector: 'fui-notification-container',\r\n standalone: true,\r\n imports: [FuiAlertComponent],\r\n templateUrl: './notification-container.component.html',\r\n styleUrls: ['./notification-container.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.None,\r\n host: {\r\n class: 'fui-notification-container',\r\n role: 'log',\r\n 'aria-live': 'polite',\r\n 'aria-atomic': 'false',\r\n },\r\n})\r\nexport class FuiNotificationContainerComponent {\r\n /**\r\n * List of active notifications\r\n */\r\n readonly notifications = signal<FuiNotificationRef[]>([]);\r\n\r\n /**\r\n * Progress values for each notification (0-100)\r\n */\r\n readonly progressValues = signal(new Map());\r\n\r\n private static readonly UPDATE_INTERVAL_MS = 50;\r\n\r\n /**\r\n * Per-notification timer state for auto-dismiss. Holds the active interval+timeout pair\r\n * plus the metadata needed to compute remaining duration when pause/resume cycles occur.\r\n */\r\n private readonly _timerStates = new Map<\r\n string,\r\n {\r\n interval: ReturnType<typeof setInterval>;\r\n timeout: ReturnType<typeof setTimeout>;\r\n notification: FuiNotificationRef;\r\n originalDuration: number;\r\n isPaused: boolean;\r\n }\r\n >();\r\n\r\n /**\r\n * Per-notification interaction counter. Increments on mouseenter/focusin, decrements on\r\n * mouseleave/focusout. Timer is paused when count goes 0→1, resumed when 1→0.\r\n * Why: hover and focus can overlap (e.g. user hovers, then tabs into the close button).\r\n * A single boolean flag would resume too early when one source leaves but the other holds.\r\n */\r\n private readonly _interactionCounts = new Map<string, number>();\r\n\r\n constructor() {\r\n // Set up effect to manage timers when notifications change\r\n effect(() => {\r\n this.notifications();\r\n // This will react to changes in the notifications signal\r\n });\r\n }\r\n\r\n /**\r\n * Add a notification to the container\r\n */\r\n addNotification(notification: FuiNotificationRef): void {\r\n this.notifications.update((notifications) => [...notifications, notification]);\r\n\r\n // Set up auto-dismiss if duration is specified\r\n const duration = notification.config.duration ?? 5000;\r\n if (duration > 0) {\r\n this._setupAutoDismiss(notification, duration);\r\n }\r\n\r\n // Listen for manual close\r\n notification.afterClosed.subscribe(() => {\r\n this._removeNotification(notification);\r\n });\r\n }\r\n\r\n /**\r\n * Remove a notification from the container\r\n */\r\n removeNotification(notificationId: string): void {\r\n const notification = this.notifications().find((n) => n.id === notificationId);\r\n if (notification) {\r\n this._removeNotification(notification);\r\n }\r\n }\r\n\r\n /**\r\n * Handle notification close button click\r\n */\r\n onNotificationClose(notification: FuiNotificationRef): void {\r\n notification.close();\r\n }\r\n\r\n /**\r\n * Get the progress value for a notification\r\n */\r\n getProgress(notificationId: string): number {\r\n return this.progressValues().get(notificationId) ?? 100;\r\n }\r\n\r\n /**\r\n * Check if notification should show progress bar\r\n */\r\n shouldShowProgress(notification: FuiNotificationRef): boolean {\r\n const duration = notification.config.duration ?? 5000;\r\n return duration > 0;\r\n }\r\n\r\n /**\r\n * Increment the interaction count for a notification. Called on mouseenter and focusin.\r\n * Pauses the timer when the count transitions from 0 to 1.\r\n */\r\n onInteractionEnter(notificationId: string): void {\r\n const newCount = (this._interactionCounts.get(notificationId) ?? 0) + 1;\r\n this._interactionCounts.set(notificationId, newCount);\r\n if (newCount === 1) {\r\n this._pauseTimer(notificationId);\r\n }\r\n }\r\n\r\n /**\r\n * Decrement the interaction count for a notification. Called on mouseleave and focusout.\r\n * Resumes the timer when the count transitions back to 0.\r\n */\r\n onInteractionLeave(notificationId: string): void {\r\n const newCount = Math.max(0, (this._interactionCounts.get(notificationId) ?? 0) - 1);\r\n this._interactionCounts.set(notificationId, newCount);\r\n if (newCount === 0) {\r\n this._resumeTimer(notificationId);\r\n }\r\n }\r\n\r\n private _setupAutoDismiss(notification: FuiNotificationRef, duration: number): void {\r\n const startTime = Date.now();\r\n\r\n const interval = this._startProgressInterval(notification.id, startTime, duration);\r\n\r\n const timeout = setTimeout(() => {\r\n notification.close();\r\n }, duration);\r\n\r\n this._timerStates.set(notification.id, {\r\n interval,\r\n timeout,\r\n notification,\r\n originalDuration: duration,\r\n isPaused: false,\r\n });\r\n }\r\n\r\n private _removeNotification(notification: FuiNotificationRef): void {\r\n const state = this._timerStates.get(notification.id);\r\n if (state) {\r\n clearInterval(state.interval);\r\n clearTimeout(state.timeout);\r\n this._timerStates.delete(notification.id);\r\n }\r\n this._interactionCounts.delete(notification.id);\r\n\r\n this.progressValues.update((values) => {\r\n const newValues = new Map(values);\r\n newValues.delete(notification.id);\r\n return newValues;\r\n });\r\n\r\n this.notifications.update((notifications) => notifications.filter((n) => n.id !== notification.id));\r\n\r\n notification._dismiss();\r\n }\r\n\r\n private _pauseTimer(notificationId: string): void {\r\n const state = this._timerStates.get(notificationId);\r\n if (!state || state.isPaused) return;\r\n\r\n clearInterval(state.interval);\r\n clearTimeout(state.timeout);\r\n state.isPaused = true;\r\n }\r\n\r\n private _resumeTimer(notificationId: string): void {\r\n const state = this._timerStates.get(notificationId);\r\n if (!state?.isPaused) return;\r\n\r\n state.isPaused = false;\r\n\r\n // Compute remaining duration from current progress (0-100)\r\n const currentProgress = this.progressValues().get(notificationId) ?? 100;\r\n const remainingDuration = (currentProgress / 100) * state.originalDuration;\r\n\r\n if (remainingDuration <= 0) {\r\n state.notification.close();\r\n return;\r\n }\r\n\r\n // Restart the progress interval from current progress\r\n const elapsedBeforeResume = state.originalDuration - remainingDuration;\r\n const newStartTime = Date.now() - elapsedBeforeResume;\r\n\r\n const newInterval = this._startProgressInterval(notificationId, newStartTime, state.originalDuration);\r\n\r\n const newTimeout = setTimeout(() => {\r\n state.notification.close();\r\n }, remainingDuration);\r\n\r\n state.interval = newInterval;\r\n state.timeout = newTimeout;\r\n }\r\n\r\n private _startProgressInterval(\r\n notificationId: string,\r\n startTime: number,\r\n totalDuration: number,\r\n ): ReturnType<typeof setInterval> {\r\n const interval = setInterval(() => {\r\n const elapsed = Date.now() - startTime;\r\n const progress = Math.max(0, 100 - (elapsed / totalDuration) * 100);\r\n this.progressValues.update((values) => {\r\n const newValues = new Map(values);\r\n newValues.set(notificationId, progress);\r\n return newValues;\r\n });\r\n if (progress <= 0) clearInterval(interval);\r\n }, FuiNotificationContainerComponent.UPDATE_INTERVAL_MS);\r\n return interval;\r\n }\r\n}\r\n","<div class=\"fui-notification-container__stack\">\r\n @for (notification of notifications(); track notification.id) {\r\n <div\r\n class=\"fui-notification-container__item\"\r\n role=\"alert\"\r\n aria-atomic=\"true\"\r\n (mouseenter)=\"onInteractionEnter(notification.id)\"\r\n (mouseleave)=\"onInteractionLeave(notification.id)\"\r\n (focusin)=\"onInteractionEnter(notification.id)\"\r\n (focusout)=\"onInteractionLeave(notification.id)\"\r\n >\r\n <fui-alert\r\n [variant]=\"notification.config.variant\"\r\n [title]=\"notification.config.title\"\r\n [description]=\"notification.config.description ?? null\"\r\n [closeable]=\"notification.config.closeable ?? true\"\r\n [icon]=\"notification.config.icon ?? null\"\r\n [progress]=\"shouldShowProgress(notification) ? getProgress(notification.id) : -1\"\r\n (closed)=\"onNotificationClose(notification)\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n","import {\r\n Injectable,\r\n Renderer2,\r\n RendererFactory2,\r\n ComponentRef,\r\n createComponent,\r\n EnvironmentInjector,\r\n inject,\r\n ApplicationRef,\r\n} from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { FuiNotificationConfig, FuiNotificationPosition } from '@raintonic/formaui/components/alert';\r\nimport { FuiNotificationRef } from './notification-ref';\r\nimport { FuiNotificationContainerComponent } from './notification-container.component';\r\n\r\n/**\r\n * # FuiNotificationService\r\n *\r\n * Service for displaying toast-style notifications using overlays.\r\n * Notifications can be displayed at various positions on the screen and will\r\n * auto-dismiss after a configurable duration with a visual countdown.\r\n *\r\n * ## Features\r\n * - Multiple notification variants (success, info, warning, error, primary, generic)\r\n * - Configurable positioning (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right)\r\n * - Auto-dismiss with progress bar countdown\r\n * - Manual dismiss via close button\r\n * - Stack multiple notifications\r\n * - Convenient shorthand methods for common variants\r\n *\r\n * ## Usage\r\n *\r\n * ### Success Notification\r\n * ```typescript\r\n * this.notificationService.success('Changes saved successfully!');\r\n * ```\r\n *\r\n * ### Error Notification with Description\r\n * ```typescript\r\n * this.notificationService.error('Failed to save', {\r\n * description: 'Please check your connection and try again.'\r\n * });\r\n * ```\r\n *\r\n * ### Custom Duration and Position\r\n * ```typescript\r\n * this.notificationService.info('New message received', {\r\n * description: 'Click to view',\r\n * duration: 10000,\r\n * position: 'bottom-right'\r\n * });\r\n * ```\r\n *\r\n * ### No Auto-Dismiss\r\n * ```typescript\r\n * this.notificationService.warning('Important notice', {\r\n * duration: 0, // Won't auto-dismiss\r\n * closeable: true\r\n * });\r\n * ```\r\n *\r\n * ### Full Configuration\r\n * ```typescript\r\n * const ref = this.notificationService.show({\r\n * variant: 'success',\r\n * title: 'Success!',\r\n * description: 'Your operation completed successfully.',\r\n * duration: 5000,\r\n * closeable: true,\r\n * icon: 'custom-icon',\r\n * position: 'top-center'\r\n * });\r\n *\r\n * // Programmatically close\r\n * ref.close();\r\n * ```\r\n */\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class FuiNotificationService {\r\n private readonly _document = inject(DOCUMENT);\r\n private readonly _rendererFactory = inject(RendererFactory2);\r\n private readonly _environmentInjector = inject(EnvironmentInjector);\r\n private readonly _applicationRef = inject(ApplicationRef);\r\n private readonly _renderer: Renderer2;\r\n\r\n /**\r\n * Map of container components by position\r\n */\r\n private readonly _containers = new Map<FuiNotificationPosition, ComponentRef<FuiNotificationContainerComponent>>();\r\n\r\n /**\r\n * Default position for notifications\r\n */\r\n private _defaultPosition: FuiNotificationPosition = 'bottom-right';\r\n\r\n /**\r\n * Next unique notification ID\r\n */\r\n private _nextId = 0;\r\n\r\n constructor() {\r\n this._renderer = this._rendererFactory.createRenderer(null, null);\r\n }\r\n\r\n /**\r\n * Set the default position for notifications\r\n */\r\n setDefaultPosition(position: FuiNotificationPosition): void {\r\n this._defaultPosition = position;\r\n }\r\n\r\n /**\r\n * Show a success notification\r\n */\r\n success(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'success',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show an info notification\r\n */\r\n info(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'info',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a warning notification\r\n */\r\n warning(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'warning',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show an error notification\r\n */\r\n error(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'error',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a primary notification\r\n */\r\n primary(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'primary',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a generic notification\r\n */\r\n generic(\r\n title: string,\r\n options?: Partial<Omit<FuiNotificationConfig, 'variant' | 'title'>> & {\r\n position?: FuiNotificationPosition;\r\n },\r\n ): FuiNotificationRef {\r\n return this.show({\r\n variant: 'generic',\r\n title,\r\n ...options,\r\n });\r\n }\r\n\r\n /**\r\n * Show a notification with full configuration\r\n */\r\n show(config: FuiNotificationConfig & { position?: FuiNotificationPosition }): FuiNotificationRef {\r\n const position = config.position ?? this._defaultPosition;\r\n const container = this._getOrCreateContainer(position);\r\n\r\n // Create notification reference\r\n const notificationId = `fui-notification-${this._nextId++}`;\r\n const notificationRef = new FuiNotificationRef(notificationId, config);\r\n\r\n // Add to container\r\n container.instance.addNotification(notificationRef);\r\n\r\n return notificationRef;\r\n }\r\n\r\n /**\r\n * Clear all notifications at a specific position\r\n */\r\n clearPosition(position: FuiNotificationPosition): void {\r\n const container = this._containers.get(position);\r\n if (container) {\r\n // Clear all notifications\r\n const notifications = container.instance.notifications();\r\n notifications.forEach((notification) => {\r\n container.instance.removeNotification(notification.id);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Clear all notifications\r\n */\r\n clearAll(): void {\r\n this._containers.forEach((container) => {\r\n const notifications = container.instance.notifications();\r\n notifications.forEach((notification) => {\r\n container.instance.removeNotification(notification.id);\r\n });\r\n });\r\n }\r\n\r\n private _getOrCreateContainer(position: FuiNotificationPosition): ComponentRef<FuiNotificationContainerComponent> {\r\n let container = this._containers.get(position);\r\n\r\n if (!container) {\r\n container = this._createContainer(position);\r\n this._containers.set(position, container);\r\n }\r\n\r\n return container;\r\n }\r\n\r\n private _createContainer(position: FuiNotificationPosition): ComponentRef<FuiNotificationContainerComponent> {\r\n // Create the container component\r\n const containerRef = createComponent(FuiNotificationContainerComponent, {\r\n environmentInjector: this._environmentInjector,\r\n });\r\n\r\n // Add position class\r\n const positionClass = `fui-notification-container--${position}`;\r\n this._renderer.addClass(containerRef.location.nativeElement, positionClass);\r\n\r\n // Attach to the application\r\n this._applicationRef.attachView(containerRef.hostView);\r\n\r\n // Append to document body\r\n const containerElement = containerRef.location.nativeElement;\r\n this._renderer.appendChild(this._document.body, containerElement);\r\n\r\n return containerRef;\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -2,144 +2,70 @@ import * as i0 from '@angular/core';
2
2
  import { inject, DestroyRef, PLATFORM_ID, signal, Injectable } from '@angular/core';
3
3
  import { DOCUMENT, isPlatformBrowser } from '@angular/common';
4
4
 
5
+ const STORAGE_KEY = 'fui-theme-mode';
5
6
  /**
6
- * # FuiTheme Service
7
- *
8
- * Central service for managing theme configuration and mode switching.
9
- * Provides runtime theme customization with CSS variables and automatic palette generation.
10
- *
11
- * ## Features
12
- * - Runtime theme customization
13
- * - Light/dark/auto mode switching
14
- * - Automatic color palette generation
15
- * - CSS variables-based theming
16
- * - Persistent theme preferences
17
- * - System theme detection
18
- *
19
- * ## Usage
20
- *
21
- * ### Basic Theme Setup
22
- * ```typescript
23
- * import { FuiThemeService } from '@raintonic/formaui/services/theme';
24
- *
25
- * constructor(private themeService: FuiThemeService) {
26
- * // Set custom theme colors
27
- * this.themeService.setTheme({
28
- * primary: '#007bff',
29
- * secondary: '#6c757d',
30
- * tertiary: '#28a745'
31
- * });
32
- * }
33
- * ```
34
- *
35
- * ### Theme Mode Switching
36
- * ```typescript
37
- * // Switch to dark mode
38
- * this.themeService.setMode('dark');
39
- *
40
- * // Switch to auto (follows system preference)
41
- * this.themeService.setMode('auto');
42
- *
43
- * // Get current mode
44
- * const currentMode = this.themeService.currentMode();
45
- * const isDark = this.themeService.isDark();
46
- * ```
47
- *
48
- * @example
49
- * ```typescript
50
- * import { Component, effect } from '@angular/core';
51
- * import { FuiThemeService } from '@raintonic/formaui/services/theme';
52
- *
53
- * @Component({
54
- * template: `
55
- * <div class="theme-controls">
56
- * <button fuiButton (click)="setLightMode()">Light</button>
57
- * <button fuiButton (click)="setDarkMode()">Dark</button>
58
- * <button fuiButton (click)="setAutoMode()">Auto</button>
59
- * </div>
60
- * `
61
- * })
62
- * export class ThemeControlsComponent {
63
- * constructor(public themeService: FuiThemeService) {}
64
- *
65
- * setLightMode() { this.themeService.setMode('light'); }
66
- * setDarkMode() { this.themeService.setMode('dark'); }
67
- * setAutoMode() { this.themeService.setMode('auto'); }
68
- * }
69
- * ```
7
+ * FuiThemeService gestione del mode tema (zoneless, signals).
8
+ * Applica il tema via attributo `data-theme="light|dark"` su `<html>`.
9
+ * Default `auto` (system-driven via prefers-color-scheme), override persistito in localStorage.
70
10
  */
71
11
  class FuiThemeService {
72
12
  _destroyRef = inject(DestroyRef);
73
13
  _document = inject(DOCUMENT);
74
14
  _platformId = inject(PLATFORM_ID);
75
- _currentMode = signal('dark', ...(ngDevMode ? [{ debugName: "_currentMode" }] : /* istanbul ignore next */ []));
15
+ _currentMode = signal('auto', ...(ngDevMode ? [{ debugName: "_currentMode" }] : /* istanbul ignore next */ []));
76
16
  _isDark = signal(false, ...(ngDevMode ? [{ debugName: "_isDark" }] : /* istanbul ignore next */ []));
17
+ /** Mode corrente (`light|dark|auto`). */
77
18
  currentMode = this._currentMode.asReadonly();
19
+ /** Dark risolto (true se il tema applicato è dark). */
78
20
  isDark = this._isDark.asReadonly();
79
21
  constructor() {
80
- this._initializeTheme();
81
- this._setupMediaQueryListener();
22
+ this._init();
23
+ this._setupMediaListener();
82
24
  }
83
- /**
84
- * Initialize theme with custom colors
85
- */
86
- initTheme(config = {}) {
87
- this.setTheme(config);
88
- }
89
- /**
90
- * Set theme colors dynamically
91
- */
92
- setTheme(config = {}) {
93
- const root = this._document.documentElement;
94
- if (config.primary) {
95
- this._setColorPalette(root, 'primary', config.primary);
96
- }
97
- if (config.secondary) {
98
- this._setColorPalette(root, 'secondary', config.secondary);
99
- }
100
- if (config.tertiary) {
101
- this._setColorPalette(root, 'tertiary', config.tertiary);
102
- }
103
- }
104
- /**
105
- * Set theme mode
106
- */
107
- setThemeMode(mode) {
108
- this.setMode(mode);
109
- }
110
- /**
111
- * Set theme mode (preferred method)
112
- */
25
+ /** Imposta il mode, persiste e applica `data-theme`. */
113
26
  setMode(mode) {
114
27
  this._currentMode.set(mode);
115
- this._applyThemeMode(mode);
28
+ if (isPlatformBrowser(this._platformId)) {
29
+ try {
30
+ this._document.defaultView?.localStorage.setItem(STORAGE_KEY, mode);
31
+ }
32
+ catch {
33
+ // localStorage non disponibile (privacy mode)
34
+ }
35
+ }
36
+ this._apply(mode);
116
37
  }
117
- /**
118
- * Toggle between light and dark themes
119
- */
38
+ /** Toggle light/dark (porta il mode su un valore esplicito). */
120
39
  toggleTheme() {
121
- const newMode = this._isDark() ? 'light' : 'dark';
122
- this.setMode(newMode);
40
+ this.setMode(this._isDark() ? 'light' : 'dark');
123
41
  }
124
- _initializeTheme() {
42
+ _init() {
125
43
  if (!isPlatformBrowser(this._platformId)) {
44
+ this._apply('auto');
126
45
  return;
127
46
  }
128
- const win = this._document.defaultView;
47
+ let saved = null;
129
48
  try {
130
- const savedMode = win?.localStorage.getItem('fui-theme-mode');
131
- if (savedMode && ['light', 'dark', 'auto'].includes(savedMode)) {
132
- this.setMode(savedMode);
133
- return;
134
- }
49
+ saved = this._document.defaultView?.localStorage.getItem(STORAGE_KEY);
135
50
  }
136
51
  catch {
137
- // localStorage may be unavailable (e.g., privacy mode)
52
+ saved = null;
53
+ }
54
+ const mode = saved && ['light', 'dark', 'auto'].includes(saved) ? saved : 'auto';
55
+ this._currentMode.set(mode);
56
+ this._apply(mode);
57
+ }
58
+ _systemPrefersDark() {
59
+ return this._document.defaultView?.matchMedia('(prefers-color-scheme: dark)').matches ?? false;
60
+ }
61
+ _apply(mode) {
62
+ const resolved = mode === 'auto' ? (this._systemPrefersDark() ? 'dark' : 'light') : mode;
63
+ this._isDark.set(resolved === 'dark');
64
+ if (isPlatformBrowser(this._platformId)) {
65
+ this._document.documentElement.setAttribute('data-theme', resolved);
138
66
  }
139
- const mediaQuery = win?.matchMedia('(prefers-color-scheme: dark)');
140
- this._updateDarkMode(mediaQuery?.matches ?? false);
141
67
  }
142
- _setupMediaQueryListener() {
68
+ _setupMediaListener() {
143
69
  if (!isPlatformBrowser(this._platformId)) {
144
70
  return;
145
71
  }
@@ -147,97 +73,21 @@ class FuiThemeService {
147
73
  if (!win) {
148
74
  return;
149
75
  }
150
- const mediaQuery = win.matchMedia('(prefers-color-scheme: dark)');
151
- const handleChange = (e) => {
76
+ const mq = win.matchMedia('(prefers-color-scheme: dark)');
77
+ const handler = () => {
152
78
  if (this._currentMode() === 'auto') {
153
- this._updateDarkMode(e.matches);
79
+ this._apply('auto');
154
80
  }
155
81
  };
156
- mediaQuery.addEventListener('change', handleChange);
157
- // Clean up listener when service is destroyed
158
- this._destroyRef.onDestroy(() => {
159
- mediaQuery.removeEventListener('change', handleChange);
160
- });
161
- // Initial check
162
- if (this._currentMode() === 'auto') {
163
- this._updateDarkMode(mediaQuery.matches);
164
- }
165
- }
166
- _applyThemeMode(mode) {
167
- const root = this._document.documentElement;
168
- // Save to localStorage
169
- try {
170
- this._document.defaultView?.localStorage.setItem('fui-theme-mode', mode);
171
- }
172
- catch {
173
- // localStorage may be unavailable
174
- }
175
- // Remove existing theme classes
176
- root.classList.remove('fui-theme-light', 'fui-theme-dark');
177
- switch (mode) {
178
- case 'light':
179
- root.classList.add('fui-theme-light');
180
- this._updateDarkMode(false);
181
- break;
182
- case 'dark':
183
- root.classList.add('fui-theme-dark');
184
- this._updateDarkMode(true);
185
- break;
186
- case 'auto': {
187
- const win = this._document.defaultView;
188
- const _isDarkPreferred = win?.matchMedia('(prefers-color-scheme: dark)').matches ?? false;
189
- this._updateDarkMode(_isDarkPreferred);
190
- break;
191
- }
192
- }
193
- }
194
- _updateDarkMode(isDark) {
195
- this._isDark.set(isDark);
196
- this._document.documentElement.classList.toggle('fui-theme-dark', isDark);
197
- }
198
- _setColorPalette(root, colorName, baseColor) {
199
- const palette = this._generateColorPalette(baseColor);
200
- Object.entries(palette).forEach(([shade, color]) => {
201
- root.style.setProperty(`--fui-${colorName}-${shade}`, color);
202
- });
203
- // Update semantic color
204
- root.style.setProperty(`--fui-${colorName}-color`, `var(--fui-${colorName}-60)`);
205
- }
206
- _generateColorPalette(baseColor) {
207
- // Convert hex to RGB
208
- const hex = baseColor.replace('#', '');
209
- const r = parseInt(hex.substr(0, 2), 16);
210
- const g = parseInt(hex.substr(2, 2), 16);
211
- const b = parseInt(hex.substr(4, 2), 16);
212
- const palette = {};
213
- // Generate lighter shades (10-50)
214
- for (let i = 10; i <= 50; i += 10) {
215
- const factor = (60 - i) / 60; // More white for lower numbers
216
- const newR = Math.round(r + (255 - r) * (1 - factor));
217
- const newG = Math.round(g + (255 - g) * (1 - factor));
218
- const newB = Math.round(b + (255 - b) * (1 - factor));
219
- palette[i.toString()] = `rgb(${newR}, ${newG}, ${newB})`;
220
- }
221
- // Base color (60)
222
- palette['60'] = baseColor;
223
- // Generate darker shades (70-100)
224
- for (let i = 70; i <= 100; i += 10) {
225
- const factor = (i - 60) / 40; // More black for higher numbers
226
- const newR = Math.round(r * (1 - factor));
227
- const newG = Math.round(g * (1 - factor));
228
- const newB = Math.round(b * (1 - factor));
229
- palette[i.toString()] = `rgb(${newR}, ${newG}, ${newB})`;
230
- }
231
- return palette;
82
+ mq.addEventListener('change', handler);
83
+ this._destroyRef.onDestroy(() => { mq.removeEventListener('change', handler); });
232
84
  }
233
85
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
234
86
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiThemeService, providedIn: 'root' });
235
87
  }
236
88
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiThemeService, decorators: [{
237
89
  type: Injectable,
238
- args: [{
239
- providedIn: 'root',
240
- }]
90
+ args: [{ providedIn: 'root' }]
241
91
  }], ctorParameters: () => [] });
242
92
 
243
93
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"raintonic-formaui-services-theme.mjs","sources":["../../../lib/services/theme/theme.service.ts","../../../lib/services/theme/raintonic-formaui-services-theme.ts"],"sourcesContent":["import { DestroyRef, inject, Injectable, PLATFORM_ID, signal, Signal, WritableSignal } from '@angular/core';\r\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\r\n\r\n/**\r\n * Configuration interface for theme customization\r\n */\r\nexport interface FuiThemeConfig {\r\n /** Primary color for the theme (hex, rgb, hsl) */\r\n primary?: string;\r\n /** Secondary color for the theme (hex, rgb, hsl) */\r\n secondary?: string;\r\n /** Tertiary color for the theme (hex, rgb, hsl) */\r\n tertiary?: string;\r\n}\r\n\r\n/**\r\n * Available theme modes\r\n */\r\nexport type FuiThemeMode = 'light' | 'dark' | 'auto';\r\n\r\n/**\r\n * # FuiTheme Service\r\n *\r\n * Central service for managing theme configuration and mode switching.\r\n * Provides runtime theme customization with CSS variables and automatic palette generation.\r\n *\r\n * ## Features\r\n * - Runtime theme customization\r\n * - Light/dark/auto mode switching\r\n * - Automatic color palette generation\r\n * - CSS variables-based theming\r\n * - Persistent theme preferences\r\n * - System theme detection\r\n *\r\n * ## Usage\r\n *\r\n * ### Basic Theme Setup\r\n * ```typescript\r\n * import { FuiThemeService } from '@raintonic/formaui/services/theme';\r\n *\r\n * constructor(private themeService: FuiThemeService) {\r\n * // Set custom theme colors\r\n * this.themeService.setTheme({\r\n * primary: '#007bff',\r\n * secondary: '#6c757d',\r\n * tertiary: '#28a745'\r\n * });\r\n * }\r\n * ```\r\n *\r\n * ### Theme Mode Switching\r\n * ```typescript\r\n * // Switch to dark mode\r\n * this.themeService.setMode('dark');\r\n *\r\n * // Switch to auto (follows system preference)\r\n * this.themeService.setMode('auto');\r\n *\r\n * // Get current mode\r\n * const currentMode = this.themeService.currentMode();\r\n * const isDark = this.themeService.isDark();\r\n * ```\r\n *\r\n * @example\r\n * ```typescript\r\n * import { Component, effect } from '@angular/core';\r\n * import { FuiThemeService } from '@raintonic/formaui/services/theme';\r\n *\r\n * @Component({\r\n * template: `\r\n * <div class=\"theme-controls\">\r\n * <button fuiButton (click)=\"setLightMode()\">Light</button>\r\n * <button fuiButton (click)=\"setDarkMode()\">Dark</button>\r\n * <button fuiButton (click)=\"setAutoMode()\">Auto</button>\r\n * </div>\r\n * `\r\n * })\r\n * export class ThemeControlsComponent {\r\n * constructor(public themeService: FuiThemeService) {}\r\n *\r\n * setLightMode() { this.themeService.setMode('light'); }\r\n * setDarkMode() { this.themeService.setMode('dark'); }\r\n * setAutoMode() { this.themeService.setMode('auto'); }\r\n * }\r\n * ```\r\n */\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class FuiThemeService {\r\n private readonly _destroyRef = inject(DestroyRef);\r\n private readonly _document = inject(DOCUMENT);\r\n private readonly _platformId = inject(PLATFORM_ID);\r\n private readonly _currentMode: WritableSignal<FuiThemeMode> = signal<FuiThemeMode>('dark');\r\n private readonly _isDark: WritableSignal<boolean> = signal(false);\r\n\r\n readonly currentMode: Signal<FuiThemeMode> = this._currentMode.asReadonly();\r\n readonly isDark: Signal<boolean> = this._isDark.asReadonly();\r\n\r\n constructor() {\r\n this._initializeTheme();\r\n this._setupMediaQueryListener();\r\n }\r\n\r\n /**\r\n * Initialize theme with custom colors\r\n */\r\n initTheme(config: FuiThemeConfig = {}): void {\r\n this.setTheme(config);\r\n }\r\n\r\n /**\r\n * Set theme colors dynamically\r\n */\r\n setTheme(config: FuiThemeConfig = {}): void {\r\n const root: HTMLElement = this._document.documentElement;\r\n\r\n if (config.primary) {\r\n this._setColorPalette(root, 'primary', config.primary);\r\n }\r\n\r\n if (config.secondary) {\r\n this._setColorPalette(root, 'secondary', config.secondary);\r\n }\r\n\r\n if (config.tertiary) {\r\n this._setColorPalette(root, 'tertiary', config.tertiary);\r\n }\r\n }\r\n\r\n /**\r\n * Set theme mode\r\n */\r\n setThemeMode(mode: FuiThemeMode): void {\r\n this.setMode(mode);\r\n }\r\n\r\n /**\r\n * Set theme mode (preferred method)\r\n */\r\n setMode(mode: FuiThemeMode): void {\r\n this._currentMode.set(mode);\r\n this._applyThemeMode(mode);\r\n }\r\n\r\n /**\r\n * Toggle between light and dark themes\r\n */\r\n toggleTheme(): void {\r\n const newMode: FuiThemeMode = this._isDark() ? 'light' : 'dark';\r\n this.setMode(newMode);\r\n }\r\n\r\n private _initializeTheme(): void {\r\n if (!isPlatformBrowser(this._platformId)) {\r\n return;\r\n }\r\n\r\n const win = this._document.defaultView;\r\n try {\r\n const savedMode: FuiThemeMode | null = win?.localStorage.getItem('fui-theme-mode') as FuiThemeMode;\r\n if (savedMode && ['light', 'dark', 'auto'].includes(savedMode)) {\r\n this.setMode(savedMode);\r\n return;\r\n }\r\n } catch {\r\n // localStorage may be unavailable (e.g., privacy mode)\r\n }\r\n\r\n const mediaQuery: MediaQueryList | undefined = win?.matchMedia('(prefers-color-scheme: dark)');\r\n this._updateDarkMode(mediaQuery?.matches ?? false);\r\n }\r\n\r\n private _setupMediaQueryListener(): void {\r\n if (!isPlatformBrowser(this._platformId)) {\r\n return;\r\n }\r\n\r\n const win = this._document.defaultView;\r\n if (!win) {\r\n return;\r\n }\r\n\r\n const mediaQuery: MediaQueryList = win.matchMedia('(prefers-color-scheme: dark)');\r\n\r\n const handleChange = (e: MediaQueryListEvent): void => {\r\n if (this._currentMode() === 'auto') {\r\n this._updateDarkMode(e.matches);\r\n }\r\n };\r\n\r\n mediaQuery.addEventListener('change', handleChange);\r\n\r\n // Clean up listener when service is destroyed\r\n this._destroyRef.onDestroy(() => {\r\n mediaQuery.removeEventListener('change', handleChange);\r\n });\r\n\r\n // Initial check\r\n if (this._currentMode() === 'auto') {\r\n this._updateDarkMode(mediaQuery.matches);\r\n }\r\n }\r\n\r\n private _applyThemeMode(mode: FuiThemeMode): void {\r\n const root: HTMLElement = this._document.documentElement;\r\n\r\n // Save to localStorage\r\n try {\r\n this._document.defaultView?.localStorage.setItem('fui-theme-mode', mode);\r\n } catch {\r\n // localStorage may be unavailable\r\n }\r\n\r\n // Remove existing theme classes\r\n root.classList.remove('fui-theme-light', 'fui-theme-dark');\r\n\r\n switch (mode) {\r\n case 'light':\r\n root.classList.add('fui-theme-light');\r\n this._updateDarkMode(false);\r\n break;\r\n case 'dark':\r\n root.classList.add('fui-theme-dark');\r\n this._updateDarkMode(true);\r\n break;\r\n case 'auto': {\r\n const win = this._document.defaultView;\r\n const _isDarkPreferred: boolean = win?.matchMedia('(prefers-color-scheme: dark)').matches ?? false;\r\n this._updateDarkMode(_isDarkPreferred);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n private _updateDarkMode(isDark: boolean): void {\r\n this._isDark.set(isDark);\r\n this._document.documentElement.classList.toggle('fui-theme-dark', isDark);\r\n }\r\n\r\n private _setColorPalette(root: HTMLElement, colorName: string, baseColor: string): void {\r\n const palette: Record<string, string> = this._generateColorPalette(baseColor);\r\n\r\n Object.entries(palette).forEach(([shade, color]) => {\r\n root.style.setProperty(`--fui-${colorName}-${shade}`, color);\r\n });\r\n\r\n // Update semantic color\r\n root.style.setProperty(`--fui-${colorName}-color`, `var(--fui-${colorName}-60)`);\r\n }\r\n\r\n private _generateColorPalette(baseColor: string): Record<string, string> {\r\n // Convert hex to RGB\r\n const hex: string = baseColor.replace('#', '');\r\n const r: number = parseInt(hex.substr(0, 2), 16);\r\n const g: number = parseInt(hex.substr(2, 2), 16);\r\n const b: number = parseInt(hex.substr(4, 2), 16);\r\n\r\n const palette: Record<string, string> = {};\r\n\r\n // Generate lighter shades (10-50)\r\n for (let i = 10; i <= 50; i += 10) {\r\n const factor: number = (60 - i) / 60; // More white for lower numbers\r\n const newR: number = Math.round(r + (255 - r) * (1 - factor));\r\n const newG: number = Math.round(g + (255 - g) * (1 - factor));\r\n const newB: number = Math.round(b + (255 - b) * (1 - factor));\r\n palette[i.toString()] = `rgb(${newR}, ${newG}, ${newB})`;\r\n }\r\n\r\n // Base color (60)\r\n palette['60'] = baseColor;\r\n\r\n // Generate darker shades (70-100)\r\n for (let i = 70; i <= 100; i += 10) {\r\n const factor: number = (i - 60) / 40; // More black for higher numbers\r\n const newR: number = Math.round(r * (1 - factor));\r\n const newG: number = Math.round(g * (1 - factor));\r\n const newB: number = Math.round(b * (1 - factor));\r\n palette[i.toString()] = `rgb(${newR}, ${newG}, ${newB})`;\r\n }\r\n\r\n return palette;\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"raintonic-formaui-services-theme.mjs","sources":["../../../lib/services/theme/theme.service.ts","../../../lib/services/theme/raintonic-formaui-services-theme.ts"],"sourcesContent":["import { DestroyRef, inject, Injectable, PLATFORM_ID, signal, Signal, WritableSignal } from '@angular/core';\r\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\r\n\r\n/** Theme mode. `auto` segue `prefers-color-scheme`. */\r\nexport type FuiThemeMode = 'light' | 'dark' | 'auto';\r\n\r\nconst STORAGE_KEY = 'fui-theme-mode';\r\n\r\n/**\r\n * FuiThemeService — gestione del mode tema (zoneless, signals).\r\n * Applica il tema via attributo `data-theme=\"light|dark\"` su `<html>`.\r\n * Default `auto` (system-driven via prefers-color-scheme), override persistito in localStorage.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class FuiThemeService {\r\n private readonly _destroyRef = inject(DestroyRef);\r\n private readonly _document = inject(DOCUMENT);\r\n private readonly _platformId = inject(PLATFORM_ID);\r\n\r\n private readonly _currentMode: WritableSignal<FuiThemeMode> = signal<FuiThemeMode>('auto');\r\n private readonly _isDark: WritableSignal<boolean> = signal(false);\r\n\r\n /** Mode corrente (`light|dark|auto`). */\r\n readonly currentMode: Signal<FuiThemeMode> = this._currentMode.asReadonly();\r\n /** Dark risolto (true se il tema applicato è dark). */\r\n readonly isDark: Signal<boolean> = this._isDark.asReadonly();\r\n\r\n constructor() {\r\n this._init();\r\n this._setupMediaListener();\r\n }\r\n\r\n /** Imposta il mode, persiste e applica `data-theme`. */\r\n setMode(mode: FuiThemeMode): void {\r\n this._currentMode.set(mode);\r\n if (isPlatformBrowser(this._platformId)) {\r\n try {\r\n this._document.defaultView?.localStorage.setItem(STORAGE_KEY, mode);\r\n } catch {\r\n // localStorage non disponibile (privacy mode)\r\n }\r\n }\r\n this._apply(mode);\r\n }\r\n\r\n /** Toggle light/dark (porta il mode su un valore esplicito). */\r\n toggleTheme(): void {\r\n this.setMode(this._isDark() ? 'light' : 'dark');\r\n }\r\n\r\n private _init(): void {\r\n if (!isPlatformBrowser(this._platformId)) {\r\n this._apply('auto');\r\n return;\r\n }\r\n let saved: FuiThemeMode | null = null;\r\n try {\r\n saved = this._document.defaultView?.localStorage.getItem(STORAGE_KEY) as FuiThemeMode | null;\r\n } catch {\r\n saved = null;\r\n }\r\n const mode: FuiThemeMode = saved && ['light', 'dark', 'auto'].includes(saved) ? saved : 'auto';\r\n this._currentMode.set(mode);\r\n this._apply(mode);\r\n }\r\n\r\n private _systemPrefersDark(): boolean {\r\n return this._document.defaultView?.matchMedia('(prefers-color-scheme: dark)').matches ?? false;\r\n }\r\n\r\n private _apply(mode: FuiThemeMode): void {\r\n const resolved: 'light' | 'dark' = mode === 'auto' ? (this._systemPrefersDark() ? 'dark' : 'light') : mode;\r\n this._isDark.set(resolved === 'dark');\r\n if (isPlatformBrowser(this._platformId)) {\r\n this._document.documentElement.setAttribute('data-theme', resolved);\r\n }\r\n }\r\n\r\n private _setupMediaListener(): void {\r\n if (!isPlatformBrowser(this._platformId)) {\r\n return;\r\n }\r\n const win = this._document.defaultView;\r\n if (!win) {\r\n return;\r\n }\r\n const mq: MediaQueryList = win.matchMedia('(prefers-color-scheme: dark)');\r\n const handler = (): void => {\r\n if (this._currentMode() === 'auto') {\r\n this._apply('auto');\r\n }\r\n };\r\n mq.addEventListener('change', handler);\r\n this._destroyRef.onDestroy(() => { mq.removeEventListener('change', handler); });\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const FORMAUI_VERSION = '0.3.1';
1
+ const FORMAUI_VERSION = '0.9.0';
2
2
  // FormaUI - Primary entry point
3
3
  // Secondary entry points are imported directly, e.g.:
4
4
  // import { FuiButtonDirective } from '@raintonic/formaui/components/button';
@@ -1 +1 @@
1
- {"version":3,"file":"raintonic-formaui.mjs","sources":["../../../lib/src/public-api.ts","../../../lib/src/raintonic-formaui.ts"],"sourcesContent":["export const FORMAUI_VERSION = '0.3.1';\r\n\r\n// FormaUI - Primary entry point\r\n// Secondary entry points are imported directly, e.g.:\r\n// import { FuiButtonDirective } from '@raintonic/formaui/components/button';\r\n// import { FuiIconComponent } from '@raintonic/formaui/components/icon';\r\n// import { ThemeService } from '@raintonic/formaui/services/theme';\r\n// import { FuiOverlayService } from '@raintonic/formaui/cdk/overlay';\r\n// import { FuiCoreModule } from '@raintonic/formaui/core';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"AAAO,MAAM,eAAe,GAAG;AAE/B;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;;AAEG;;;;"}
1
+ {"version":3,"file":"raintonic-formaui.mjs","sources":["../../../lib/src/public-api.ts","../../../lib/src/raintonic-formaui.ts"],"sourcesContent":["export const FORMAUI_VERSION = '0.9.0';\r\n\r\n// FormaUI - Primary entry point\r\n// Secondary entry points are imported directly, e.g.:\r\n// import { FuiButtonDirective } from '@raintonic/formaui/components/button';\r\n// import { FuiIconComponent } from '@raintonic/formaui/components/icon';\r\n// import { ThemeService } from '@raintonic/formaui/services/theme';\r\n// import { FuiOverlayService } from '@raintonic/formaui/cdk/overlay';\r\n// import { FuiCoreModule } from '@raintonic/formaui/core';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"AAAO,MAAM,eAAe,GAAG;AAE/B;AACA;AACA;AACA;AACA;AACA;AACA;;ACRA;;AAEG;;;;"}