@frame-kit/ui-ng 0.0.1

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 (220) hide show
  1. package/COMPONENTS.md +683 -0
  2. package/DEVELOPMENT_GUIDE.md +1102 -0
  3. package/LICENSE +21 -0
  4. package/README.md +69 -0
  5. package/THEMING.md +130 -0
  6. package/core/headline/README.md +121 -0
  7. package/core/icon/README.md +173 -0
  8. package/core/image/README.md +210 -0
  9. package/core/link/README.md +297 -0
  10. package/core/separator/README.md +145 -0
  11. package/core/text/README.md +240 -0
  12. package/directives/infinite-scroll/README.md +102 -0
  13. package/directives/spotlight/README.md +154 -0
  14. package/directives/tooltip/README.md +147 -0
  15. package/docs/endpoint-link/README.md +142 -0
  16. package/docs/method-badge/README.md +154 -0
  17. package/fesm2022/frame-kit-ui-ng-core-headline.mjs +122 -0
  18. package/fesm2022/frame-kit-ui-ng-core-headline.mjs.map +1 -0
  19. package/fesm2022/frame-kit-ui-ng-core-icon.mjs +189 -0
  20. package/fesm2022/frame-kit-ui-ng-core-icon.mjs.map +1 -0
  21. package/fesm2022/frame-kit-ui-ng-core-image.mjs +123 -0
  22. package/fesm2022/frame-kit-ui-ng-core-image.mjs.map +1 -0
  23. package/fesm2022/frame-kit-ui-ng-core-link.mjs +369 -0
  24. package/fesm2022/frame-kit-ui-ng-core-link.mjs.map +1 -0
  25. package/fesm2022/frame-kit-ui-ng-core-separator.mjs +59 -0
  26. package/fesm2022/frame-kit-ui-ng-core-separator.mjs.map +1 -0
  27. package/fesm2022/frame-kit-ui-ng-core-text.mjs +204 -0
  28. package/fesm2022/frame-kit-ui-ng-core-text.mjs.map +1 -0
  29. package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs +74 -0
  30. package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs.map +1 -0
  31. package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs +76 -0
  32. package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs.map +1 -0
  33. package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs +425 -0
  34. package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs.map +1 -0
  35. package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs +63 -0
  36. package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs.map +1 -0
  37. package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs +43 -0
  38. package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs.map +1 -0
  39. package/fesm2022/frame-kit-ui-ng-forms.mjs +3632 -0
  40. package/fesm2022/frame-kit-ui-ng-forms.mjs.map +1 -0
  41. package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs +239 -0
  42. package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs.map +1 -0
  43. package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs +132 -0
  44. package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs.map +1 -0
  45. package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs +133 -0
  46. package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs.map +1 -0
  47. package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs +60 -0
  48. package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs.map +1 -0
  49. package/fesm2022/frame-kit-ui-ng-services-toast.mjs +166 -0
  50. package/fesm2022/frame-kit-ui-ng-services-toast.mjs.map +1 -0
  51. package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs +214 -0
  52. package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs.map +1 -0
  53. package/fesm2022/frame-kit-ui-ng-ui-alert.mjs +82 -0
  54. package/fesm2022/frame-kit-ui-ng-ui-alert.mjs.map +1 -0
  55. package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs +76 -0
  56. package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs.map +1 -0
  57. package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs +81 -0
  58. package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs.map +1 -0
  59. package/fesm2022/frame-kit-ui-ng-ui-badge.mjs +81 -0
  60. package/fesm2022/frame-kit-ui-ng-ui-badge.mjs.map +1 -0
  61. package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs +68 -0
  62. package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs.map +1 -0
  63. package/fesm2022/frame-kit-ui-ng-ui-button.mjs +108 -0
  64. package/fesm2022/frame-kit-ui-ng-ui-button.mjs.map +1 -0
  65. package/fesm2022/frame-kit-ui-ng-ui-callout.mjs +58 -0
  66. package/fesm2022/frame-kit-ui-ng-ui-callout.mjs.map +1 -0
  67. package/fesm2022/frame-kit-ui-ng-ui-card.mjs +70 -0
  68. package/fesm2022/frame-kit-ui-ng-ui-card.mjs.map +1 -0
  69. package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs +113 -0
  70. package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs.map +1 -0
  71. package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs +1288 -0
  72. package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs.map +1 -0
  73. package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs +456 -0
  74. package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs.map +1 -0
  75. package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs +398 -0
  76. package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs.map +1 -0
  77. package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs +398 -0
  78. package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs.map +1 -0
  79. package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs +125 -0
  80. package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs.map +1 -0
  81. package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs +113 -0
  82. package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs.map +1 -0
  83. package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs +111 -0
  84. package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs.map +1 -0
  85. package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs +103 -0
  86. package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs.map +1 -0
  87. package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs +135 -0
  88. package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs.map +1 -0
  89. package/fesm2022/frame-kit-ui-ng-ui-loader.mjs +81 -0
  90. package/fesm2022/frame-kit-ui-ng-ui-loader.mjs.map +1 -0
  91. package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs +79 -0
  92. package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs.map +1 -0
  93. package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs +40 -0
  94. package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs.map +1 -0
  95. package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs +110 -0
  96. package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs.map +1 -0
  97. package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs +91 -0
  98. package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs.map +1 -0
  99. package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs +86 -0
  100. package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs.map +1 -0
  101. package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs +443 -0
  102. package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs.map +1 -0
  103. package/fesm2022/frame-kit-ui-ng-ui-note.mjs +56 -0
  104. package/fesm2022/frame-kit-ui-ng-ui-note.mjs.map +1 -0
  105. package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs +105 -0
  106. package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs.map +1 -0
  107. package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs +110 -0
  108. package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs.map +1 -0
  109. package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs +129 -0
  110. package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs.map +1 -0
  111. package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs +42 -0
  112. package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs.map +1 -0
  113. package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs +894 -0
  114. package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs.map +1 -0
  115. package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs +81 -0
  116. package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs.map +1 -0
  117. package/fesm2022/frame-kit-ui-ng-ui-toast.mjs +179 -0
  118. package/fesm2022/frame-kit-ui-ng-ui-toast.mjs.map +1 -0
  119. package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs +143 -0
  120. package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs.map +1 -0
  121. package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs +191 -0
  122. package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs.map +1 -0
  123. package/fesm2022/frame-kit-ui-ng.mjs +58 -0
  124. package/fesm2022/frame-kit-ui-ng.mjs.map +1 -0
  125. package/layouts/app-shell/README.md +357 -0
  126. package/layouts/content-split/README.md +180 -0
  127. package/package.json +253 -0
  128. package/services/overlay-orchestrator/README.md +184 -0
  129. package/services/spotlight/README.md +61 -0
  130. package/services/toast/README.md +118 -0
  131. package/types/frame-kit-ui-ng-core-headline.d.ts +38 -0
  132. package/types/frame-kit-ui-ng-core-icon.d.ts +74 -0
  133. package/types/frame-kit-ui-ng-core-image.d.ts +93 -0
  134. package/types/frame-kit-ui-ng-core-link.d.ts +251 -0
  135. package/types/frame-kit-ui-ng-core-separator.d.ts +28 -0
  136. package/types/frame-kit-ui-ng-core-text.d.ts +186 -0
  137. package/types/frame-kit-ui-ng-directives-infinite-scroll.d.ts +42 -0
  138. package/types/frame-kit-ui-ng-directives-spotlight.d.ts +51 -0
  139. package/types/frame-kit-ui-ng-directives-tooltip.d.ts +70 -0
  140. package/types/frame-kit-ui-ng-docs-endpoint-link.d.ts +43 -0
  141. package/types/frame-kit-ui-ng-docs-method-badge.d.ts +30 -0
  142. package/types/frame-kit-ui-ng-forms.d.ts +1674 -0
  143. package/types/frame-kit-ui-ng-layouts-app-shell.d.ts +75 -0
  144. package/types/frame-kit-ui-ng-layouts-content-split.d.ts +43 -0
  145. package/types/frame-kit-ui-ng-services-overlay-orchestrator.d.ts +96 -0
  146. package/types/frame-kit-ui-ng-services-spotlight.d.ts +32 -0
  147. package/types/frame-kit-ui-ng-services-toast.d.ts +100 -0
  148. package/types/frame-kit-ui-ng-ui-accordion.d.ts +86 -0
  149. package/types/frame-kit-ui-ng-ui-alert.d.ts +34 -0
  150. package/types/frame-kit-ui-ng-ui-avatar-stack.d.ts +38 -0
  151. package/types/frame-kit-ui-ng-ui-avatar.d.ts +36 -0
  152. package/types/frame-kit-ui-ng-ui-badge.d.ts +33 -0
  153. package/types/frame-kit-ui-ng-ui-breadcrumb.d.ts +45 -0
  154. package/types/frame-kit-ui-ng-ui-button.d.ts +48 -0
  155. package/types/frame-kit-ui-ng-ui-callout.d.ts +26 -0
  156. package/types/frame-kit-ui-ng-ui-card.d.ts +30 -0
  157. package/types/frame-kit-ui-ng-ui-copyable-field.d.ts +62 -0
  158. package/types/frame-kit-ui-ng-ui-data-table.d.ts +482 -0
  159. package/types/frame-kit-ui-ng-ui-dialog.d.ts +166 -0
  160. package/types/frame-kit-ui-ng-ui-drawer.d.ts +130 -0
  161. package/types/frame-kit-ui-ng-ui-dropdown-menu.d.ts +77 -0
  162. package/types/frame-kit-ui-ng-ui-editable-field.d.ts +65 -0
  163. package/types/frame-kit-ui-ng-ui-icon-badge.d.ts +45 -0
  164. package/types/frame-kit-ui-ng-ui-icon-list.d.ts +67 -0
  165. package/types/frame-kit-ui-ng-ui-inline-edit.d.ts +44 -0
  166. package/types/frame-kit-ui-ng-ui-list-editor.d.ts +56 -0
  167. package/types/frame-kit-ui-ng-ui-loader.d.ts +32 -0
  168. package/types/frame-kit-ui-ng-ui-menu-item.d.ts +27 -0
  169. package/types/frame-kit-ui-ng-ui-nav-brand.d.ts +25 -0
  170. package/types/frame-kit-ui-ng-ui-nav-group.d.ts +60 -0
  171. package/types/frame-kit-ui-ng-ui-nav-separator.d.ts +33 -0
  172. package/types/frame-kit-ui-ng-ui-node-tree-breadcrumb.d.ts +35 -0
  173. package/types/frame-kit-ui-ng-ui-node-tree.d.ts +135 -0
  174. package/types/frame-kit-ui-ng-ui-note.d.ts +22 -0
  175. package/types/frame-kit-ui-ng-ui-numbered-list.d.ts +52 -0
  176. package/types/frame-kit-ui-ng-ui-pagination.d.ts +49 -0
  177. package/types/frame-kit-ui-ng-ui-progress-bar.d.ts +50 -0
  178. package/types/frame-kit-ui-ng-ui-sidenav-link.d.ts +24 -0
  179. package/types/frame-kit-ui-ng-ui-tabs.d.ts +266 -0
  180. package/types/frame-kit-ui-ng-ui-timeline.d.ts +42 -0
  181. package/types/frame-kit-ui-ng-ui-toast.d.ts +56 -0
  182. package/types/frame-kit-ui-ng-ui-user-menu.d.ts +87 -0
  183. package/types/frame-kit-ui-ng-ui-wizard-dialog.d.ts +116 -0
  184. package/types/frame-kit-ui-ng.d.ts +53 -0
  185. package/ui/accordion/README.md +261 -0
  186. package/ui/alert/README.md +211 -0
  187. package/ui/avatar/README.md +167 -0
  188. package/ui/avatar-stack/README.md +164 -0
  189. package/ui/badge/README.md +162 -0
  190. package/ui/breadcrumb/README.md +240 -0
  191. package/ui/button/README.md +184 -0
  192. package/ui/callout/README.md +159 -0
  193. package/ui/card/README.md +174 -0
  194. package/ui/copyable-field/README.md +235 -0
  195. package/ui/data-table/README.md +408 -0
  196. package/ui/dialog/README.md +222 -0
  197. package/ui/drawer/README.md +274 -0
  198. package/ui/dropdown-menu/README.md +336 -0
  199. package/ui/editable-field/README.md +171 -0
  200. package/ui/icon-badge/README.md +131 -0
  201. package/ui/icon-list/README.md +205 -0
  202. package/ui/inline-edit/README.md +135 -0
  203. package/ui/list-editor/README.md +162 -0
  204. package/ui/loader/README.md +160 -0
  205. package/ui/menu-item/README.md +204 -0
  206. package/ui/nav-brand/README.md +111 -0
  207. package/ui/nav-group/README.md +145 -0
  208. package/ui/nav-separator/README.md +44 -0
  209. package/ui/node-tree/README.md +278 -0
  210. package/ui/node-tree-breadcrumb/README.md +164 -0
  211. package/ui/note/README.md +146 -0
  212. package/ui/numbered-list/README.md +187 -0
  213. package/ui/pagination/README.md +174 -0
  214. package/ui/progress-bar/README.md +223 -0
  215. package/ui/sidenav-link/README.md +214 -0
  216. package/ui/tabs/README.md +204 -0
  217. package/ui/timeline/README.md +285 -0
  218. package/ui/toast/README.md +243 -0
  219. package/ui/user-menu/README.md +260 -0
  220. package/ui/wizard-dialog/README.md +283 -0
@@ -0,0 +1,3632 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, HostBinding, ChangeDetectionStrategy, Component, InjectionToken, signal, output, Directive, contentChild, inject, ChangeDetectorRef, effect, Injectable, TemplateRef, viewChild, forwardRef, DestroyRef, ViewContainerRef, ElementRef } from '@angular/core';
3
+ import * as i1 from '@angular/forms';
4
+ import { Validators, FormControl, FormGroup, FormArray, NgControl, ReactiveFormsModule } from '@angular/forms';
5
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+ import { map, distinctUntilChanged } from 'rxjs';
7
+ import { IconComponent } from '@frame-kit/ui-ng/core/icon';
8
+ import { LinkComponent } from '@frame-kit/ui-ng/core/link';
9
+ import { NgTemplateOutlet } from '@angular/common';
10
+ import { TextComponent } from '@frame-kit/ui-ng/core/text';
11
+
12
+ let nextErrorId = 0;
13
+ class ErrorComponent {
14
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
15
+ /** Stable element ID wired to the field's aria-describedby. */
16
+ id = input(`fk-error-${nextErrorId++}`, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
17
+ classes = computed(() => ['fk-error', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
18
+ get hostClass() {
19
+ return this.classes();
20
+ }
21
+ get hostId() {
22
+ return this.id();
23
+ }
24
+ get hostRole() {
25
+ return 'alert';
26
+ }
27
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
28
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: ErrorComponent, isStandalone: true, selector: "fk-error", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.role": "this.hostRole" } }, ngImport: i0, template: "<ng-content />\n", styles: [":host{display:block;font-family:var( --fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\" );font-size:var( --fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem) );color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
29
+ }
30
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ErrorComponent, decorators: [{
31
+ type: Component,
32
+ args: [{ selector: 'fk-error', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content />\n", styles: [":host{display:block;font-family:var( --fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\" );font-size:var( --fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem) );color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}\n"] }]
33
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], hostClass: [{
34
+ type: HostBinding,
35
+ args: ['class']
36
+ }], hostId: [{
37
+ type: HostBinding,
38
+ args: ['attr.id']
39
+ }], hostRole: [{
40
+ type: HostBinding,
41
+ args: ['attr.role']
42
+ }] } });
43
+
44
+ class FieldGroupComponent {
45
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
46
+ /** Optional group label rendered as a fieldset legend. */
47
+ label = input(null, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
48
+ /** Layout direction for child fields. */
49
+ layout = input('stack', ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
50
+ classes = computed(() => [
51
+ 'fk-field-group',
52
+ this.layout() === 'inline' ? 'fk-field-group--inline' : '',
53
+ this.className(),
54
+ ]
55
+ .filter(Boolean)
56
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
57
+ get hostClass() {
58
+ return this.classes();
59
+ }
60
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
61
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FieldGroupComponent, isStandalone: true, selector: "fk-field-group", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<fieldset class=\"fk-field-group__fieldset\">\n @if (label()) {\n <legend class=\"fk-field-group__legend\">{{ label() }}</legend>\n }\n <div class=\"fk-field-group__content\">\n <ng-content />\n </div>\n</fieldset>\n", styles: [":host{display:block}.fk-field-group__fieldset{margin:0;padding:0;border:none}.fk-field-group__legend{font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));padding:0;margin-bottom:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-field-group__content{display:flex;flex-direction:column;row-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}:host(.fk-field-group--inline) .fk-field-group__content{flex-direction:row;flex-wrap:wrap;column-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}:host(.fk-field-group--inline) .fk-field-group__content>*{flex:1;min-width:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
62
+ }
63
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldGroupComponent, decorators: [{
64
+ type: Component,
65
+ args: [{ selector: 'fk-field-group', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<fieldset class=\"fk-field-group__fieldset\">\n @if (label()) {\n <legend class=\"fk-field-group__legend\">{{ label() }}</legend>\n }\n <div class=\"fk-field-group__content\">\n <ng-content />\n </div>\n</fieldset>\n", styles: [":host{display:block}.fk-field-group__fieldset{margin:0;padding:0;border:none}.fk-field-group__legend{font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));padding:0;margin-bottom:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-field-group__content{display:flex;flex-direction:column;row-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}:host(.fk-field-group--inline) .fk-field-group__content{flex-direction:row;flex-wrap:wrap;column-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}:host(.fk-field-group--inline) .fk-field-group__content>*{flex:1;min-width:0}\n"] }]
66
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], hostClass: [{
67
+ type: HostBinding,
68
+ args: ['class']
69
+ }] } });
70
+
71
+ /**
72
+ * Token that child fk-form-field components can inject to read
73
+ * the form's submitted state.
74
+ */
75
+ const FORM = new InjectionToken('FORM');
76
+ class FormComponent {
77
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
78
+ /** Whether the form has been submitted at least once. */
79
+ submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
80
+ /** Current submission status. */
81
+ status = signal('idle', ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
82
+ /** Derived convenience signals. */
83
+ isSubmitting = computed(() => this.status() === 'submitting', ...(ngDevMode ? [{ debugName: "isSubmitting" }] : /* istanbul ignore next */ []));
84
+ isSuccess = computed(() => this.status() === 'success', ...(ngDevMode ? [{ debugName: "isSuccess" }] : /* istanbul ignore next */ []));
85
+ isError = computed(() => this.status() === 'error', ...(ngDevMode ? [{ debugName: "isError" }] : /* istanbul ignore next */ []));
86
+ isIdle = computed(() => this.status() === 'idle', ...(ngDevMode ? [{ debugName: "isIdle" }] : /* istanbul ignore next */ []));
87
+ classes = computed(() => [
88
+ 'fk-form',
89
+ this.isSubmitting() ? 'fk-form--submitting' : '',
90
+ this.isSuccess() ? 'fk-form--success' : '',
91
+ this.isError() ? 'fk-form--error' : '',
92
+ this.className(),
93
+ ]
94
+ .filter(Boolean)
95
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
96
+ get hostClass() {
97
+ return this.classes();
98
+ }
99
+ /** Emits when the form is submitted. */
100
+ formSubmit = output();
101
+ onSubmit() {
102
+ this.submitted.set(true);
103
+ this.formSubmit.emit();
104
+ }
105
+ /**
106
+ * Sets the form status to 'submitting'.
107
+ * Call this before starting an async submission.
108
+ */
109
+ startSubmitting() {
110
+ this.status.set('submitting');
111
+ }
112
+ /**
113
+ * Sets the form status to 'success'.
114
+ * Call this when the submission succeeds.
115
+ */
116
+ setSuccess() {
117
+ this.status.set('success');
118
+ }
119
+ /**
120
+ * Sets the form status to 'error'.
121
+ * Call this when the submission fails.
122
+ */
123
+ setError() {
124
+ this.status.set('error');
125
+ }
126
+ /** Resets the submitted state and status to idle. */
127
+ reset() {
128
+ this.submitted.set(false);
129
+ this.status.set('idle');
130
+ }
131
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
132
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: FormComponent, isStandalone: true, selector: "fk-form", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit" }, host: { properties: { "class": "this.hostClass" } }, providers: [{ provide: FORM, useExisting: FormComponent }], ngImport: i0, template: "<form class=\"fk-form__native\" (submit)=\"$event.preventDefault(); onSubmit()\">\n <ng-content />\n</form>\n", styles: [":host{display:block}.fk-form__native{display:flex;flex-direction:column}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
133
+ }
134
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormComponent, decorators: [{
135
+ type: Component,
136
+ args: [{ selector: 'fk-form', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: FORM, useExisting: FormComponent }], template: "<form class=\"fk-form__native\" (submit)=\"$event.preventDefault(); onSubmit()\">\n <ng-content />\n</form>\n", styles: [":host{display:block}.fk-form__native{display:flex;flex-direction:column}\n"] }]
137
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
138
+ type: HostBinding,
139
+ args: ['class']
140
+ }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }] } });
141
+
142
+ const FIELD_CONTROL = new InjectionToken('FIELD_CONTROL');
143
+
144
+ /**
145
+ * Default error messages for common Angular validators.
146
+ *
147
+ * Keys match the error keys emitted by Angular's built-in Validators
148
+ * (lowercase: 'required', 'minlength', 'maxlength', 'email', 'min', 'max', 'pattern').
149
+ *
150
+ * Templates use `{key}` syntax for interpolation with the validation
151
+ * error object properties.
152
+ */
153
+ const DEFAULT_ERROR_MESSAGES = {
154
+ required: 'This field is required',
155
+ minlength: 'Must be at least {requiredLength} characters',
156
+ maxlength: 'Must be no more than {requiredLength} characters',
157
+ email: 'Please enter a valid email address',
158
+ min: 'Must be at least {min}',
159
+ max: 'Must be no more than {max}',
160
+ pattern: 'Invalid format',
161
+ };
162
+ /**
163
+ * Injection token for providing custom error messages.
164
+ *
165
+ * Provide at the root or module level to override default messages globally,
166
+ * or at a component level for scoped overrides.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * providers: [
171
+ * {
172
+ * provide: ERROR_MESSAGES,
173
+ * useValue: {
174
+ * required: 'Required',
175
+ * email: 'Invalid email',
176
+ * },
177
+ * },
178
+ * ]
179
+ * ```
180
+ */
181
+ const ERROR_MESSAGES = new InjectionToken('ERROR_MESSAGES', {
182
+ providedIn: 'root',
183
+ factory: () => DEFAULT_ERROR_MESSAGES,
184
+ });
185
+ /**
186
+ * Interpolates a message template with values from a validation error object.
187
+ *
188
+ * Replaces `{key}` placeholders with corresponding values from the error details.
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * interpolateMessage('Must be at least {requiredLength} characters', { requiredLength: 3 });
193
+ * // => 'Must be at least 3 characters'
194
+ * ```
195
+ */
196
+ function interpolateMessage(template, errorValue) {
197
+ return template.replace(/{(\w+)}/g, (_, key) => {
198
+ const val = errorValue[key];
199
+ return val !== undefined && val !== null ? String(val) : `{${key}}`;
200
+ });
201
+ }
202
+ /**
203
+ * Resolves the first error message for a control's current validation errors.
204
+ *
205
+ * Resolution order:
206
+ * 1. Field-level overrides (`fieldMessages`)
207
+ * 2. Global messages (injected via `ERROR_MESSAGES`)
208
+ * 3. Falls back to the error key itself
209
+ *
210
+ * @param errors - The validation errors object from `AbstractControl.errors`
211
+ * @param globalMessages - Global error message map
212
+ * @param fieldMessages - Per-field error message overrides
213
+ * @returns The resolved, interpolated error message, or `null` if no errors
214
+ */
215
+ function resolveErrorMessage(errors, globalMessages, fieldMessages) {
216
+ if (!errors) {
217
+ return null;
218
+ }
219
+ const errorKey = Object.keys(errors)[0];
220
+ if (!errorKey) {
221
+ return null;
222
+ }
223
+ const errorValue = errors[errorKey];
224
+ // Check field-level override first
225
+ const template = fieldMessages?.[errorKey] ?? globalMessages[errorKey] ?? errorKey;
226
+ // Interpolate if the error value is an object with properties
227
+ if (errorValue && typeof errorValue === 'object') {
228
+ return interpolateMessage(template, errorValue);
229
+ }
230
+ return template;
231
+ }
232
+
233
+ let nextHintId = 0;
234
+ class HintComponent {
235
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
236
+ /** Stable element ID wired to the field's aria-describedby. */
237
+ id = input(`fk-hint-${nextHintId++}`, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
238
+ classes = computed(() => ['fk-hint', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
239
+ get hostClass() {
240
+ return this.classes();
241
+ }
242
+ get hostId() {
243
+ return this.id();
244
+ }
245
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HintComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
246
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: HintComponent, isStandalone: true, selector: "fk-hint", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, ngImport: i0, template: "<ng-content />\n", styles: [":host{display:block;font-family:var( --fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\" );font-size:var( --fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem) );color:var(--fk-form-hint-color, var(--fk-color-muted, #8a98a8))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
247
+ }
248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HintComponent, decorators: [{
249
+ type: Component,
250
+ args: [{ selector: 'fk-hint', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content />\n", styles: [":host{display:block;font-family:var( --fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\" );font-size:var( --fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem) );color:var(--fk-form-hint-color, var(--fk-color-muted, #8a98a8))}\n"] }]
251
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], hostClass: [{
252
+ type: HostBinding,
253
+ args: ['class']
254
+ }], hostId: [{
255
+ type: HostBinding,
256
+ args: ['attr.id']
257
+ }] } });
258
+
259
+ let nextLabelId = 0;
260
+ class LabelComponent {
261
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
262
+ /** Stable element ID used to reference this label from other elements. */
263
+ id = input(`fk-label-${nextLabelId++}`, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
264
+ /** ID of the form control this label describes; renders as the `for` attribute. */
265
+ for = input(null, ...(ngDevMode ? [{ debugName: "for" }] : /* istanbul ignore next */ []));
266
+ classes = computed(() => ['fk-label', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
267
+ get hostClass() {
268
+ return this.classes();
269
+ }
270
+ get hostId() {
271
+ return this.id();
272
+ }
273
+ get hostFor() {
274
+ return this.for();
275
+ }
276
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
277
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: LabelComponent, isStandalone: true, selector: "fk-label", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, for: { classPropertyName: "for", publicName: "for", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId", "attr.for": "this.hostFor" } }, ngImport: i0, template: "<ng-content />\n", styles: [":host{display:block;font-family:var( --fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\" );font-size:var( --fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem) );font-weight:var( --fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600) );line-height:var( --fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25) );color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
278
+ }
279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LabelComponent, decorators: [{
280
+ type: Component,
281
+ args: [{ selector: 'fk-label', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content />\n", styles: [":host{display:block;font-family:var( --fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\" );font-size:var( --fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem) );font-weight:var( --fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600) );line-height:var( --fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25) );color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420))}\n"] }]
282
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], hostClass: [{
283
+ type: HostBinding,
284
+ args: ['class']
285
+ }], hostId: [{
286
+ type: HostBinding,
287
+ args: ['attr.id']
288
+ }], hostFor: [{
289
+ type: HostBinding,
290
+ args: ['attr.for']
291
+ }] } });
292
+
293
+ const FK_PREFIX = new InjectionToken('FK_PREFIX');
294
+ class FkPrefixDirective {
295
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkPrefixDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
296
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkPrefixDirective, isStandalone: true, selector: "[fkPrefix]", providers: [{ provide: FK_PREFIX, useExisting: FkPrefixDirective }], ngImport: i0 });
297
+ }
298
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkPrefixDirective, decorators: [{
299
+ type: Directive,
300
+ args: [{
301
+ selector: '[fkPrefix]',
302
+ standalone: true,
303
+ providers: [{ provide: FK_PREFIX, useExisting: FkPrefixDirective }],
304
+ }]
305
+ }] });
306
+
307
+ const FK_SUFFIX = new InjectionToken('FK_SUFFIX');
308
+ class FkSuffixDirective {
309
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkSuffixDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
310
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkSuffixDirective, isStandalone: true, selector: "[fkSuffix]", providers: [{ provide: FK_SUFFIX, useExisting: FkSuffixDirective }], ngImport: i0 });
311
+ }
312
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkSuffixDirective, decorators: [{
313
+ type: Directive,
314
+ args: [{
315
+ selector: '[fkSuffix]',
316
+ standalone: true,
317
+ providers: [{ provide: FK_SUFFIX, useExisting: FkSuffixDirective }],
318
+ }]
319
+ }] });
320
+
321
+ let nextFieldId = 0;
322
+ class FormFieldComponent {
323
+ // ===== INPUTS =====
324
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
325
+ /** Text label rendered above the field; forwarded to the projected fk-label when present. */
326
+ label = input(null, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
327
+ /** Condition under which validation errors are displayed. */
328
+ showErrorsOn = input('touched', ...(ngDevMode ? [{ debugName: "showErrorsOn" }] : /* istanbul ignore next */ []));
329
+ /** Whether the parent form has been submitted; used when showErrorsOn is "submit". */
330
+ submitted = input(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
331
+ /** Additional aria-describedby ID to append to the field's accessibility wiring. */
332
+ externalDescribedBy = input(null, ...(ngDevMode ? [{ debugName: "externalDescribedBy" }] : /* istanbul ignore next */ []));
333
+ /**
334
+ * Fallback AbstractControl for dynamically created fields
335
+ * where NgControl is not available.
336
+ */
337
+ fallbackControl = input(null, ...(ngDevMode ? [{ debugName: "fallbackControl" }] : /* istanbul ignore next */ []));
338
+ /**
339
+ * Per-field error message overrides.
340
+ * Keys are validation error keys (e.g. 'required', 'minlength').
341
+ * Values are message templates supporting `{key}` interpolation.
342
+ *
343
+ * When provided, these take priority over global error messages.
344
+ * When neither field-level nor global messages are provided for
345
+ * an error key, the key itself is used as the message.
346
+ */
347
+ errorMessages = input(null, ...(ngDevMode ? [{ debugName: "errorMessages" }] : /* istanbul ignore next */ []));
348
+ // ===== PROJECTED CONTENT =====
349
+ control = contentChild(FIELD_CONTROL, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
350
+ hintRef = contentChild(HintComponent, ...(ngDevMode ? [{ debugName: "hintRef" }] : /* istanbul ignore next */ []));
351
+ errorRef = contentChild(ErrorComponent, ...(ngDevMode ? [{ debugName: "errorRef" }] : /* istanbul ignore next */ []));
352
+ labelRef = contentChild(LabelComponent, ...(ngDevMode ? [{ debugName: "labelRef" }] : /* istanbul ignore next */ []));
353
+ prefixRef = contentChild(FK_PREFIX, ...(ngDevMode ? [{ debugName: "prefixRef" }] : /* istanbul ignore next */ []));
354
+ suffixRef = contentChild(FK_SUFFIX, ...(ngDevMode ? [{ debugName: "suffixRef" }] : /* istanbul ignore next */ []));
355
+ // ===== INJECTED SERVICES =====
356
+ globalErrorMessages = inject(ERROR_MESSAGES);
357
+ cdr = inject(ChangeDetectorRef);
358
+ // ===== HOST CLASS =====
359
+ get hostClass() {
360
+ return [
361
+ 'fk-form-field',
362
+ this.isFocused() ? 'fk-form-field--focused' : '',
363
+ this.isInvalid() ? 'fk-form-field--invalid' : '',
364
+ this.isRequired() ? 'fk-form-field--required' : '',
365
+ this.isDisabled() ? 'fk-form-field--disabled' : '',
366
+ this.isReadonly() ? 'fk-form-field--readonly' : '',
367
+ !this.isEmpty() ? 'fk-form-field--filled' : '',
368
+ this.prefixRef() ? 'fk-form-field--has-prefix' : '',
369
+ this.suffixRef() ? 'fk-form-field--has-suffix' : '',
370
+ this.className(),
371
+ ]
372
+ .filter(Boolean)
373
+ .join(' ');
374
+ }
375
+ // ===== INTERNAL STATE =====
376
+ fieldId = `fk-field-${nextFieldId++}`;
377
+ labelId = `${this.fieldId}-label`;
378
+ autoErrorId = `${this.fieldId}-error`;
379
+ /**
380
+ * Whether this field uses auto error messages (no projected fk-error).
381
+ */
382
+ get hasAutoError() {
383
+ return !this.errorRef();
384
+ }
385
+ // ===== DERIVED STATE =====
386
+ // These are methods (not computed signals) because they read non-signal
387
+ // values from NgControl (touched, dirty, invalid). The host bindings
388
+ // evaluate them during each change detection cycle.
389
+ /**
390
+ * Resolves the AbstractControl to use for validation checks.
391
+ * Prefers NgControl from the projected FieldControl, falls back
392
+ * to the explicitly provided AbstractControl (for dynamic fields).
393
+ */
394
+ resolveControl() {
395
+ return this.control()?.ngControl?.control ?? this.fallbackControl();
396
+ }
397
+ /** Returns true when the field's validation error should be displayed. */
398
+ showError() {
399
+ const ctrl = this.resolveControl();
400
+ if (!ctrl || !ctrl.invalid) {
401
+ return false;
402
+ }
403
+ switch (this.showErrorsOn()) {
404
+ case 'touched':
405
+ return !!ctrl.touched;
406
+ case 'dirty':
407
+ return !!ctrl.dirty;
408
+ case 'submit':
409
+ return this.submitted();
410
+ default:
411
+ return !!ctrl.touched;
412
+ }
413
+ }
414
+ /**
415
+ * Resolves the auto error message from the control's validation errors.
416
+ * Uses field-level overrides first, then global messages, then the error key.
417
+ */
418
+ autoErrorMessage() {
419
+ const ctrl = this.resolveControl();
420
+ if (!ctrl || !ctrl.errors) {
421
+ return null;
422
+ }
423
+ return resolveErrorMessage(ctrl.errors, this.globalErrorMessages, this.errorMessages() ?? undefined);
424
+ }
425
+ /** Returns true when the projected field control currently has focus. */
426
+ isFocused() {
427
+ return this.control()?.focused ?? false;
428
+ }
429
+ /** Returns true when the projected field control has no value. */
430
+ isEmpty() {
431
+ return this.control()?.empty ?? true;
432
+ }
433
+ /** Returns true when the projected field control is disabled. */
434
+ isDisabled() {
435
+ return this.control()?.disabled ?? false;
436
+ }
437
+ /** Returns true when the field is marked required by the control or validator. */
438
+ isRequired() {
439
+ const control = this.control();
440
+ if (control?.required) {
441
+ return true;
442
+ }
443
+ const ctrl = this.resolveControl();
444
+ return ctrl ? ctrl.hasValidator(Validators.required) : false;
445
+ }
446
+ /** Returns true when the projected field control is read-only. */
447
+ isReadonly() {
448
+ return this.control()?.readonly ?? false;
449
+ }
450
+ /** Returns true when the field is in an invalid state that should be shown. */
451
+ isInvalid() {
452
+ return this.showError();
453
+ }
454
+ constructor() {
455
+ effect(() => {
456
+ this.syncDescribedByIds();
457
+ });
458
+ effect(() => {
459
+ const ctrl = this.resolveControl();
460
+ if (ctrl) {
461
+ const sub = ctrl.events?.subscribe(() => {
462
+ this.cdr.markForCheck();
463
+ });
464
+ return () => sub?.unsubscribe();
465
+ }
466
+ return;
467
+ });
468
+ }
469
+ // ===== DESCRIBED-BY WIRING =====
470
+ syncDescribedByIds() {
471
+ const control = this.control();
472
+ if (!control) {
473
+ return;
474
+ }
475
+ const ids = [];
476
+ const hint = this.hintRef();
477
+ const error = this.errorRef();
478
+ const external = this.externalDescribedBy();
479
+ const showingError = this.showError();
480
+ if (hint && !showingError) {
481
+ ids.push(hint.id());
482
+ }
483
+ if (showingError) {
484
+ if (error) {
485
+ ids.push(error.id());
486
+ }
487
+ else {
488
+ // Auto error message — use the generated ID
489
+ ids.push(this.autoErrorId);
490
+ }
491
+ }
492
+ if (external) {
493
+ ids.push(external);
494
+ }
495
+ control.setDescribedByIds(ids);
496
+ }
497
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
498
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FormFieldComponent, isStandalone: true, selector: "fk-form-field", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showErrorsOn: { classPropertyName: "showErrorsOn", publicName: "showErrorsOn", isSignal: true, isRequired: false, transformFunction: null }, submitted: { classPropertyName: "submitted", publicName: "submitted", isSignal: true, isRequired: false, transformFunction: null }, externalDescribedBy: { classPropertyName: "externalDescribedBy", publicName: "externalDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, fallbackControl: { classPropertyName: "fallbackControl", publicName: "fallbackControl", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, queries: [{ propertyName: "control", first: true, predicate: FIELD_CONTROL, descendants: true, isSignal: true }, { propertyName: "hintRef", first: true, predicate: HintComponent, descendants: true, isSignal: true }, { propertyName: "errorRef", first: true, predicate: ErrorComponent, descendants: true, isSignal: true }, { propertyName: "labelRef", first: true, predicate: LabelComponent, descendants: true, isSignal: true }, { propertyName: "prefixRef", first: true, predicate: FK_PREFIX, descendants: true, isSignal: true }, { propertyName: "suffixRef", first: true, predicate: FK_SUFFIX, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-form-field__wrapper\">\n @if (label() || labelRef()) {\n <label\n class=\"fk-form-field__label\"\n [attr.id]=\"labelId\"\n [attr.for]=\"control()?.id ?? null\"\n >\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content select=\"fk-label\" />\n }\n @if (isRequired()) {\n <span class=\"fk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-form-field__control\"\n (click)=\"control()?.onContainerClick?.($event)\"\n >\n @if (prefixRef()) {\n <div class=\"fk-form-field__prefix\">\n <ng-content select=\"[fkPrefix]\" />\n </div>\n }\n <ng-content />\n @if (suffixRef()) {\n <div class=\"fk-form-field__suffix\">\n <ng-content select=\"[fkSuffix]\" />\n </div>\n }\n </div>\n\n <div class=\"fk-form-field__subscript\">\n @if (showError()) {\n @if (hasAutoError) {\n <span class=\"fk-error\" role=\"alert\" [attr.id]=\"autoErrorId\">\n {{ autoErrorMessage() }}\n </span>\n } @else {\n <ng-content select=\"fk-error\" />\n }\n } @else {\n <ng-content select=\"fk-hint\" />\n }\n </div>\n</div>\n", styles: [":host{display:block}.fk-form-field__wrapper{display:flex;flex-direction:column}.fk-form-field__label{display:flex;align-items:baseline;gap:.25em;margin-top:var(--fk-form-label-margin-top, var(--fk-rhythm-2, .5rem));font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));cursor:default}.fk-form-field__required{color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}.fk-form-field__control{position:relative;margin-top:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-form-field__prefix,.fk-form-field__suffix{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8));z-index:1}.fk-form-field__prefix{pointer-events:none}.fk-form-field__prefix{left:var(--fk-input-affix-offset, .75rem)}.fk-form-field__suffix{right:var(--fk-input-affix-offset, .75rem)}.fk-form-field__subscript{display:var(--fk-form-subscript-display, block);min-height:var(--fk-form-subscript-min-height, calc(var(--fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem)) * 1.4));margin-top:var(--fk-form-subscript-gap, var(--fk-rhythm-1, .25rem))}.fk-form-field__subscript .fk-error{display:block;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem));line-height:1.4;color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}:host(.fk-form-field--focused) .fk-form-field__label{color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff))}:host(.fk-form-field--disabled) .fk-form-field__label{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
499
+ }
500
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormFieldComponent, decorators: [{
501
+ type: Component,
502
+ args: [{ selector: 'fk-form-field', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-form-field__wrapper\">\n @if (label() || labelRef()) {\n <label\n class=\"fk-form-field__label\"\n [attr.id]=\"labelId\"\n [attr.for]=\"control()?.id ?? null\"\n >\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content select=\"fk-label\" />\n }\n @if (isRequired()) {\n <span class=\"fk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-form-field__control\"\n (click)=\"control()?.onContainerClick?.($event)\"\n >\n @if (prefixRef()) {\n <div class=\"fk-form-field__prefix\">\n <ng-content select=\"[fkPrefix]\" />\n </div>\n }\n <ng-content />\n @if (suffixRef()) {\n <div class=\"fk-form-field__suffix\">\n <ng-content select=\"[fkSuffix]\" />\n </div>\n }\n </div>\n\n <div class=\"fk-form-field__subscript\">\n @if (showError()) {\n @if (hasAutoError) {\n <span class=\"fk-error\" role=\"alert\" [attr.id]=\"autoErrorId\">\n {{ autoErrorMessage() }}\n </span>\n } @else {\n <ng-content select=\"fk-error\" />\n }\n } @else {\n <ng-content select=\"fk-hint\" />\n }\n </div>\n</div>\n", styles: [":host{display:block}.fk-form-field__wrapper{display:flex;flex-direction:column}.fk-form-field__label{display:flex;align-items:baseline;gap:.25em;margin-top:var(--fk-form-label-margin-top, var(--fk-rhythm-2, .5rem));font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));cursor:default}.fk-form-field__required{color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}.fk-form-field__control{position:relative;margin-top:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-form-field__prefix,.fk-form-field__suffix{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8));z-index:1}.fk-form-field__prefix{pointer-events:none}.fk-form-field__prefix{left:var(--fk-input-affix-offset, .75rem)}.fk-form-field__suffix{right:var(--fk-input-affix-offset, .75rem)}.fk-form-field__subscript{display:var(--fk-form-subscript-display, block);min-height:var(--fk-form-subscript-min-height, calc(var(--fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem)) * 1.4));margin-top:var(--fk-form-subscript-gap, var(--fk-rhythm-1, .25rem))}.fk-form-field__subscript .fk-error{display:block;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem));line-height:1.4;color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}:host(.fk-form-field--focused) .fk-form-field__label{color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff))}:host(.fk-form-field--disabled) .fk-form-field__label{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}\n"] }]
503
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], showErrorsOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrorsOn", required: false }] }], submitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitted", required: false }] }], externalDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalDescribedBy", required: false }] }], fallbackControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackControl", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FIELD_CONTROL), { isSignal: true }] }], hintRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => HintComponent), { isSignal: true }] }], errorRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ErrorComponent), { isSignal: true }] }], labelRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => LabelComponent), { isSignal: true }] }], prefixRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_PREFIX), { isSignal: true }] }], suffixRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_SUFFIX), { isSignal: true }] }], hostClass: [{
504
+ type: HostBinding,
505
+ args: ['class']
506
+ }] } });
507
+
508
+ /**
509
+ * Registry that maps validator type names to factory functions.
510
+ *
511
+ * Built-in validators (required, minLength, maxLength, email, min, max, pattern)
512
+ * are registered by default. Custom validators can be registered at any time.
513
+ *
514
+ * Used by the schema engine (Phase 3) to create ValidatorFn arrays from
515
+ * schema configurations, and available for programmatic use.
516
+ *
517
+ * @example
518
+ * ```ts
519
+ * // Register a custom validator
520
+ * registry.register('phone', () =>
521
+ * Validators.pattern(/^\+?[\d\s-]{10,}$/)
522
+ * );
523
+ *
524
+ * // Resolve validators from config
525
+ * const validators = registry.resolve([
526
+ * { type: 'required' },
527
+ * { type: 'minLength', value: 3 },
528
+ * { type: 'phone' },
529
+ * ]);
530
+ * ```
531
+ */
532
+ class ValidationRegistry {
533
+ validators = new Map();
534
+ asyncValidators = new Map();
535
+ constructor() {
536
+ this.registerDefaults();
537
+ }
538
+ /**
539
+ * Registers a synchronous validator factory.
540
+ *
541
+ * @param type - The validator type key
542
+ * @param factory - Factory function that creates a ValidatorFn
543
+ */
544
+ register(type, factory) {
545
+ this.validators.set(type, factory);
546
+ }
547
+ /**
548
+ * Registers an asynchronous validator factory.
549
+ *
550
+ * @param type - The async validator type key
551
+ * @param factory - Factory function that creates an AsyncValidatorFn
552
+ */
553
+ registerAsync(type, factory) {
554
+ this.asyncValidators.set(type, factory);
555
+ }
556
+ /**
557
+ * Checks if a synchronous validator type is registered.
558
+ */
559
+ has(type) {
560
+ return this.validators.has(type);
561
+ }
562
+ /**
563
+ * Checks if an asynchronous validator type is registered.
564
+ */
565
+ hasAsync(type) {
566
+ return this.asyncValidators.has(type);
567
+ }
568
+ /**
569
+ * Resolves an array of validator configs into ValidatorFn instances.
570
+ *
571
+ * @throws Error if a validator type is not registered
572
+ */
573
+ resolve(configs) {
574
+ return configs.map((config) => {
575
+ const factory = this.validators.get(config.type);
576
+ if (!factory) {
577
+ throw new Error(`ValidationRegistry: unknown validator type "${config.type}". ` +
578
+ `Register it with registry.register("${config.type}", factory).`);
579
+ }
580
+ return factory(config.value);
581
+ });
582
+ }
583
+ /**
584
+ * Resolves an array of async validator configs into AsyncValidatorFn instances.
585
+ *
586
+ * @throws Error if an async validator type is not registered
587
+ */
588
+ resolveAsync(configs) {
589
+ return configs.map((config) => {
590
+ const factory = this.asyncValidators.get(config.type);
591
+ if (!factory) {
592
+ throw new Error(`ValidationRegistry: unknown async validator type "${config.type}". ` +
593
+ `Register it with registry.registerAsync("${config.type}", factory).`);
594
+ }
595
+ return factory(config.value);
596
+ });
597
+ }
598
+ registerDefaults() {
599
+ this.register('required', () => Validators.required);
600
+ this.register('requiredTrue', () => Validators.requiredTrue);
601
+ this.register('email', () => Validators.email);
602
+ this.register('minLength', (value) => Validators.minLength(value));
603
+ this.register('maxLength', (value) => Validators.maxLength(value));
604
+ this.register('min', (value) => Validators.min(value));
605
+ this.register('max', (value) => Validators.max(value));
606
+ this.register('pattern', (value) => Validators.pattern(value));
607
+ }
608
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ValidationRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
609
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ValidationRegistry, providedIn: 'root' });
610
+ }
611
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ValidationRegistry, decorators: [{
612
+ type: Injectable,
613
+ args: [{ providedIn: 'root' }]
614
+ }], ctorParameters: () => [] });
615
+
616
+ /**
617
+ * Parses a FormSchema into an Angular FormGroup.
618
+ *
619
+ * Resolves validators from the validation registry and applies
620
+ * default values, disabled state, and required constraints.
621
+ *
622
+ * @example
623
+ * ```ts
624
+ * const schema: FormSchema = {
625
+ * fields: [
626
+ * { key: 'email', type: 'input', required: true, validators: [{ type: 'email' }] },
627
+ * { key: 'name', type: 'input', defaultValue: 'John' },
628
+ * ],
629
+ * };
630
+ *
631
+ * const form = parser.buildFormGroup(schema);
632
+ * // form.get('email') => FormControl with required + email validators
633
+ * // form.get('name') => FormControl with value 'John'
634
+ * ```
635
+ */
636
+ class SchemaParser {
637
+ validationRegistry = inject(ValidationRegistry);
638
+ /**
639
+ * Builds a FormGroup from a form schema.
640
+ *
641
+ * Supports flat fields, nested groups (via `children`),
642
+ * field arrays (via `arrayFields`), and layout sections.
643
+ */
644
+ buildFormGroup(schema) {
645
+ const allFields = this.collectFields(schema);
646
+ return this.buildGroupFromFields(allFields);
647
+ }
648
+ /**
649
+ * Builds a single FormControl from a field schema.
650
+ */
651
+ buildControl(field) {
652
+ const validators = this.resolveValidators(field);
653
+ const asyncValidators = this.resolveAsyncValidators(field);
654
+ return new FormControl({ value: field.defaultValue ?? null, disabled: field.disabled ?? false }, { validators, asyncValidators });
655
+ }
656
+ /**
657
+ * Builds a nested FormGroup from a field with `children`.
658
+ */
659
+ buildNestedGroup(field) {
660
+ if (!field.children?.length) {
661
+ return new FormGroup({});
662
+ }
663
+ return this.buildGroupFromFields(field.children);
664
+ }
665
+ /**
666
+ * Builds a FormArray from a field with `arrayFields`.
667
+ *
668
+ * Creates one initial row from the array field definitions.
669
+ * Additional rows can be added programmatically.
670
+ */
671
+ buildFieldArray(field) {
672
+ const initialRows = field.defaultValue ?? [null];
673
+ const controls = initialRows.map((rowValue) => {
674
+ if (field.arrayFields?.length) {
675
+ const group = this.buildGroupFromFields(field.arrayFields);
676
+ if (rowValue && typeof rowValue === 'object') {
677
+ group.patchValue(rowValue);
678
+ }
679
+ return group;
680
+ }
681
+ return new FormControl(rowValue ?? null);
682
+ });
683
+ return new FormArray(controls);
684
+ }
685
+ /**
686
+ * Creates a new row FormGroup for a field array.
687
+ */
688
+ buildArrayRow(field) {
689
+ if (!field.arrayFields?.length) {
690
+ return new FormGroup({});
691
+ }
692
+ return this.buildGroupFromFields(field.arrayFields);
693
+ }
694
+ buildGroupFromFields(fields) {
695
+ const controls = {};
696
+ for (const field of fields) {
697
+ if (field.children) {
698
+ controls[field.key] = this.buildNestedGroup(field);
699
+ }
700
+ else if (field.arrayFields) {
701
+ controls[field.key] = this.buildFieldArray(field);
702
+ }
703
+ else {
704
+ controls[field.key] = this.buildControl(field);
705
+ }
706
+ }
707
+ return new FormGroup(controls);
708
+ }
709
+ /**
710
+ * Collects all fields from a schema, including fields
711
+ * within layout sections.
712
+ */
713
+ collectFields(schema) {
714
+ if (!schema.sections?.length) {
715
+ return schema.fields;
716
+ }
717
+ const fields = [...schema.fields];
718
+ for (const section of schema.sections) {
719
+ fields.push(...section.fields);
720
+ }
721
+ return fields;
722
+ }
723
+ resolveValidators(field) {
724
+ const validators = [];
725
+ if (field.required) {
726
+ validators.push(Validators.required);
727
+ }
728
+ if (field.validators?.length) {
729
+ validators.push(...this.validationRegistry.resolve(field.validators));
730
+ }
731
+ return validators;
732
+ }
733
+ resolveAsyncValidators(field) {
734
+ if (!field.asyncValidators?.length) {
735
+ return [];
736
+ }
737
+ return this.validationRegistry.resolveAsync(field.asyncValidators);
738
+ }
739
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SchemaParser, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
740
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SchemaParser, providedIn: 'root' });
741
+ }
742
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SchemaParser, decorators: [{
743
+ type: Injectable,
744
+ args: [{ providedIn: 'root' }]
745
+ }] });
746
+
747
+ const FK_AUTOCOMPLETE_OPTION_TPL = new InjectionToken('FK_AUTOCOMPLETE_OPTION_TPL');
748
+ class FkAutocompleteOptionTplDirective {
749
+ templateRef = inject(TemplateRef);
750
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkAutocompleteOptionTplDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
751
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkAutocompleteOptionTplDirective, isStandalone: true, selector: "[fkAutocompleteOption]", providers: [
752
+ {
753
+ provide: FK_AUTOCOMPLETE_OPTION_TPL,
754
+ useExisting: FkAutocompleteOptionTplDirective,
755
+ },
756
+ ], ngImport: i0 });
757
+ }
758
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkAutocompleteOptionTplDirective, decorators: [{
759
+ type: Directive,
760
+ args: [{
761
+ selector: '[fkAutocompleteOption]',
762
+ standalone: true,
763
+ providers: [
764
+ {
765
+ provide: FK_AUTOCOMPLETE_OPTION_TPL,
766
+ useExisting: FkAutocompleteOptionTplDirective,
767
+ },
768
+ ],
769
+ }]
770
+ }] });
771
+ let nextAutocompleteId = 0;
772
+ class AutocompleteComponent {
773
+ cdr = inject(ChangeDetectorRef);
774
+ ngControl = inject(NgControl, {
775
+ optional: true,
776
+ self: true,
777
+ });
778
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
779
+ // ===== INPUTS =====
780
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
781
+ /** Placeholder text shown in the text input when no value is selected. */
782
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
783
+ // eslint-disable-next-line @angular-eslint/no-input-rename
784
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
785
+ /** List of selectable options rendered in the dropdown. */
786
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
787
+ /**
788
+ * 'local' (default): filters options client-side.
789
+ * 'manual': skips internal filtering — renders options as-is.
790
+ * Use with searchChange output for server-side search.
791
+ */
792
+ filterMode = input('local', ...(ngDevMode ? [{ debugName: "filterMode" }] : /* istanbul ignore next */ []));
793
+ /** Whether async options are currently loading. */
794
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
795
+ // ===== OUTPUTS =====
796
+ /**
797
+ * Emits the current input value on each keystroke.
798
+ * Use in manual filterMode for server-side search.
799
+ */
800
+ searchChange = output();
801
+ // ===== CONTENT CHILDREN =====
802
+ /** Custom template for rendering each option in the dropdown. */
803
+ optionTpl = contentChild(FK_AUTOCOMPLETE_OPTION_TPL, ...(ngDevMode ? [{ debugName: "optionTpl" }] : /* istanbul ignore next */ []));
804
+ // ===== FieldControl =====
805
+ id = `fk-autocomplete-${nextAutocompleteId++}`;
806
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
807
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
808
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
809
+ // ===== DROPDOWN STATE =====
810
+ showDropdown = signal(false, ...(ngDevMode ? [{ debugName: "showDropdown" }] : /* istanbul ignore next */ []));
811
+ classes = computed(() => [
812
+ 'fk-autocomplete',
813
+ this.isFocused() ? 'fk-autocomplete--focused' : '',
814
+ this.isDisabledSignal() ? 'fk-autocomplete--disabled' : '',
815
+ this.showDropdown() ? 'fk-autocomplete--open' : '',
816
+ this.className(),
817
+ ]
818
+ .filter(Boolean)
819
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
820
+ get hostClass() {
821
+ return this.classes();
822
+ }
823
+ filteredOptions = signal([], ...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
824
+ activeIndex = signal(-1, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
825
+ /** The options to render in the dropdown — filtered or raw depending on mode. */
826
+ visibleOptions = computed(() => {
827
+ if (this.filterMode() === 'manual') {
828
+ return this.options();
829
+ }
830
+ return this.filteredOptions();
831
+ }, ...(ngDevMode ? [{ debugName: "visibleOptions" }] : /* istanbul ignore next */ []));
832
+ displayValue = '';
833
+ get focused() {
834
+ return this.isFocused();
835
+ }
836
+ get empty() {
837
+ return !this.selectedValue;
838
+ }
839
+ get disabled() {
840
+ return this.isDisabledSignal();
841
+ }
842
+ get required() {
843
+ return this.isRequired();
844
+ }
845
+ get describedByIds() {
846
+ return this.describedByIdsSignal();
847
+ }
848
+ constructor() {
849
+ if (this.ngControl) {
850
+ this.ngControl.valueAccessor = this;
851
+ }
852
+ // In manual mode, show/hide dropdown when options change
853
+ effect(() => {
854
+ if (this.filterMode() !== 'manual') {
855
+ return;
856
+ }
857
+ const opts = this.options();
858
+ if (opts.length > 0 && this.isFocused()) {
859
+ this.showDropdown.set(true);
860
+ this.activeIndex.set(-1);
861
+ }
862
+ else if (opts.length === 0 && !this.loading()) {
863
+ this.showDropdown.set(this.displayValue.length > 0 && this.isFocused());
864
+ }
865
+ });
866
+ }
867
+ setDescribedByIds(ids) {
868
+ this.describedByIdsSignal.set(ids);
869
+ }
870
+ onContainerClick(_event) {
871
+ const input = this.inputRef()?.nativeElement;
872
+ if (input && !this.isDisabledSignal()) {
873
+ input.focus();
874
+ }
875
+ }
876
+ // ===== ControlValueAccessor =====
877
+ selectedValue = null;
878
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
879
+ onChange = () => { };
880
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
881
+ onTouched = () => { };
882
+ writeValue(value) {
883
+ this.selectedValue = value;
884
+ const match = this.options().find((o) => o.value === value);
885
+ this.displayValue = match?.label ?? (value !== null ? String(value) : '');
886
+ const input = this.inputRef()?.nativeElement;
887
+ if (input) {
888
+ input.value = this.displayValue;
889
+ }
890
+ }
891
+ registerOnChange(fn) {
892
+ this.onChange = fn;
893
+ }
894
+ registerOnTouched(fn) {
895
+ this.onTouched = fn;
896
+ }
897
+ setDisabledState(isDisabled) {
898
+ this.isDisabledSignal.set(isDisabled);
899
+ if (isDisabled) {
900
+ this.showDropdown.set(false);
901
+ this.activeIndex.set(-1);
902
+ }
903
+ this.cdr.markForCheck();
904
+ }
905
+ // ===== OPTION MANAGEMENT =====
906
+ /** Commits the given option as the selected value and closes the dropdown. */
907
+ selectOption(option) {
908
+ this.selectedValue = option.value;
909
+ this.displayValue = option.label;
910
+ const input = this.inputRef()?.nativeElement;
911
+ if (input) {
912
+ input.value = option.label;
913
+ }
914
+ this.showDropdown.set(false);
915
+ this.activeIndex.set(-1);
916
+ this.onChange(this.selectedValue);
917
+ }
918
+ filterOptions(query) {
919
+ const lower = query.toLowerCase();
920
+ const filtered = this.options().filter((o) => o.label.toLowerCase().includes(lower));
921
+ this.filteredOptions.set(filtered);
922
+ this.showDropdown.set(filtered.length > 0 && query.length > 0);
923
+ this.activeIndex.set(-1);
924
+ }
925
+ // ===== EVENT HANDLERS =====
926
+ onInput(event) {
927
+ if (this.isDisabledSignal()) {
928
+ return;
929
+ }
930
+ const input = event.target;
931
+ this.displayValue = input.value;
932
+ // Clear the selected value when typing
933
+ this.selectedValue = null;
934
+ this.onChange(null);
935
+ if (this.filterMode() === 'manual') {
936
+ this.searchChange.emit(input.value);
937
+ // In manual mode, show dropdown if there are options
938
+ // (the parent controls options via the input binding)
939
+ this.showDropdown.set(input.value.length > 0);
940
+ this.activeIndex.set(-1);
941
+ }
942
+ else {
943
+ this.filterOptions(input.value);
944
+ }
945
+ }
946
+ onKeydown(event) {
947
+ if (this.isDisabledSignal()) {
948
+ return;
949
+ }
950
+ const visible = this.visibleOptions();
951
+ if (!this.showDropdown() || visible.length === 0) {
952
+ return;
953
+ }
954
+ switch (event.key) {
955
+ case 'ArrowDown': {
956
+ event.preventDefault();
957
+ const nextIndex = Math.min(this.activeIndex() + 1, visible.length - 1);
958
+ this.activeIndex.set(nextIndex);
959
+ break;
960
+ }
961
+ case 'ArrowUp': {
962
+ event.preventDefault();
963
+ const prevIndex = Math.max(this.activeIndex() - 1, 0);
964
+ this.activeIndex.set(prevIndex);
965
+ break;
966
+ }
967
+ case 'Enter': {
968
+ event.preventDefault();
969
+ const idx = this.activeIndex();
970
+ if (idx >= 0 && idx < visible.length) {
971
+ this.selectOption(visible[idx]);
972
+ }
973
+ break;
974
+ }
975
+ case 'Tab': {
976
+ const tabIdx = this.activeIndex();
977
+ if (tabIdx >= 0 && tabIdx < visible.length) {
978
+ this.selectOption(visible[tabIdx]);
979
+ }
980
+ // Don't prevent default — let focus move to next field
981
+ break;
982
+ }
983
+ case 'Escape': {
984
+ this.showDropdown.set(false);
985
+ this.activeIndex.set(-1);
986
+ break;
987
+ }
988
+ }
989
+ }
990
+ onFocus() {
991
+ if (this.isDisabledSignal()) {
992
+ return;
993
+ }
994
+ this.isFocused.set(true);
995
+ if (this.filterMode() === 'local' && this.displayValue) {
996
+ this.filterOptions(this.displayValue);
997
+ }
998
+ }
999
+ onBlur() {
1000
+ this.isFocused.set(false);
1001
+ this.showDropdown.set(false);
1002
+ this.activeIndex.set(-1);
1003
+ this.onTouched();
1004
+ }
1005
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1006
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AutocompleteComponent, isStandalone: true, selector: "fk-autocomplete", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, filterMode: { classPropertyName: "filterMode", publicName: "filterMode", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchChange: "searchChange" }, host: { properties: { "class": "this.hostClass" } }, providers: [
1007
+ {
1008
+ provide: FIELD_CONTROL,
1009
+ useExisting: forwardRef(() => AutocompleteComponent),
1010
+ },
1011
+ ], queries: [{ propertyName: "optionTpl", first: true, predicate: FK_AUTOCOMPLETE_OPTION_TPL, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-autocomplete__wrapper\">\n <input\n #inputEl\n type=\"text\"\n class=\"fk-autocomplete__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.aria-expanded]=\"showDropdown()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.role]=\"'combobox'\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (showDropdown()) {\n <ul class=\"fk-autocomplete__dropdown\" role=\"listbox\">\n @if (loading()) {\n <li class=\"fk-autocomplete__status\" aria-disabled=\"true\">Loading\u2026</li>\n } @else if (visibleOptions().length > 0) {\n @for (option of visibleOptions(); track option.value; let i = $index) {\n <li\n class=\"fk-autocomplete__option\"\n [class.fk-autocomplete__option--active]=\"i === activeIndex()\"\n role=\"option\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mousedown)=\"selectOption(option)\"\n >\n @if (optionTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"optionTpl()!.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: option }\"\n />\n } @else {\n {{ option.label }}\n }\n </li>\n }\n } @else {\n <li class=\"fk-autocomplete__status\" aria-disabled=\"true\">\n No results found\n </li>\n }\n </ul>\n }\n</div>\n", styles: [":host{display:block}.fk-autocomplete__wrapper{position:relative}.fk-autocomplete__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, .5rem .75rem);font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-autocomplete__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-autocomplete__native:hover:not(:disabled){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-autocomplete__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-autocomplete__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-autocomplete__dropdown{position:absolute;top:100%;left:0;right:0;z-index:10;max-height:200px;margin:4px 0 0;padding:4px 0;list-style:none;overflow-y:auto;background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));box-shadow:var(--fk-autocomplete-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, .1))}.fk-autocomplete__option{padding:var(--fk-input-padding, .5rem .75rem);font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));cursor:pointer;transition:background .1s ease}.fk-autocomplete__option:hover,.fk-autocomplete__option--active{background:var(--fk-color-surface-muted, #f7f9fb)}.fk-autocomplete__status{padding:var(--fk-input-padding, .5rem .75rem);font-size:var(--fk-input-font-size, .875rem);color:var(--fk-color-muted, #8a98a8);font-style:italic;list-style:none}:host-context(.fk-form-field--invalid) .fk-autocomplete__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-autocomplete__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1012
+ }
1013
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AutocompleteComponent, decorators: [{
1014
+ type: Component,
1015
+ args: [{ selector: 'fk-autocomplete', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1016
+ {
1017
+ provide: FIELD_CONTROL,
1018
+ useExisting: forwardRef(() => AutocompleteComponent),
1019
+ },
1020
+ ], template: "<div class=\"fk-autocomplete__wrapper\">\n <input\n #inputEl\n type=\"text\"\n class=\"fk-autocomplete__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.aria-expanded]=\"showDropdown()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.role]=\"'combobox'\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n @if (showDropdown()) {\n <ul class=\"fk-autocomplete__dropdown\" role=\"listbox\">\n @if (loading()) {\n <li class=\"fk-autocomplete__status\" aria-disabled=\"true\">Loading\u2026</li>\n } @else if (visibleOptions().length > 0) {\n @for (option of visibleOptions(); track option.value; let i = $index) {\n <li\n class=\"fk-autocomplete__option\"\n [class.fk-autocomplete__option--active]=\"i === activeIndex()\"\n role=\"option\"\n [attr.aria-selected]=\"i === activeIndex()\"\n (mousedown)=\"selectOption(option)\"\n >\n @if (optionTpl()) {\n <ng-container\n [ngTemplateOutlet]=\"optionTpl()!.templateRef\"\n [ngTemplateOutletContext]=\"{ $implicit: option }\"\n />\n } @else {\n {{ option.label }}\n }\n </li>\n }\n } @else {\n <li class=\"fk-autocomplete__status\" aria-disabled=\"true\">\n No results found\n </li>\n }\n </ul>\n }\n</div>\n", styles: [":host{display:block}.fk-autocomplete__wrapper{position:relative}.fk-autocomplete__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, .5rem .75rem);font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-autocomplete__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-autocomplete__native:hover:not(:disabled){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-autocomplete__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-autocomplete__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-autocomplete__dropdown{position:absolute;top:100%;left:0;right:0;z-index:10;max-height:200px;margin:4px 0 0;padding:4px 0;list-style:none;overflow-y:auto;background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));box-shadow:var(--fk-autocomplete-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, .1))}.fk-autocomplete__option{padding:var(--fk-input-padding, .5rem .75rem);font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));cursor:pointer;transition:background .1s ease}.fk-autocomplete__option:hover,.fk-autocomplete__option--active{background:var(--fk-color-surface-muted, #f7f9fb)}.fk-autocomplete__status{padding:var(--fk-input-padding, .5rem .75rem);font-size:var(--fk-input-font-size, .875rem);color:var(--fk-color-muted, #8a98a8);font-style:italic;list-style:none}:host-context(.fk-form-field--invalid) .fk-autocomplete__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-autocomplete__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1021
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], filterMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMode", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], optionTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_AUTOCOMPLETE_OPTION_TPL), { isSignal: true }] }], hostClass: [{
1022
+ type: HostBinding,
1023
+ args: ['class']
1024
+ }] } });
1025
+
1026
+ let nextCheckboxId = 0;
1027
+ class CheckboxComponent {
1028
+ cdr = inject(ChangeDetectorRef);
1029
+ ngControl = inject(NgControl, {
1030
+ optional: true,
1031
+ self: true,
1032
+ });
1033
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1034
+ // ===== INPUTS =====
1035
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1036
+ /** Text label rendered alongside the checkbox. */
1037
+ label = input(null, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1038
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1039
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1040
+ // ===== INTERNAL STATE =====
1041
+ checked = signal(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
1042
+ // ===== FieldControl =====
1043
+ id = `fk-checkbox-${nextCheckboxId++}`;
1044
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1045
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1046
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1047
+ classes = computed(() => [
1048
+ 'fk-checkbox',
1049
+ this.isFocused() ? 'fk-checkbox--focused' : '',
1050
+ this.isDisabledSignal() ? 'fk-checkbox--disabled' : '',
1051
+ this.checked() ? 'fk-checkbox--checked' : '',
1052
+ this.className(),
1053
+ ]
1054
+ .filter(Boolean)
1055
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1056
+ get hostClass() {
1057
+ return this.classes();
1058
+ }
1059
+ get focused() {
1060
+ return this.isFocused();
1061
+ }
1062
+ get empty() {
1063
+ return !this.checked();
1064
+ }
1065
+ get disabled() {
1066
+ return this.isDisabledSignal();
1067
+ }
1068
+ get required() {
1069
+ return this.isRequired();
1070
+ }
1071
+ get describedByIds() {
1072
+ return this.describedByIdsSignal();
1073
+ }
1074
+ constructor() {
1075
+ if (this.ngControl) {
1076
+ this.ngControl.valueAccessor = this;
1077
+ }
1078
+ }
1079
+ setDescribedByIds(ids) {
1080
+ this.describedByIdsSignal.set(ids);
1081
+ }
1082
+ onContainerClick(_event) {
1083
+ // No-op: the checkbox <label> already handles click-to-toggle natively.
1084
+ // Calling input.click() here would double-toggle when inside fk-form-field.
1085
+ }
1086
+ // ===== ControlValueAccessor =====
1087
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1088
+ onChange = () => { };
1089
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1090
+ onTouched = () => { };
1091
+ writeValue(value) {
1092
+ this.checked.set(!!value);
1093
+ }
1094
+ registerOnChange(fn) {
1095
+ this.onChange = fn;
1096
+ }
1097
+ registerOnTouched(fn) {
1098
+ this.onTouched = fn;
1099
+ }
1100
+ setDisabledState(isDisabled) {
1101
+ this.isDisabledSignal.set(isDisabled);
1102
+ this.cdr.markForCheck();
1103
+ }
1104
+ // ===== EVENT HANDLERS =====
1105
+ onChanged(event) {
1106
+ const input = event.target;
1107
+ this.checked.set(input.checked);
1108
+ this.onChange(input.checked);
1109
+ }
1110
+ onFocus() {
1111
+ this.isFocused.set(true);
1112
+ }
1113
+ onBlur() {
1114
+ this.isFocused.set(false);
1115
+ this.onTouched();
1116
+ }
1117
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1118
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: CheckboxComponent, isStandalone: true, selector: "fk-checkbox", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1119
+ {
1120
+ provide: FIELD_CONTROL,
1121
+ useExisting: forwardRef(() => CheckboxComponent),
1122
+ },
1123
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<label class=\"fk-checkbox__layout\">\n <input\n #inputEl\n type=\"checkbox\"\n class=\"fk-checkbox__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onChanged($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <span class=\"fk-checkbox__box\">\n <svg\n class=\"fk-checkbox__check\"\n viewBox=\"0 0 12 10\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"1.5 5 4.5 8 10.5 2\" />\n </svg>\n </span>\n <span class=\"fk-checkbox__label\">\n @if (label()) {\n {{ label() }}\n }\n <ng-content />\n </span>\n</label>\n", styles: [":host{display:block}.fk-checkbox__layout{display:inline-flex;align-items:flex-start;gap:var(--fk-checkbox-gap, .5rem);cursor:pointer}.fk-checkbox__native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fk-checkbox__box{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:var(--fk-checkbox-size, 1.125rem);height:var(--fk-checkbox-size, 1.125rem);margin-top:.125rem;border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-checkbox-border-radius, var(--fk-radius-sm, .25rem));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));transition:border-color .15s ease,background-color .15s ease,box-shadow .15s ease}.fk-checkbox__check{width:.75rem;height:.75rem;opacity:0;transform:scale(.5);transition:opacity .1s ease,transform .1s ease}.fk-checkbox__label{font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));-webkit-user-select:none;user-select:none}.fk-checkbox__layout:hover .fk-checkbox__box{border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-checkbox__native:focus-visible~.fk-checkbox__box{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}:host(.fk-checkbox--checked) .fk-checkbox__box{background:var(--fk-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-checkbox-checked-bg, var(--fk-color-primary, #0a84ff))}:host(.fk-checkbox--checked) .fk-checkbox__check{opacity:1;transform:scale(1);color:var(--fk-checkbox-check-color, #ffffff)}:host(.fk-checkbox--disabled) .fk-checkbox__layout{cursor:not-allowed}:host(.fk-checkbox--disabled) .fk-checkbox__box{opacity:var(--fk-input-disabled-opacity, .5)}:host(.fk-checkbox--disabled) .fk-checkbox__label{opacity:var(--fk-input-disabled-opacity, .5)}:host-context(.fk-form-field--invalid) .fk-checkbox__box{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1124
+ }
1125
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CheckboxComponent, decorators: [{
1126
+ type: Component,
1127
+ args: [{ selector: 'fk-checkbox', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1128
+ {
1129
+ provide: FIELD_CONTROL,
1130
+ useExisting: forwardRef(() => CheckboxComponent),
1131
+ },
1132
+ ], template: "<label class=\"fk-checkbox__layout\">\n <input\n #inputEl\n type=\"checkbox\"\n class=\"fk-checkbox__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onChanged($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <span class=\"fk-checkbox__box\">\n <svg\n class=\"fk-checkbox__check\"\n viewBox=\"0 0 12 10\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"1.5 5 4.5 8 10.5 2\" />\n </svg>\n </span>\n <span class=\"fk-checkbox__label\">\n @if (label()) {\n {{ label() }}\n }\n <ng-content />\n </span>\n</label>\n", styles: [":host{display:block}.fk-checkbox__layout{display:inline-flex;align-items:flex-start;gap:var(--fk-checkbox-gap, .5rem);cursor:pointer}.fk-checkbox__native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fk-checkbox__box{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:var(--fk-checkbox-size, 1.125rem);height:var(--fk-checkbox-size, 1.125rem);margin-top:.125rem;border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-checkbox-border-radius, var(--fk-radius-sm, .25rem));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));transition:border-color .15s ease,background-color .15s ease,box-shadow .15s ease}.fk-checkbox__check{width:.75rem;height:.75rem;opacity:0;transform:scale(.5);transition:opacity .1s ease,transform .1s ease}.fk-checkbox__label{font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));-webkit-user-select:none;user-select:none}.fk-checkbox__layout:hover .fk-checkbox__box{border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-checkbox__native:focus-visible~.fk-checkbox__box{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}:host(.fk-checkbox--checked) .fk-checkbox__box{background:var(--fk-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-checkbox-checked-bg, var(--fk-color-primary, #0a84ff))}:host(.fk-checkbox--checked) .fk-checkbox__check{opacity:1;transform:scale(1);color:var(--fk-checkbox-check-color, #ffffff)}:host(.fk-checkbox--disabled) .fk-checkbox__layout{cursor:not-allowed}:host(.fk-checkbox--disabled) .fk-checkbox__box{opacity:var(--fk-input-disabled-opacity, .5)}:host(.fk-checkbox--disabled) .fk-checkbox__label{opacity:var(--fk-input-disabled-opacity, .5)}:host-context(.fk-form-field--invalid) .fk-checkbox__box{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}\n"] }]
1133
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hostClass: [{
1134
+ type: HostBinding,
1135
+ args: ['class']
1136
+ }] } });
1137
+
1138
+ let nextDateInputId = 0;
1139
+ class DateInputComponent {
1140
+ cdr = inject(ChangeDetectorRef);
1141
+ ngControl = inject(NgControl, {
1142
+ optional: true,
1143
+ self: true,
1144
+ });
1145
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1146
+ // ===== INPUTS =====
1147
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1148
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1149
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1150
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1151
+ isReadonly = input(false, { ...(ngDevMode ? { debugName: "isReadonly" } : /* istanbul ignore next */ {}), alias: 'readonly' });
1152
+ /** Earliest selectable date as an ISO 8601 date string (YYYY-MM-DD). */
1153
+ min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
1154
+ /** Latest selectable date as an ISO 8601 date string (YYYY-MM-DD). */
1155
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
1156
+ // ===== FieldControl =====
1157
+ id = `fk-date-input-${nextDateInputId++}`;
1158
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1159
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1160
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1161
+ classes = computed(() => [
1162
+ 'fk-date-input',
1163
+ this.isFocused() ? 'fk-date-input--focused' : '',
1164
+ this.isDisabledSignal() ? 'fk-date-input--disabled' : '',
1165
+ this.isReadonly() ? 'fk-date-input--readonly' : '',
1166
+ this.className(),
1167
+ ]
1168
+ .filter(Boolean)
1169
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1170
+ get hostClass() {
1171
+ return this.classes();
1172
+ }
1173
+ get focused() {
1174
+ return this.isFocused();
1175
+ }
1176
+ get empty() {
1177
+ return !this.value;
1178
+ }
1179
+ get disabled() {
1180
+ return this.isDisabledSignal();
1181
+ }
1182
+ get required() {
1183
+ return this.isRequired();
1184
+ }
1185
+ get readonly() {
1186
+ return this.isReadonly();
1187
+ }
1188
+ get describedByIds() {
1189
+ return this.describedByIdsSignal();
1190
+ }
1191
+ constructor() {
1192
+ if (this.ngControl) {
1193
+ this.ngControl.valueAccessor = this;
1194
+ }
1195
+ }
1196
+ setDescribedByIds(ids) {
1197
+ this.describedByIdsSignal.set(ids);
1198
+ }
1199
+ onContainerClick(_event) {
1200
+ const input = this.inputRef()?.nativeElement;
1201
+ if (input && !this.isDisabledSignal()) {
1202
+ input.focus();
1203
+ }
1204
+ }
1205
+ // ===== ControlValueAccessor =====
1206
+ value = '';
1207
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1208
+ onChange = () => { };
1209
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1210
+ onTouched = () => { };
1211
+ writeValue(value) {
1212
+ this.value = value ?? '';
1213
+ const input = this.inputRef()?.nativeElement;
1214
+ if (input) {
1215
+ input.value = this.value;
1216
+ }
1217
+ }
1218
+ registerOnChange(fn) {
1219
+ this.onChange = fn;
1220
+ }
1221
+ registerOnTouched(fn) {
1222
+ this.onTouched = fn;
1223
+ }
1224
+ setDisabledState(isDisabled) {
1225
+ this.isDisabledSignal.set(isDisabled);
1226
+ this.cdr.markForCheck();
1227
+ }
1228
+ // ===== EVENT HANDLERS =====
1229
+ onInput(event) {
1230
+ const input = event.target;
1231
+ this.value = input.value;
1232
+ this.onChange(this.value);
1233
+ }
1234
+ onFocus() {
1235
+ this.isFocused.set(true);
1236
+ }
1237
+ onBlur() {
1238
+ this.isFocused.set(false);
1239
+ this.onTouched();
1240
+ }
1241
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DateInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1242
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: DateInputComponent, isStandalone: true, selector: "fk-date-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1243
+ {
1244
+ provide: FIELD_CONTROL,
1245
+ useExisting: forwardRef(() => DateInputComponent),
1246
+ },
1247
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #inputEl\n type=\"date\"\n class=\"fk-date-input__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-date-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-date-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-date-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-date-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-date-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-date-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-date-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1248
+ }
1249
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DateInputComponent, decorators: [{
1250
+ type: Component,
1251
+ args: [{ selector: 'fk-date-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1252
+ {
1253
+ provide: FIELD_CONTROL,
1254
+ useExisting: forwardRef(() => DateInputComponent),
1255
+ },
1256
+ ], template: "<input\n #inputEl\n type=\"date\"\n class=\"fk-date-input__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-date-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-date-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-date-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-date-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-date-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-date-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-date-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1257
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], isReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], hostClass: [{
1258
+ type: HostBinding,
1259
+ args: ['class']
1260
+ }] } });
1261
+
1262
+ let nextDatetimeInputId = 0;
1263
+ class DatetimeInputComponent {
1264
+ cdr = inject(ChangeDetectorRef);
1265
+ ngControl = inject(NgControl, {
1266
+ optional: true,
1267
+ self: true,
1268
+ });
1269
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1270
+ // ===== INPUTS =====
1271
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1272
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1273
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1274
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1275
+ isReadonly = input(false, { ...(ngDevMode ? { debugName: "isReadonly" } : /* istanbul ignore next */ {}), alias: 'readonly' });
1276
+ /** Earliest selectable datetime as an ISO 8601 local datetime string. */
1277
+ min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
1278
+ /** Latest selectable datetime as an ISO 8601 local datetime string. */
1279
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
1280
+ // ===== FieldControl =====
1281
+ id = `fk-datetime-input-${nextDatetimeInputId++}`;
1282
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1283
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1284
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1285
+ classes = computed(() => [
1286
+ 'fk-datetime-input',
1287
+ this.isFocused() ? 'fk-datetime-input--focused' : '',
1288
+ this.isDisabledSignal() ? 'fk-datetime-input--disabled' : '',
1289
+ this.isReadonly() ? 'fk-datetime-input--readonly' : '',
1290
+ this.className(),
1291
+ ]
1292
+ .filter(Boolean)
1293
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1294
+ get hostClass() {
1295
+ return this.classes();
1296
+ }
1297
+ get focused() {
1298
+ return this.isFocused();
1299
+ }
1300
+ get empty() {
1301
+ return !this.value;
1302
+ }
1303
+ get disabled() {
1304
+ return this.isDisabledSignal();
1305
+ }
1306
+ get required() {
1307
+ return this.isRequired();
1308
+ }
1309
+ get readonly() {
1310
+ return this.isReadonly();
1311
+ }
1312
+ get describedByIds() {
1313
+ return this.describedByIdsSignal();
1314
+ }
1315
+ constructor() {
1316
+ if (this.ngControl) {
1317
+ this.ngControl.valueAccessor = this;
1318
+ }
1319
+ }
1320
+ setDescribedByIds(ids) {
1321
+ this.describedByIdsSignal.set(ids);
1322
+ }
1323
+ onContainerClick(_event) {
1324
+ const input = this.inputRef()?.nativeElement;
1325
+ if (input && !this.isDisabledSignal()) {
1326
+ input.focus();
1327
+ }
1328
+ }
1329
+ // ===== ControlValueAccessor =====
1330
+ value = '';
1331
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1332
+ onChange = () => { };
1333
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1334
+ onTouched = () => { };
1335
+ writeValue(value) {
1336
+ this.value = value ?? '';
1337
+ const input = this.inputRef()?.nativeElement;
1338
+ if (input) {
1339
+ input.value = this.value;
1340
+ }
1341
+ }
1342
+ registerOnChange(fn) {
1343
+ this.onChange = fn;
1344
+ }
1345
+ registerOnTouched(fn) {
1346
+ this.onTouched = fn;
1347
+ }
1348
+ setDisabledState(isDisabled) {
1349
+ this.isDisabledSignal.set(isDisabled);
1350
+ this.cdr.markForCheck();
1351
+ }
1352
+ // ===== EVENT HANDLERS =====
1353
+ onInput(event) {
1354
+ const input = event.target;
1355
+ this.value = input.value;
1356
+ this.onChange(this.value);
1357
+ }
1358
+ onFocus() {
1359
+ this.isFocused.set(true);
1360
+ }
1361
+ onBlur() {
1362
+ this.isFocused.set(false);
1363
+ this.onTouched();
1364
+ }
1365
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatetimeInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1366
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: DatetimeInputComponent, isStandalone: true, selector: "fk-datetime-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1367
+ {
1368
+ provide: FIELD_CONTROL,
1369
+ useExisting: forwardRef(() => DatetimeInputComponent),
1370
+ },
1371
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #inputEl\n type=\"datetime-local\"\n class=\"fk-datetime-input__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-datetime-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-datetime-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-datetime-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-datetime-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-datetime-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-datetime-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-datetime-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1372
+ }
1373
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DatetimeInputComponent, decorators: [{
1374
+ type: Component,
1375
+ args: [{ selector: 'fk-datetime-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1376
+ {
1377
+ provide: FIELD_CONTROL,
1378
+ useExisting: forwardRef(() => DatetimeInputComponent),
1379
+ },
1380
+ ], template: "<input\n #inputEl\n type=\"datetime-local\"\n class=\"fk-datetime-input__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-datetime-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-datetime-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-datetime-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-datetime-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-datetime-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-datetime-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-datetime-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1381
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], isReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], hostClass: [{
1382
+ type: HostBinding,
1383
+ args: ['class']
1384
+ }] } });
1385
+
1386
+ let nextFileUploadId = 0;
1387
+ class FileUploadComponent {
1388
+ cdr = inject(ChangeDetectorRef);
1389
+ ngControl = inject(NgControl, {
1390
+ optional: true,
1391
+ self: true,
1392
+ });
1393
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1394
+ // ===== INPUTS =====
1395
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1396
+ /** Placeholder text shown when no file has been selected. */
1397
+ placeholder = input('Choose file...', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1398
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1399
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1400
+ /** MIME types or file extensions the native file picker should accept. */
1401
+ accept = input(null, ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
1402
+ /** Whether the user can select more than one file at a time. */
1403
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
1404
+ // ===== FieldControl =====
1405
+ id = `fk-file-upload-${nextFileUploadId++}`;
1406
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1407
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1408
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1409
+ fileNames = signal([], ...(ngDevMode ? [{ debugName: "fileNames" }] : /* istanbul ignore next */ []));
1410
+ classes = computed(() => [
1411
+ 'fk-file-upload',
1412
+ this.isFocused() ? 'fk-file-upload--focused' : '',
1413
+ this.isDisabledSignal() ? 'fk-file-upload--disabled' : '',
1414
+ this.fileNames().length > 0 ? 'fk-file-upload--filled' : '',
1415
+ this.className(),
1416
+ ]
1417
+ .filter(Boolean)
1418
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1419
+ get hostClass() {
1420
+ return this.classes();
1421
+ }
1422
+ files = null;
1423
+ get focused() {
1424
+ return this.isFocused();
1425
+ }
1426
+ get empty() {
1427
+ return !this.files || this.files.length === 0;
1428
+ }
1429
+ get disabled() {
1430
+ return this.isDisabledSignal();
1431
+ }
1432
+ get required() {
1433
+ return this.isRequired();
1434
+ }
1435
+ get describedByIds() {
1436
+ return this.describedByIdsSignal();
1437
+ }
1438
+ constructor() {
1439
+ if (this.ngControl) {
1440
+ this.ngControl.valueAccessor = this;
1441
+ }
1442
+ }
1443
+ setDescribedByIds(ids) {
1444
+ this.describedByIdsSignal.set(ids);
1445
+ }
1446
+ onContainerClick(_event) {
1447
+ const input = this.inputRef()?.nativeElement;
1448
+ if (input && !this.isDisabledSignal()) {
1449
+ input.click();
1450
+ }
1451
+ }
1452
+ // ===== ControlValueAccessor =====
1453
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1454
+ onChange = () => { };
1455
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1456
+ onTouched = () => { };
1457
+ writeValue(value) {
1458
+ this.files = value;
1459
+ if (value && value.length > 0) {
1460
+ const names = [];
1461
+ for (let i = 0; i < value.length; i++) {
1462
+ names.push(value[i].name);
1463
+ }
1464
+ this.fileNames.set(names);
1465
+ }
1466
+ else {
1467
+ this.fileNames.set([]);
1468
+ }
1469
+ }
1470
+ registerOnChange(fn) {
1471
+ this.onChange = fn;
1472
+ }
1473
+ registerOnTouched(fn) {
1474
+ this.onTouched = fn;
1475
+ }
1476
+ setDisabledState(isDisabled) {
1477
+ this.isDisabledSignal.set(isDisabled);
1478
+ this.cdr.markForCheck();
1479
+ }
1480
+ // ===== EVENT HANDLERS =====
1481
+ onFileChange(event) {
1482
+ const input = event.target;
1483
+ this.files = input.files;
1484
+ if (this.files && this.files.length > 0) {
1485
+ const names = [];
1486
+ for (let i = 0; i < this.files.length; i++) {
1487
+ names.push(this.files[i].name);
1488
+ }
1489
+ this.fileNames.set(names);
1490
+ }
1491
+ else {
1492
+ this.fileNames.set([]);
1493
+ }
1494
+ this.onChange(this.files);
1495
+ }
1496
+ onFocus() {
1497
+ this.isFocused.set(true);
1498
+ }
1499
+ onBlur() {
1500
+ this.isFocused.set(false);
1501
+ this.onTouched();
1502
+ }
1503
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1504
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FileUploadComponent, isStandalone: true, selector: "fk-file-upload", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1505
+ {
1506
+ provide: FIELD_CONTROL,
1507
+ useExisting: forwardRef(() => FileUploadComponent),
1508
+ },
1509
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-file-upload__wrapper\">\n <input\n #inputEl\n type=\"file\"\n class=\"fk-file-upload__native\"\n [attr.id]=\"id\"\n [attr.accept]=\"accept()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [multiple]=\"multiple()\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onFileChange($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <div class=\"fk-file-upload__display\">\n <span class=\"fk-file-upload__text\">\n @if (fileNames().length > 0) {\n {{ fileNames().join(', ') }}\n } @else {\n {{ placeholder() }}\n }\n </span>\n <span class=\"fk-file-upload__button\">Browse</span>\n </div>\n</div>\n", styles: [":host{display:block}.fk-file-upload__wrapper{position:relative}.fk-file-upload__native{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.fk-file-upload__native:disabled{cursor:not-allowed}.fk-file-upload__display{display:flex;align-items:center;width:100%;box-sizing:border-box;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));transition:border-color .15s ease,box-shadow .15s ease}:host(:hover:not(.fk-file-upload--disabled)) .fk-file-upload__display{border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}:host(.fk-file-upload--focused) .fk-file-upload__display{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}:host(.fk-file-upload--disabled) .fk-file-upload__display{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-file-upload__text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}:host(.fk-file-upload--filled) .fk-file-upload__text{color:var(--fk-input-color, var(--fk-color-text, #1f2d3d))}.fk-file-upload__button{flex-shrink:0;margin-left:var(--fk-rhythm-3, .75rem);padding:2px 12px;font-size:calc(var(--fk-input-font-size, .875rem) * .85);color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));background:transparent;border:1px solid var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));border-radius:calc(var(--fk-input-border-radius, var(--fk-radius-md, .375rem)) - 2px)}:host-context(.fk-form-field--invalid) .fk-file-upload__display{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid):host(.fk-file-upload--focused) .fk-file-upload__display{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1510
+ }
1511
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileUploadComponent, decorators: [{
1512
+ type: Component,
1513
+ args: [{ selector: 'fk-file-upload', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1514
+ {
1515
+ provide: FIELD_CONTROL,
1516
+ useExisting: forwardRef(() => FileUploadComponent),
1517
+ },
1518
+ ], template: "<div class=\"fk-file-upload__wrapper\">\n <input\n #inputEl\n type=\"file\"\n class=\"fk-file-upload__native\"\n [attr.id]=\"id\"\n [attr.accept]=\"accept()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [multiple]=\"multiple()\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onFileChange($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <div class=\"fk-file-upload__display\">\n <span class=\"fk-file-upload__text\">\n @if (fileNames().length > 0) {\n {{ fileNames().join(', ') }}\n } @else {\n {{ placeholder() }}\n }\n </span>\n <span class=\"fk-file-upload__button\">Browse</span>\n </div>\n</div>\n", styles: [":host{display:block}.fk-file-upload__wrapper{position:relative}.fk-file-upload__native{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.fk-file-upload__native:disabled{cursor:not-allowed}.fk-file-upload__display{display:flex;align-items:center;width:100%;box-sizing:border-box;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));transition:border-color .15s ease,box-shadow .15s ease}:host(:hover:not(.fk-file-upload--disabled)) .fk-file-upload__display{border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}:host(.fk-file-upload--focused) .fk-file-upload__display{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}:host(.fk-file-upload--disabled) .fk-file-upload__display{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-file-upload__text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}:host(.fk-file-upload--filled) .fk-file-upload__text{color:var(--fk-input-color, var(--fk-color-text, #1f2d3d))}.fk-file-upload__button{flex-shrink:0;margin-left:var(--fk-rhythm-3, .75rem);padding:2px 12px;font-size:calc(var(--fk-input-font-size, .875rem) * .85);color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));background:transparent;border:1px solid var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));border-radius:calc(var(--fk-input-border-radius, var(--fk-radius-md, .375rem)) - 2px)}:host-context(.fk-form-field--invalid) .fk-file-upload__display{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid):host(.fk-file-upload--focused) .fk-file-upload__display{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1519
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], hostClass: [{
1520
+ type: HostBinding,
1521
+ args: ['class']
1522
+ }] } });
1523
+
1524
+ let nextInputId = 0;
1525
+ class InputComponent {
1526
+ cdr = inject(ChangeDetectorRef);
1527
+ ngControl = inject(NgControl, {
1528
+ optional: true,
1529
+ self: true,
1530
+ });
1531
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1532
+ // ===== INPUTS =====
1533
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1534
+ /** HTML input type attribute (e.g. "text", "email", "search"). */
1535
+ type = input('text', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
1536
+ /** Placeholder text shown when the input has no value. */
1537
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1538
+ /**
1539
+ * Native `autocomplete` attribute token (e.g. "username", "email",
1540
+ * "name", "off"). Null omits the attribute entirely. Set this on
1541
+ * identifying fields (login email, name) so browsers and password
1542
+ * managers behave correctly — see fk-password-input for the password
1543
+ * counterpart.
1544
+ */
1545
+ autocomplete = input(null, ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ []));
1546
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1547
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1548
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1549
+ isReadonly = input(false, { ...(ngDevMode ? { debugName: "isReadonly" } : /* istanbul ignore next */ {}), alias: 'readonly' });
1550
+ // ===== FieldControl =====
1551
+ id = `fk-input-${nextInputId++}`;
1552
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1553
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1554
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1555
+ classes = computed(() => [
1556
+ 'fk-input',
1557
+ this.isFocused() ? 'fk-input--focused' : '',
1558
+ this.isDisabledSignal() ? 'fk-input--disabled' : '',
1559
+ this.isReadonly() ? 'fk-input--readonly' : '',
1560
+ this.className(),
1561
+ ]
1562
+ .filter(Boolean)
1563
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1564
+ get hostClass() {
1565
+ return this.classes();
1566
+ }
1567
+ get focused() {
1568
+ return this.isFocused();
1569
+ }
1570
+ get empty() {
1571
+ return !this.value;
1572
+ }
1573
+ get disabled() {
1574
+ return this.isDisabledSignal();
1575
+ }
1576
+ get required() {
1577
+ return this.isRequired();
1578
+ }
1579
+ get readonly() {
1580
+ return this.isReadonly();
1581
+ }
1582
+ constructor() {
1583
+ if (this.ngControl) {
1584
+ this.ngControl.valueAccessor = this;
1585
+ }
1586
+ }
1587
+ get describedByIds() {
1588
+ return this.describedByIdsSignal();
1589
+ }
1590
+ setDescribedByIds(ids) {
1591
+ this.describedByIdsSignal.set(ids);
1592
+ }
1593
+ onContainerClick(_event) {
1594
+ const input = this.inputRef()?.nativeElement;
1595
+ if (input && !this.isDisabledSignal()) {
1596
+ input.focus();
1597
+ }
1598
+ }
1599
+ // ===== ControlValueAccessor =====
1600
+ value = '';
1601
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1602
+ onChange = () => { };
1603
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1604
+ onTouched = () => { };
1605
+ writeValue(value) {
1606
+ this.value = value ?? '';
1607
+ const input = this.inputRef()?.nativeElement;
1608
+ if (input) {
1609
+ input.value = this.value;
1610
+ }
1611
+ }
1612
+ registerOnChange(fn) {
1613
+ this.onChange = fn;
1614
+ }
1615
+ registerOnTouched(fn) {
1616
+ this.onTouched = fn;
1617
+ }
1618
+ setDisabledState(isDisabled) {
1619
+ this.isDisabledSignal.set(isDisabled);
1620
+ this.cdr.markForCheck();
1621
+ }
1622
+ // ===== EVENT HANDLERS =====
1623
+ onInput(event) {
1624
+ const input = event.target;
1625
+ this.value = input.value;
1626
+ this.onChange(this.value);
1627
+ }
1628
+ onFocus() {
1629
+ this.isFocused.set(true);
1630
+ }
1631
+ onBlur() {
1632
+ this.isFocused.set(false);
1633
+ this.onTouched();
1634
+ }
1635
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1636
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: InputComponent, isStandalone: true, selector: "fk-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1637
+ {
1638
+ provide: FIELD_CONTROL,
1639
+ useExisting: forwardRef(() => InputComponent),
1640
+ },
1641
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #inputEl\n class=\"fk-input__native\"\n [attr.id]=\"id\"\n [attr.type]=\"type()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-input__native:read-only{cursor:default}:host-context(.fk-form-field--has-prefix) .fk-input__native{padding-left:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--has-suffix) .fk-input__native{padding-right:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--invalid) .fk-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1642
+ }
1643
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: InputComponent, decorators: [{
1644
+ type: Component,
1645
+ args: [{ selector: 'fk-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1646
+ {
1647
+ provide: FIELD_CONTROL,
1648
+ useExisting: forwardRef(() => InputComponent),
1649
+ },
1650
+ ], template: "<input\n #inputEl\n class=\"fk-input__native\"\n [attr.id]=\"id\"\n [attr.type]=\"type()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-input__native:read-only{cursor:default}:host-context(.fk-form-field--has-prefix) .fk-input__native{padding-left:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--has-suffix) .fk-input__native{padding-right:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--invalid) .fk-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1651
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], isReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], hostClass: [{
1652
+ type: HostBinding,
1653
+ args: ['class']
1654
+ }] } });
1655
+
1656
+ let nextNumberInputId = 0;
1657
+ class NumberInputComponent {
1658
+ cdr = inject(ChangeDetectorRef);
1659
+ ngControl = inject(NgControl, {
1660
+ optional: true,
1661
+ self: true,
1662
+ });
1663
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1664
+ // ===== INPUTS =====
1665
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1666
+ /** Placeholder text shown when the input has no value. */
1667
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1668
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1669
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1670
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1671
+ isReadonly = input(false, { ...(ngDevMode ? { debugName: "isReadonly" } : /* istanbul ignore next */ {}), alias: 'readonly' });
1672
+ /** Minimum allowed numeric value. */
1673
+ min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
1674
+ /** Maximum allowed numeric value. */
1675
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
1676
+ /** Increment step for the native number input spinner. */
1677
+ step = input(null, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
1678
+ // ===== FieldControl =====
1679
+ id = `fk-number-input-${nextNumberInputId++}`;
1680
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1681
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1682
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1683
+ classes = computed(() => [
1684
+ 'fk-number-input',
1685
+ this.isFocused() ? 'fk-number-input--focused' : '',
1686
+ this.isDisabledSignal() ? 'fk-number-input--disabled' : '',
1687
+ this.isReadonly() ? 'fk-number-input--readonly' : '',
1688
+ this.className(),
1689
+ ]
1690
+ .filter(Boolean)
1691
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1692
+ get hostClass() {
1693
+ return this.classes();
1694
+ }
1695
+ get focused() {
1696
+ return this.isFocused();
1697
+ }
1698
+ get empty() {
1699
+ return this.value === null || this.value === undefined;
1700
+ }
1701
+ get disabled() {
1702
+ return this.isDisabledSignal();
1703
+ }
1704
+ get required() {
1705
+ return this.isRequired();
1706
+ }
1707
+ get readonly() {
1708
+ return this.isReadonly();
1709
+ }
1710
+ get describedByIds() {
1711
+ return this.describedByIdsSignal();
1712
+ }
1713
+ constructor() {
1714
+ if (this.ngControl) {
1715
+ this.ngControl.valueAccessor = this;
1716
+ }
1717
+ }
1718
+ setDescribedByIds(ids) {
1719
+ this.describedByIdsSignal.set(ids);
1720
+ }
1721
+ onContainerClick(_event) {
1722
+ const input = this.inputRef()?.nativeElement;
1723
+ if (input && !this.isDisabledSignal()) {
1724
+ input.focus();
1725
+ }
1726
+ }
1727
+ // ===== ControlValueAccessor =====
1728
+ value = null;
1729
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1730
+ onChange = () => { };
1731
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1732
+ onTouched = () => { };
1733
+ writeValue(value) {
1734
+ this.value = value ?? null;
1735
+ const input = this.inputRef()?.nativeElement;
1736
+ if (input) {
1737
+ input.value = this.value !== null ? String(this.value) : '';
1738
+ }
1739
+ }
1740
+ registerOnChange(fn) {
1741
+ this.onChange = fn;
1742
+ }
1743
+ registerOnTouched(fn) {
1744
+ this.onTouched = fn;
1745
+ }
1746
+ setDisabledState(isDisabled) {
1747
+ this.isDisabledSignal.set(isDisabled);
1748
+ this.cdr.markForCheck();
1749
+ }
1750
+ // ===== EVENT HANDLERS =====
1751
+ onInput(event) {
1752
+ const input = event.target;
1753
+ this.value = input.value === '' ? null : Number(input.value);
1754
+ this.onChange(this.value);
1755
+ }
1756
+ onFocus() {
1757
+ this.isFocused.set(true);
1758
+ }
1759
+ onBlur() {
1760
+ this.isFocused.set(false);
1761
+ this.onTouched();
1762
+ }
1763
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NumberInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1764
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: NumberInputComponent, isStandalone: true, selector: "fk-number-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1765
+ {
1766
+ provide: FIELD_CONTROL,
1767
+ useExisting: forwardRef(() => NumberInputComponent),
1768
+ },
1769
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #inputEl\n type=\"number\"\n class=\"fk-number-input__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.step]=\"step()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-number-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-number-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-number-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-number-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-number-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-number-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-number-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-number-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1770
+ }
1771
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NumberInputComponent, decorators: [{
1772
+ type: Component,
1773
+ args: [{ selector: 'fk-number-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1774
+ {
1775
+ provide: FIELD_CONTROL,
1776
+ useExisting: forwardRef(() => NumberInputComponent),
1777
+ },
1778
+ ], template: "<input\n #inputEl\n type=\"number\"\n class=\"fk-number-input__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.step]=\"step()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-number-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-number-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-number-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-number-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-number-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-number-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-number-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-number-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1779
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], isReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], hostClass: [{
1780
+ type: HostBinding,
1781
+ args: ['class']
1782
+ }] } });
1783
+
1784
+ let nextPasswordId = 0;
1785
+ const ICON_SIZE_PRESETS = ['xs', 'sm', 'md', 'lg', 'xl'];
1786
+ const ICON_COLOR_PRESETS = ['default', 'muted', 'primary', 'danger', 'success'];
1787
+ class PasswordInputComponent {
1788
+ cdr = inject(ChangeDetectorRef);
1789
+ ngControl = inject(NgControl, {
1790
+ optional: true,
1791
+ self: true,
1792
+ });
1793
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
1794
+ // ===== INPUTS =====
1795
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1796
+ /** Placeholder text shown when the input has no value. */
1797
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1798
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1799
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1800
+ /** Autocomplete hint passed to the native input — use "current-password" or "new-password". */
1801
+ autocomplete = input('current-password', ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ []));
1802
+ /** Whether the show/hide password toggle button is rendered. */
1803
+ showToggle = input(true, ...(ngDevMode ? [{ debugName: "showToggle" }] : /* istanbul ignore next */ []));
1804
+ /** Icon size for the visibility toggle button. */
1805
+ toggleIconSize = input('sm', ...(ngDevMode ? [{ debugName: "toggleIconSize" }] : /* istanbul ignore next */ []));
1806
+ /** Icon color for the visibility toggle button. */
1807
+ toggleIconColor = input('muted', ...(ngDevMode ? [{ debugName: "toggleIconColor" }] : /* istanbul ignore next */ []));
1808
+ // ===== INTERNAL STATE =====
1809
+ visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
1810
+ // ===== FieldControl =====
1811
+ id = `fk-password-${nextPasswordId++}`;
1812
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1813
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1814
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1815
+ classes = computed(() => [
1816
+ 'fk-password-input',
1817
+ this.showToggle() ? 'fk-password-input--has-toggle' : '',
1818
+ this.isFocused() ? 'fk-password-input--focused' : '',
1819
+ this.isDisabledSignal() ? 'fk-password-input--disabled' : '',
1820
+ this.className(),
1821
+ ]
1822
+ .filter(Boolean)
1823
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1824
+ toggleSizeStyle = computed(() => {
1825
+ const size = this.toggleIconSize();
1826
+ if (ICON_SIZE_PRESETS.includes(size)) {
1827
+ return `var(--fk-icon-size-${size})`;
1828
+ }
1829
+ return size;
1830
+ }, ...(ngDevMode ? [{ debugName: "toggleSizeStyle" }] : /* istanbul ignore next */ []));
1831
+ toggleColorStyle = computed(() => {
1832
+ const color = this.toggleIconColor();
1833
+ if (color === 'inherit') {
1834
+ return null;
1835
+ }
1836
+ if (ICON_COLOR_PRESETS.includes(color)) {
1837
+ return `var(--fk-icon-color-${color})`;
1838
+ }
1839
+ return color;
1840
+ }, ...(ngDevMode ? [{ debugName: "toggleColorStyle" }] : /* istanbul ignore next */ []));
1841
+ get hostClass() {
1842
+ return this.classes();
1843
+ }
1844
+ get focused() {
1845
+ return this.isFocused();
1846
+ }
1847
+ get empty() {
1848
+ return !this.value;
1849
+ }
1850
+ get disabled() {
1851
+ return this.isDisabledSignal();
1852
+ }
1853
+ get required() {
1854
+ return this.isRequired();
1855
+ }
1856
+ get describedByIds() {
1857
+ return this.describedByIdsSignal();
1858
+ }
1859
+ constructor() {
1860
+ if (this.ngControl) {
1861
+ this.ngControl.valueAccessor = this;
1862
+ }
1863
+ }
1864
+ setDescribedByIds(ids) {
1865
+ this.describedByIdsSignal.set(ids);
1866
+ }
1867
+ onContainerClick(_event) {
1868
+ const input = this.inputRef()?.nativeElement;
1869
+ if (input && !this.isDisabledSignal()) {
1870
+ input.focus();
1871
+ }
1872
+ }
1873
+ /** Toggles the password field between visible plain text and masked mode. */
1874
+ toggleVisibility() {
1875
+ this.visible.update((v) => !v);
1876
+ }
1877
+ // ===== ControlValueAccessor =====
1878
+ value = '';
1879
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1880
+ onChange = () => { };
1881
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1882
+ onTouched = () => { };
1883
+ writeValue(value) {
1884
+ this.value = value ?? '';
1885
+ const input = this.inputRef()?.nativeElement;
1886
+ if (input) {
1887
+ input.value = this.value;
1888
+ }
1889
+ }
1890
+ registerOnChange(fn) {
1891
+ this.onChange = fn;
1892
+ }
1893
+ registerOnTouched(fn) {
1894
+ this.onTouched = fn;
1895
+ }
1896
+ setDisabledState(isDisabled) {
1897
+ this.isDisabledSignal.set(isDisabled);
1898
+ this.cdr.markForCheck();
1899
+ }
1900
+ // ===== EVENT HANDLERS =====
1901
+ onInput(event) {
1902
+ const input = event.target;
1903
+ this.value = input.value;
1904
+ this.onChange(this.value);
1905
+ }
1906
+ onFocus() {
1907
+ this.isFocused.set(true);
1908
+ }
1909
+ onBlur() {
1910
+ this.isFocused.set(false);
1911
+ this.onTouched();
1912
+ }
1913
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PasswordInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1914
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: PasswordInputComponent, isStandalone: true, selector: "fk-password-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, showToggle: { classPropertyName: "showToggle", publicName: "showToggle", isSignal: true, isRequired: false, transformFunction: null }, toggleIconSize: { classPropertyName: "toggleIconSize", publicName: "toggleIconSize", isSignal: true, isRequired: false, transformFunction: null }, toggleIconColor: { classPropertyName: "toggleIconColor", publicName: "toggleIconColor", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
1915
+ {
1916
+ provide: FIELD_CONTROL,
1917
+ useExisting: forwardRef(() => PasswordInputComponent),
1918
+ },
1919
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-password-input__wrapper\">\n <input\n #inputEl\n class=\"fk-password-input__native\"\n [attr.id]=\"id\"\n [attr.type]=\"visible() ? 'text' : 'password'\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabledSignal()\"\n [attr.autocomplete]=\"autocomplete()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n @if (showToggle()) {\n <button\n type=\"button\"\n class=\"fk-password-input__toggle\"\n [attr.aria-label]=\"visible() ? 'Hide password' : 'Show password'\"\n [disabled]=\"isDisabledSignal()\"\n [style.font-size]=\"toggleSizeStyle()\"\n [style.color]=\"toggleColorStyle()\"\n (click)=\"toggleVisibility()\"\n tabindex=\"-1\"\n >\n @if (visible()) {\n <svg\n class=\"fk-password-input__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n >\n <path\n d=\"M13.455 13.455A7.553 7.553 0 0 1 9 15c-4.5 0-7.5-6-7.5-6a13.838 13.838 0 0 1 3.795-4.455\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M7.425 3.18A6.84 6.84 0 0 1 9 3c4.5 0 7.5 6 7.5 6a13.875 13.875 0 0 1-1.62 2.393\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <line\n x1=\"1.5\"\n y1=\"1.5\"\n x2=\"16.5\"\n y2=\"16.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n } @else {\n <svg\n class=\"fk-password-input__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n >\n <path\n d=\"M1.5 9s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6S1.5 9 1.5 9z\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <circle\n cx=\"9\"\n cy=\"9\"\n r=\"2.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </button>\n }\n</div>\n", styles: [":host{display:block}.fk-password-input__wrapper{position:relative;display:flex;align-items:center}.fk-password-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-password-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-password-input__native:hover:not(:disabled){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-password-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-password-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}:host(.fk-password-input--has-toggle) .fk-password-input__native{padding-right:2.5rem}.fk-password-input__toggle{position:absolute;right:.5rem;display:flex;align-items:center;justify-content:center;padding:.25rem;background:none;border:none;border-radius:var(--fk-radius-sm, .25rem);cursor:pointer;transition:color .15s ease}.fk-password-input__toggle:hover:not(:disabled){color:var(--fk-color-text, #1f2d3d)}.fk-password-input__toggle:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-password-input__icon{width:1em;height:1em;display:block}:host-context(.fk-form-field--has-prefix) .fk-password-input__native{padding-left:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--invalid) .fk-password-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-password-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1920
+ }
1921
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: PasswordInputComponent, decorators: [{
1922
+ type: Component,
1923
+ args: [{ selector: 'fk-password-input', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1924
+ {
1925
+ provide: FIELD_CONTROL,
1926
+ useExisting: forwardRef(() => PasswordInputComponent),
1927
+ },
1928
+ ], template: "<div class=\"fk-password-input__wrapper\">\n <input\n #inputEl\n class=\"fk-password-input__native\"\n [attr.id]=\"id\"\n [attr.type]=\"visible() ? 'text' : 'password'\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabledSignal()\"\n [attr.autocomplete]=\"autocomplete()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n @if (showToggle()) {\n <button\n type=\"button\"\n class=\"fk-password-input__toggle\"\n [attr.aria-label]=\"visible() ? 'Hide password' : 'Show password'\"\n [disabled]=\"isDisabledSignal()\"\n [style.font-size]=\"toggleSizeStyle()\"\n [style.color]=\"toggleColorStyle()\"\n (click)=\"toggleVisibility()\"\n tabindex=\"-1\"\n >\n @if (visible()) {\n <svg\n class=\"fk-password-input__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n >\n <path\n d=\"M13.455 13.455A7.553 7.553 0 0 1 9 15c-4.5 0-7.5-6-7.5-6a13.838 13.838 0 0 1 3.795-4.455\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M7.425 3.18A6.84 6.84 0 0 1 9 3c4.5 0 7.5 6 7.5 6a13.875 13.875 0 0 1-1.62 2.393\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <line\n x1=\"1.5\"\n y1=\"1.5\"\n x2=\"16.5\"\n y2=\"16.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n } @else {\n <svg\n class=\"fk-password-input__icon\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n >\n <path\n d=\"M1.5 9s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6S1.5 9 1.5 9z\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <circle\n cx=\"9\"\n cy=\"9\"\n r=\"2.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </button>\n }\n</div>\n", styles: [":host{display:block}.fk-password-input__wrapper{position:relative;display:flex;align-items:center}.fk-password-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-password-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-password-input__native:hover:not(:disabled){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-password-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-password-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}:host(.fk-password-input--has-toggle) .fk-password-input__native{padding-right:2.5rem}.fk-password-input__toggle{position:absolute;right:.5rem;display:flex;align-items:center;justify-content:center;padding:.25rem;background:none;border:none;border-radius:var(--fk-radius-sm, .25rem);cursor:pointer;transition:color .15s ease}.fk-password-input__toggle:hover:not(:disabled){color:var(--fk-color-text, #1f2d3d)}.fk-password-input__toggle:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-password-input__icon{width:1em;height:1em;display:block}:host-context(.fk-form-field--has-prefix) .fk-password-input__native{padding-left:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--invalid) .fk-password-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-password-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
1929
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], showToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToggle", required: false }] }], toggleIconSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleIconSize", required: false }] }], toggleIconColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleIconColor", required: false }] }], hostClass: [{
1930
+ type: HostBinding,
1931
+ args: ['class']
1932
+ }] } });
1933
+
1934
+ let nextRadioId = 0;
1935
+ class RadioComponent {
1936
+ cdr = inject(ChangeDetectorRef);
1937
+ ngControl = inject(NgControl, {
1938
+ optional: true,
1939
+ self: true,
1940
+ });
1941
+ // ===== INPUTS =====
1942
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1943
+ /** List of radio options to render. */
1944
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1945
+ /** HTML name attribute shared across all radio buttons in the group. */
1946
+ name = input(`fk-radio-group-${nextRadioId}`, ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
1947
+ // eslint-disable-next-line @angular-eslint/no-input-rename
1948
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
1949
+ // ===== FieldControl =====
1950
+ id = `fk-radio-${nextRadioId++}`;
1951
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
1952
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
1953
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
1954
+ classes = computed(() => [
1955
+ 'fk-radio',
1956
+ this.isFocused() ? 'fk-radio--focused' : '',
1957
+ this.isDisabledSignal() ? 'fk-radio--disabled' : '',
1958
+ this.className(),
1959
+ ]
1960
+ .filter(Boolean)
1961
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1962
+ get hostClass() {
1963
+ return this.classes();
1964
+ }
1965
+ value = '';
1966
+ get focused() {
1967
+ return this.isFocused();
1968
+ }
1969
+ get empty() {
1970
+ return this.value === '' || this.value === null || this.value === undefined;
1971
+ }
1972
+ get disabled() {
1973
+ return this.isDisabledSignal();
1974
+ }
1975
+ get required() {
1976
+ return this.isRequired();
1977
+ }
1978
+ get describedByIds() {
1979
+ return this.describedByIdsSignal();
1980
+ }
1981
+ constructor() {
1982
+ if (this.ngControl) {
1983
+ this.ngControl.valueAccessor = this;
1984
+ }
1985
+ }
1986
+ setDescribedByIds(ids) {
1987
+ this.describedByIdsSignal.set(ids);
1988
+ }
1989
+ // ===== ControlValueAccessor =====
1990
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1991
+ onChange = () => { };
1992
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1993
+ onTouched = () => { };
1994
+ writeValue(value) {
1995
+ this.value = value ?? '';
1996
+ }
1997
+ registerOnChange(fn) {
1998
+ this.onChange = fn;
1999
+ }
2000
+ registerOnTouched(fn) {
2001
+ this.onTouched = fn;
2002
+ }
2003
+ setDisabledState(isDisabled) {
2004
+ this.isDisabledSignal.set(isDisabled);
2005
+ this.cdr.markForCheck();
2006
+ }
2007
+ // ===== EVENT HANDLERS =====
2008
+ onSelectionChange(value) {
2009
+ this.value = value;
2010
+ this.onChange(this.value);
2011
+ }
2012
+ onFocus() {
2013
+ this.isFocused.set(true);
2014
+ }
2015
+ onBlur() {
2016
+ this.isFocused.set(false);
2017
+ this.onTouched();
2018
+ }
2019
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2020
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RadioComponent, isStandalone: true, selector: "fk-radio", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2021
+ {
2022
+ provide: FIELD_CONTROL,
2023
+ useExisting: forwardRef(() => RadioComponent),
2024
+ },
2025
+ ], ngImport: i0, template: "<div\n class=\"fk-radio__group\"\n role=\"radiogroup\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-required]=\"isRequired() || null\"\n>\n @for (option of options(); track option.value) {\n <label\n class=\"fk-radio__option\"\n [class.fk-radio__option--disabled]=\"option.disabled || isDisabledSignal()\"\n [class.fk-radio__option--checked]=\"option.value === value\"\n >\n <input\n type=\"radio\"\n class=\"fk-radio__native\"\n [attr.name]=\"name()\"\n [value]=\"option.value\"\n [checked]=\"option.value === value\"\n [disabled]=\"option.disabled || isDisabledSignal()\"\n (change)=\"onSelectionChange(option.value)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <span class=\"fk-radio__indicator\"></span>\n <span class=\"fk-radio__label\">{{ option.label }}</span>\n </label>\n }\n</div>\n", styles: [":host{display:block}.fk-radio__group{display:flex;flex-direction:column;gap:var(--fk-radio-gap, var(--fk-rhythm-2, .5rem))}.fk-radio__option{display:inline-flex;align-items:flex-start;gap:var(--fk-radio-gap, var(--fk-rhythm-2, .5rem));cursor:pointer}.fk-radio__native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fk-radio__indicator{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:var(--fk-radio-size, 1.125rem);height:var(--fk-radio-size, 1.125rem);margin-top:.125rem;border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-radius-full, 9999px);background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));transition:border-color .15s ease,background-color .15s ease,box-shadow .15s ease}.fk-radio__indicator:after{content:\"\";display:block;width:.5rem;height:.5rem;border-radius:var(--fk-radius-full, 9999px);background:transparent;transition:background-color .15s ease,transform .15s ease;transform:scale(0)}.fk-radio__label{font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));-webkit-user-select:none;user-select:none}.fk-radio__option:hover .fk-radio__indicator{border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-radio__native:focus-visible~.fk-radio__indicator{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-radio__option--checked .fk-radio__indicator{border-color:var(--fk-color-primary, #0a84ff)}.fk-radio__option--checked .fk-radio__indicator:after{background:var(--fk-color-primary, #0a84ff);transform:scale(1)}.fk-radio__option--disabled{cursor:not-allowed}.fk-radio__option--disabled .fk-radio__indicator,.fk-radio__option--disabled .fk-radio__label{opacity:var(--fk-input-disabled-opacity, .5)}:host-context(.fk-form-field--invalid) .fk-radio__indicator{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2026
+ }
2027
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RadioComponent, decorators: [{
2028
+ type: Component,
2029
+ args: [{ selector: 'fk-radio', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2030
+ {
2031
+ provide: FIELD_CONTROL,
2032
+ useExisting: forwardRef(() => RadioComponent),
2033
+ },
2034
+ ], template: "<div\n class=\"fk-radio__group\"\n role=\"radiogroup\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-required]=\"isRequired() || null\"\n>\n @for (option of options(); track option.value) {\n <label\n class=\"fk-radio__option\"\n [class.fk-radio__option--disabled]=\"option.disabled || isDisabledSignal()\"\n [class.fk-radio__option--checked]=\"option.value === value\"\n >\n <input\n type=\"radio\"\n class=\"fk-radio__native\"\n [attr.name]=\"name()\"\n [value]=\"option.value\"\n [checked]=\"option.value === value\"\n [disabled]=\"option.disabled || isDisabledSignal()\"\n (change)=\"onSelectionChange(option.value)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <span class=\"fk-radio__indicator\"></span>\n <span class=\"fk-radio__label\">{{ option.label }}</span>\n </label>\n }\n</div>\n", styles: [":host{display:block}.fk-radio__group{display:flex;flex-direction:column;gap:var(--fk-radio-gap, var(--fk-rhythm-2, .5rem))}.fk-radio__option{display:inline-flex;align-items:flex-start;gap:var(--fk-radio-gap, var(--fk-rhythm-2, .5rem));cursor:pointer}.fk-radio__native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fk-radio__indicator{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:var(--fk-radio-size, 1.125rem);height:var(--fk-radio-size, 1.125rem);margin-top:.125rem;border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-radius-full, 9999px);background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));transition:border-color .15s ease,background-color .15s ease,box-shadow .15s ease}.fk-radio__indicator:after{content:\"\";display:block;width:.5rem;height:.5rem;border-radius:var(--fk-radius-full, 9999px);background:transparent;transition:background-color .15s ease,transform .15s ease;transform:scale(0)}.fk-radio__label{font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));-webkit-user-select:none;user-select:none}.fk-radio__option:hover .fk-radio__indicator{border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-radio__native:focus-visible~.fk-radio__indicator{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-radio__option--checked .fk-radio__indicator{border-color:var(--fk-color-primary, #0a84ff)}.fk-radio__option--checked .fk-radio__indicator:after{background:var(--fk-color-primary, #0a84ff);transform:scale(1)}.fk-radio__option--disabled{cursor:not-allowed}.fk-radio__option--disabled .fk-radio__indicator,.fk-radio__option--disabled .fk-radio__label{opacity:var(--fk-input-disabled-opacity, .5)}:host-context(.fk-form-field--invalid) .fk-radio__indicator{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}\n"] }]
2035
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hostClass: [{
2036
+ type: HostBinding,
2037
+ args: ['class']
2038
+ }] } });
2039
+
2040
+ let nextSelectId = 0;
2041
+ class SelectComponent {
2042
+ cdr = inject(ChangeDetectorRef);
2043
+ ngControl = inject(NgControl, {
2044
+ optional: true,
2045
+ self: true,
2046
+ });
2047
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
2048
+ // ===== INPUTS =====
2049
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2050
+ /** List of options to render inside the native select element. */
2051
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
2052
+ /** Placeholder option displayed when no value is selected. */
2053
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
2054
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2055
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
2056
+ // ===== FieldControl =====
2057
+ id = `fk-select-${nextSelectId++}`;
2058
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
2059
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2060
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
2061
+ classes = computed(() => [
2062
+ 'fk-select',
2063
+ this.isFocused() ? 'fk-select--focused' : '',
2064
+ this.isDisabledSignal() ? 'fk-select--disabled' : '',
2065
+ this.className(),
2066
+ ]
2067
+ .filter(Boolean)
2068
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2069
+ get hostClass() {
2070
+ return this.classes();
2071
+ }
2072
+ value = '';
2073
+ get focused() {
2074
+ return this.isFocused();
2075
+ }
2076
+ get empty() {
2077
+ return this.value === '' || this.value === null || this.value === undefined;
2078
+ }
2079
+ get disabled() {
2080
+ return this.isDisabledSignal();
2081
+ }
2082
+ get required() {
2083
+ return this.isRequired();
2084
+ }
2085
+ get describedByIds() {
2086
+ return this.describedByIdsSignal();
2087
+ }
2088
+ constructor() {
2089
+ if (this.ngControl) {
2090
+ this.ngControl.valueAccessor = this;
2091
+ }
2092
+ }
2093
+ setDescribedByIds(ids) {
2094
+ this.describedByIdsSignal.set(ids);
2095
+ }
2096
+ onContainerClick(_event) {
2097
+ const select = this.inputRef()?.nativeElement;
2098
+ if (select && !this.isDisabledSignal()) {
2099
+ select.focus();
2100
+ }
2101
+ }
2102
+ // ===== ControlValueAccessor =====
2103
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2104
+ onChange = () => { };
2105
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2106
+ onTouched = () => { };
2107
+ writeValue(value) {
2108
+ this.value = value ?? '';
2109
+ const select = this.inputRef()?.nativeElement;
2110
+ if (select) {
2111
+ select.value = String(this.value);
2112
+ }
2113
+ }
2114
+ registerOnChange(fn) {
2115
+ this.onChange = fn;
2116
+ }
2117
+ registerOnTouched(fn) {
2118
+ this.onTouched = fn;
2119
+ }
2120
+ setDisabledState(isDisabled) {
2121
+ this.isDisabledSignal.set(isDisabled);
2122
+ this.cdr.markForCheck();
2123
+ }
2124
+ // ===== EVENT HANDLERS =====
2125
+ onSelectionChange(event) {
2126
+ const select = event.target;
2127
+ this.value = select.value;
2128
+ this.onChange(this.value);
2129
+ }
2130
+ onFocus() {
2131
+ this.isFocused.set(true);
2132
+ }
2133
+ onBlur() {
2134
+ this.isFocused.set(false);
2135
+ this.onTouched();
2136
+ }
2137
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2138
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SelectComponent, isStandalone: true, selector: "fk-select", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2139
+ {
2140
+ provide: FIELD_CONTROL,
2141
+ useExisting: forwardRef(() => SelectComponent),
2142
+ },
2143
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-select__wrapper\">\n <select\n #inputEl\n class=\"fk-select__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onSelectionChange($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n >\n @if (placeholder()) {\n <option value=\"\" disabled [selected]=\"!value\">\n {{ placeholder() }}\n </option>\n }\n @for (option of options(); track option.value) {\n <option\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [selected]=\"option.value === value\"\n >\n {{ option.label }}\n </option>\n }\n </select>\n <svg\n class=\"fk-select__arrow\"\n viewBox=\"0 0 12 8\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"1 1.5 6 6.5 11 1.5\" />\n </svg>\n</div>\n", styles: [":host{display:block}.fk-select__wrapper{position:relative;display:flex;align-items:center}.fk-select__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));padding-right:2.25rem;font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;appearance:none;cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease}.fk-select__native:hover:not(:disabled){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-select__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-select__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-select__arrow{position:absolute;right:.75rem;width:.75rem;height:.75rem;color:var(--fk-color-muted, #8a98a8);pointer-events:none}:host-context(.fk-form-field--invalid) .fk-select__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field:not(.fk-form-field--filled)) .fk-select__native{color:var(--fk-select-placeholder-color, var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8)))}:host-context(.fk-form-field--filled) .fk-select__native{color:var(--fk-input-color, var(--fk-color-text, #1f2d3d))}:host-context(.fk-form-field--has-prefix) .fk-select__native{padding-left:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--has-suffix) .fk-select__native{padding-right:calc(var(--fk-input-affix-width, 2.25rem) + 1.5rem)}:host-context(.fk-form-field--has-suffix) .fk-select__arrow{right:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--invalid) .fk-select__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2144
+ }
2145
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SelectComponent, decorators: [{
2146
+ type: Component,
2147
+ args: [{ selector: 'fk-select', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2148
+ {
2149
+ provide: FIELD_CONTROL,
2150
+ useExisting: forwardRef(() => SelectComponent),
2151
+ },
2152
+ ], template: "<div class=\"fk-select__wrapper\">\n <select\n #inputEl\n class=\"fk-select__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onSelectionChange($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n >\n @if (placeholder()) {\n <option value=\"\" disabled [selected]=\"!value\">\n {{ placeholder() }}\n </option>\n }\n @for (option of options(); track option.value) {\n <option\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [selected]=\"option.value === value\"\n >\n {{ option.label }}\n </option>\n }\n </select>\n <svg\n class=\"fk-select__arrow\"\n viewBox=\"0 0 12 8\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <polyline points=\"1 1.5 6 6.5 11 1.5\" />\n </svg>\n</div>\n", styles: [":host{display:block}.fk-select__wrapper{position:relative;display:flex;align-items:center}.fk-select__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));padding-right:2.25rem;font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;appearance:none;cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease}.fk-select__native:hover:not(:disabled){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-select__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-select__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-select__arrow{position:absolute;right:.75rem;width:.75rem;height:.75rem;color:var(--fk-color-muted, #8a98a8);pointer-events:none}:host-context(.fk-form-field--invalid) .fk-select__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field:not(.fk-form-field--filled)) .fk-select__native{color:var(--fk-select-placeholder-color, var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8)))}:host-context(.fk-form-field--filled) .fk-select__native{color:var(--fk-input-color, var(--fk-color-text, #1f2d3d))}:host-context(.fk-form-field--has-prefix) .fk-select__native{padding-left:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--has-suffix) .fk-select__native{padding-right:calc(var(--fk-input-affix-width, 2.25rem) + 1.5rem)}:host-context(.fk-form-field--has-suffix) .fk-select__arrow{right:var(--fk-input-affix-width, 2.25rem)}:host-context(.fk-form-field--invalid) .fk-select__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
2153
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hostClass: [{
2154
+ type: HostBinding,
2155
+ args: ['class']
2156
+ }] } });
2157
+
2158
+ let nextSliderId = 0;
2159
+ class SliderComponent {
2160
+ cdr = inject(ChangeDetectorRef);
2161
+ ngControl = inject(NgControl, {
2162
+ optional: true,
2163
+ self: true,
2164
+ });
2165
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
2166
+ // ===== INPUTS =====
2167
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2168
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2169
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
2170
+ /** Minimum value of the slider range. */
2171
+ min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
2172
+ /** Maximum value of the slider range. */
2173
+ max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
2174
+ /** Increment step between slider positions. */
2175
+ step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
2176
+ /** Whether the current numeric value is displayed alongside the thumb. */
2177
+ showValue = input(false, ...(ngDevMode ? [{ debugName: "showValue" }] : /* istanbul ignore next */ []));
2178
+ // ===== FieldControl =====
2179
+ id = `fk-slider-${nextSliderId++}`;
2180
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
2181
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2182
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
2183
+ classes = computed(() => [
2184
+ 'fk-slider',
2185
+ this.isFocused() ? 'fk-slider--focused' : '',
2186
+ this.isDisabledSignal() ? 'fk-slider--disabled' : '',
2187
+ this.className(),
2188
+ ]
2189
+ .filter(Boolean)
2190
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2191
+ get hostClass() {
2192
+ return this.classes();
2193
+ }
2194
+ currentValue = 0;
2195
+ get focused() {
2196
+ return this.isFocused();
2197
+ }
2198
+ get empty() {
2199
+ return false; // Sliders always have a value
2200
+ }
2201
+ get disabled() {
2202
+ return this.isDisabledSignal();
2203
+ }
2204
+ get required() {
2205
+ return this.isRequired();
2206
+ }
2207
+ get describedByIds() {
2208
+ return this.describedByIdsSignal();
2209
+ }
2210
+ constructor() {
2211
+ if (this.ngControl) {
2212
+ this.ngControl.valueAccessor = this;
2213
+ }
2214
+ }
2215
+ setDescribedByIds(ids) {
2216
+ this.describedByIdsSignal.set(ids);
2217
+ }
2218
+ onContainerClick(_event) {
2219
+ const input = this.inputRef()?.nativeElement;
2220
+ if (input && !this.isDisabledSignal()) {
2221
+ input.focus();
2222
+ }
2223
+ }
2224
+ // ===== ControlValueAccessor =====
2225
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2226
+ onChange = () => { };
2227
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2228
+ onTouched = () => { };
2229
+ writeValue(value) {
2230
+ this.currentValue = value ?? this.min();
2231
+ const input = this.inputRef()?.nativeElement;
2232
+ if (input) {
2233
+ input.value = String(this.currentValue);
2234
+ }
2235
+ }
2236
+ registerOnChange(fn) {
2237
+ this.onChange = fn;
2238
+ }
2239
+ registerOnTouched(fn) {
2240
+ this.onTouched = fn;
2241
+ }
2242
+ setDisabledState(isDisabled) {
2243
+ this.isDisabledSignal.set(isDisabled);
2244
+ this.cdr.markForCheck();
2245
+ }
2246
+ // ===== EVENT HANDLERS =====
2247
+ onInput(event) {
2248
+ const input = event.target;
2249
+ this.currentValue = Number(input.value);
2250
+ this.onChange(this.currentValue);
2251
+ }
2252
+ onFocus() {
2253
+ this.isFocused.set(true);
2254
+ }
2255
+ onBlur() {
2256
+ this.isFocused.set(false);
2257
+ this.onTouched();
2258
+ }
2259
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SliderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2260
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SliderComponent, isStandalone: true, selector: "fk-slider", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2261
+ {
2262
+ provide: FIELD_CONTROL,
2263
+ useExisting: forwardRef(() => SliderComponent),
2264
+ },
2265
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-slider__wrapper\">\n <input\n #inputEl\n type=\"range\"\n class=\"fk-slider__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"currentValue\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.step]=\"step()\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n @if (showValue()) {\n <span class=\"fk-slider__value\">{{ currentValue }}</span>\n }\n</div>\n", styles: [":host{display:block}.fk-slider__wrapper{display:flex;align-items:center;gap:var(--fk-rhythm-3, .75rem)}.fk-slider__native{flex:1;width:100%;height:var(--fk-slider-track-height, 6px);margin:0;padding:0;appearance:none;background:var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border:none;border-radius:var(--fk-slider-track-radius, 3px);outline:none;cursor:pointer;transition:background .15s ease}.fk-slider__native::-webkit-slider-thumb{appearance:none;width:var(--fk-slider-thumb-size, 18px);height:var(--fk-slider-thumb-size, 18px);border:2px solid var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));border-radius:50%;background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease}.fk-slider__native::-moz-range-thumb{width:var(--fk-slider-thumb-size, 18px);height:var(--fk-slider-thumb-size, 18px);border:2px solid var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));border-radius:50%;background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));cursor:pointer}.fk-slider__native:focus::-webkit-slider-thumb{box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-slider__native:focus::-moz-range-thumb{box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-slider__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-slider__native:disabled::-webkit-slider-thumb{cursor:not-allowed}.fk-slider__native:disabled::-moz-range-thumb{cursor:not-allowed}.fk-slider__value{min-width:2.5em;text-align:right;font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);color:var(--fk-input-color, var(--fk-color-text, #1f2d3d))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2266
+ }
2267
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SliderComponent, decorators: [{
2268
+ type: Component,
2269
+ args: [{ selector: 'fk-slider', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2270
+ {
2271
+ provide: FIELD_CONTROL,
2272
+ useExisting: forwardRef(() => SliderComponent),
2273
+ },
2274
+ ], template: "<div class=\"fk-slider__wrapper\">\n <input\n #inputEl\n type=\"range\"\n class=\"fk-slider__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"currentValue\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.step]=\"step()\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n @if (showValue()) {\n <span class=\"fk-slider__value\">{{ currentValue }}</span>\n }\n</div>\n", styles: [":host{display:block}.fk-slider__wrapper{display:flex;align-items:center;gap:var(--fk-rhythm-3, .75rem)}.fk-slider__native{flex:1;width:100%;height:var(--fk-slider-track-height, 6px);margin:0;padding:0;appearance:none;background:var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border:none;border-radius:var(--fk-slider-track-radius, 3px);outline:none;cursor:pointer;transition:background .15s ease}.fk-slider__native::-webkit-slider-thumb{appearance:none;width:var(--fk-slider-thumb-size, 18px);height:var(--fk-slider-thumb-size, 18px);border:2px solid var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));border-radius:50%;background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:border-color .15s ease,box-shadow .15s ease}.fk-slider__native::-moz-range-thumb{width:var(--fk-slider-thumb-size, 18px);height:var(--fk-slider-thumb-size, 18px);border:2px solid var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));border-radius:50%;background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));cursor:pointer}.fk-slider__native:focus::-webkit-slider-thumb{box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-slider__native:focus::-moz-range-thumb{box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-slider__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-slider__native:disabled::-webkit-slider-thumb{cursor:not-allowed}.fk-slider__native:disabled::-moz-range-thumb{cursor:not-allowed}.fk-slider__value{min-width:2.5em;text-align:right;font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);color:var(--fk-input-color, var(--fk-color-text, #1f2d3d))}\n"] }]
2275
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], hostClass: [{
2276
+ type: HostBinding,
2277
+ args: ['class']
2278
+ }] } });
2279
+
2280
+ let nextSwitchId = 0;
2281
+ class SwitchComponent {
2282
+ cdr = inject(ChangeDetectorRef);
2283
+ ngControl = inject(NgControl, {
2284
+ optional: true,
2285
+ self: true,
2286
+ });
2287
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
2288
+ // ===== INPUTS =====
2289
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2290
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2291
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
2292
+ // ===== INTERNAL STATE =====
2293
+ checked = signal(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
2294
+ // ===== FieldControl =====
2295
+ id = `fk-switch-${nextSwitchId++}`;
2296
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
2297
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2298
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
2299
+ classes = computed(() => [
2300
+ 'fk-switch',
2301
+ this.isFocused() ? 'fk-switch--focused' : '',
2302
+ this.isDisabledSignal() ? 'fk-switch--disabled' : '',
2303
+ this.checked() ? 'fk-switch--checked' : '',
2304
+ this.className(),
2305
+ ]
2306
+ .filter(Boolean)
2307
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2308
+ get hostClass() {
2309
+ return this.classes();
2310
+ }
2311
+ get focused() {
2312
+ return this.isFocused();
2313
+ }
2314
+ get empty() {
2315
+ return !this.checked();
2316
+ }
2317
+ get disabled() {
2318
+ return this.isDisabledSignal();
2319
+ }
2320
+ get required() {
2321
+ return this.isRequired();
2322
+ }
2323
+ get describedByIds() {
2324
+ return this.describedByIdsSignal();
2325
+ }
2326
+ constructor() {
2327
+ if (this.ngControl) {
2328
+ this.ngControl.valueAccessor = this;
2329
+ }
2330
+ }
2331
+ setDescribedByIds(ids) {
2332
+ this.describedByIdsSignal.set(ids);
2333
+ }
2334
+ onContainerClick(_event) {
2335
+ const input = this.inputRef()?.nativeElement;
2336
+ if (input && !this.isDisabledSignal()) {
2337
+ input.focus();
2338
+ input.click();
2339
+ }
2340
+ }
2341
+ // ===== ControlValueAccessor =====
2342
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2343
+ onChange = () => { };
2344
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2345
+ onTouched = () => { };
2346
+ writeValue(value) {
2347
+ this.checked.set(!!value);
2348
+ }
2349
+ registerOnChange(fn) {
2350
+ this.onChange = fn;
2351
+ }
2352
+ registerOnTouched(fn) {
2353
+ this.onTouched = fn;
2354
+ }
2355
+ setDisabledState(isDisabled) {
2356
+ this.isDisabledSignal.set(isDisabled);
2357
+ this.cdr.markForCheck();
2358
+ }
2359
+ // ===== EVENT HANDLERS =====
2360
+ onChanged(event) {
2361
+ const input = event.target;
2362
+ this.checked.set(input.checked);
2363
+ this.onChange(input.checked);
2364
+ }
2365
+ onFocus() {
2366
+ this.isFocused.set(true);
2367
+ }
2368
+ onBlur() {
2369
+ this.isFocused.set(false);
2370
+ this.onTouched();
2371
+ }
2372
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2373
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: SwitchComponent, isStandalone: true, selector: "fk-switch", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2374
+ {
2375
+ provide: FIELD_CONTROL,
2376
+ useExisting: forwardRef(() => SwitchComponent),
2377
+ },
2378
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<label class=\"fk-switch__layout\">\n <input\n #inputEl\n type=\"checkbox\"\n role=\"switch\"\n class=\"fk-switch__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-checked]=\"checked()\"\n [attr.aria-required]=\"isRequired() || null\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onChanged($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <span class=\"fk-switch__track\">\n <span class=\"fk-switch__thumb\"></span>\n </span>\n <span class=\"fk-switch__label\">\n <ng-content />\n </span>\n</label>\n", styles: [":host{display:block}.fk-switch__layout{display:inline-flex;align-items:center;gap:var(--fk-switch-gap, var(--fk-rhythm-2, .5rem));cursor:pointer}.fk-switch__native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fk-switch__track{flex-shrink:0;position:relative;display:inline-flex;align-items:center;width:var(--fk-switch-width, 2.25rem);height:var(--fk-switch-height, 1.25rem);border-radius:var(--fk-radius-full, 9999px);background:var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));transition:background-color .2s ease,box-shadow .15s ease}.fk-switch__thumb{position:absolute;left:2px;width:var(--fk-switch-thumb-size, 1rem);height:var(--fk-switch-thumb-size, 1rem);border-radius:var(--fk-radius-full, 9999px);background:var(--fk-color-surface, #ffffff);box-shadow:var(--fk-switch-thumb-shadow, 0 1px 3px rgba(0, 0, 0, .15));transition:transform .2s ease}.fk-switch__label{font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));-webkit-user-select:none;user-select:none}.fk-switch__layout:hover .fk-switch__track{background:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-switch__native:focus-visible~.fk-switch__track{box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}:host(.fk-switch--checked) .fk-switch__track{background:var(--fk-color-primary, #0a84ff)}:host(.fk-switch--checked) .fk-switch__layout:hover .fk-switch__track{background:var(--fk-color-primary-hover, #0670e0)}:host(.fk-switch--checked) .fk-switch__thumb{transform:translate(1rem)}:host(.fk-switch--disabled) .fk-switch__layout{cursor:not-allowed}:host(.fk-switch--disabled) .fk-switch__track{opacity:var(--fk-input-disabled-opacity, .5)}:host(.fk-switch--disabled) .fk-switch__label{opacity:var(--fk-input-disabled-opacity, .5)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2379
+ }
2380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SwitchComponent, decorators: [{
2381
+ type: Component,
2382
+ args: [{ selector: 'fk-switch', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2383
+ {
2384
+ provide: FIELD_CONTROL,
2385
+ useExisting: forwardRef(() => SwitchComponent),
2386
+ },
2387
+ ], template: "<label class=\"fk-switch__layout\">\n <input\n #inputEl\n type=\"checkbox\"\n role=\"switch\"\n class=\"fk-switch__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-checked]=\"checked()\"\n [attr.aria-required]=\"isRequired() || null\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabledSignal()\"\n (change)=\"onChanged($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n <span class=\"fk-switch__track\">\n <span class=\"fk-switch__thumb\"></span>\n </span>\n <span class=\"fk-switch__label\">\n <ng-content />\n </span>\n</label>\n", styles: [":host{display:block}.fk-switch__layout{display:inline-flex;align-items:center;gap:var(--fk-switch-gap, var(--fk-rhythm-2, .5rem));cursor:pointer}.fk-switch__native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fk-switch__track{flex-shrink:0;position:relative;display:inline-flex;align-items:center;width:var(--fk-switch-width, 2.25rem);height:var(--fk-switch-height, 1.25rem);border-radius:var(--fk-radius-full, 9999px);background:var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));transition:background-color .2s ease,box-shadow .15s ease}.fk-switch__thumb{position:absolute;left:2px;width:var(--fk-switch-thumb-size, 1rem);height:var(--fk-switch-thumb-size, 1rem);border-radius:var(--fk-radius-full, 9999px);background:var(--fk-color-surface, #ffffff);box-shadow:var(--fk-switch-thumb-shadow, 0 1px 3px rgba(0, 0, 0, .15));transition:transform .2s ease}.fk-switch__label{font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));-webkit-user-select:none;user-select:none}.fk-switch__layout:hover .fk-switch__track{background:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-switch__native:focus-visible~.fk-switch__track{box-shadow:var(--fk-input-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}:host(.fk-switch--checked) .fk-switch__track{background:var(--fk-color-primary, #0a84ff)}:host(.fk-switch--checked) .fk-switch__layout:hover .fk-switch__track{background:var(--fk-color-primary-hover, #0670e0)}:host(.fk-switch--checked) .fk-switch__thumb{transform:translate(1rem)}:host(.fk-switch--disabled) .fk-switch__layout{cursor:not-allowed}:host(.fk-switch--disabled) .fk-switch__track{opacity:var(--fk-input-disabled-opacity, .5)}:host(.fk-switch--disabled) .fk-switch__label{opacity:var(--fk-input-disabled-opacity, .5)}\n"] }]
2388
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hostClass: [{
2389
+ type: HostBinding,
2390
+ args: ['class']
2391
+ }] } });
2392
+
2393
+ let nextTagInputId = 0;
2394
+ class TagInputComponent {
2395
+ cdr = inject(ChangeDetectorRef);
2396
+ ngControl = inject(NgControl, {
2397
+ optional: true,
2398
+ self: true,
2399
+ });
2400
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
2401
+ // ===== INPUTS =====
2402
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2403
+ /** Placeholder text shown in the text input when there are no tags. */
2404
+ placeholder = input('Add a tag...', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
2405
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2406
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
2407
+ /** Character(s) that commit the current input as a new tag (default: comma). */
2408
+ separator = input(',', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
2409
+ // ===== FieldControl =====
2410
+ id = `fk-tag-input-${nextTagInputId++}`;
2411
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
2412
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2413
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
2414
+ tags = signal([], ...(ngDevMode ? [{ debugName: "tags" }] : /* istanbul ignore next */ []));
2415
+ classes = computed(() => [
2416
+ 'fk-tag-input',
2417
+ this.isFocused() ? 'fk-tag-input--focused' : '',
2418
+ this.isDisabledSignal() ? 'fk-tag-input--disabled' : '',
2419
+ this.className(),
2420
+ ]
2421
+ .filter(Boolean)
2422
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2423
+ get hostClass() {
2424
+ return this.classes();
2425
+ }
2426
+ get focused() {
2427
+ return this.isFocused();
2428
+ }
2429
+ get empty() {
2430
+ return this.tags().length === 0;
2431
+ }
2432
+ get disabled() {
2433
+ return this.isDisabledSignal();
2434
+ }
2435
+ get required() {
2436
+ return this.isRequired();
2437
+ }
2438
+ get describedByIds() {
2439
+ return this.describedByIdsSignal();
2440
+ }
2441
+ constructor() {
2442
+ if (this.ngControl) {
2443
+ this.ngControl.valueAccessor = this;
2444
+ }
2445
+ }
2446
+ setDescribedByIds(ids) {
2447
+ this.describedByIdsSignal.set(ids);
2448
+ }
2449
+ onContainerClick(_event) {
2450
+ this.focusInput();
2451
+ }
2452
+ /** Focuses the inner text input element. */
2453
+ focusInput() {
2454
+ const input = this.inputRef()?.nativeElement;
2455
+ if (input && !this.isDisabledSignal()) {
2456
+ input.focus();
2457
+ }
2458
+ }
2459
+ // ===== ControlValueAccessor =====
2460
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2461
+ onChange = () => { };
2462
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2463
+ onTouched = () => { };
2464
+ writeValue(value) {
2465
+ this.tags.set(Array.isArray(value) ? [...value] : []);
2466
+ }
2467
+ registerOnChange(fn) {
2468
+ this.onChange = fn;
2469
+ }
2470
+ registerOnTouched(fn) {
2471
+ this.onTouched = fn;
2472
+ }
2473
+ setDisabledState(isDisabled) {
2474
+ this.isDisabledSignal.set(isDisabled);
2475
+ this.cdr.markForCheck();
2476
+ }
2477
+ // ===== TAG MANAGEMENT =====
2478
+ /** Adds a tag to the list if it is non-empty and not already present. */
2479
+ addTag(tag) {
2480
+ const trimmed = tag.trim();
2481
+ if (!trimmed || this.tags().includes(trimmed)) {
2482
+ return;
2483
+ }
2484
+ this.tags.update((current) => [...current, trimmed]);
2485
+ this.onChange(this.tags());
2486
+ }
2487
+ /** Removes the given tag from the list. */
2488
+ removeTag(tag, event) {
2489
+ event?.stopPropagation();
2490
+ this.tags.update((current) => current.filter((t) => t !== tag));
2491
+ this.onChange(this.tags());
2492
+ }
2493
+ // ===== EVENT HANDLERS =====
2494
+ onKeydown(event) {
2495
+ const input = event.target;
2496
+ const value = input.value;
2497
+ if (event.key === 'Enter' || this.separator().includes(event.key)) {
2498
+ event.preventDefault();
2499
+ if (value.trim()) {
2500
+ this.addTag(value);
2501
+ input.value = '';
2502
+ }
2503
+ }
2504
+ if (event.key === 'Backspace' && !value && this.tags().length > 0) {
2505
+ const lastTag = this.tags()[this.tags().length - 1];
2506
+ this.removeTag(lastTag);
2507
+ }
2508
+ }
2509
+ onFocus() {
2510
+ this.isFocused.set(true);
2511
+ }
2512
+ onBlur() {
2513
+ this.isFocused.set(false);
2514
+ // Add any remaining text as a tag
2515
+ const input = this.inputRef()?.nativeElement;
2516
+ if (input && input.value.trim()) {
2517
+ this.addTag(input.value);
2518
+ input.value = '';
2519
+ }
2520
+ this.onTouched();
2521
+ }
2522
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TagInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2523
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: TagInputComponent, isStandalone: true, selector: "fk-tag-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2524
+ {
2525
+ provide: FIELD_CONTROL,
2526
+ useExisting: forwardRef(() => TagInputComponent),
2527
+ },
2528
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"fk-tag-input__container\"\n role=\"group\"\n tabindex=\"-1\"\n (click)=\"focusInput()\"\n (keydown.enter)=\"focusInput()\"\n>\n @for (tag of tags(); track tag) {\n <span class=\"fk-tag-input__tag\">\n {{ tag }}\n @if (!isDisabledSignal()) {\n <button\n type=\"button\"\n class=\"fk-tag-input__remove\"\n [attr.aria-label]=\"'Remove ' + tag\"\n tabindex=\"-1\"\n (click)=\"removeTag(tag, $event)\"\n >\n &times;\n </button>\n }\n </span>\n }\n <input\n #inputEl\n type=\"text\"\n class=\"fk-tag-input__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"tags().length === 0 ? placeholder() : ''\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabledSignal()\"\n (keydown)=\"onKeydown($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n</div>\n", styles: [":host{display:block}.fk-tag-input__container{display:flex;flex-wrap:wrap;align-items:center;gap:4px;width:100%;box-sizing:border-box;min-height:calc(var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5)) * var(--fk-input-font-size, .875rem) + var(--fk-rhythm-2, .5rem) * 2 + 2px);padding:4px var(--fk-rhythm-3, .75rem);font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));cursor:text;transition:border-color .15s ease,box-shadow .15s ease}:host(.fk-tag-input--focused) .fk-tag-input__container{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}:host(.fk-tag-input--disabled) .fk-tag-input__container{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-tag-input__tag{display:inline-flex;align-items:center;gap:2px;padding:2px 8px;font-size:calc(var(--fk-input-font-size, .875rem) * .85);line-height:1.4;color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-color-muted-bg, #f1f5f9);border-radius:calc(var(--fk-input-border-radius, var(--fk-radius-md, .375rem)) - 2px);white-space:nowrap}.fk-tag-input__remove{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;margin:0;font-size:var(--fk-font-size-sm, 14px);line-height:1;color:var(--fk-color-muted, #8a98a8);background:none;border:none;border-radius:50%;cursor:pointer}.fk-tag-input__remove:hover{color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-tag-input-remove-bg-hover, rgba(0, 0, 0, .1))}.fk-tag-input__native{flex:1;min-width:60px;padding:4px 0;font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:transparent;border:none;outline:none}.fk-tag-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-tag-input__native:disabled{cursor:not-allowed}:host-context(.fk-form-field--invalid) .fk-tag-input__container{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid):host(.fk-tag-input--focused) .fk-tag-input__container{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2529
+ }
2530
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TagInputComponent, decorators: [{
2531
+ type: Component,
2532
+ args: [{ selector: 'fk-tag-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2533
+ {
2534
+ provide: FIELD_CONTROL,
2535
+ useExisting: forwardRef(() => TagInputComponent),
2536
+ },
2537
+ ], template: "<div\n class=\"fk-tag-input__container\"\n role=\"group\"\n tabindex=\"-1\"\n (click)=\"focusInput()\"\n (keydown.enter)=\"focusInput()\"\n>\n @for (tag of tags(); track tag) {\n <span class=\"fk-tag-input__tag\">\n {{ tag }}\n @if (!isDisabledSignal()) {\n <button\n type=\"button\"\n class=\"fk-tag-input__remove\"\n [attr.aria-label]=\"'Remove ' + tag\"\n tabindex=\"-1\"\n (click)=\"removeTag(tag, $event)\"\n >\n &times;\n </button>\n }\n </span>\n }\n <input\n #inputEl\n type=\"text\"\n class=\"fk-tag-input__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"tags().length === 0 ? placeholder() : ''\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabledSignal()\"\n (keydown)=\"onKeydown($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n />\n</div>\n", styles: [":host{display:block}.fk-tag-input__container{display:flex;flex-wrap:wrap;align-items:center;gap:4px;width:100%;box-sizing:border-box;min-height:calc(var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5)) * var(--fk-input-font-size, .875rem) + var(--fk-rhythm-2, .5rem) * 2 + 2px);padding:4px var(--fk-rhythm-3, .75rem);font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));cursor:text;transition:border-color .15s ease,box-shadow .15s ease}:host(.fk-tag-input--focused) .fk-tag-input__container{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}:host(.fk-tag-input--disabled) .fk-tag-input__container{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-tag-input__tag{display:inline-flex;align-items:center;gap:2px;padding:2px 8px;font-size:calc(var(--fk-input-font-size, .875rem) * .85);line-height:1.4;color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-color-muted-bg, #f1f5f9);border-radius:calc(var(--fk-input-border-radius, var(--fk-radius-md, .375rem)) - 2px);white-space:nowrap}.fk-tag-input__remove{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;margin:0;font-size:var(--fk-font-size-sm, 14px);line-height:1;color:var(--fk-color-muted, #8a98a8);background:none;border:none;border-radius:50%;cursor:pointer}.fk-tag-input__remove:hover{color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-tag-input-remove-bg-hover, rgba(0, 0, 0, .1))}.fk-tag-input__native{flex:1;min-width:60px;padding:4px 0;font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:transparent;border:none;outline:none}.fk-tag-input__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-tag-input__native:disabled{cursor:not-allowed}:host-context(.fk-form-field--invalid) .fk-tag-input__container{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid):host(.fk-tag-input--focused) .fk-tag-input__container{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
2538
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], hostClass: [{
2539
+ type: HostBinding,
2540
+ args: ['class']
2541
+ }] } });
2542
+
2543
+ let nextTextareaId = 0;
2544
+ class TextareaComponent {
2545
+ cdr = inject(ChangeDetectorRef);
2546
+ ngControl = inject(NgControl, {
2547
+ optional: true,
2548
+ self: true,
2549
+ });
2550
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
2551
+ // ===== INPUTS =====
2552
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2553
+ /** Placeholder text shown when the textarea has no value. */
2554
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
2555
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2556
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
2557
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2558
+ isReadonly = input(false, { ...(ngDevMode ? { debugName: "isReadonly" } : /* istanbul ignore next */ {}), alias: 'readonly' });
2559
+ /** Number of visible text rows for the initial textarea height. */
2560
+ rows = input(3, ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
2561
+ // ===== FieldControl =====
2562
+ id = `fk-textarea-${nextTextareaId++}`;
2563
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
2564
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2565
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
2566
+ classes = computed(() => [
2567
+ 'fk-textarea',
2568
+ this.isFocused() ? 'fk-textarea--focused' : '',
2569
+ this.isDisabledSignal() ? 'fk-textarea--disabled' : '',
2570
+ this.isReadonly() ? 'fk-textarea--readonly' : '',
2571
+ this.className(),
2572
+ ]
2573
+ .filter(Boolean)
2574
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2575
+ get hostClass() {
2576
+ return this.classes();
2577
+ }
2578
+ get focused() {
2579
+ return this.isFocused();
2580
+ }
2581
+ get empty() {
2582
+ return !this.value;
2583
+ }
2584
+ get disabled() {
2585
+ return this.isDisabledSignal();
2586
+ }
2587
+ get required() {
2588
+ return this.isRequired();
2589
+ }
2590
+ get readonly() {
2591
+ return this.isReadonly();
2592
+ }
2593
+ get describedByIds() {
2594
+ return this.describedByIdsSignal();
2595
+ }
2596
+ constructor() {
2597
+ if (this.ngControl) {
2598
+ this.ngControl.valueAccessor = this;
2599
+ }
2600
+ }
2601
+ setDescribedByIds(ids) {
2602
+ this.describedByIdsSignal.set(ids);
2603
+ }
2604
+ onContainerClick(_event) {
2605
+ const input = this.inputRef()?.nativeElement;
2606
+ if (input && !this.isDisabledSignal()) {
2607
+ input.focus();
2608
+ }
2609
+ }
2610
+ // ===== ControlValueAccessor =====
2611
+ value = '';
2612
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2613
+ onChange = () => { };
2614
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2615
+ onTouched = () => { };
2616
+ writeValue(value) {
2617
+ this.value = value ?? '';
2618
+ const input = this.inputRef()?.nativeElement;
2619
+ if (input) {
2620
+ input.value = this.value;
2621
+ }
2622
+ }
2623
+ registerOnChange(fn) {
2624
+ this.onChange = fn;
2625
+ }
2626
+ registerOnTouched(fn) {
2627
+ this.onTouched = fn;
2628
+ }
2629
+ setDisabledState(isDisabled) {
2630
+ this.isDisabledSignal.set(isDisabled);
2631
+ this.cdr.markForCheck();
2632
+ }
2633
+ // ===== EVENT HANDLERS =====
2634
+ onInput(event) {
2635
+ const input = event.target;
2636
+ this.value = input.value;
2637
+ this.onChange(this.value);
2638
+ }
2639
+ onFocus() {
2640
+ this.isFocused.set(true);
2641
+ }
2642
+ onBlur() {
2643
+ this.isFocused.set(false);
2644
+ this.onTouched();
2645
+ }
2646
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2647
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: TextareaComponent, isStandalone: true, selector: "fk-textarea", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2648
+ {
2649
+ provide: FIELD_CONTROL,
2650
+ useExisting: forwardRef(() => TextareaComponent),
2651
+ },
2652
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<textarea\n #inputEl\n class=\"fk-textarea__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.readonly]=\"isReadonly() || null\"\n [attr.rows]=\"rows()\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n></textarea>\n", styles: [":host{display:block}.fk-textarea__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;resize:vertical;transition:border-color .15s ease,box-shadow .15s ease}.fk-textarea__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-textarea__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-textarea__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-textarea__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-textarea__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-textarea__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-textarea__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2653
+ }
2654
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TextareaComponent, decorators: [{
2655
+ type: Component,
2656
+ args: [{ selector: 'fk-textarea', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2657
+ {
2658
+ provide: FIELD_CONTROL,
2659
+ useExisting: forwardRef(() => TextareaComponent),
2660
+ },
2661
+ ], template: "<textarea\n #inputEl\n class=\"fk-textarea__native\"\n [attr.id]=\"id\"\n [attr.placeholder]=\"placeholder()\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.readonly]=\"isReadonly() || null\"\n [attr.rows]=\"rows()\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n></textarea>\n", styles: [":host{display:block}.fk-textarea__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;resize:vertical;transition:border-color .15s ease,box-shadow .15s ease}.fk-textarea__native::placeholder{color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8))}.fk-textarea__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-textarea__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-textarea__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-textarea__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-textarea__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-textarea__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
2662
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], isReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], hostClass: [{
2663
+ type: HostBinding,
2664
+ args: ['class']
2665
+ }] } });
2666
+
2667
+ let nextTimeInputId = 0;
2668
+ class TimeInputComponent {
2669
+ cdr = inject(ChangeDetectorRef);
2670
+ ngControl = inject(NgControl, {
2671
+ optional: true,
2672
+ self: true,
2673
+ });
2674
+ inputRef = viewChild('inputEl', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
2675
+ // ===== INPUTS =====
2676
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2677
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2678
+ isRequired = input(false, { ...(ngDevMode ? { debugName: "isRequired" } : /* istanbul ignore next */ {}), alias: 'required' });
2679
+ // eslint-disable-next-line @angular-eslint/no-input-rename
2680
+ isReadonly = input(false, { ...(ngDevMode ? { debugName: "isReadonly" } : /* istanbul ignore next */ {}), alias: 'readonly' });
2681
+ /** Earliest selectable time as an HH:MM string. */
2682
+ min = input(null, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
2683
+ /** Latest selectable time as an HH:MM string. */
2684
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
2685
+ /** Step in seconds between selectable time values. */
2686
+ step = input(null, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
2687
+ // ===== FieldControl =====
2688
+ id = `fk-time-input-${nextTimeInputId++}`;
2689
+ describedByIdsSignal = signal([], ...(ngDevMode ? [{ debugName: "describedByIdsSignal" }] : /* istanbul ignore next */ []));
2690
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
2691
+ isDisabledSignal = signal(false, ...(ngDevMode ? [{ debugName: "isDisabledSignal" }] : /* istanbul ignore next */ []));
2692
+ classes = computed(() => [
2693
+ 'fk-time-input',
2694
+ this.isFocused() ? 'fk-time-input--focused' : '',
2695
+ this.isDisabledSignal() ? 'fk-time-input--disabled' : '',
2696
+ this.isReadonly() ? 'fk-time-input--readonly' : '',
2697
+ this.className(),
2698
+ ]
2699
+ .filter(Boolean)
2700
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2701
+ get hostClass() {
2702
+ return this.classes();
2703
+ }
2704
+ get focused() {
2705
+ return this.isFocused();
2706
+ }
2707
+ get empty() {
2708
+ return !this.value;
2709
+ }
2710
+ get disabled() {
2711
+ return this.isDisabledSignal();
2712
+ }
2713
+ get required() {
2714
+ return this.isRequired();
2715
+ }
2716
+ get readonly() {
2717
+ return this.isReadonly();
2718
+ }
2719
+ get describedByIds() {
2720
+ return this.describedByIdsSignal();
2721
+ }
2722
+ constructor() {
2723
+ if (this.ngControl) {
2724
+ this.ngControl.valueAccessor = this;
2725
+ }
2726
+ }
2727
+ setDescribedByIds(ids) {
2728
+ this.describedByIdsSignal.set(ids);
2729
+ }
2730
+ onContainerClick(_event) {
2731
+ const input = this.inputRef()?.nativeElement;
2732
+ if (input && !this.isDisabledSignal()) {
2733
+ input.focus();
2734
+ }
2735
+ }
2736
+ // ===== ControlValueAccessor =====
2737
+ value = '';
2738
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2739
+ onChange = () => { };
2740
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2741
+ onTouched = () => { };
2742
+ writeValue(value) {
2743
+ this.value = value ?? '';
2744
+ const input = this.inputRef()?.nativeElement;
2745
+ if (input) {
2746
+ input.value = this.value;
2747
+ }
2748
+ }
2749
+ registerOnChange(fn) {
2750
+ this.onChange = fn;
2751
+ }
2752
+ registerOnTouched(fn) {
2753
+ this.onTouched = fn;
2754
+ }
2755
+ setDisabledState(isDisabled) {
2756
+ this.isDisabledSignal.set(isDisabled);
2757
+ this.cdr.markForCheck();
2758
+ }
2759
+ // ===== EVENT HANDLERS =====
2760
+ onInput(event) {
2761
+ const input = event.target;
2762
+ this.value = input.value;
2763
+ this.onChange(this.value);
2764
+ }
2765
+ onFocus() {
2766
+ this.isFocused.set(true);
2767
+ }
2768
+ onBlur() {
2769
+ this.isFocused.set(false);
2770
+ this.onTouched();
2771
+ }
2772
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TimeInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2773
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: TimeInputComponent, isStandalone: true, selector: "fk-time-input", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, isRequired: { classPropertyName: "isRequired", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, isReadonly: { classPropertyName: "isReadonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [
2774
+ {
2775
+ provide: FIELD_CONTROL,
2776
+ useExisting: forwardRef(() => TimeInputComponent),
2777
+ },
2778
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #inputEl\n type=\"time\"\n class=\"fk-time-input__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.step]=\"step()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-time-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-time-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-time-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-time-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-time-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-time-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-time-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2779
+ }
2780
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TimeInputComponent, decorators: [{
2781
+ type: Component,
2782
+ args: [{ selector: 'fk-time-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2783
+ {
2784
+ provide: FIELD_CONTROL,
2785
+ useExisting: forwardRef(() => TimeInputComponent),
2786
+ },
2787
+ ], template: "<input\n #inputEl\n type=\"time\"\n class=\"fk-time-input__native\"\n [attr.id]=\"id\"\n [attr.aria-describedby]=\"\n describedByIdsSignal().length ? describedByIdsSignal().join(' ') : null\n \"\n [attr.aria-invalid]=\"ngControl?.invalid ?? null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n [attr.step]=\"step()\"\n [attr.readonly]=\"isReadonly() || null\"\n [disabled]=\"isDisabledSignal()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block}.fk-time-input__native{display:block;width:100%;box-sizing:border-box;margin:0;padding:var(--fk-input-padding, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-family:var(--fk-input-font-family, var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\"));font-size:var(--fk-input-font-size, .875rem);font-weight:var(--fk-input-font-weight, var(--fk-font-weight-normal, 400));line-height:var(--fk-input-line-height, var(--fk-typography-body-line-height, 1.5));color:var(--fk-input-color, var(--fk-color-text, #1f2d3d));background:var(--fk-input-bg, var(--fk-color-surface, #ffffff));border:var(--fk-input-border-width, 1px) solid var(--fk-input-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-input-border-radius, var(--fk-radius-md, .375rem));outline:none;transition:border-color .15s ease,box-shadow .15s ease}.fk-time-input__native:hover:not(:disabled):not(:read-only){border-color:var(--fk-input-border-color-hover, var(--fk-color-muted, #8a98a8))}.fk-time-input__native:focus{border-color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff));box-shadow:var(--fk-input-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)))}.fk-time-input__native:disabled{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}.fk-time-input__native:read-only{cursor:default}:host-context(.fk-form-field--invalid) .fk-time-input__native{border-color:var(--fk-input-border-color-error, var(--fk-color-danger, #e02424))}:host-context(.fk-form-field--invalid) .fk-time-input__native:focus{box-shadow:var(--fk-input-error-focus-ring, 0 0 0 3px rgba(224, 36, 36, .2))}\n"] }]
2788
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], isRequired: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], isReadonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], hostClass: [{
2789
+ type: HostBinding,
2790
+ args: ['class']
2791
+ }] } });
2792
+
2793
+ /**
2794
+ * Registry that maps field type strings to Angular component classes.
2795
+ *
2796
+ * Built-in field types are registered by default. Custom field types
2797
+ * can be registered at any time to extend the schema engine.
2798
+ *
2799
+ * @example
2800
+ * ```ts
2801
+ * // Register a custom field type
2802
+ * registry.register('currency', CurrencyInputComponent);
2803
+ *
2804
+ * // Use in schema
2805
+ * { key: 'price', type: 'currency', label: 'Price' }
2806
+ * ```
2807
+ */
2808
+ class FieldRegistry {
2809
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2810
+ components = new Map();
2811
+ constructor() {
2812
+ this.registerDefaults();
2813
+ }
2814
+ /**
2815
+ * Registers a component class for a field type key.
2816
+ */
2817
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2818
+ register(type, component) {
2819
+ this.components.set(type, component);
2820
+ }
2821
+ /**
2822
+ * Returns the component class for a field type key.
2823
+ *
2824
+ * @throws Error if the type is not registered
2825
+ */
2826
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2827
+ get(type) {
2828
+ const component = this.components.get(type);
2829
+ if (!component) {
2830
+ throw new Error(`FieldRegistry: unknown field type "${type}". ` +
2831
+ `Register it with registry.register("${type}", Component).`);
2832
+ }
2833
+ return component;
2834
+ }
2835
+ /**
2836
+ * Checks if a field type is registered.
2837
+ */
2838
+ has(type) {
2839
+ return this.components.has(type);
2840
+ }
2841
+ registerDefaults() {
2842
+ this.register('input', InputComponent);
2843
+ this.register('password', PasswordInputComponent);
2844
+ this.register('textarea', TextareaComponent);
2845
+ this.register('number', NumberInputComponent);
2846
+ this.register('select', SelectComponent);
2847
+ this.register('checkbox', CheckboxComponent);
2848
+ this.register('radio', RadioComponent);
2849
+ this.register('switch', SwitchComponent);
2850
+ this.register('date', DateInputComponent);
2851
+ this.register('time', TimeInputComponent);
2852
+ this.register('datetime', DatetimeInputComponent);
2853
+ this.register('autocomplete', AutocompleteComponent);
2854
+ this.register('file', FileUploadComponent);
2855
+ this.register('tags', TagInputComponent);
2856
+ this.register('slider', SliderComponent);
2857
+ }
2858
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2859
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldRegistry, providedIn: 'root' });
2860
+ }
2861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldRegistry, decorators: [{
2862
+ type: Injectable,
2863
+ args: [{ providedIn: 'root' }]
2864
+ }], ctorParameters: () => [] });
2865
+
2866
+ /**
2867
+ * Dynamically renders a single field from a schema definition.
2868
+ *
2869
+ * Wraps the control in a `fk-form-field` for consistent layout,
2870
+ * accessibility, and error handling. Uses `ViewContainerRef` to
2871
+ * instantiate the appropriate control component from the field registry.
2872
+ *
2873
+ * @example
2874
+ * ```html
2875
+ * <fk-field-outlet
2876
+ * [field]="fieldSchema"
2877
+ * [form]="formGroup"
2878
+ * [submitted]="submitted()"
2879
+ * />
2880
+ * ```
2881
+ */
2882
+ class FieldOutletComponent {
2883
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
2884
+ /** Schema definition for the field to render. */
2885
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
2886
+ /** Parent FormGroup used to bind the field's reactive control. */
2887
+ form = input.required(...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
2888
+ /** Whether the parent form has been submitted; propagated to fk-form-field. */
2889
+ submitted = input(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
2890
+ /** Whether this field is visible (for conditional visibility). */
2891
+ visible = signal(true, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
2892
+ classes = computed(() => ['fk-field-outlet', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
2893
+ get hostClass() {
2894
+ return this.classes();
2895
+ }
2896
+ get hostDisplay() {
2897
+ return this.visible() ? '' : 'none';
2898
+ }
2899
+ fieldRegistry = inject(FieldRegistry);
2900
+ destroyRef = inject(DestroyRef);
2901
+ controlHostRef = viewChild('controlHost', { ...(ngDevMode ? { debugName: "controlHostRef" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
2902
+ /** Track what was last rendered to avoid unnecessary re-creation. */
2903
+ renderedField = null;
2904
+ renderedForm = null;
2905
+ renderedType = null;
2906
+ /** Subscription to statusChanges for disabled sync — cleaned up on re-render. */
2907
+ statusSub = null;
2908
+ /** Resolved component type from the registry. */
2909
+ componentType = computed(() => this.fieldRegistry.get(this.field().type), ...(ngDevMode ? [{ debugName: "componentType" }] : /* istanbul ignore next */ []));
2910
+ constructor() {
2911
+ effect(() => {
2912
+ this.renderControl();
2913
+ });
2914
+ effect(() => {
2915
+ this.setupVisibility();
2916
+ });
2917
+ }
2918
+ renderControl() {
2919
+ const container = this.controlHostRef();
2920
+ if (!container) {
2921
+ return;
2922
+ }
2923
+ const field = this.field();
2924
+ const form = this.form();
2925
+ const componentType = this.componentType();
2926
+ // Skip re-render if the inputs haven't changed
2927
+ if (field === this.renderedField &&
2928
+ form === this.renderedForm &&
2929
+ componentType === this.renderedType) {
2930
+ return;
2931
+ }
2932
+ this.renderedField = field;
2933
+ this.renderedForm = form;
2934
+ this.renderedType = componentType;
2935
+ // Clean up previous statusChanges subscription
2936
+ this.statusSub?.unsubscribe();
2937
+ this.statusSub = null;
2938
+ container.clear();
2939
+ const control = form.get(field.key);
2940
+ if (!control) {
2941
+ return;
2942
+ }
2943
+ const componentRef = container.createComponent(componentType);
2944
+ const instance = componentRef.instance;
2945
+ // Bind inputs that the control components typically accept
2946
+ if (field.placeholder !== undefined && 'placeholder' in instance) {
2947
+ componentRef.setInput('placeholder', field.placeholder);
2948
+ }
2949
+ if (field.autocomplete !== undefined && 'autocomplete' in instance) {
2950
+ componentRef.setInput('autocomplete', field.autocomplete);
2951
+ }
2952
+ if (field.required && 'required' in instance) {
2953
+ componentRef.setInput('required', true);
2954
+ }
2955
+ if (field.readonly && 'readonly' in instance) {
2956
+ componentRef.setInput('readonly', true);
2957
+ }
2958
+ if (field.options && 'options' in instance) {
2959
+ componentRef.setInput('options', field.options);
2960
+ }
2961
+ if (field.label && 'label' in instance) {
2962
+ componentRef.setInput('label', field.label);
2963
+ }
2964
+ // Bind reactive form control
2965
+ if (instance.ngControl === undefined || instance.ngControl === null) {
2966
+ // Manually wire the control if no NgControl injected
2967
+ if (typeof instance.writeValue === 'function') {
2968
+ instance.writeValue(control.value);
2969
+ }
2970
+ if (typeof instance.registerOnChange === 'function') {
2971
+ instance.registerOnChange((value) => {
2972
+ control.setValue(value);
2973
+ control.markAsDirty();
2974
+ });
2975
+ }
2976
+ if (typeof instance.registerOnTouched === 'function') {
2977
+ instance.registerOnTouched(() => {
2978
+ control.markAsTouched();
2979
+ });
2980
+ }
2981
+ if (typeof instance.setDisabledState === 'function') {
2982
+ instance.setDisabledState(control.disabled);
2983
+ this.statusSub = control.statusChanges
2984
+ .pipe(map(() => control.disabled), distinctUntilChanged())
2985
+ .subscribe((disabled) => {
2986
+ instance.setDisabledState(disabled);
2987
+ });
2988
+ }
2989
+ }
2990
+ }
2991
+ setupVisibility() {
2992
+ const field = this.field();
2993
+ const form = this.form();
2994
+ const rule = field.visibleWhen;
2995
+ if (!rule) {
2996
+ this.visible.set(true);
2997
+ return;
2998
+ }
2999
+ const dependentControl = form.get(rule.field);
3000
+ if (!dependentControl) {
3001
+ this.visible.set(true);
3002
+ return;
3003
+ }
3004
+ // Evaluate initial visibility
3005
+ this.evaluateVisibility(dependentControl.value, rule);
3006
+ // Subscribe to changes
3007
+ dependentControl.valueChanges
3008
+ .pipe(takeUntilDestroyed(this.destroyRef))
3009
+ .subscribe((value) => {
3010
+ this.evaluateVisibility(value, rule);
3011
+ });
3012
+ }
3013
+ evaluateVisibility(value, rule) {
3014
+ if (rule.equals !== undefined) {
3015
+ this.visible.set(value === rule.equals);
3016
+ }
3017
+ else if (rule.notEquals !== undefined) {
3018
+ this.visible.set(value !== rule.notEquals);
3019
+ }
3020
+ else {
3021
+ this.visible.set(true);
3022
+ }
3023
+ }
3024
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3025
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FieldOutletComponent, isStandalone: true, selector: "fk-field-outlet", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, submitted: { classPropertyName: "submitted", publicName: "submitted", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "style.display": "this.hostDisplay" } }, viewQueries: [{ propertyName: "controlHostRef", first: true, predicate: ["controlHost"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "@if (field().ui?.labelEndLink) {\n <div class=\"fk-field-outlet__label-row\">\n <span class=\"fk-field-outlet__label-text\">\n {{ field().label }}\n @if (field().required) {\n <span class=\"fk-field-outlet__required\" aria-hidden=\"true\">*</span>\n }\n </span>\n <fk-link\n variant=\"primary\"\n size=\"small\"\n [routerLink]=\"field().ui!.labelEndLink!.route\"\n >\n {{ field().ui!.labelEndLink!.text }}\n </fk-link>\n </div>\n}\n\n<fk-form-field\n [label]=\"\n field().ui?.labelEndLink\n ? null\n : field().type === 'checkbox'\n ? null\n : (field().label ?? null)\n \"\n [showErrorsOn]=\"'touched'\"\n [submitted]=\"submitted()\"\n [errorMessages]=\"field().errorMessages ?? null\"\n [fallbackControl]=\"form().get(field().key)\"\n [style.display]=\"visible() ? '' : 'none'\"\n>\n @if (field().ui?.prefixIcon) {\n <fk-icon\n fkPrefix\n [name]=\"field().ui!.prefixIcon!\"\n size=\"sm\"\n color=\"muted\"\n />\n }\n <ng-container #controlHost />\n @if (field().ui?.hint) {\n <fk-hint>{{ field().ui?.hint }}</fk-hint>\n }\n</fk-form-field>\n", styles: [".fk-field-outlet__label-row{display:flex;align-items:baseline;justify-content:space-between;font-family:var(--fk-font-family-base);font-size:var(--fk-form-label-font-size);font-weight:var(--fk-form-label-font-weight);line-height:var(--fk-form-label-line-height);color:var(--fk-form-label-color)}.fk-field-outlet__label-text{display:flex;align-items:baseline;gap:.25em}.fk-field-outlet__required{color:var(--fk-form-error-color)}.fk-field-outlet__label-row fk-link{--fk-link-font-size-small: .75rem;--fk-link-font-size-medium: .75rem}.fk-field-outlet__label-row+fk-form-field{margin-top:0}\n"], dependencies: [{ kind: "component", type: FormFieldComponent, selector: "fk-form-field", inputs: ["className", "label", "showErrorsOn", "submitted", "externalDescribedBy", "fallbackControl", "errorMessages"] }, { kind: "directive", type: FkPrefixDirective, selector: "[fkPrefix]" }, { kind: "component", type: HintComponent, selector: "fk-hint", inputs: ["className", "id"] }, { kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: LinkComponent, selector: "fk-link", inputs: ["href", "routerLink", "queryParams", "variant", "size", "underline", "target", "rel", "external", "disabled", "loading", "download", "className", "id", "ariaLabel", "ariaDescribedBy", "ariaCurrent", "role", "tabIndex", "trackingId", "trackingCategory", "trackingLabel", "dataAttributes"], outputs: ["clicked"] }, { kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3026
+ }
3027
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldOutletComponent, decorators: [{
3028
+ type: Component,
3029
+ args: [{ selector: 'fk-field-outlet', standalone: true, imports: [
3030
+ FormFieldComponent,
3031
+ FkPrefixDirective,
3032
+ HintComponent,
3033
+ IconComponent,
3034
+ LinkComponent,
3035
+ ReactiveFormsModule,
3036
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (field().ui?.labelEndLink) {\n <div class=\"fk-field-outlet__label-row\">\n <span class=\"fk-field-outlet__label-text\">\n {{ field().label }}\n @if (field().required) {\n <span class=\"fk-field-outlet__required\" aria-hidden=\"true\">*</span>\n }\n </span>\n <fk-link\n variant=\"primary\"\n size=\"small\"\n [routerLink]=\"field().ui!.labelEndLink!.route\"\n >\n {{ field().ui!.labelEndLink!.text }}\n </fk-link>\n </div>\n}\n\n<fk-form-field\n [label]=\"\n field().ui?.labelEndLink\n ? null\n : field().type === 'checkbox'\n ? null\n : (field().label ?? null)\n \"\n [showErrorsOn]=\"'touched'\"\n [submitted]=\"submitted()\"\n [errorMessages]=\"field().errorMessages ?? null\"\n [fallbackControl]=\"form().get(field().key)\"\n [style.display]=\"visible() ? '' : 'none'\"\n>\n @if (field().ui?.prefixIcon) {\n <fk-icon\n fkPrefix\n [name]=\"field().ui!.prefixIcon!\"\n size=\"sm\"\n color=\"muted\"\n />\n }\n <ng-container #controlHost />\n @if (field().ui?.hint) {\n <fk-hint>{{ field().ui?.hint }}</fk-hint>\n }\n</fk-form-field>\n", styles: [".fk-field-outlet__label-row{display:flex;align-items:baseline;justify-content:space-between;font-family:var(--fk-font-family-base);font-size:var(--fk-form-label-font-size);font-weight:var(--fk-form-label-font-weight);line-height:var(--fk-form-label-line-height);color:var(--fk-form-label-color)}.fk-field-outlet__label-text{display:flex;align-items:baseline;gap:.25em}.fk-field-outlet__required{color:var(--fk-form-error-color)}.fk-field-outlet__label-row fk-link{--fk-link-font-size-small: .75rem;--fk-link-font-size-medium: .75rem}.fk-field-outlet__label-row+fk-form-field{margin-top:0}\n"] }]
3037
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], submitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitted", required: false }] }], hostClass: [{
3038
+ type: HostBinding,
3039
+ args: ['class']
3040
+ }], hostDisplay: [{
3041
+ type: HostBinding,
3042
+ args: ['style.display']
3043
+ }], controlHostRef: [{ type: i0.ViewChild, args: ['controlHost', { ...{
3044
+ read: ViewContainerRef,
3045
+ }, isSignal: true }] }] } });
3046
+
3047
+ /**
3048
+ * Renders a FormArray as repeatable rows of fields.
3049
+ *
3050
+ * Each row is a FormGroup built from the field's `arrayFields` definition.
3051
+ * Provides add/remove row functionality with min/max constraints.
3052
+ *
3053
+ * @example
3054
+ * ```html
3055
+ * <fk-field-array-outlet
3056
+ * [field]="phoneNumbersField"
3057
+ * [formArray]="form.get('phones') as FormArray"
3058
+ * [submitted]="submitted()"
3059
+ * />
3060
+ * ```
3061
+ */
3062
+ class FieldArrayOutletComponent {
3063
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
3064
+ /** Schema definition for the repeating field, including arrayFields and row constraints. */
3065
+ field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
3066
+ /** The FormArray backing the repeatable rows. */
3067
+ formArray = input.required(...(ngDevMode ? [{ debugName: "formArray" }] : /* istanbul ignore next */ []));
3068
+ /** Whether the parent form has been submitted; propagated to each fk-field-outlet. */
3069
+ submitted = input(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
3070
+ /** Fires with the index of the newly added row. */
3071
+ rowAdded = output();
3072
+ /** Fires with the index of the removed row. */
3073
+ rowRemoved = output();
3074
+ classes = computed(() => ['fk-field-array', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3075
+ get hostClass() {
3076
+ return this.classes();
3077
+ }
3078
+ schemaParser = inject(SchemaParser);
3079
+ /** Returns the current FormGroups for each rendered row. */
3080
+ rows() {
3081
+ const arr = this.formArray();
3082
+ return arr.controls;
3083
+ }
3084
+ /** Returns true when another row can be added without exceeding maxRows. */
3085
+ canAdd() {
3086
+ const max = this.field().maxRows;
3087
+ if (max === undefined) {
3088
+ return true;
3089
+ }
3090
+ return this.formArray().length < max;
3091
+ }
3092
+ /** Returns true when a row can be removed without going below minRows. */
3093
+ canRemove() {
3094
+ const min = this.field().minRows ?? 0;
3095
+ return this.formArray().length > min;
3096
+ }
3097
+ /** Appends a new blank row to the FormArray. */
3098
+ addRow() {
3099
+ if (!this.canAdd()) {
3100
+ return;
3101
+ }
3102
+ const newRow = this.schemaParser.buildArrayRow(this.field());
3103
+ this.formArray().push(newRow);
3104
+ this.rowAdded.emit(this.formArray().length - 1);
3105
+ }
3106
+ /** Removes the row at the given index from the FormArray. */
3107
+ removeRow(index) {
3108
+ if (!this.canRemove()) {
3109
+ return;
3110
+ }
3111
+ this.formArray().removeAt(index);
3112
+ this.rowRemoved.emit(index);
3113
+ }
3114
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldArrayOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3115
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FieldArrayOutletComponent, isStandalone: true, selector: "fk-field-array-outlet", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, formArray: { classPropertyName: "formArray", publicName: "formArray", isSignal: true, isRequired: true, transformFunction: null }, submitted: { classPropertyName: "submitted", publicName: "submitted", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowAdded: "rowAdded", rowRemoved: "rowRemoved" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "@if (field().label) {\n <legend class=\"fk-field-array__legend\">{{ field().label }}</legend>\n}\n@for (row of rows(); track $index) {\n <div class=\"fk-field-array__row\" [formGroup]=\"row\">\n @for (childField of field().arrayFields ?? []; track childField.key) {\n <fk-field-outlet\n [field]=\"childField\"\n [form]=\"row\"\n [submitted]=\"submitted()\"\n />\n }\n @if (canRemove()) {\n <button\n type=\"button\"\n class=\"fk-field-array__remove\"\n aria-label=\"Remove row\"\n (click)=\"removeRow($index)\"\n >\n &times;\n </button>\n }\n </div>\n}\n@if (canAdd()) {\n <button type=\"button\" class=\"fk-field-array__add\" (click)=\"addRow()\">\n + Add\n </button>\n}\n", styles: [":host{display:block}.fk-field-array__legend{font-size:var( --fk-field-array-label-font-size, var(--fk-form-label-font-size, .875rem) );font-weight:var( --fk-field-array-label-font-weight, var(--fk-form-label-font-weight, 500) );color:var( --fk-field-array-label-color, var(--fk-form-label-color, #1f2d3d) )}.fk-field-array__row{display:flex;column-gap:var(--fk-field-array-row-gap, var(--fk-rhythm-3, .75rem));align-items:flex-start;margin-top:var(--fk-field-array-row-gap, var(--fk-rhythm-3, .75rem))}.fk-field-array__legend+.fk-field-array__row{margin-top:var( --fk-field-array-label-gap, var(--fk-form-label-gap, .375rem) )}.fk-field-array__row>fk-field-outlet{flex:1;--fk-form-label-gap: 0}.fk-field-array__remove{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-field-array-remove-size, var(--fk-rhythm-8, 2rem));height:var(--fk-field-array-remove-size, var(--fk-rhythm-8, 2rem));margin-top:var(--fk-field-array-remove-offset, .25rem);padding:0;font-size:1.125rem;line-height:1;color:var(--fk-field-array-remove-color, var(--fk-color-muted, #8a98a8));background:transparent;border:1px solid var( --fk-field-array-remove-border-color, var(--fk-color-border, #d9e2ee) );border-radius:var( --fk-field-array-remove-radius, var(--fk-radius-md, .5rem) );cursor:pointer;transition:color .15s,border-color .15s}.fk-field-array__remove:hover{color:var( --fk-field-array-remove-hover-color, var(--fk-form-error-color, #dc2626) );border-color:var( --fk-field-array-remove-hover-color, var(--fk-form-error-color, #dc2626) )}.fk-field-array__remove:focus-visible{outline:none;box-shadow:var( --fk-field-array-remove-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)) )}.fk-field-array__add{margin-top:var(--fk-field-array-add-gap, var(--fk-rhythm-3, .75rem));padding:var(--fk-field-array-add-padding-y, var(--fk-rhythm-2, .5rem)) var(--fk-field-array-add-padding-x, var(--fk-rhythm-4, 1rem));font-size:var( --fk-field-array-add-font-size, var(--fk-typography-small-font-size, .8125rem) );color:var(--fk-field-array-add-color, var(--fk-color-primary, #0a84ff));background:transparent;border:1px dashed var(--fk-field-array-add-border-color, var(--fk-color-border, #d9e2ee));border-radius:var( --fk-field-array-add-radius, var(--fk-radius-md, .5rem) );cursor:pointer;transition:color .15s,border-color .15s}.fk-field-array__add:hover{border-color:var( --fk-field-array-add-hover-border-color, var(--fk-color-primary, #0a84ff) )}.fk-field-array__add:focus-visible{outline:none;box-shadow:var( --fk-field-array-add-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)) )}\n"], dependencies: [{ kind: "component", type: FieldOutletComponent, selector: "fk-field-outlet", inputs: ["className", "field", "form", "submitted"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3116
+ }
3117
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FieldArrayOutletComponent, decorators: [{
3118
+ type: Component,
3119
+ args: [{ selector: 'fk-field-array-outlet', standalone: true, imports: [FieldOutletComponent, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (field().label) {\n <legend class=\"fk-field-array__legend\">{{ field().label }}</legend>\n}\n@for (row of rows(); track $index) {\n <div class=\"fk-field-array__row\" [formGroup]=\"row\">\n @for (childField of field().arrayFields ?? []; track childField.key) {\n <fk-field-outlet\n [field]=\"childField\"\n [form]=\"row\"\n [submitted]=\"submitted()\"\n />\n }\n @if (canRemove()) {\n <button\n type=\"button\"\n class=\"fk-field-array__remove\"\n aria-label=\"Remove row\"\n (click)=\"removeRow($index)\"\n >\n &times;\n </button>\n }\n </div>\n}\n@if (canAdd()) {\n <button type=\"button\" class=\"fk-field-array__add\" (click)=\"addRow()\">\n + Add\n </button>\n}\n", styles: [":host{display:block}.fk-field-array__legend{font-size:var( --fk-field-array-label-font-size, var(--fk-form-label-font-size, .875rem) );font-weight:var( --fk-field-array-label-font-weight, var(--fk-form-label-font-weight, 500) );color:var( --fk-field-array-label-color, var(--fk-form-label-color, #1f2d3d) )}.fk-field-array__row{display:flex;column-gap:var(--fk-field-array-row-gap, var(--fk-rhythm-3, .75rem));align-items:flex-start;margin-top:var(--fk-field-array-row-gap, var(--fk-rhythm-3, .75rem))}.fk-field-array__legend+.fk-field-array__row{margin-top:var( --fk-field-array-label-gap, var(--fk-form-label-gap, .375rem) )}.fk-field-array__row>fk-field-outlet{flex:1;--fk-form-label-gap: 0}.fk-field-array__remove{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-field-array-remove-size, var(--fk-rhythm-8, 2rem));height:var(--fk-field-array-remove-size, var(--fk-rhythm-8, 2rem));margin-top:var(--fk-field-array-remove-offset, .25rem);padding:0;font-size:1.125rem;line-height:1;color:var(--fk-field-array-remove-color, var(--fk-color-muted, #8a98a8));background:transparent;border:1px solid var( --fk-field-array-remove-border-color, var(--fk-color-border, #d9e2ee) );border-radius:var( --fk-field-array-remove-radius, var(--fk-radius-md, .5rem) );cursor:pointer;transition:color .15s,border-color .15s}.fk-field-array__remove:hover{color:var( --fk-field-array-remove-hover-color, var(--fk-form-error-color, #dc2626) );border-color:var( --fk-field-array-remove-hover-color, var(--fk-form-error-color, #dc2626) )}.fk-field-array__remove:focus-visible{outline:none;box-shadow:var( --fk-field-array-remove-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)) )}.fk-field-array__add{margin-top:var(--fk-field-array-add-gap, var(--fk-rhythm-3, .75rem));padding:var(--fk-field-array-add-padding-y, var(--fk-rhythm-2, .5rem)) var(--fk-field-array-add-padding-x, var(--fk-rhythm-4, 1rem));font-size:var( --fk-field-array-add-font-size, var(--fk-typography-small-font-size, .8125rem) );color:var(--fk-field-array-add-color, var(--fk-color-primary, #0a84ff));background:transparent;border:1px dashed var(--fk-field-array-add-border-color, var(--fk-color-border, #d9e2ee));border-radius:var( --fk-field-array-add-radius, var(--fk-radius-md, .5rem) );cursor:pointer;transition:color .15s,border-color .15s}.fk-field-array__add:hover{border-color:var( --fk-field-array-add-hover-border-color, var(--fk-color-primary, #0a84ff) )}.fk-field-array__add:focus-visible{outline:none;box-shadow:var( --fk-field-array-add-focus-ring, var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18)) )}\n"] }]
3120
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], formArray: [{ type: i0.Input, args: [{ isSignal: true, alias: "formArray", required: true }] }], submitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitted", required: false }] }], rowAdded: [{ type: i0.Output, args: ["rowAdded"] }], rowRemoved: [{ type: i0.Output, args: ["rowRemoved"] }], hostClass: [{
3121
+ type: HostBinding,
3122
+ args: ['class']
3123
+ }] } });
3124
+
3125
+ /**
3126
+ * Renders a form section with an optional title, description,
3127
+ * and layout-aware field rendering.
3128
+ *
3129
+ * @example
3130
+ * ```html
3131
+ * <fk-section-outlet
3132
+ * [section]="section"
3133
+ * [form]="formGroup"
3134
+ * [submitted]="submitted()"
3135
+ * />
3136
+ * ```
3137
+ */
3138
+ class SectionOutletComponent {
3139
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
3140
+ /** Schema section definition including its title, description, and field list. */
3141
+ section = input.required(...(ngDevMode ? [{ debugName: "section" }] : /* istanbul ignore next */ []));
3142
+ /** Parent FormGroup used to bind each field's reactive control. */
3143
+ form = input.required(...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
3144
+ /** Whether the parent form has been submitted; propagated to each fk-field-outlet. */
3145
+ submitted = input(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
3146
+ classes = computed(() => ['fk-section-outlet', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3147
+ get hostClass() {
3148
+ return this.classes();
3149
+ }
3150
+ /** Looks up a field definition within this section by its key. */
3151
+ getField(key) {
3152
+ return this.section().fields.find((f) => f.key === key);
3153
+ }
3154
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SectionOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3155
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SectionOutletComponent, isStandalone: true, selector: "fk-section-outlet", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, submitted: { classPropertyName: "submitted", publicName: "submitted", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "@if (section().title) {\n <h3 class=\"fk-section__title\">{{ section().title }}</h3>\n}\n@if (section().description) {\n <p class=\"fk-section__description\">{{ section().description }}</p>\n}\n<div class=\"fk-section__fields\">\n @if (section().layout?.length) {\n @for (row of section().layout!; track $index) {\n <div class=\"fk-section__row\">\n @for (col of row.columns; track col.fieldKey) {\n <div class=\"fk-section__col\" [style.--fk-col-span]=\"col.span ?? 12\">\n @if (getField(col.fieldKey); as field) {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"form()\"\n [submitted]=\"submitted()\"\n />\n }\n </div>\n }\n </div>\n }\n } @else {\n @for (field of section().fields; track field.key) {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"form()\"\n [submitted]=\"submitted()\"\n />\n }\n }\n</div>\n", styles: [":host{display:block;margin-bottom:var(--fk-rhythm-6, 1.5rem)}.fk-section__title{font-size:1.125rem;font-weight:600;color:var(--fk-form-label-color);margin:0 0 var(--fk-rhythm-2, .5rem)}.fk-section__description{font-size:var(--fk-form-hint-font-size);color:var(--fk-form-hint-color);margin:0 0 var(--fk-rhythm-4, 1rem)}.fk-section__fields{display:flex;flex-direction:column}.fk-section__row{display:grid;grid-template-columns:repeat(12,1fr);column-gap:var(--fk-form-field-gap)}.fk-section__col{grid-column:span var(--fk-col-span, 12)}\n"], dependencies: [{ kind: "component", type: FieldOutletComponent, selector: "fk-field-outlet", inputs: ["className", "field", "form", "submitted"] }, { kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3156
+ }
3157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SectionOutletComponent, decorators: [{
3158
+ type: Component,
3159
+ args: [{ selector: 'fk-section-outlet', standalone: true, imports: [FieldOutletComponent, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (section().title) {\n <h3 class=\"fk-section__title\">{{ section().title }}</h3>\n}\n@if (section().description) {\n <p class=\"fk-section__description\">{{ section().description }}</p>\n}\n<div class=\"fk-section__fields\">\n @if (section().layout?.length) {\n @for (row of section().layout!; track $index) {\n <div class=\"fk-section__row\">\n @for (col of row.columns; track col.fieldKey) {\n <div class=\"fk-section__col\" [style.--fk-col-span]=\"col.span ?? 12\">\n @if (getField(col.fieldKey); as field) {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"form()\"\n [submitted]=\"submitted()\"\n />\n }\n </div>\n }\n </div>\n }\n } @else {\n @for (field of section().fields; track field.key) {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"form()\"\n [submitted]=\"submitted()\"\n />\n }\n }\n</div>\n", styles: [":host{display:block;margin-bottom:var(--fk-rhythm-6, 1.5rem)}.fk-section__title{font-size:1.125rem;font-weight:600;color:var(--fk-form-label-color);margin:0 0 var(--fk-rhythm-2, .5rem)}.fk-section__description{font-size:var(--fk-form-hint-font-size);color:var(--fk-form-hint-color);margin:0 0 var(--fk-rhythm-4, 1rem)}.fk-section__fields{display:flex;flex-direction:column}.fk-section__row{display:grid;grid-template-columns:repeat(12,1fr);column-gap:var(--fk-form-field-gap)}.fk-section__col{grid-column:span var(--fk-col-span, 12)}\n"] }]
3160
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], submitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitted", required: false }] }], hostClass: [{
3161
+ type: HostBinding,
3162
+ args: ['class']
3163
+ }] } });
3164
+
3165
+ /**
3166
+ * Renders a complete form from a schema definition.
3167
+ *
3168
+ * Generates a FormGroup from the schema, wraps it in a `fk-form`,
3169
+ * and renders each field via `fk-field-outlet`. Supports layout
3170
+ * sections for organized field grouping.
3171
+ *
3172
+ * @example
3173
+ * ```ts
3174
+ * schema: FormSchema = {
3175
+ * fields: [
3176
+ * { key: 'email', type: 'input', label: 'Email', required: true },
3177
+ * { key: 'password', type: 'password', label: 'Password', required: true },
3178
+ * ],
3179
+ * };
3180
+ * ```
3181
+ *
3182
+ * ```html
3183
+ * <fk-form-renderer
3184
+ * [schema]="schema"
3185
+ * (formSubmit)="onSubmit($event)"
3186
+ * />
3187
+ * ```
3188
+ */
3189
+ class FormRendererComponent {
3190
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
3191
+ /** The form schema to render. */
3192
+ schema = input.required(...(ngDevMode ? [{ debugName: "schema" }] : /* istanbul ignore next */ []));
3193
+ /**
3194
+ * Optional externally provided FormGroup.
3195
+ * If not provided, one is auto-generated from the schema.
3196
+ */
3197
+ form = input(null, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
3198
+ /** Emits the FormGroup value when the form is submitted. */
3199
+ formSubmit = output();
3200
+ /** The resolved FormGroup (either provided or auto-generated). */
3201
+ resolvedForm = signal(null, ...(ngDevMode ? [{ debugName: "resolvedForm" }] : /* istanbul ignore next */ []));
3202
+ /** Reference to the inner fk-form for submission state. */
3203
+ formRef = signal(null, ...(ngDevMode ? [{ debugName: "formRef" }] : /* istanbul ignore next */ []));
3204
+ classes = computed(() => ['fk-form-renderer', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3205
+ get hostClass() {
3206
+ return this.classes();
3207
+ }
3208
+ schemaParser = inject(SchemaParser);
3209
+ el = inject(ElementRef);
3210
+ ngOnInit() {
3211
+ const externalForm = this.form();
3212
+ if (externalForm) {
3213
+ this.resolvedForm.set(externalForm);
3214
+ }
3215
+ else {
3216
+ const generated = this.schemaParser.buildFormGroup(this.schema());
3217
+ this.resolvedForm.set(generated);
3218
+ }
3219
+ }
3220
+ /** Looks up a schema field definition by its key. */
3221
+ getField(key) {
3222
+ return this.schema().fields.find((f) => f.key === key);
3223
+ }
3224
+ /** Returns the FormArray for a repeating field at the given key. */
3225
+ getFormArray(key) {
3226
+ return this.resolvedForm()?.get(key);
3227
+ }
3228
+ onFormSubmit() {
3229
+ const form = this.resolvedForm();
3230
+ if (!form) {
3231
+ return;
3232
+ }
3233
+ this.formSubmit.emit(form.getRawValue());
3234
+ }
3235
+ /**
3236
+ * Finds the first invalid field in schema order and focuses its input.
3237
+ * Call this after markAllAsTouched() to direct the user to the first error.
3238
+ */
3239
+ focusFirstInvalidField() {
3240
+ const form = this.resolvedForm();
3241
+ if (!form) {
3242
+ return;
3243
+ }
3244
+ const fields = this.schema().fields;
3245
+ const firstInvalidIndex = fields.findIndex((f) => form.get(f.key)?.invalid);
3246
+ if (firstInvalidIndex < 0) {
3247
+ return;
3248
+ }
3249
+ setTimeout(() => {
3250
+ const host = this.el.nativeElement;
3251
+ const outlets = Array.from(host.querySelectorAll('fk-field-outlet'));
3252
+ const target = outlets[firstInvalidIndex];
3253
+ if (!target) {
3254
+ return;
3255
+ }
3256
+ const input = target.querySelector('input, select, textarea');
3257
+ if (input) {
3258
+ input.focus();
3259
+ }
3260
+ });
3261
+ }
3262
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3263
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FormRendererComponent, isStandalone: true, selector: "fk-form-renderer", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: true, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "@if (resolvedForm()) {\n <fk-form (formSubmit)=\"onFormSubmit()\">\n <div class=\"fk-form-renderer__fields\" [formGroup]=\"resolvedForm()!\">\n @if (schema().sections?.length) {\n @for (section of schema().sections!; track $index) {\n <fk-section-outlet\n [section]=\"section\"\n [form]=\"resolvedForm()!\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n }\n }\n @if (schema().layout?.length) {\n @for (row of schema().layout!; track $index) {\n <div class=\"fk-form-renderer__row\">\n @for (col of row.columns; track col.fieldKey) {\n <div\n class=\"fk-form-renderer__col\"\n [style.--fk-col-span]=\"col.span ?? 12\"\n >\n @if (getField(col.fieldKey); as field) {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"resolvedForm()!\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n }\n </div>\n }\n </div>\n }\n } @else {\n @for (field of schema().fields; track field.key) {\n @if (field.arrayFields?.length) {\n <fk-field-array-outlet\n [field]=\"field\"\n [formArray]=\"getFormArray(field.key)\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n } @else {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"resolvedForm()!\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n }\n }\n }\n </div>\n <ng-content />\n </fk-form>\n}\n", styles: [":host{display:block}.fk-form-renderer__fields{display:flex;flex-direction:column;row-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}.fk-form-renderer__row{display:grid;grid-template-columns:repeat(12,1fr);column-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}.fk-form-renderer__col{grid-column:span var(--fk-col-span, 12)}\n"], dependencies: [{ kind: "component", type: FieldArrayOutletComponent, selector: "fk-field-array-outlet", inputs: ["className", "field", "formArray", "submitted"], outputs: ["rowAdded", "rowRemoved"] }, { kind: "component", type: FieldOutletComponent, selector: "fk-field-outlet", inputs: ["className", "field", "form", "submitted"] }, { kind: "component", type: FormComponent, selector: "fk-form", inputs: ["className"], outputs: ["formSubmit"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormArrayDirective, selector: "[formArray]", inputs: ["formArray"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: SectionOutletComponent, selector: "fk-section-outlet", inputs: ["className", "section", "form", "submitted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3264
+ }
3265
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormRendererComponent, decorators: [{
3266
+ type: Component,
3267
+ args: [{ selector: 'fk-form-renderer', standalone: true, imports: [
3268
+ FieldArrayOutletComponent,
3269
+ FieldOutletComponent,
3270
+ FormComponent,
3271
+ ReactiveFormsModule,
3272
+ SectionOutletComponent,
3273
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (resolvedForm()) {\n <fk-form (formSubmit)=\"onFormSubmit()\">\n <div class=\"fk-form-renderer__fields\" [formGroup]=\"resolvedForm()!\">\n @if (schema().sections?.length) {\n @for (section of schema().sections!; track $index) {\n <fk-section-outlet\n [section]=\"section\"\n [form]=\"resolvedForm()!\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n }\n }\n @if (schema().layout?.length) {\n @for (row of schema().layout!; track $index) {\n <div class=\"fk-form-renderer__row\">\n @for (col of row.columns; track col.fieldKey) {\n <div\n class=\"fk-form-renderer__col\"\n [style.--fk-col-span]=\"col.span ?? 12\"\n >\n @if (getField(col.fieldKey); as field) {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"resolvedForm()!\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n }\n </div>\n }\n </div>\n }\n } @else {\n @for (field of schema().fields; track field.key) {\n @if (field.arrayFields?.length) {\n <fk-field-array-outlet\n [field]=\"field\"\n [formArray]=\"getFormArray(field.key)\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n } @else {\n <fk-field-outlet\n [field]=\"field\"\n [form]=\"resolvedForm()!\"\n [submitted]=\"formRef()?.submitted() ?? false\"\n />\n }\n }\n }\n </div>\n <ng-content />\n </fk-form>\n}\n", styles: [":host{display:block}.fk-form-renderer__fields{display:flex;flex-direction:column;row-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}.fk-form-renderer__row{display:grid;grid-template-columns:repeat(12,1fr);column-gap:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}.fk-form-renderer__col{grid-column:span var(--fk-col-span, 12)}\n"] }]
3274
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: true }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }], hostClass: [{
3275
+ type: HostBinding,
3276
+ args: ['class']
3277
+ }] } });
3278
+
3279
+ /**
3280
+ * Public types for `fk-form-renderer`.
3281
+ *
3282
+ * The renderer is a thin presentation layer over the schema engine —
3283
+ * its public type contract is the engine's schema vocabulary
3284
+ * (`FormSchema`, `FieldSchema`, `FieldUiConfig`, `Option`, etc.) plus
3285
+ * the runtime emit shape. Re-exporting from here gives consumers a
3286
+ * single discoverable surface (`@frame-kit/ui-ng`) without forcing them
3287
+ * to know which layer the types live in.
3288
+ */
3289
+
3290
+ class FormSectionComponent {
3291
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
3292
+ /** Section heading. */
3293
+ title = input(null, ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
3294
+ /** Optional description below the heading. */
3295
+ description = input(null, ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
3296
+ classes = computed(() => ['fk-form-section', this.className()].filter(Boolean).join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3297
+ get hostClass() {
3298
+ return this.classes();
3299
+ }
3300
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3301
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FormSectionComponent, isStandalone: true, selector: "fk-form-section", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "@if (title() || description()) {\n <div class=\"fk-form-section__header\">\n @if (title()) {\n <h3 class=\"fk-form-section__title\">{{ title() }}</h3>\n }\n @if (description()) {\n <p class=\"fk-form-section__description\">{{ description() }}</p>\n }\n </div>\n}\n<div class=\"fk-form-section__content\">\n <ng-content />\n</div>\n", styles: [":host{display:block}.fk-form-section__header{margin-bottom:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}.fk-form-section__title{margin:0;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-typography-h4-font-size, 1.125rem);font-weight:var(--fk-typography-h4-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h4-line-height, 1.25);color:var(--fk-color-text, #1f2d3d)}.fk-form-section__description{margin:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem)) 0 0;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-typography-body-font-size, .9375rem);line-height:var(--fk-typography-body-line-height, 1.5);color:var(--fk-color-muted, #8a98a8)}.fk-form-section__content{display:flex;flex-direction:column}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3302
+ }
3303
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormSectionComponent, decorators: [{
3304
+ type: Component,
3305
+ args: [{ selector: 'fk-form-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (title() || description()) {\n <div class=\"fk-form-section__header\">\n @if (title()) {\n <h3 class=\"fk-form-section__title\">{{ title() }}</h3>\n }\n @if (description()) {\n <p class=\"fk-form-section__description\">{{ description() }}</p>\n }\n </div>\n}\n<div class=\"fk-form-section__content\">\n <ng-content />\n</div>\n", styles: [":host{display:block}.fk-form-section__header{margin-bottom:var(--fk-form-field-gap, var(--fk-rhythm-4, 1rem))}.fk-form-section__title{margin:0;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-typography-h4-font-size, 1.125rem);font-weight:var(--fk-typography-h4-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h4-line-height, 1.25);color:var(--fk-color-text, #1f2d3d)}.fk-form-section__description{margin:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem)) 0 0;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-typography-body-font-size, .9375rem);line-height:var(--fk-typography-body-line-height, 1.5);color:var(--fk-color-muted, #8a98a8)}.fk-form-section__content{display:flex;flex-direction:column}\n"] }]
3306
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], hostClass: [{
3307
+ type: HostBinding,
3308
+ args: ['class']
3309
+ }] } });
3310
+
3311
+ /**
3312
+ * Returns a snapshot of a control's state.
3313
+ *
3314
+ * @example
3315
+ * ```ts
3316
+ * const state = getFieldState(form.get('email'));
3317
+ * if (state.invalid && state.touched) {
3318
+ * // show error
3319
+ * }
3320
+ * ```
3321
+ */
3322
+ function getFieldState(control) {
3323
+ if (!control) {
3324
+ return null;
3325
+ }
3326
+ return {
3327
+ touched: control.touched,
3328
+ dirty: control.dirty,
3329
+ invalid: control.invalid,
3330
+ valid: control.valid,
3331
+ pending: control.pending,
3332
+ disabled: control.disabled,
3333
+ enabled: control.enabled,
3334
+ pristine: control.pristine,
3335
+ untouched: control.untouched,
3336
+ errors: control.errors,
3337
+ value: control.value,
3338
+ };
3339
+ }
3340
+ /**
3341
+ * Returns whether a control has a specific validation error.
3342
+ *
3343
+ * @example
3344
+ * ```ts
3345
+ * if (hasError(form.get('email'), 'required')) {
3346
+ * // ...
3347
+ * }
3348
+ * ```
3349
+ */
3350
+ function hasError(control, errorKey) {
3351
+ if (!control) {
3352
+ return false;
3353
+ }
3354
+ return control.hasError(errorKey);
3355
+ }
3356
+ /**
3357
+ * Returns the error value for a specific validation error key.
3358
+ *
3359
+ * @example
3360
+ * ```ts
3361
+ * const minErr = getError(form.get('age'), 'min');
3362
+ * // { min: 18, actual: 12 }
3363
+ * ```
3364
+ */
3365
+ function getError(control, errorKey) {
3366
+ if (!control) {
3367
+ return null;
3368
+ }
3369
+ return control.getError(errorKey);
3370
+ }
3371
+ /**
3372
+ * Marks all controls in a FormGroup as touched.
3373
+ * Useful for triggering validation display on form submission.
3374
+ *
3375
+ * @example
3376
+ * ```ts
3377
+ * if (form.invalid) {
3378
+ * markAllTouched(form);
3379
+ * return;
3380
+ * }
3381
+ * ```
3382
+ */
3383
+ function markAllTouched(group) {
3384
+ group.markAllAsTouched();
3385
+ }
3386
+ /**
3387
+ * Returns whether all controls in a FormGroup are valid.
3388
+ */
3389
+ function isFormValid(group) {
3390
+ return group.valid;
3391
+ }
3392
+ /**
3393
+ * Returns all invalid control keys in a FormGroup (top-level only).
3394
+ *
3395
+ * @example
3396
+ * ```ts
3397
+ * const invalid = getInvalidFields(form);
3398
+ * // ['email', 'password']
3399
+ * ```
3400
+ */
3401
+ function getInvalidFields(group) {
3402
+ return Object.entries(group.controls)
3403
+ .filter(([, control]) => control.invalid)
3404
+ .map(([key]) => key);
3405
+ }
3406
+
3407
+ /**
3408
+ * A drag-and-drop file input. Pairs a styled, tokenized "drop here or
3409
+ * click to browse" surface with a hidden native file input so the
3410
+ * component remains keyboard-, click-, and drag-accessible without
3411
+ * any consumer wiring.
3412
+ *
3413
+ * Event-out (not CVA-bound) — emits a `File[]` from `(filesSelected)`
3414
+ * for both drop and browse paths. Use `fk-file-upload` instead when
3415
+ * you need a reactive-forms control with `FileList` value semantics.
3416
+ */
3417
+ class FileDropzoneComponent {
3418
+ // ===== INPUTS =====
3419
+ /** MIME types or file extensions the native file picker should accept (e.g. "image/*,.pdf"). */
3420
+ accept = input(null, ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
3421
+ /** Whether the user can select more than one file at a time. */
3422
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
3423
+ /** When true, prevents drag-and-drop and click-to-browse interaction. */
3424
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3425
+ /** Icon name rendered inside the dropzone surface. */
3426
+ icon = input('upload', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
3427
+ /** Primary prompt text displayed inside the dropzone. */
3428
+ prompt = input.required(...(ngDevMode ? [{ debugName: "prompt" }] : /* istanbul ignore next */ []));
3429
+ /** Secondary hint text shown below the prompt. */
3430
+ hint = input(null, ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
3431
+ // ===== BASE PROPS =====
3432
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
3433
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3434
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
3435
+ // ===== OUTPUTS =====
3436
+ /** Fires with the selected File array whenever the user drops or browses files. */
3437
+ filesSelected = output();
3438
+ // ===== INTERNAL =====
3439
+ isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
3440
+ // ===== COMPUTED =====
3441
+ classes = computed(() => [
3442
+ 'fk-file-dropzone',
3443
+ this.isDragging() ? 'fk-file-dropzone--dragging' : '',
3444
+ this.disabled() ? 'fk-file-dropzone--disabled' : '',
3445
+ this.className(),
3446
+ ]
3447
+ .filter(Boolean)
3448
+ .join(' '), ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3449
+ get hostClass() {
3450
+ return this.classes();
3451
+ }
3452
+ // ===== EVENT HANDLERS =====
3453
+ onDragOver(event) {
3454
+ if (this.disabled()) {
3455
+ return;
3456
+ }
3457
+ event.preventDefault();
3458
+ this.isDragging.set(true);
3459
+ }
3460
+ onDragLeave(event) {
3461
+ event.preventDefault();
3462
+ this.isDragging.set(false);
3463
+ }
3464
+ onDrop(event) {
3465
+ event.preventDefault();
3466
+ this.isDragging.set(false);
3467
+ if (this.disabled()) {
3468
+ return;
3469
+ }
3470
+ const files = event.dataTransfer?.files;
3471
+ this.emit(files);
3472
+ }
3473
+ onChange(event) {
3474
+ const native = event.target;
3475
+ this.emit(native.files);
3476
+ // Allow re-selecting the same file path on a subsequent attempt.
3477
+ native.value = '';
3478
+ }
3479
+ emit(list) {
3480
+ if (!list || list.length === 0) {
3481
+ return;
3482
+ }
3483
+ const files = [];
3484
+ for (let i = 0; i < list.length; i++) {
3485
+ const file = list.item(i);
3486
+ if (file) {
3487
+ files.push(file);
3488
+ }
3489
+ }
3490
+ if (files.length === 0) {
3491
+ return;
3492
+ }
3493
+ this.filesSelected.emit(this.multiple() ? files : [files[0]]);
3494
+ }
3495
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropzoneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3496
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FileDropzoneComponent, isStandalone: true, selector: "fk-file-dropzone", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, prompt: { classPropertyName: "prompt", publicName: "prompt", isSignal: true, isRequired: true, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filesSelected: "filesSelected" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<label\n class=\"fk-file-dropzone__container\"\n [attr.id]=\"id() ?? undefined\"\n [attr.aria-label]=\"ariaLabel()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <fk-icon [name]=\"icon()\" size=\"lg\" class=\"fk-file-dropzone__icon\" />\n\n <fk-text tone=\"muted\" class=\"fk-file-dropzone__prompt\">\n {{ prompt() }}\n </fk-text>\n\n @if (hint(); as h) {\n <fk-text tone=\"muted\" class=\"fk-file-dropzone__hint\">\n {{ h }}\n </fk-text>\n }\n\n <input\n #nativeInput\n type=\"file\"\n class=\"fk-file-dropzone__native\"\n [attr.accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onChange($event)\"\n />\n</label>\n", styles: [":host{display:block}.fk-file-dropzone__container{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--fk-file-dropzone-gap, var(--fk-rhythm-2, .5rem));min-height:var(--fk-file-dropzone-min-height, 10rem);padding:var(--fk-file-dropzone-padding, var(--fk-rhythm-10, 2.5rem));background:var(--fk-file-dropzone-bg, var(--fk-color-surface-raised, #f8fafc));border:var(--fk-file-dropzone-border-width, 2px) var(--fk-file-dropzone-border-style, dashed) var(--fk-file-dropzone-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-file-dropzone-border-radius, var(--fk-radius-lg, .75rem));cursor:pointer;transition:border-color .15s ease,background-color .15s ease}.fk-file-dropzone__container:hover{border-color:var(--fk-file-dropzone-border-color-hover, var(--fk-color-primary, #0a84ff))}.fk-file-dropzone__container:focus-within{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-file-dropzone__prompt{font-weight:var(--fk-font-weight-semibold, 600);color:var(--fk-file-dropzone-prompt-color, var(--fk-color-text, #1f2d3d))}.fk-file-dropzone__hint{font-size:var(--fk-file-dropzone-hint-font-size, .8125rem)}.fk-file-dropzone__native{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}:host.fk-file-dropzone--dragging .fk-file-dropzone__container{border-color:var(--fk-file-dropzone-border-color-dragging, var(--fk-color-primary, #0a84ff));background:var(--fk-file-dropzone-bg-dragging, rgba(10, 132, 255, .04))}:host.fk-file-dropzone--disabled .fk-file-dropzone__container{cursor:not-allowed;opacity:var(--fk-file-dropzone-opacity-disabled, .6)}:host.fk-file-dropzone--disabled .fk-file-dropzone__container:hover{border-color:var(--fk-file-dropzone-border-color, var(--fk-color-border, #d9e2ee))}:host.fk-file-dropzone--disabled .fk-file-dropzone__icon{color:var(--fk-file-dropzone-icon-color-disabled, var(--fk-color-muted, #8a98a8))}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "fk-icon", inputs: ["name", "size", "color", "className", "id", "ariaLabel", "ariaHidden"] }, { kind: "component", type: TextComponent, selector: "fk-text", inputs: ["as", "variant", "tone", "align", "weight", "truncate", "maxLines", "gutterBottom", "italic", "visuallyHidden", "className", "id", "ariaLabel", "ariaDescribedBy", "role", "htmlFor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3497
+ }
3498
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FileDropzoneComponent, decorators: [{
3499
+ type: Component,
3500
+ args: [{ selector: 'fk-file-dropzone', standalone: true, imports: [IconComponent, TextComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<label\n class=\"fk-file-dropzone__container\"\n [attr.id]=\"id() ?? undefined\"\n [attr.aria-label]=\"ariaLabel()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n>\n <fk-icon [name]=\"icon()\" size=\"lg\" class=\"fk-file-dropzone__icon\" />\n\n <fk-text tone=\"muted\" class=\"fk-file-dropzone__prompt\">\n {{ prompt() }}\n </fk-text>\n\n @if (hint(); as h) {\n <fk-text tone=\"muted\" class=\"fk-file-dropzone__hint\">\n {{ h }}\n </fk-text>\n }\n\n <input\n #nativeInput\n type=\"file\"\n class=\"fk-file-dropzone__native\"\n [attr.accept]=\"accept()\"\n [multiple]=\"multiple()\"\n [disabled]=\"disabled()\"\n (change)=\"onChange($event)\"\n />\n</label>\n", styles: [":host{display:block}.fk-file-dropzone__container{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--fk-file-dropzone-gap, var(--fk-rhythm-2, .5rem));min-height:var(--fk-file-dropzone-min-height, 10rem);padding:var(--fk-file-dropzone-padding, var(--fk-rhythm-10, 2.5rem));background:var(--fk-file-dropzone-bg, var(--fk-color-surface-raised, #f8fafc));border:var(--fk-file-dropzone-border-width, 2px) var(--fk-file-dropzone-border-style, dashed) var(--fk-file-dropzone-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-file-dropzone-border-radius, var(--fk-radius-lg, .75rem));cursor:pointer;transition:border-color .15s ease,background-color .15s ease}.fk-file-dropzone__container:hover{border-color:var(--fk-file-dropzone-border-color-hover, var(--fk-color-primary, #0a84ff))}.fk-file-dropzone__container:focus-within{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-file-dropzone__prompt{font-weight:var(--fk-font-weight-semibold, 600);color:var(--fk-file-dropzone-prompt-color, var(--fk-color-text, #1f2d3d))}.fk-file-dropzone__hint{font-size:var(--fk-file-dropzone-hint-font-size, .8125rem)}.fk-file-dropzone__native{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}:host.fk-file-dropzone--dragging .fk-file-dropzone__container{border-color:var(--fk-file-dropzone-border-color-dragging, var(--fk-color-primary, #0a84ff));background:var(--fk-file-dropzone-bg-dragging, rgba(10, 132, 255, .04))}:host.fk-file-dropzone--disabled .fk-file-dropzone__container{cursor:not-allowed;opacity:var(--fk-file-dropzone-opacity-disabled, .6)}:host.fk-file-dropzone--disabled .fk-file-dropzone__container:hover{border-color:var(--fk-file-dropzone-border-color, var(--fk-color-border, #d9e2ee))}:host.fk-file-dropzone--disabled .fk-file-dropzone__icon{color:var(--fk-file-dropzone-icon-color-disabled, var(--fk-color-muted, #8a98a8))}\n"] }]
3501
+ }], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], prompt: [{ type: i0.Input, args: [{ isSignal: true, alias: "prompt", required: true }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], filesSelected: [{ type: i0.Output, args: ["filesSelected"] }], hostClass: [{
3502
+ type: HostBinding,
3503
+ args: ['class']
3504
+ }] } });
3505
+
3506
+ class SegmentedBarComponent {
3507
+ // ===== INPUTS =====
3508
+ /** Segments to render, each with a value, optional label, and color. */
3509
+ segments = input([], ...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
3510
+ /** Total maximum value; defaults to the sum of all segment values when null. */
3511
+ max = input(null, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
3512
+ /** Size variant controlling bar height via design tokens. */
3513
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3514
+ /** CSS color for the unfilled track; defaults to the theme token when null. */
3515
+ trackColor = input(null, ...(ngDevMode ? [{ debugName: "trackColor" }] : /* istanbul ignore next */ []));
3516
+ /** Explicit bar height that overrides the size-token value. */
3517
+ height = input(null, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
3518
+ // ===== BASE PROPS =====
3519
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
3520
+ ariaDescribedBy = input(null, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : /* istanbul ignore next */ []));
3521
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3522
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
3523
+ // ===== COMPUTED =====
3524
+ segmentTotal = computed(() => {
3525
+ return this.segments().reduce((sum, seg) => sum + seg.value, 0);
3526
+ }, ...(ngDevMode ? [{ debugName: "segmentTotal" }] : /* istanbul ignore next */ []));
3527
+ effectiveMax = computed(() => {
3528
+ return this.max() ?? this.segmentTotal();
3529
+ }, ...(ngDevMode ? [{ debugName: "effectiveMax" }] : /* istanbul ignore next */ []));
3530
+ normalizedSegments = computed(() => {
3531
+ const em = this.effectiveMax();
3532
+ if (em <= 0) {
3533
+ return this.segments().map((seg) => ({ ...seg, percentage: 0 }));
3534
+ }
3535
+ return this.segments().map((seg) => ({
3536
+ ...seg,
3537
+ percentage: (seg.value / em) * 100,
3538
+ }));
3539
+ }, ...(ngDevMode ? [{ debugName: "normalizedSegments" }] : /* istanbul ignore next */ []));
3540
+ valueText = computed(() => {
3541
+ const segs = this.segments();
3542
+ if (segs.length === 0) {
3543
+ return 'Empty';
3544
+ }
3545
+ const parts = segs
3546
+ .map((seg) => {
3547
+ if (seg.label) {
3548
+ return `${seg.value} ${seg.label}`;
3549
+ }
3550
+ return `${seg.value}`;
3551
+ })
3552
+ .join(', ');
3553
+ return `${parts} out of ${this.effectiveMax()}`;
3554
+ }, ...(ngDevMode ? [{ debugName: "valueText" }] : /* istanbul ignore next */ []));
3555
+ classes = computed(() => {
3556
+ return [
3557
+ 'fk-segmented-bar',
3558
+ `fk-segmented-bar--${this.size()}`,
3559
+ this.className(),
3560
+ ]
3561
+ .filter(Boolean)
3562
+ .join(' ');
3563
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
3564
+ // ===== HOST BINDINGS =====
3565
+ get hostClass() {
3566
+ return this.classes();
3567
+ }
3568
+ get hostRole() {
3569
+ return 'progressbar';
3570
+ }
3571
+ get hostAriaValueMin() {
3572
+ return 0;
3573
+ }
3574
+ get hostAriaValueMax() {
3575
+ return this.effectiveMax();
3576
+ }
3577
+ get hostAriaValueNow() {
3578
+ return this.segmentTotal();
3579
+ }
3580
+ get hostAriaValueText() {
3581
+ return this.valueText();
3582
+ }
3583
+ get hostAriaLabel() {
3584
+ return this.ariaLabel();
3585
+ }
3586
+ get hostAriaDescribedBy() {
3587
+ return this.ariaDescribedBy();
3588
+ }
3589
+ get hostId() {
3590
+ return this.id();
3591
+ }
3592
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SegmentedBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3593
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: SegmentedBarComponent, isStandalone: true, selector: "fk-segmented-bar", inputs: { segments: { classPropertyName: "segments", publicName: "segments", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.role": "this.hostRole", "attr.aria-valuemin": "this.hostAriaValueMin", "attr.aria-valuemax": "this.hostAriaValueMax", "attr.aria-valuenow": "this.hostAriaValueNow", "attr.aria-valuetext": "this.hostAriaValueText", "attr.aria-label": "this.hostAriaLabel", "attr.aria-describedby": "this.hostAriaDescribedBy", "attr.id": "this.hostId" } }, ngImport: i0, template: "<div\n class=\"fk-segmented-bar__track\"\n [style.background-color]=\"trackColor()\"\n [style.height]=\"height()\"\n>\n @for (seg of normalizedSegments(); track $index) {\n <div\n class=\"fk-segmented-bar__segment\"\n [style.width.%]=\"seg.percentage\"\n [style.background-color]=\"seg.color ?? null\"\n ></div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.fk-segmented-bar__track{display:flex;border-radius:var(--fk-segmented-bar-radius, var(--fk-radius-full, 9999px));overflow:hidden;background:var(--fk-segmented-bar-bg, var(--fk-color-surface-muted, #f1f5f9))}.fk-segmented-bar__segment{height:100%;background-color:var(--fk-segmented-bar-segment-color, var(--fk-color-primary, #0a84ff));transition:width var(--fk-segmented-bar-transition, .3s ease)}:host.fk-segmented-bar--sm .fk-segmented-bar__track{height:var(--fk-segmented-bar-height-sm, var(--fk-rhythm-1, .25rem))}:host.fk-segmented-bar--md .fk-segmented-bar__track{height:var(--fk-segmented-bar-height-md, var(--fk-rhythm-2, .5rem))}:host.fk-segmented-bar--lg .fk-segmented-bar__track{height:var(--fk-segmented-bar-height-lg, var(--fk-rhythm-3, .75rem))}@media(prefers-reduced-motion:reduce){.fk-segmented-bar__segment{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3594
+ }
3595
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: SegmentedBarComponent, decorators: [{
3596
+ type: Component,
3597
+ args: [{ selector: 'fk-segmented-bar', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"fk-segmented-bar__track\"\n [style.background-color]=\"trackColor()\"\n [style.height]=\"height()\"\n>\n @for (seg of normalizedSegments(); track $index) {\n <div\n class=\"fk-segmented-bar__segment\"\n [style.width.%]=\"seg.percentage\"\n [style.background-color]=\"seg.color ?? null\"\n ></div>\n }\n</div>\n", styles: [":host{display:block;width:100%}.fk-segmented-bar__track{display:flex;border-radius:var(--fk-segmented-bar-radius, var(--fk-radius-full, 9999px));overflow:hidden;background:var(--fk-segmented-bar-bg, var(--fk-color-surface-muted, #f1f5f9))}.fk-segmented-bar__segment{height:100%;background-color:var(--fk-segmented-bar-segment-color, var(--fk-color-primary, #0a84ff));transition:width var(--fk-segmented-bar-transition, .3s ease)}:host.fk-segmented-bar--sm .fk-segmented-bar__track{height:var(--fk-segmented-bar-height-sm, var(--fk-rhythm-1, .25rem))}:host.fk-segmented-bar--md .fk-segmented-bar__track{height:var(--fk-segmented-bar-height-md, var(--fk-rhythm-2, .5rem))}:host.fk-segmented-bar--lg .fk-segmented-bar__track{height:var(--fk-segmented-bar-height-lg, var(--fk-rhythm-3, .75rem))}@media(prefers-reduced-motion:reduce){.fk-segmented-bar__segment{transition:none}}\n"] }]
3598
+ }], propDecorators: { segments: [{ type: i0.Input, args: [{ isSignal: true, alias: "segments", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], trackColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackColor", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
3599
+ type: HostBinding,
3600
+ args: ['class']
3601
+ }], hostRole: [{
3602
+ type: HostBinding,
3603
+ args: ['attr.role']
3604
+ }], hostAriaValueMin: [{
3605
+ type: HostBinding,
3606
+ args: ['attr.aria-valuemin']
3607
+ }], hostAriaValueMax: [{
3608
+ type: HostBinding,
3609
+ args: ['attr.aria-valuemax']
3610
+ }], hostAriaValueNow: [{
3611
+ type: HostBinding,
3612
+ args: ['attr.aria-valuenow']
3613
+ }], hostAriaValueText: [{
3614
+ type: HostBinding,
3615
+ args: ['attr.aria-valuetext']
3616
+ }], hostAriaLabel: [{
3617
+ type: HostBinding,
3618
+ args: ['attr.aria-label']
3619
+ }], hostAriaDescribedBy: [{
3620
+ type: HostBinding,
3621
+ args: ['attr.aria-describedby']
3622
+ }], hostId: [{
3623
+ type: HostBinding,
3624
+ args: ['attr.id']
3625
+ }] } });
3626
+
3627
+ /**
3628
+ * Generated bundle index. Do not edit.
3629
+ */
3630
+
3631
+ export { AutocompleteComponent, CheckboxComponent, DEFAULT_ERROR_MESSAGES, DateInputComponent, DatetimeInputComponent, ERROR_MESSAGES, ErrorComponent, FIELD_CONTROL, FK_AUTOCOMPLETE_OPTION_TPL, FK_PREFIX, FK_SUFFIX, FORM, FieldArrayOutletComponent, FieldGroupComponent, FieldOutletComponent, FieldRegistry, FileDropzoneComponent, FileUploadComponent, FkAutocompleteOptionTplDirective, FkPrefixDirective, FkSuffixDirective, FormComponent, FormFieldComponent, FormRendererComponent, FormSectionComponent, HintComponent, InputComponent, LabelComponent, NumberInputComponent, PasswordInputComponent, RadioComponent, SchemaParser, SectionOutletComponent, SegmentedBarComponent, SelectComponent, SliderComponent, SwitchComponent, TagInputComponent, TextareaComponent, TimeInputComponent, ValidationRegistry, getError, getFieldState, getInvalidFields, hasError, interpolateMessage, isFormValid, markAllTouched, resolveErrorMessage };
3632
+ //# sourceMappingURL=frame-kit-ui-ng-forms.mjs.map