@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,317 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, booleanAttribute, signal, viewChild, TemplateRef, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, InjectionToken, output, contentChildren, effect } from '@angular/core';
3
+ import { NgTemplateOutlet } from '@angular/common';
4
+ import { FuiIconComponent } from '@raintonic/formaui/components/icon';
5
+
6
+ /**
7
+ * # FuiStepComponent
8
+ *
9
+ * Represents a single step within an `fui-stepper`.
10
+ * Each step has a label, optional description/icon, and content that is lazily rendered.
11
+ *
12
+ * ## Usage
13
+ *
14
+ * ```html
15
+ * <fui-stepper>
16
+ * <fui-step label="Account" description="Create your account">
17
+ * <p>Account creation form here...</p>
18
+ * </fui-step>
19
+ * <fui-step label="Profile" icon="user">
20
+ * <p>Profile setup here...</p>
21
+ * </fui-step>
22
+ * </fui-stepper>
23
+ * ```
24
+ */
25
+ class FuiStepComponent {
26
+ // Inputs
27
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
28
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
29
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
30
+ completed = input(false, { ...(ngDevMode ? { debugName: "completed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
31
+ editable = input(true, { ...(ngDevMode ? { debugName: "editable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
32
+ errorMessage = input('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
33
+ stepControl = input(null, ...(ngDevMode ? [{ debugName: "stepControl" }] : /* istanbul ignore next */ []));
34
+ optional = input(false, { ...(ngDevMode ? { debugName: "optional" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
35
+ // Internal state
36
+ _interacted = signal(false, ...(ngDevMode ? [{ debugName: "_interacted" }] : /* istanbul ignore next */ []));
37
+ // Content template reference for lazy rendering
38
+ contentTemplate = viewChild(TemplateRef, ...(ngDevMode ? [{ debugName: "contentTemplate" }] : /* istanbul ignore next */ []));
39
+ // Computed state
40
+ hasError = computed(() => {
41
+ const errMsg = this.errorMessage();
42
+ if (errMsg && errMsg.length > 0) {
43
+ return true;
44
+ }
45
+ const control = this.stepControl();
46
+ return !!(control && control.invalid && this._interacted());
47
+ }, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
48
+ state = computed(() => {
49
+ if (this.hasError()) {
50
+ return 'error';
51
+ }
52
+ if (this.completed()) {
53
+ return 'completed';
54
+ }
55
+ return 'default';
56
+ }, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
57
+ /** Mark this step as interacted and select it via the parent stepper. */
58
+ select() {
59
+ this._interacted.set(true);
60
+ }
61
+ /** Reset the step to its initial state. */
62
+ reset() {
63
+ this._interacted.set(false);
64
+ const control = this.stepControl();
65
+ if (control) {
66
+ control.reset();
67
+ }
68
+ }
69
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
70
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.6", type: FuiStepComponent, isStandalone: true, selector: "fui-step", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, completed: { classPropertyName: "completed", publicName: "completed", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, stepControl: { classPropertyName: "stepControl", publicName: "stepControl", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "contentTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: '<ng-template><ng-content></ng-content></ng-template>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
71
+ }
72
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiStepComponent, decorators: [{
73
+ type: Component,
74
+ args: [{
75
+ selector: 'fui-step',
76
+ standalone: true,
77
+ template: '<ng-template><ng-content></ng-content></ng-template>',
78
+ changeDetection: ChangeDetectionStrategy.OnPush,
79
+ encapsulation: ViewEncapsulation.None,
80
+ }]
81
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], completed: [{ type: i0.Input, args: [{ isSignal: true, alias: "completed", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], stepControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepControl", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], contentTemplate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
82
+
83
+ const FORMAUI_STEPPER = new InjectionToken('FORMAUI_STEPPER');
84
+
85
+ let nextStepperId = 0;
86
+ /**
87
+ * # FuiStepperComponent
88
+ *
89
+ * A stepper component that guides users through a sequence of steps.
90
+ * Supports horizontal and vertical orientations, linear and non-linear navigation,
91
+ * step validation, and lazy content rendering.
92
+ *
93
+ * ## Usage
94
+ *
95
+ * ### Basic horizontal stepper
96
+ * ```html
97
+ * <fui-stepper>
98
+ * <fui-step label="Step 1">Content 1</fui-step>
99
+ * <fui-step label="Step 2">Content 2</fui-step>
100
+ * <fui-step label="Step 3">Content 3</fui-step>
101
+ * </fui-stepper>
102
+ * ```
103
+ *
104
+ * ### Vertical stepper
105
+ * ```html
106
+ * <fui-stepper orientation="vertical">
107
+ * <fui-step label="Step 1">Content 1</fui-step>
108
+ * <fui-step label="Step 2">Content 2</fui-step>
109
+ * </fui-stepper>
110
+ * ```
111
+ *
112
+ * ### Linear stepper with form validation
113
+ * ```html
114
+ * <fui-stepper [linear]="true">
115
+ * <fui-step label="Account" [stepControl]="accountForm">
116
+ * <form [formGroup]="accountForm">...</form>
117
+ * </fui-step>
118
+ * <fui-step label="Address" [stepControl]="addressForm">
119
+ * <form [formGroup]="addressForm">...</form>
120
+ * </fui-step>
121
+ * </fui-stepper>
122
+ * ```
123
+ */
124
+ class FuiStepperComponent {
125
+ // Inputs
126
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
127
+ linear = input(false, { ...(ngDevMode ? { debugName: "linear" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
128
+ selectedIndex = input(0, ...(ngDevMode ? [{ debugName: "selectedIndex" }] : /* istanbul ignore next */ []));
129
+ // Outputs
130
+ selectionChange = output();
131
+ animationDone = output();
132
+ // Content children
133
+ steps = contentChildren(FuiStepComponent, ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
134
+ // Internal state
135
+ _selectedIndex = signal(0, ...(ngDevMode ? [{ debugName: "_selectedIndex" }] : /* istanbul ignore next */ []));
136
+ // Unique ID for ARIA linking
137
+ _uniqueId = nextStepperId++;
138
+ // Computed: active step
139
+ _selectedStep = computed(() => {
140
+ const allSteps = this.steps();
141
+ const idx = this._selectedIndex();
142
+ return allSteps[idx] ?? null;
143
+ }, ...(ngDevMode ? [{ debugName: "_selectedStep" }] : /* istanbul ignore next */ []));
144
+ constructor() {
145
+ // Sync selectedIndex input with internal state
146
+ effect(() => {
147
+ const idx = this.selectedIndex();
148
+ this._selectedIndex.set(idx);
149
+ });
150
+ }
151
+ /** Navigate to the next step. In linear mode, validates the current step first. */
152
+ next() {
153
+ const allSteps = this.steps();
154
+ const currentIdx = this._selectedIndex();
155
+ if (currentIdx >= allSteps.length - 1) {
156
+ return;
157
+ }
158
+ if (this.linear()) {
159
+ const currentStep = allSteps[currentIdx];
160
+ if (currentStep) {
161
+ currentStep._interacted.set(true);
162
+ const control = currentStep.stepControl();
163
+ if (control?.invalid) {
164
+ return;
165
+ }
166
+ }
167
+ }
168
+ this._setSelectedIndex(currentIdx + 1);
169
+ }
170
+ /** Navigate to the previous step. */
171
+ previous() {
172
+ const currentIdx = this._selectedIndex();
173
+ if (currentIdx > 0) {
174
+ this._setSelectedIndex(currentIdx - 1);
175
+ }
176
+ }
177
+ /** Navigate directly to a specific step by index. */
178
+ goToStep(index) {
179
+ const allSteps = this.steps();
180
+ if (index < 0 || index >= allSteps.length) {
181
+ return;
182
+ }
183
+ if (this.linear() && index > this._selectedIndex()) {
184
+ // In linear mode, can only go forward if all preceding steps are valid
185
+ for (let i = this._selectedIndex(); i < index; i++) {
186
+ const step = allSteps[i];
187
+ if (step) {
188
+ step._interacted.set(true);
189
+ const control = step.stepControl();
190
+ if (control?.invalid) {
191
+ return;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ this._setSelectedIndex(index);
197
+ }
198
+ /** Reset all steps and return to the first step. */
199
+ reset() {
200
+ this._selectedIndex.set(0);
201
+ this.steps().forEach((step) => {
202
+ step.reset();
203
+ });
204
+ }
205
+ /** @internal Handle step header click */
206
+ _onStepHeaderClick(index) {
207
+ this.goToStep(index);
208
+ }
209
+ /** @internal Check if a step is disabled for navigation */
210
+ _isStepDisabled(index) {
211
+ if (!this.linear()) {
212
+ return false;
213
+ }
214
+ const allSteps = this.steps();
215
+ // Can always go backward
216
+ if (index < this._selectedIndex()) {
217
+ // Check if the target step is editable
218
+ const targetStep = allSteps[index];
219
+ return targetStep ? !targetStep.editable() : false;
220
+ }
221
+ // For forward navigation in linear mode, check all preceding steps
222
+ if (index > this._selectedIndex()) {
223
+ for (let i = this._selectedIndex(); i < index; i++) {
224
+ const step = allSteps[i];
225
+ if (step) {
226
+ const control = step.stepControl();
227
+ if (control?.invalid) {
228
+ return true;
229
+ }
230
+ }
231
+ }
232
+ }
233
+ return false;
234
+ }
235
+ /** @internal Handle keyboard navigation on step headers */
236
+ _onHeaderKeydown(event, currentIndex) {
237
+ const isHorizontal = this.orientation() === 'horizontal';
238
+ const allSteps = this.steps();
239
+ let newIndex = currentIndex;
240
+ switch (event.key) {
241
+ case 'ArrowRight':
242
+ if (isHorizontal && currentIndex < allSteps.length - 1) {
243
+ newIndex = currentIndex + 1;
244
+ }
245
+ break;
246
+ case 'ArrowLeft':
247
+ if (isHorizontal && currentIndex > 0) {
248
+ newIndex = currentIndex - 1;
249
+ }
250
+ break;
251
+ case 'ArrowDown':
252
+ if (!isHorizontal && currentIndex < allSteps.length - 1) {
253
+ newIndex = currentIndex + 1;
254
+ event.preventDefault();
255
+ }
256
+ break;
257
+ case 'ArrowUp':
258
+ if (!isHorizontal && currentIndex > 0) {
259
+ newIndex = currentIndex - 1;
260
+ event.preventDefault();
261
+ }
262
+ break;
263
+ case 'Home':
264
+ newIndex = 0;
265
+ event.preventDefault();
266
+ break;
267
+ case 'End':
268
+ newIndex = allSteps.length - 1;
269
+ event.preventDefault();
270
+ break;
271
+ default:
272
+ return;
273
+ }
274
+ if (newIndex !== currentIndex) {
275
+ this._onStepHeaderClick(newIndex);
276
+ }
277
+ }
278
+ /** @internal Set the selected index and emit selection change */
279
+ _setSelectedIndex(newIndex) {
280
+ const allSteps = this.steps();
281
+ const previousIndex = this._selectedIndex();
282
+ if (newIndex === previousIndex) {
283
+ return;
284
+ }
285
+ const previousStep = allSteps[previousIndex] ?? null;
286
+ const selectedStep = allSteps[newIndex] ?? null;
287
+ // Mark the new step as interacted
288
+ if (selectedStep) {
289
+ selectedStep._interacted.set(true);
290
+ }
291
+ this._selectedIndex.set(newIndex);
292
+ this.selectionChange.emit({
293
+ selectedIndex: newIndex,
294
+ previousIndex,
295
+ selectedStep,
296
+ previousStep,
297
+ });
298
+ }
299
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiStepperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
300
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiStepperComponent, isStandalone: true, selector: "fui-stepper", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", animationDone: "animationDone" }, host: { properties: { "class.fui-stepper--horizontal": "orientation() === \"horizontal\"", "class.fui-stepper--vertical": "orientation() === \"vertical\"", "class.fui-stepper--linear": "linear()" }, classAttribute: "fui-stepper" }, providers: [{ provide: FORMAUI_STEPPER, useExisting: FuiStepperComponent }], queries: [{ propertyName: "steps", predicate: FuiStepComponent, isSignal: true }], ngImport: i0, template: "<!-- Step Headers -->\n<div class=\"fui-stepper__header\" role=\"tablist\" [attr.aria-orientation]=\"orientation()\">\n @for (step of steps(); track $index; let last = $last) {\n <button\n class=\"fui-stepper__step-header\"\n [class.fui-stepper__step-header--active]=\"_selectedIndex() === $index\"\n [class.fui-stepper__step-header--completed]=\"step.state() === 'completed'\"\n [class.fui-stepper__step-header--error]=\"step.hasError()\"\n [class.fui-stepper__step-header--disabled]=\"_isStepDisabled($index)\"\n [attr.aria-selected]=\"_selectedIndex() === $index\"\n [attr.aria-disabled]=\"_isStepDisabled($index)\"\n [attr.aria-current]=\"_selectedIndex() === $index ? 'step' : null\"\n [attr.aria-label]=\"\n step.label() + (step.state() === 'completed' ? ' (completed)' : step.hasError() ? ' (error)' : '')\n \"\n [disabled]=\"_isStepDisabled($index)\"\n role=\"tab\"\n [attr.id]=\"'fui-step-header-' + _uniqueId + '-' + $index\"\n [attr.aria-controls]=\"'fui-step-content-' + _uniqueId + '-' + $index\"\n (click)=\"_onStepHeaderClick($index)\"\n (keydown)=\"_onHeaderKeydown($event, $index)\"\n type=\"button\"\n >\n <div class=\"fui-stepper__step-icon\">\n @if (step.state() === 'completed' && !step.icon()) {\n <fui-icon name=\"check\" size=\"sm\"></fui-icon>\n } @else if (step.hasError()) {\n <fui-icon name=\"warning\" size=\"sm\"></fui-icon>\n } @else if (step.icon()) {\n <fui-icon [name]=\"step.icon()\" size=\"sm\"></fui-icon>\n } @else {\n <span class=\"fui-stepper__step-number\">{{ $index + 1 }}</span>\n }\n </div>\n <div class=\"fui-stepper__step-text\">\n <span class=\"fui-stepper__step-label\">{{ step.label() }}</span>\n @if (step.description()) {\n <span class=\"fui-stepper__step-description\">{{ step.description() }}</span>\n }\n @if (step.hasError()) {\n <span class=\"fui-stepper__step-error\">{{ step.errorMessage() }}</span>\n }\n </div>\n </button>\n @if (!last) {\n <div\n class=\"fui-stepper__connector\"\n [class.fui-stepper__connector--completed]=\"step.state() === 'completed'\"\n ></div>\n }\n }\n</div>\n\n<!-- Step Content -->\n<div class=\"fui-stepper__content\">\n @for (step of steps(); track $index) {\n <div\n class=\"fui-stepper__step-body\"\n [class.fui-stepper__step-body--active]=\"_selectedIndex() === $index\"\n role=\"tabpanel\"\n [attr.id]=\"'fui-step-content-' + _uniqueId + '-' + $index\"\n [attr.aria-labelledby]=\"'fui-step-header-' + _uniqueId + '-' + $index\"\n [attr.aria-expanded]=\"_selectedIndex() === $index\"\n >\n @if (_selectedIndex() === $index || step._interacted()) {\n <ng-container [ngTemplateOutlet]=\"step.contentTemplate()!\"></ng-container>\n }\n </div>\n }\n</div>\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}.fui-stepper{display:block;font-family:var(--fui-font-family-sans)}.fui-stepper__header{display:flex;align-items:center}.fui-stepper__step-header{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-stepper__step-header{display:flex;align-items:center;gap:var(--fui-gap-8);padding:var(--fui-padding-8) var(--fui-padding-12);border-radius:var(--fui-border-radius-md);transition:background-color,color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms;cursor:pointer;flex-shrink:0}.fui-stepper__step-header:hover:not(:disabled){background-color:var(--fui-surface-02)}.fui-stepper__step-header:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-stepper__step-header--active .fui-stepper__step-icon{background-color:var(--fui-primary);color:var(--fui-primary-contrast);border-color:var(--fui-primary)}.fui-stepper__step-header--active .fui-stepper__step-label{color:var(--fui-text-primary);font-weight:var(--fui-font-weight-semibold)}.fui-stepper__step-header--completed .fui-stepper__step-icon{background-color:var(--fui-success);color:var(--fui-white);border-color:var(--fui-success)}.fui-stepper__step-header--completed .fui-stepper__step-label{color:var(--fui-text-primary)}.fui-stepper__step-header--error .fui-stepper__step-icon{background-color:var(--fui-danger);color:var(--fui-white);border-color:var(--fui-danger)}.fui-stepper__step-header--error .fui-stepper__step-label{color:var(--fui-danger)}.fui-stepper__step-header--disabled{cursor:not-allowed;opacity:.5}.fui-stepper__step-header--disabled .fui-stepper__step-icon{background-color:var(--fui-surface-03);color:var(--fui-text-disabled);border-color:var(--fui-border-color)}.fui-stepper__step-header--disabled .fui-stepper__step-label{color:var(--fui-text-disabled)}.fui-stepper__step-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;border:2px solid var(--fui-border-color);background-color:var(--fui-surface-01);color:var(--fui-text-secondary);flex-shrink:0;transition:background-color,color,border-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-stepper__step-number{font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-semibold);line-height:1}.fui-stepper__step-text{display:flex;flex-direction:column;gap:2px;text-align:left}.fui-stepper__step-label{font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-medium);color:var(--fui-text-secondary);transition:color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-stepper__step-description{font-size:var(--fui-font-size-01);color:var(--fui-text-secondary)}.fui-stepper__step-error{font-size:var(--fui-font-size-01);color:var(--fui-danger)}.fui-stepper__connector{flex:1;height:2px;min-width:24px;background-color:var(--fui-border-color);transition:background-color var(--fui-duration-moderate-01) var(--fui-ease-standard) 0ms}.fui-stepper__connector--completed{background-color:var(--fui-success)}.fui-stepper__content{margin-top:var(--fui-spacing-05)}.fui-stepper__step-body{display:none}.fui-stepper__step-body--active{display:block;animation:fui-stepper-fade-in var(--fui-duration-fast-02) var(--fui-ease-entrance)}.fui-stepper--horizontal .fui-stepper__header{flex-direction:row}.fui-stepper--vertical .fui-stepper__header{flex-direction:column;align-items:stretch}.fui-stepper--vertical .fui-stepper__connector{width:2px;height:24px;min-width:unset;flex:unset;margin-left:23px}.fui-stepper--vertical .fui-stepper__step-header{width:100%}@keyframes fui-stepper-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: FuiIconComponent, selector: "fui-icon", inputs: ["name", "size", "weight", "color", "ariaLabel", "spin", "pulse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
301
+ }
302
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiStepperComponent, decorators: [{
303
+ type: Component,
304
+ args: [{ selector: 'fui-stepper', standalone: true, imports: [NgTemplateOutlet, FuiIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
305
+ class: 'fui-stepper',
306
+ '[class.fui-stepper--horizontal]': 'orientation() === "horizontal"',
307
+ '[class.fui-stepper--vertical]': 'orientation() === "vertical"',
308
+ '[class.fui-stepper--linear]': 'linear()',
309
+ }, providers: [{ provide: FORMAUI_STEPPER, useExisting: FuiStepperComponent }], template: "<!-- Step Headers -->\n<div class=\"fui-stepper__header\" role=\"tablist\" [attr.aria-orientation]=\"orientation()\">\n @for (step of steps(); track $index; let last = $last) {\n <button\n class=\"fui-stepper__step-header\"\n [class.fui-stepper__step-header--active]=\"_selectedIndex() === $index\"\n [class.fui-stepper__step-header--completed]=\"step.state() === 'completed'\"\n [class.fui-stepper__step-header--error]=\"step.hasError()\"\n [class.fui-stepper__step-header--disabled]=\"_isStepDisabled($index)\"\n [attr.aria-selected]=\"_selectedIndex() === $index\"\n [attr.aria-disabled]=\"_isStepDisabled($index)\"\n [attr.aria-current]=\"_selectedIndex() === $index ? 'step' : null\"\n [attr.aria-label]=\"\n step.label() + (step.state() === 'completed' ? ' (completed)' : step.hasError() ? ' (error)' : '')\n \"\n [disabled]=\"_isStepDisabled($index)\"\n role=\"tab\"\n [attr.id]=\"'fui-step-header-' + _uniqueId + '-' + $index\"\n [attr.aria-controls]=\"'fui-step-content-' + _uniqueId + '-' + $index\"\n (click)=\"_onStepHeaderClick($index)\"\n (keydown)=\"_onHeaderKeydown($event, $index)\"\n type=\"button\"\n >\n <div class=\"fui-stepper__step-icon\">\n @if (step.state() === 'completed' && !step.icon()) {\n <fui-icon name=\"check\" size=\"sm\"></fui-icon>\n } @else if (step.hasError()) {\n <fui-icon name=\"warning\" size=\"sm\"></fui-icon>\n } @else if (step.icon()) {\n <fui-icon [name]=\"step.icon()\" size=\"sm\"></fui-icon>\n } @else {\n <span class=\"fui-stepper__step-number\">{{ $index + 1 }}</span>\n }\n </div>\n <div class=\"fui-stepper__step-text\">\n <span class=\"fui-stepper__step-label\">{{ step.label() }}</span>\n @if (step.description()) {\n <span class=\"fui-stepper__step-description\">{{ step.description() }}</span>\n }\n @if (step.hasError()) {\n <span class=\"fui-stepper__step-error\">{{ step.errorMessage() }}</span>\n }\n </div>\n </button>\n @if (!last) {\n <div\n class=\"fui-stepper__connector\"\n [class.fui-stepper__connector--completed]=\"step.state() === 'completed'\"\n ></div>\n }\n }\n</div>\n\n<!-- Step Content -->\n<div class=\"fui-stepper__content\">\n @for (step of steps(); track $index) {\n <div\n class=\"fui-stepper__step-body\"\n [class.fui-stepper__step-body--active]=\"_selectedIndex() === $index\"\n role=\"tabpanel\"\n [attr.id]=\"'fui-step-content-' + _uniqueId + '-' + $index\"\n [attr.aria-labelledby]=\"'fui-step-header-' + _uniqueId + '-' + $index\"\n [attr.aria-expanded]=\"_selectedIndex() === $index\"\n >\n @if (_selectedIndex() === $index || step._interacted()) {\n <ng-container [ngTemplateOutlet]=\"step.contentTemplate()!\"></ng-container>\n }\n </div>\n }\n</div>\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}.fui-stepper{display:block;font-family:var(--fui-font-family-sans)}.fui-stepper__header{display:flex;align-items:center}.fui-stepper__step-header{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-stepper__step-header{display:flex;align-items:center;gap:var(--fui-gap-8);padding:var(--fui-padding-8) var(--fui-padding-12);border-radius:var(--fui-border-radius-md);transition:background-color,color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms;cursor:pointer;flex-shrink:0}.fui-stepper__step-header:hover:not(:disabled){background-color:var(--fui-surface-02)}.fui-stepper__step-header:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-stepper__step-header--active .fui-stepper__step-icon{background-color:var(--fui-primary);color:var(--fui-primary-contrast);border-color:var(--fui-primary)}.fui-stepper__step-header--active .fui-stepper__step-label{color:var(--fui-text-primary);font-weight:var(--fui-font-weight-semibold)}.fui-stepper__step-header--completed .fui-stepper__step-icon{background-color:var(--fui-success);color:var(--fui-white);border-color:var(--fui-success)}.fui-stepper__step-header--completed .fui-stepper__step-label{color:var(--fui-text-primary)}.fui-stepper__step-header--error .fui-stepper__step-icon{background-color:var(--fui-danger);color:var(--fui-white);border-color:var(--fui-danger)}.fui-stepper__step-header--error .fui-stepper__step-label{color:var(--fui-danger)}.fui-stepper__step-header--disabled{cursor:not-allowed;opacity:.5}.fui-stepper__step-header--disabled .fui-stepper__step-icon{background-color:var(--fui-surface-03);color:var(--fui-text-disabled);border-color:var(--fui-border-color)}.fui-stepper__step-header--disabled .fui-stepper__step-label{color:var(--fui-text-disabled)}.fui-stepper__step-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;border:2px solid var(--fui-border-color);background-color:var(--fui-surface-01);color:var(--fui-text-secondary);flex-shrink:0;transition:background-color,color,border-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-stepper__step-number{font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-semibold);line-height:1}.fui-stepper__step-text{display:flex;flex-direction:column;gap:2px;text-align:left}.fui-stepper__step-label{font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-medium);color:var(--fui-text-secondary);transition:color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-stepper__step-description{font-size:var(--fui-font-size-01);color:var(--fui-text-secondary)}.fui-stepper__step-error{font-size:var(--fui-font-size-01);color:var(--fui-danger)}.fui-stepper__connector{flex:1;height:2px;min-width:24px;background-color:var(--fui-border-color);transition:background-color var(--fui-duration-moderate-01) var(--fui-ease-standard) 0ms}.fui-stepper__connector--completed{background-color:var(--fui-success)}.fui-stepper__content{margin-top:var(--fui-spacing-05)}.fui-stepper__step-body{display:none}.fui-stepper__step-body--active{display:block;animation:fui-stepper-fade-in var(--fui-duration-fast-02) var(--fui-ease-entrance)}.fui-stepper--horizontal .fui-stepper__header{flex-direction:row}.fui-stepper--vertical .fui-stepper__header{flex-direction:column;align-items:stretch}.fui-stepper--vertical .fui-stepper__connector{width:2px;height:24px;min-width:unset;flex:unset;margin-left:23px}.fui-stepper--vertical .fui-stepper__step-header{width:100%}@keyframes fui-stepper-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
310
+ }], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], animationDone: [{ type: i0.Output, args: ["animationDone"] }], steps: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => FuiStepComponent), { isSignal: true }] }] } });
311
+
312
+ /**
313
+ * Generated bundle index. Do not edit.
314
+ */
315
+
316
+ export { FORMAUI_STEPPER, FuiStepComponent, FuiStepperComponent };
317
+ //# sourceMappingURL=raintonic-formaui-components-stepper.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raintonic-formaui-components-stepper.mjs","sources":["../../../lib/components/stepper/step.component.ts","../../../lib/components/stepper/stepper.types.ts","../../../lib/components/stepper/stepper.component.ts","../../../lib/components/stepper/stepper.component.html","../../../lib/components/stepper/raintonic-formaui-components-stepper.ts"],"sourcesContent":["import {\n Component,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n input,\n signal,\n computed,\n viewChild,\n TemplateRef,\n booleanAttribute,\n WritableSignal,\n Signal,\n} from '@angular/core';\nimport { AbstractControl } from '@angular/forms';\nimport { StepState } from './stepper.types';\n\n/**\n * # FuiStepComponent\n *\n * Represents a single step within an `fui-stepper`.\n * Each step has a label, optional description/icon, and content that is lazily rendered.\n *\n * ## Usage\n *\n * ```html\n * <fui-stepper>\n * <fui-step label=\"Account\" description=\"Create your account\">\n * <p>Account creation form here...</p>\n * </fui-step>\n * <fui-step label=\"Profile\" icon=\"user\">\n * <p>Profile setup here...</p>\n * </fui-step>\n * </fui-stepper>\n * ```\n */\n@Component({\n selector: 'fui-step',\n standalone: true,\n template: '<ng-template><ng-content></ng-content></ng-template>',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class FuiStepComponent {\n // Inputs\n readonly label = input.required<string>();\n readonly description = input('');\n readonly icon = input('');\n readonly completed = input<boolean, unknown>(false, { transform: booleanAttribute });\n readonly editable = input<boolean, unknown>(true, { transform: booleanAttribute });\n readonly errorMessage = input('');\n readonly stepControl = input<AbstractControl | null>(null);\n readonly optional = input<boolean, unknown>(false, { transform: booleanAttribute });\n\n // Internal state\n readonly _interacted: WritableSignal<boolean> = signal(false);\n\n // Content template reference for lazy rendering\n readonly contentTemplate: Signal<TemplateRef<unknown> | undefined> = viewChild(TemplateRef);\n\n // Computed state\n readonly hasError: Signal<boolean> = computed(() => {\n const errMsg = this.errorMessage();\n if (errMsg && errMsg.length > 0) {\n return true;\n }\n const control = this.stepControl();\n return !!(control && control.invalid && this._interacted());\n });\n\n readonly state: Signal<StepState> = computed(() => {\n if (this.hasError()) {\n return 'error';\n }\n if (this.completed()) {\n return 'completed';\n }\n return 'default';\n });\n\n /** Mark this step as interacted and select it via the parent stepper. */\n select(): void {\n this._interacted.set(true);\n }\n\n /** Reset the step to its initial state. */\n reset(): void {\n this._interacted.set(false);\n const control = this.stepControl();\n if (control) {\n control.reset();\n }\n }\n}\n","import { InjectionToken } from '@angular/core';\n\nexport type StepperOrientation = 'horizontal' | 'vertical';\nexport type StepState = 'active' | 'completed' | 'error' | 'disabled' | 'default';\n\nexport interface StepSelectionChange {\n selectedIndex: number;\n previousIndex: number;\n selectedStep: unknown;\n previousStep: unknown;\n}\n\nexport const FORMAUI_STEPPER = new InjectionToken<unknown>('FORMAUI_STEPPER');\n","import {\n Component,\n ChangeDetectionStrategy,\n ViewEncapsulation,\n input,\n output,\n signal,\n computed,\n effect,\n contentChildren,\n booleanAttribute,\n WritableSignal,\n} from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { FuiIconComponent } from '@raintonic/formaui/components/icon';\nimport { FuiStepComponent } from './step.component';\nimport { StepperOrientation, StepSelectionChange, FORMAUI_STEPPER } from './stepper.types';\n\nlet nextStepperId = 0;\n\n/**\n * # FuiStepperComponent\n *\n * A stepper component that guides users through a sequence of steps.\n * Supports horizontal and vertical orientations, linear and non-linear navigation,\n * step validation, and lazy content rendering.\n *\n * ## Usage\n *\n * ### Basic horizontal stepper\n * ```html\n * <fui-stepper>\n * <fui-step label=\"Step 1\">Content 1</fui-step>\n * <fui-step label=\"Step 2\">Content 2</fui-step>\n * <fui-step label=\"Step 3\">Content 3</fui-step>\n * </fui-stepper>\n * ```\n *\n * ### Vertical stepper\n * ```html\n * <fui-stepper orientation=\"vertical\">\n * <fui-step label=\"Step 1\">Content 1</fui-step>\n * <fui-step label=\"Step 2\">Content 2</fui-step>\n * </fui-stepper>\n * ```\n *\n * ### Linear stepper with form validation\n * ```html\n * <fui-stepper [linear]=\"true\">\n * <fui-step label=\"Account\" [stepControl]=\"accountForm\">\n * <form [formGroup]=\"accountForm\">...</form>\n * </fui-step>\n * <fui-step label=\"Address\" [stepControl]=\"addressForm\">\n * <form [formGroup]=\"addressForm\">...</form>\n * </fui-step>\n * </fui-stepper>\n * ```\n */\n@Component({\n selector: 'fui-stepper',\n standalone: true,\n imports: [NgTemplateOutlet, FuiIconComponent],\n templateUrl: './stepper.component.html',\n styleUrls: ['./stepper.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'fui-stepper',\n '[class.fui-stepper--horizontal]': 'orientation() === \"horizontal\"',\n '[class.fui-stepper--vertical]': 'orientation() === \"vertical\"',\n '[class.fui-stepper--linear]': 'linear()',\n },\n providers: [{ provide: FORMAUI_STEPPER, useExisting: FuiStepperComponent }],\n})\nexport class FuiStepperComponent {\n // Inputs\n readonly orientation = input<StepperOrientation>('horizontal');\n readonly linear = input<boolean, unknown>(false, { transform: booleanAttribute });\n readonly selectedIndex = input(0);\n\n // Outputs\n readonly selectionChange = output<StepSelectionChange>();\n readonly animationDone = output();\n\n // Content children\n readonly steps = contentChildren(FuiStepComponent);\n\n // Internal state\n readonly _selectedIndex: WritableSignal<number> = signal(0);\n\n // Unique ID for ARIA linking\n readonly _uniqueId = nextStepperId++;\n\n // Computed: active step\n readonly _selectedStep = computed(() => {\n const allSteps = this.steps();\n const idx = this._selectedIndex();\n return allSteps[idx] ?? null;\n });\n\n constructor() {\n // Sync selectedIndex input with internal state\n effect(() => {\n const idx = this.selectedIndex();\n this._selectedIndex.set(idx);\n });\n }\n\n /** Navigate to the next step. In linear mode, validates the current step first. */\n next(): void {\n const allSteps = this.steps();\n const currentIdx = this._selectedIndex();\n\n if (currentIdx >= allSteps.length - 1) {\n return;\n }\n\n if (this.linear()) {\n const currentStep = allSteps[currentIdx];\n if (currentStep) {\n currentStep._interacted.set(true);\n const control = currentStep.stepControl();\n if (control?.invalid) {\n return;\n }\n }\n }\n\n this._setSelectedIndex(currentIdx + 1);\n }\n\n /** Navigate to the previous step. */\n previous(): void {\n const currentIdx = this._selectedIndex();\n if (currentIdx > 0) {\n this._setSelectedIndex(currentIdx - 1);\n }\n }\n\n /** Navigate directly to a specific step by index. */\n goToStep(index: number): void {\n const allSteps = this.steps();\n if (index < 0 || index >= allSteps.length) {\n return;\n }\n\n if (this.linear() && index > this._selectedIndex()) {\n // In linear mode, can only go forward if all preceding steps are valid\n for (let i = this._selectedIndex(); i < index; i++) {\n const step = allSteps[i];\n if (step) {\n step._interacted.set(true);\n const control = step.stepControl();\n if (control?.invalid) {\n return;\n }\n }\n }\n }\n\n this._setSelectedIndex(index);\n }\n\n /** Reset all steps and return to the first step. */\n reset(): void {\n this._selectedIndex.set(0);\n this.steps().forEach((step) => {\n step.reset();\n });\n }\n\n /** @internal Handle step header click */\n _onStepHeaderClick(index: number): void {\n this.goToStep(index);\n }\n\n /** @internal Check if a step is disabled for navigation */\n _isStepDisabled(index: number): boolean {\n if (!this.linear()) {\n return false;\n }\n\n const allSteps = this.steps();\n\n // Can always go backward\n if (index < this._selectedIndex()) {\n // Check if the target step is editable\n const targetStep = allSteps[index];\n return targetStep ? !targetStep.editable() : false;\n }\n\n // For forward navigation in linear mode, check all preceding steps\n if (index > this._selectedIndex()) {\n for (let i = this._selectedIndex(); i < index; i++) {\n const step = allSteps[i];\n if (step) {\n const control = step.stepControl();\n if (control?.invalid) {\n return true;\n }\n }\n }\n }\n\n return false;\n }\n\n /** @internal Handle keyboard navigation on step headers */\n _onHeaderKeydown(event: KeyboardEvent, currentIndex: number): void {\n const isHorizontal = this.orientation() === 'horizontal';\n const allSteps = this.steps();\n let newIndex = currentIndex;\n\n switch (event.key) {\n case 'ArrowRight':\n if (isHorizontal && currentIndex < allSteps.length - 1) {\n newIndex = currentIndex + 1;\n }\n break;\n case 'ArrowLeft':\n if (isHorizontal && currentIndex > 0) {\n newIndex = currentIndex - 1;\n }\n break;\n case 'ArrowDown':\n if (!isHorizontal && currentIndex < allSteps.length - 1) {\n newIndex = currentIndex + 1;\n event.preventDefault();\n }\n break;\n case 'ArrowUp':\n if (!isHorizontal && currentIndex > 0) {\n newIndex = currentIndex - 1;\n event.preventDefault();\n }\n break;\n case 'Home':\n newIndex = 0;\n event.preventDefault();\n break;\n case 'End':\n newIndex = allSteps.length - 1;\n event.preventDefault();\n break;\n default:\n return;\n }\n\n if (newIndex !== currentIndex) {\n this._onStepHeaderClick(newIndex);\n }\n }\n\n /** @internal Set the selected index and emit selection change */\n private _setSelectedIndex(newIndex: number): void {\n const allSteps = this.steps();\n const previousIndex = this._selectedIndex();\n\n if (newIndex === previousIndex) {\n return;\n }\n\n const previousStep = allSteps[previousIndex] ?? null;\n const selectedStep = allSteps[newIndex] ?? null;\n\n // Mark the new step as interacted\n if (selectedStep) {\n selectedStep._interacted.set(true);\n }\n\n this._selectedIndex.set(newIndex);\n\n this.selectionChange.emit({\n selectedIndex: newIndex,\n previousIndex,\n selectedStep,\n previousStep,\n });\n }\n}\n","<!-- Step Headers -->\n<div class=\"fui-stepper__header\" role=\"tablist\" [attr.aria-orientation]=\"orientation()\">\n @for (step of steps(); track $index; let last = $last) {\n <button\n class=\"fui-stepper__step-header\"\n [class.fui-stepper__step-header--active]=\"_selectedIndex() === $index\"\n [class.fui-stepper__step-header--completed]=\"step.state() === 'completed'\"\n [class.fui-stepper__step-header--error]=\"step.hasError()\"\n [class.fui-stepper__step-header--disabled]=\"_isStepDisabled($index)\"\n [attr.aria-selected]=\"_selectedIndex() === $index\"\n [attr.aria-disabled]=\"_isStepDisabled($index)\"\n [attr.aria-current]=\"_selectedIndex() === $index ? 'step' : null\"\n [attr.aria-label]=\"\n step.label() + (step.state() === 'completed' ? ' (completed)' : step.hasError() ? ' (error)' : '')\n \"\n [disabled]=\"_isStepDisabled($index)\"\n role=\"tab\"\n [attr.id]=\"'fui-step-header-' + _uniqueId + '-' + $index\"\n [attr.aria-controls]=\"'fui-step-content-' + _uniqueId + '-' + $index\"\n (click)=\"_onStepHeaderClick($index)\"\n (keydown)=\"_onHeaderKeydown($event, $index)\"\n type=\"button\"\n >\n <div class=\"fui-stepper__step-icon\">\n @if (step.state() === 'completed' && !step.icon()) {\n <fui-icon name=\"check\" size=\"sm\"></fui-icon>\n } @else if (step.hasError()) {\n <fui-icon name=\"warning\" size=\"sm\"></fui-icon>\n } @else if (step.icon()) {\n <fui-icon [name]=\"step.icon()\" size=\"sm\"></fui-icon>\n } @else {\n <span class=\"fui-stepper__step-number\">{{ $index + 1 }}</span>\n }\n </div>\n <div class=\"fui-stepper__step-text\">\n <span class=\"fui-stepper__step-label\">{{ step.label() }}</span>\n @if (step.description()) {\n <span class=\"fui-stepper__step-description\">{{ step.description() }}</span>\n }\n @if (step.hasError()) {\n <span class=\"fui-stepper__step-error\">{{ step.errorMessage() }}</span>\n }\n </div>\n </button>\n @if (!last) {\n <div\n class=\"fui-stepper__connector\"\n [class.fui-stepper__connector--completed]=\"step.state() === 'completed'\"\n ></div>\n }\n }\n</div>\n\n<!-- Step Content -->\n<div class=\"fui-stepper__content\">\n @for (step of steps(); track $index) {\n <div\n class=\"fui-stepper__step-body\"\n [class.fui-stepper__step-body--active]=\"_selectedIndex() === $index\"\n role=\"tabpanel\"\n [attr.id]=\"'fui-step-content-' + _uniqueId + '-' + $index\"\n [attr.aria-labelledby]=\"'fui-step-header-' + _uniqueId + '-' + $index\"\n [attr.aria-expanded]=\"_selectedIndex() === $index\"\n >\n @if (_selectedIndex() === $index || step._interacted()) {\n <ng-container [ngTemplateOutlet]=\"step.contentTemplate()!\"></ng-container>\n }\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, TemplateRef, ViewChild, Component, model, output, signal, effect, ViewChildren, ContentChildren, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+
6
+ /**
7
+ * @component FuiTabComponent
8
+ * @selector fui-tab
9
+ * @description Individual tab definition used inside `fui-tab-group`. Provides a label
10
+ * for the tab header and wraps its content in an `ng-template` for lazy rendering.
11
+ *
12
+ * @input label - (required) Text displayed in the tab header button
13
+ * @input disabled - Whether the tab is disabled and cannot be selected (default: false)
14
+ *
15
+ * @example
16
+ * <fui-tab label="Settings" [disabled]="!canEdit">
17
+ * <p>Settings panel content here.</p>
18
+ * </fui-tab>
19
+ */
20
+ class FuiTabComponent {
21
+ /**
22
+ * The label for the tab
23
+ */
24
+ label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
25
+ /**
26
+ * Whether the tab is disabled
27
+ */
28
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
29
+ /**
30
+ * Template reference for the tab content
31
+ */
32
+ content;
33
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
34
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.6", type: FuiTabComponent, isStandalone: true, selector: "fui-tab", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "content", first: true, predicate: TemplateRef, descendants: true, static: true }], ngImport: i0, template: `
35
+ <ng-template>
36
+ <ng-content></ng-content>
37
+ </ng-template>
38
+ `, isInline: true });
39
+ }
40
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTabComponent, decorators: [{
41
+ type: Component,
42
+ args: [{
43
+ selector: 'fui-tab',
44
+ standalone: true,
45
+ template: `
46
+ <ng-template>
47
+ <ng-content></ng-content>
48
+ </ng-template>
49
+ `,
50
+ }]
51
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], content: [{
52
+ type: ViewChild,
53
+ args: [TemplateRef, { static: true }]
54
+ }] } });
55
+
56
+ let nextTabGroupId = 0;
57
+ /**
58
+ * @component FuiTabGroupComponent
59
+ * @selector fui-tab-group
60
+ * @description Container for `fui-tab` components. Manages tab selection, keyboard navigation
61
+ * (ArrowLeft/Right, Home, End, Enter, Space), and roving tabindex for accessibility.
62
+ * Uses content projection to collect tab definitions and renders their content lazily.
63
+ *
64
+ * @input selectedIndex - Two-way model signal for the active tab index (default: 0)
65
+ *
66
+ * @output selectedTabChange - Emits the FuiTabComponent instance when a new tab is selected
67
+ *
68
+ * @cssvar --fui-tab-label-font-size - Font size of tab labels
69
+ * @cssvar --fui-tab-label-min-height - Minimum height of tab header buttons
70
+ * @cssvar --fui-tab-label-padding-x - Horizontal padding of tab labels
71
+ * @cssvar --fui-tab-label-padding-y - Vertical padding of tab labels
72
+ * @cssvar --fui-tab-panel-padding - Padding of the tab content panel
73
+ * @cssvar --fui-tab-indicator-height - Height of the active-tab underline indicator
74
+ * @cssvar --fui-tab-active-color - Color of the active tab label and indicator
75
+ *
76
+ * @example
77
+ * <fui-tab-group [(selectedIndex)]="activeTab">
78
+ * <fui-tab label="Overview">Overview content</fui-tab>
79
+ * <fui-tab label="Details">Details content</fui-tab>
80
+ * <fui-tab label="History" [disabled]="true">History content</fui-tab>
81
+ * </fui-tab-group>
82
+ */
83
+ class FuiTabGroupComponent {
84
+ /** Unique ID prefix for this tab group instance to avoid collisions */
85
+ tabGroupId = `fui-tab-group-${nextTabGroupId++}`;
86
+ /**
87
+ * The index of the selected tab
88
+ */
89
+ selectedIndex = model(0, ...(ngDevMode ? [{ debugName: "selectedIndex" }] : /* istanbul ignore next */ []));
90
+ /**
91
+ * Event emitted when a tab is selected
92
+ */
93
+ selectedTabChange = output();
94
+ /**
95
+ * Query for all tab components
96
+ */
97
+ tabComponents;
98
+ /**
99
+ * Query for tab header buttons for focus management
100
+ */
101
+ tabButtons;
102
+ /**
103
+ * Signal containing all tabs
104
+ */
105
+ tabs = signal([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
106
+ constructor() {
107
+ // React to external or internal selectedIndex changes and emit selectedTabChange accordingly
108
+ effect(() => {
109
+ const idx = this.selectedIndex();
110
+ const tabs = this.tabs();
111
+ if (!tabs?.length)
112
+ return;
113
+ if (idx >= 0 && idx < tabs.length) {
114
+ const tab = tabs[idx];
115
+ if (!tab.disabled()) {
116
+ this.selectedTabChange.emit(tab);
117
+ }
118
+ }
119
+ });
120
+ }
121
+ ngAfterContentInit() {
122
+ this.tabs.set(this.tabComponents.toArray());
123
+ // Listen for changes in tab components
124
+ this.tabComponents.changes.subscribe(() => {
125
+ this.tabs.set(this.tabComponents.toArray());
126
+ });
127
+ }
128
+ /**
129
+ * Select a tab by index
130
+ */
131
+ selectTab(index) {
132
+ const tabs = this.tabs();
133
+ if (index >= 0 && index < tabs.length && !tabs[index].disabled()) {
134
+ // Update the model; the effect defined in ngAfterContentInit will emit selectedTabChange
135
+ this.selectedIndex.set(index);
136
+ }
137
+ }
138
+ onKeydown(event, index) {
139
+ const tabs = this.tabs();
140
+ if (!tabs?.length)
141
+ return;
142
+ const last = tabs.length - 1;
143
+ let next = index;
144
+ switch (event.key) {
145
+ case 'ArrowRight':
146
+ next = index === last ? 0 : index + 1;
147
+ break;
148
+ case 'ArrowLeft':
149
+ next = index === 0 ? last : index - 1;
150
+ break;
151
+ case 'Home':
152
+ next = 0;
153
+ break;
154
+ case 'End':
155
+ next = last;
156
+ break;
157
+ case 'Enter':
158
+ case ' ': // Space key
159
+ this.selectTab(index);
160
+ return;
161
+ default:
162
+ return;
163
+ }
164
+ event.preventDefault();
165
+ while (tabs[next]?.disabled()) {
166
+ next = (next + 1) % tabs.length;
167
+ if (next === index)
168
+ break;
169
+ }
170
+ // Focus the target tab button (roving tabindex)
171
+ const buttons = this.tabButtons?.toArray();
172
+ if (buttons?.[next]) {
173
+ buttons[next].nativeElement.focus();
174
+ }
175
+ }
176
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTabGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
177
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTabGroupComponent, isStandalone: true, selector: "fui-tab-group", inputs: { selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange", selectedTabChange: "selectedTabChange" }, host: { classAttribute: "fui-tab-group" }, queries: [{ propertyName: "tabComponents", predicate: FuiTabComponent }], viewQueries: [{ propertyName: "tabButtons", predicate: ["tabButton"], descendants: true }], ngImport: i0, template: "<div class=\"fui-tab-group\">\n <div class=\"fui-tab-header\" role=\"tablist\">\n @for (tab of tabs(); track $index) {\n <button\n #tabButton\n class=\"fui-tab-label\"\n [class.fui-tab-label--active]=\"selectedIndex() === $index\"\n [class.fui-tab-label--disabled]=\"tab.disabled()\"\n [disabled]=\"tab.disabled()\"\n [attr.aria-selected]=\"selectedIndex() === $index\"\n [attr.tabindex]=\"selectedIndex() === $index ? 0 : -1\"\n [attr.aria-controls]=\"tabGroupId + '-panel-' + $index\"\n [id]=\"tabGroupId + '-tab-' + $index\"\n role=\"tab\"\n type=\"button\"\n (click)=\"selectTab($index)\"\n (keydown)=\"onKeydown($event, $index)\"\n >\n {{ tab.label() }}\n </button>\n }\n </div>\n\n <div class=\"fui-tab-content\">\n @for (tab of tabs(); track $index) {\n <div\n class=\"fui-tab-panel\"\n [class.fui-tab-panel--active]=\"selectedIndex() === $index\"\n [attr.aria-labelledby]=\"tabGroupId + '-tab-' + $index\"\n [id]=\"tabGroupId + '-panel-' + $index\"\n role=\"tabpanel\"\n [attr.tabindex]=\"selectedIndex() === $index ? 0 : null\"\n [attr.aria-hidden]=\"selectedIndex() !== $index\"\n >\n @if (selectedIndex() === $index) {\n <ng-container [ngTemplateOutlet]=\"tab.content\"></ng-container>\n }\n </div>\n }\n </div>\n</div>\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}.fui-tab-group{--fui-tab-label-font-size: var(--fui-font-size-02);--fui-tab-label-min-height: 3rem;--fui-tab-label-padding-x: 1.5rem;--fui-tab-label-padding-y: .75rem;--fui-tab-panel-padding: 1.5rem;--fui-tab-indicator-height: 2px;--fui-tab-active-color: var(--fui-primary);contain:layout style;display:flex;flex-direction:column;width:100%}.fui-tab-header{display:flex;border-bottom:1px solid var(--fui-border-color);background-color:color-mix(in srgb,var(--fui-surface-02) 80%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);position:sticky;top:0;left:0;z-index:var(--fui-z-sticky, 1020)}.fui-tab-header:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:1px;background-color:var(--fui-border-color)}.fui-tab-label{display:flex;align-items:center;justify-content:center;padding:var(--fui-tab-label-padding-y) var(--fui-tab-label-padding-x);border:none;background:transparent;color:var(--fui-text-secondary);font-size:var(--fui-tab-label-font-size);font-weight:400;line-height:1.25rem;cursor:pointer;position:relative;transition:color,background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms;white-space:nowrap;min-height:var(--fui-tab-label-min-height)}.fui-tab-label:hover:not(.fui-tab-label--disabled){color:var(--fui-text-primary);background-color:var(--fui-surface-02)}.fui-tab-label--active{color:var(--fui-tab-active-color);font-weight:600}.fui-tab-label--active:after{content:\"\";position:absolute;bottom:-1px;left:0;right:0;height:var(--fui-tab-indicator-height);background-color:var(--fui-tab-active-color);z-index:1}.fui-tab-label--disabled{color:var(--fui-text-secondary);cursor:not-allowed;opacity:var(--fui-opacity-disabled)}.fui-tab-label--disabled:hover{background-color:transparent}.fui-tab-label:focus-visible{outline:2px solid var(--fui-primary);outline-offset:-2px}.fui-tab-content{flex:1;z-index:9;position:relative;overflow:hidden}.fui-tab-panel{display:none;padding:var(--fui-tab-panel-padding);background:transparent;animation:fadeIn var(--fui-duration-fast-02, .11s) var(--fui-ease-entrance, cubic-bezier(0, 0, .38, .9))}.fui-tab-panel--active{display:block}@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@media(max-width:576px){.fui-tab-header{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.fui-tab-header::-webkit-scrollbar{display:none}.fui-tab-label{flex-shrink:0;padding:.5rem 1rem;min-height:2.5rem;font-size:.8125rem}.fui-tab-panel{padding:1rem}}@media(prefers-reduced-motion:reduce){.fui-tab-label{transition:none}.fui-tab-panel{animation:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
178
+ }
179
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTabGroupComponent, decorators: [{
180
+ type: Component,
181
+ args: [{ selector: 'fui-tab-group', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
182
+ class: 'fui-tab-group',
183
+ }, template: "<div class=\"fui-tab-group\">\n <div class=\"fui-tab-header\" role=\"tablist\">\n @for (tab of tabs(); track $index) {\n <button\n #tabButton\n class=\"fui-tab-label\"\n [class.fui-tab-label--active]=\"selectedIndex() === $index\"\n [class.fui-tab-label--disabled]=\"tab.disabled()\"\n [disabled]=\"tab.disabled()\"\n [attr.aria-selected]=\"selectedIndex() === $index\"\n [attr.tabindex]=\"selectedIndex() === $index ? 0 : -1\"\n [attr.aria-controls]=\"tabGroupId + '-panel-' + $index\"\n [id]=\"tabGroupId + '-tab-' + $index\"\n role=\"tab\"\n type=\"button\"\n (click)=\"selectTab($index)\"\n (keydown)=\"onKeydown($event, $index)\"\n >\n {{ tab.label() }}\n </button>\n }\n </div>\n\n <div class=\"fui-tab-content\">\n @for (tab of tabs(); track $index) {\n <div\n class=\"fui-tab-panel\"\n [class.fui-tab-panel--active]=\"selectedIndex() === $index\"\n [attr.aria-labelledby]=\"tabGroupId + '-tab-' + $index\"\n [id]=\"tabGroupId + '-panel-' + $index\"\n role=\"tabpanel\"\n [attr.tabindex]=\"selectedIndex() === $index ? 0 : null\"\n [attr.aria-hidden]=\"selectedIndex() !== $index\"\n >\n @if (selectedIndex() === $index) {\n <ng-container [ngTemplateOutlet]=\"tab.content\"></ng-container>\n }\n </div>\n }\n </div>\n</div>\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}.fui-tab-group{--fui-tab-label-font-size: var(--fui-font-size-02);--fui-tab-label-min-height: 3rem;--fui-tab-label-padding-x: 1.5rem;--fui-tab-label-padding-y: .75rem;--fui-tab-panel-padding: 1.5rem;--fui-tab-indicator-height: 2px;--fui-tab-active-color: var(--fui-primary);contain:layout style;display:flex;flex-direction:column;width:100%}.fui-tab-header{display:flex;border-bottom:1px solid var(--fui-border-color);background-color:color-mix(in srgb,var(--fui-surface-02) 80%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);position:sticky;top:0;left:0;z-index:var(--fui-z-sticky, 1020)}.fui-tab-header:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:1px;background-color:var(--fui-border-color)}.fui-tab-label{display:flex;align-items:center;justify-content:center;padding:var(--fui-tab-label-padding-y) var(--fui-tab-label-padding-x);border:none;background:transparent;color:var(--fui-text-secondary);font-size:var(--fui-tab-label-font-size);font-weight:400;line-height:1.25rem;cursor:pointer;position:relative;transition:color,background-color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms;white-space:nowrap;min-height:var(--fui-tab-label-min-height)}.fui-tab-label:hover:not(.fui-tab-label--disabled){color:var(--fui-text-primary);background-color:var(--fui-surface-02)}.fui-tab-label--active{color:var(--fui-tab-active-color);font-weight:600}.fui-tab-label--active:after{content:\"\";position:absolute;bottom:-1px;left:0;right:0;height:var(--fui-tab-indicator-height);background-color:var(--fui-tab-active-color);z-index:1}.fui-tab-label--disabled{color:var(--fui-text-secondary);cursor:not-allowed;opacity:var(--fui-opacity-disabled)}.fui-tab-label--disabled:hover{background-color:transparent}.fui-tab-label:focus-visible{outline:2px solid var(--fui-primary);outline-offset:-2px}.fui-tab-content{flex:1;z-index:9;position:relative;overflow:hidden}.fui-tab-panel{display:none;padding:var(--fui-tab-panel-padding);background:transparent;animation:fadeIn var(--fui-duration-fast-02, .11s) var(--fui-ease-entrance, cubic-bezier(0, 0, .38, .9))}.fui-tab-panel--active{display:block}@keyframes fadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@media(max-width:576px){.fui-tab-header{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.fui-tab-header::-webkit-scrollbar{display:none}.fui-tab-label{flex-shrink:0;padding:.5rem 1rem;min-height:2.5rem;font-size:.8125rem}.fui-tab-panel{padding:1rem}}@media(prefers-reduced-motion:reduce){.fui-tab-label{transition:none}.fui-tab-panel{animation:none}}\n"] }]
184
+ }], ctorParameters: () => [], propDecorators: { selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }, { type: i0.Output, args: ["selectedIndexChange"] }], selectedTabChange: [{ type: i0.Output, args: ["selectedTabChange"] }], tabComponents: [{
185
+ type: ContentChildren,
186
+ args: [FuiTabComponent]
187
+ }], tabButtons: [{
188
+ type: ViewChildren,
189
+ args: ['tabButton']
190
+ }] } });
191
+
192
+ /**
193
+ * Generated bundle index. Do not edit.
194
+ */
195
+
196
+ export { FuiTabComponent, FuiTabGroupComponent };
197
+ //# sourceMappingURL=raintonic-formaui-components-tab.mjs.map