@raintonic/formaui 0.4.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/CHANGELOG.md +80 -35
  2. package/README.md +22 -26
  3. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs +39 -41
  4. package/fesm2022/raintonic-formaui-cdk-drag-drop.mjs.map +1 -1
  5. package/fesm2022/raintonic-formaui-cdk-form-field.mjs +207 -3
  6. package/fesm2022/raintonic-formaui-cdk-form-field.mjs.map +1 -1
  7. package/fesm2022/raintonic-formaui-cdk-overlay.mjs +19 -1
  8. package/fesm2022/raintonic-formaui-cdk-overlay.mjs.map +1 -1
  9. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs +5 -12
  10. package/fesm2022/raintonic-formaui-cdk-virtual-scroll.mjs.map +1 -1
  11. package/fesm2022/raintonic-formaui-components-accordion.mjs +8 -5
  12. package/fesm2022/raintonic-formaui-components-accordion.mjs.map +1 -1
  13. package/fesm2022/raintonic-formaui-components-alert.mjs +16 -2
  14. package/fesm2022/raintonic-formaui-components-alert.mjs.map +1 -1
  15. package/fesm2022/raintonic-formaui-components-autocomplete.mjs +255 -462
  16. package/fesm2022/raintonic-formaui-components-autocomplete.mjs.map +1 -1
  17. package/fesm2022/raintonic-formaui-components-avatar.mjs +34 -59
  18. package/fesm2022/raintonic-formaui-components-avatar.mjs.map +1 -1
  19. package/fesm2022/raintonic-formaui-components-badge.mjs +2 -2
  20. package/fesm2022/raintonic-formaui-components-badge.mjs.map +1 -1
  21. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs +4 -4
  22. package/fesm2022/raintonic-formaui-components-breadcrumb.mjs.map +1 -1
  23. package/fesm2022/raintonic-formaui-components-button-group.mjs +2 -2
  24. package/fesm2022/raintonic-formaui-components-button-group.mjs.map +1 -1
  25. package/fesm2022/raintonic-formaui-components-button.mjs +15 -20
  26. package/fesm2022/raintonic-formaui-components-button.mjs.map +1 -1
  27. package/fesm2022/raintonic-formaui-components-card.mjs +2 -2
  28. package/fesm2022/raintonic-formaui-components-card.mjs.map +1 -1
  29. package/fesm2022/raintonic-formaui-components-checkbox.mjs +2 -2
  30. package/fesm2022/raintonic-formaui-components-checkbox.mjs.map +1 -1
  31. package/fesm2022/raintonic-formaui-components-chip.mjs +97 -0
  32. package/fesm2022/raintonic-formaui-components-chip.mjs.map +1 -0
  33. package/fesm2022/raintonic-formaui-components-data-table.mjs +69 -29
  34. package/fesm2022/raintonic-formaui-components-data-table.mjs.map +1 -1
  35. package/fesm2022/raintonic-formaui-components-date-picker.mjs +223 -144
  36. package/fesm2022/raintonic-formaui-components-date-picker.mjs.map +1 -1
  37. package/fesm2022/raintonic-formaui-components-divider.mjs +2 -2
  38. package/fesm2022/raintonic-formaui-components-divider.mjs.map +1 -1
  39. package/fesm2022/raintonic-formaui-components-drawer.mjs +2 -2
  40. package/fesm2022/raintonic-formaui-components-drawer.mjs.map +1 -1
  41. package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs +888 -0
  42. package/fesm2022/raintonic-formaui-components-dropdown-menu.mjs.map +1 -0
  43. package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs +774 -0
  44. package/fesm2022/raintonic-formaui-components-dual-tier-navigation.mjs.map +1 -0
  45. package/fesm2022/raintonic-formaui-components-empty-state.mjs +2 -2
  46. package/fesm2022/raintonic-formaui-components-empty-state.mjs.map +1 -1
  47. package/fesm2022/raintonic-formaui-components-file-upload.mjs +2 -2
  48. package/fesm2022/raintonic-formaui-components-file-upload.mjs.map +1 -1
  49. package/fesm2022/raintonic-formaui-components-form-field.mjs +81 -50
  50. package/fesm2022/raintonic-formaui-components-form-field.mjs.map +1 -1
  51. package/fesm2022/raintonic-formaui-components-icon.mjs +2 -2
  52. package/fesm2022/raintonic-formaui-components-icon.mjs.map +1 -1
  53. package/fesm2022/raintonic-formaui-components-input.mjs +47 -12
  54. package/fesm2022/raintonic-formaui-components-input.mjs.map +1 -1
  55. package/fesm2022/raintonic-formaui-components-list.mjs +4 -4
  56. package/fesm2022/raintonic-formaui-components-list.mjs.map +1 -1
  57. package/fesm2022/raintonic-formaui-components-number-input.mjs +20 -12
  58. package/fesm2022/raintonic-formaui-components-number-input.mjs.map +1 -1
  59. package/fesm2022/raintonic-formaui-components-paginator.mjs +2 -2
  60. package/fesm2022/raintonic-formaui-components-paginator.mjs.map +1 -1
  61. package/fesm2022/raintonic-formaui-components-password-input.mjs +35 -110
  62. package/fesm2022/raintonic-formaui-components-password-input.mjs.map +1 -1
  63. package/fesm2022/raintonic-formaui-components-popover.mjs +3 -2
  64. package/fesm2022/raintonic-formaui-components-popover.mjs.map +1 -1
  65. package/fesm2022/raintonic-formaui-components-progressbar.mjs +3 -2
  66. package/fesm2022/raintonic-formaui-components-progressbar.mjs.map +1 -1
  67. package/fesm2022/raintonic-formaui-components-radio.mjs +5 -6
  68. package/fesm2022/raintonic-formaui-components-radio.mjs.map +1 -1
  69. package/fesm2022/raintonic-formaui-components-select.mjs +257 -412
  70. package/fesm2022/raintonic-formaui-components-select.mjs.map +1 -1
  71. package/fesm2022/raintonic-formaui-components-side-panel.mjs +2 -2
  72. package/fesm2022/raintonic-formaui-components-side-panel.mjs.map +1 -1
  73. package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs +525 -0
  74. package/fesm2022/raintonic-formaui-components-sidebar-nav-menu.mjs.map +1 -0
  75. package/fesm2022/raintonic-formaui-components-skeleton.mjs +2 -2
  76. package/fesm2022/raintonic-formaui-components-skeleton.mjs.map +1 -1
  77. package/fesm2022/raintonic-formaui-components-slider.mjs +2 -2
  78. package/fesm2022/raintonic-formaui-components-slider.mjs.map +1 -1
  79. package/fesm2022/raintonic-formaui-components-spinner.mjs +2 -2
  80. package/fesm2022/raintonic-formaui-components-spinner.mjs.map +1 -1
  81. package/fesm2022/raintonic-formaui-components-stepper.mjs +50 -45
  82. package/fesm2022/raintonic-formaui-components-stepper.mjs.map +1 -1
  83. package/fesm2022/raintonic-formaui-components-strength-meter.mjs +149 -0
  84. package/fesm2022/raintonic-formaui-components-strength-meter.mjs.map +1 -0
  85. package/fesm2022/raintonic-formaui-components-tab.mjs +2 -2
  86. package/fesm2022/raintonic-formaui-components-tab.mjs.map +1 -1
  87. package/fesm2022/raintonic-formaui-components-time-picker.mjs +194 -154
  88. package/fesm2022/raintonic-formaui-components-time-picker.mjs.map +1 -1
  89. package/fesm2022/raintonic-formaui-components-toggle-group.mjs +302 -0
  90. package/fesm2022/raintonic-formaui-components-toggle-group.mjs.map +1 -0
  91. package/fesm2022/raintonic-formaui-components-toggle.mjs +2 -2
  92. package/fesm2022/raintonic-formaui-components-toggle.mjs.map +1 -1
  93. package/fesm2022/raintonic-formaui-components-toolbar.mjs +2 -2
  94. package/fesm2022/raintonic-formaui-components-toolbar.mjs.map +1 -1
  95. package/fesm2022/raintonic-formaui-components-tooltip.mjs +10 -4
  96. package/fesm2022/raintonic-formaui-components-tooltip.mjs.map +1 -1
  97. package/fesm2022/raintonic-formaui-components-topbar.mjs +60 -0
  98. package/fesm2022/raintonic-formaui-components-topbar.mjs.map +1 -0
  99. package/fesm2022/raintonic-formaui-components-tree-select.mjs +59 -69
  100. package/fesm2022/raintonic-formaui-components-tree-select.mjs.map +1 -1
  101. package/fesm2022/raintonic-formaui-components-tree-table.mjs +2 -2
  102. package/fesm2022/raintonic-formaui-components-tree-table.mjs.map +1 -1
  103. package/fesm2022/raintonic-formaui-components-tree.mjs +31 -5
  104. package/fesm2022/raintonic-formaui-components-tree.mjs.map +1 -1
  105. package/fesm2022/raintonic-formaui-core.mjs +279 -1
  106. package/fesm2022/raintonic-formaui-core.mjs.map +1 -1
  107. package/fesm2022/raintonic-formaui-services-breakpoint.mjs +93 -0
  108. package/fesm2022/raintonic-formaui-services-breakpoint.mjs.map +1 -0
  109. package/fesm2022/raintonic-formaui-services-dialog.mjs +314 -16
  110. package/fesm2022/raintonic-formaui-services-dialog.mjs.map +1 -1
  111. package/fesm2022/raintonic-formaui-services-notification.mjs +93 -29
  112. package/fesm2022/raintonic-formaui-services-notification.mjs.map +1 -1
  113. package/fesm2022/raintonic-formaui-services-theme.mjs +46 -196
  114. package/fesm2022/raintonic-formaui-services-theme.mjs.map +1 -1
  115. package/fesm2022/raintonic-formaui.mjs +1 -1
  116. package/fesm2022/raintonic-formaui.mjs.map +1 -1
  117. package/llms-full.txt +2329 -450
  118. package/llms.txt +36 -33
  119. package/package.json +42 -19
  120. package/styles/fonts/Geist-Bold.woff2 +0 -0
  121. package/styles/fonts/Geist-Italic.woff2 +0 -0
  122. package/styles/fonts/Geist-Light.woff2 +0 -0
  123. package/styles/fonts/Geist-Medium.woff2 +0 -0
  124. package/styles/fonts/Geist-Regular.woff2 +0 -0
  125. package/styles/fonts/Geist-SemiBold.woff2 +0 -0
  126. package/styles/fonts/GeistMono-Regular.woff2 +0 -0
  127. package/styles/generated/_tokens.scss +906 -0
  128. package/styles/index.scss +11 -10
  129. package/styles/partials/_brand.scss +46 -0
  130. package/styles/partials/_constants.scss +22 -20
  131. package/styles/partials/_fonts.scss +54 -10
  132. package/styles/partials/_grid.scss +29 -18
  133. package/styles/partials/_mixins.scss +69 -27
  134. package/styles/partials/_motion.scss +28 -33
  135. package/styles/partials/_theme.scss +28 -255
  136. package/styles/partials/_type.scss +117 -0
  137. package/styles/partials/_typography.scss +45 -45
  138. package/styles/partials/_utilities.scss +198 -98
  139. package/styles/partials/components/_button.scss +144 -75
  140. package/styles/partials/components/_dialog.scss +181 -180
  141. package/styles/partials/components/_overlay.scss +87 -87
  142. package/styles/partials/themes/_dark.scss +3 -268
  143. package/styles/partials/themes/_light.scss +4 -268
  144. package/styles/styles.css +7744 -0
  145. package/styles/styles.entry.scss +3 -0
  146. package/styles/utilities.css +4802 -0
  147. package/styles/utilities.entry.scss +3 -0
  148. package/types/raintonic-formaui-cdk-drag-drop.d.ts +0 -1
  149. package/types/raintonic-formaui-cdk-drag-drop.d.ts.map +1 -1
  150. package/types/raintonic-formaui-cdk-form-field.d.ts +118 -2
  151. package/types/raintonic-formaui-cdk-form-field.d.ts.map +1 -1
  152. package/types/raintonic-formaui-cdk-overlay.d.ts +2 -0
  153. package/types/raintonic-formaui-cdk-overlay.d.ts.map +1 -1
  154. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts +0 -1
  155. package/types/raintonic-formaui-cdk-virtual-scroll.d.ts.map +1 -1
  156. package/types/raintonic-formaui-components-accordion.d.ts +1 -1
  157. package/types/raintonic-formaui-components-accordion.d.ts.map +1 -1
  158. package/types/raintonic-formaui-components-alert.d.ts +6 -1
  159. package/types/raintonic-formaui-components-alert.d.ts.map +1 -1
  160. package/types/raintonic-formaui-components-autocomplete.d.ts +73 -116
  161. package/types/raintonic-formaui-components-autocomplete.d.ts.map +1 -1
  162. package/types/raintonic-formaui-components-avatar.d.ts +13 -31
  163. package/types/raintonic-formaui-components-avatar.d.ts.map +1 -1
  164. package/types/raintonic-formaui-components-button.d.ts +4 -10
  165. package/types/raintonic-formaui-components-button.d.ts.map +1 -1
  166. package/types/raintonic-formaui-components-chip.d.ts +43 -0
  167. package/types/raintonic-formaui-components-chip.d.ts.map +1 -0
  168. package/types/raintonic-formaui-components-data-table.d.ts +48 -11
  169. package/types/raintonic-formaui-components-data-table.d.ts.map +1 -1
  170. package/types/raintonic-formaui-components-date-picker.d.ts +59 -23
  171. package/types/raintonic-formaui-components-date-picker.d.ts.map +1 -1
  172. package/types/raintonic-formaui-components-dropdown-menu.d.ts +394 -0
  173. package/types/raintonic-formaui-components-dropdown-menu.d.ts.map +1 -0
  174. package/types/raintonic-formaui-components-dual-tier-navigation.d.ts +87 -0
  175. package/types/raintonic-formaui-components-dual-tier-navigation.d.ts.map +1 -0
  176. package/types/raintonic-formaui-components-form-field.d.ts +51 -21
  177. package/types/raintonic-formaui-components-form-field.d.ts.map +1 -1
  178. package/types/raintonic-formaui-components-input.d.ts +20 -11
  179. package/types/raintonic-formaui-components-input.d.ts.map +1 -1
  180. package/types/raintonic-formaui-components-number-input.d.ts +5 -3
  181. package/types/raintonic-formaui-components-number-input.d.ts.map +1 -1
  182. package/types/raintonic-formaui-components-password-input.d.ts +18 -32
  183. package/types/raintonic-formaui-components-password-input.d.ts.map +1 -1
  184. package/types/raintonic-formaui-components-popover.d.ts.map +1 -1
  185. package/types/raintonic-formaui-components-progressbar.d.ts +1 -1
  186. package/types/raintonic-formaui-components-progressbar.d.ts.map +1 -1
  187. package/types/raintonic-formaui-components-radio.d.ts +1 -2
  188. package/types/raintonic-formaui-components-radio.d.ts.map +1 -1
  189. package/types/raintonic-formaui-components-select.d.ts +107 -76
  190. package/types/raintonic-formaui-components-select.d.ts.map +1 -1
  191. package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts +223 -0
  192. package/types/raintonic-formaui-components-sidebar-nav-menu.d.ts.map +1 -0
  193. package/types/raintonic-formaui-components-stepper.d.ts +4 -2
  194. package/types/raintonic-formaui-components-stepper.d.ts.map +1 -1
  195. package/types/raintonic-formaui-components-strength-meter.d.ts +78 -0
  196. package/types/raintonic-formaui-components-strength-meter.d.ts.map +1 -0
  197. package/types/raintonic-formaui-components-time-picker.d.ts +44 -24
  198. package/types/raintonic-formaui-components-time-picker.d.ts.map +1 -1
  199. package/types/raintonic-formaui-components-toggle-group.d.ts +100 -0
  200. package/types/raintonic-formaui-components-toggle-group.d.ts.map +1 -0
  201. package/types/raintonic-formaui-components-tooltip.d.ts +2 -1
  202. package/types/raintonic-formaui-components-tooltip.d.ts.map +1 -1
  203. package/types/raintonic-formaui-components-topbar.d.ts +48 -0
  204. package/types/raintonic-formaui-components-topbar.d.ts.map +1 -0
  205. package/types/raintonic-formaui-components-tree-select.d.ts +25 -9
  206. package/types/raintonic-formaui-components-tree-select.d.ts.map +1 -1
  207. package/types/raintonic-formaui-components-tree.d.ts +12 -1
  208. package/types/raintonic-formaui-components-tree.d.ts.map +1 -1
  209. package/types/raintonic-formaui-core.d.ts +243 -5
  210. package/types/raintonic-formaui-core.d.ts.map +1 -1
  211. package/types/raintonic-formaui-services-breakpoint.d.ts +44 -0
  212. package/types/raintonic-formaui-services-breakpoint.d.ts.map +1 -0
  213. package/types/raintonic-formaui-services-dialog.d.ts +141 -2
  214. package/types/raintonic-formaui-services-dialog.d.ts.map +1 -1
  215. package/types/raintonic-formaui-services-notification.d.ts +24 -2
  216. package/types/raintonic-formaui-services-notification.d.ts.map +1 -1
  217. package/types/raintonic-formaui-services-theme.d.ts +13 -103
  218. package/types/raintonic-formaui-services-theme.d.ts.map +1 -1
  219. package/types/raintonic-formaui.d.ts +1 -1
  220. package/fesm2022/raintonic-formaui-components-big-menu.mjs +0 -86
  221. package/fesm2022/raintonic-formaui-components-big-menu.mjs.map +0 -1
  222. package/fesm2022/raintonic-formaui-components-menu.mjs +0 -896
  223. package/fesm2022/raintonic-formaui-components-menu.mjs.map +0 -1
  224. package/fesm2022/raintonic-formaui-components-sidebar.mjs +0 -275
  225. package/fesm2022/raintonic-formaui-components-sidebar.mjs.map +0 -1
  226. package/fesm2022/raintonic-formaui-components-tag.mjs +0 -95
  227. package/fesm2022/raintonic-formaui-components-tag.mjs.map +0 -1
  228. package/styles/_fonts-entry.scss +0 -3
  229. package/styles/fonts/inter-tight-latin-italic.woff2 +0 -0
  230. package/styles/fonts/inter-tight-latin.woff2 +0 -0
  231. package/types/raintonic-formaui-components-big-menu.d.ts +0 -73
  232. package/types/raintonic-formaui-components-big-menu.d.ts.map +0 -1
  233. package/types/raintonic-formaui-components-menu.d.ts +0 -403
  234. package/types/raintonic-formaui-components-menu.d.ts.map +0 -1
  235. package/types/raintonic-formaui-components-sidebar.d.ts +0 -185
  236. package/types/raintonic-formaui-components-sidebar.d.ts.map +0 -1
  237. package/types/raintonic-formaui-components-tag.d.ts +0 -43
  238. package/types/raintonic-formaui-components-tag.d.ts.map +0 -1
@@ -1,33 +1,39 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, input, output, signal, inject, ChangeDetectorRef, effect, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, booleanAttribute, computed, ElementRef, NgZone } from '@angular/core';
2
+ import { Injectable, input, output, signal, computed, inject, ChangeDetectorRef, effect, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, booleanAttribute } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
- import { NgForm, FormGroupDirective, NG_VALUE_ACCESSOR } from '@angular/forms';
5
- import { DOCUMENT } from '@angular/common';
6
- import { Subject, Subscription, fromEvent } from 'rxjs';
7
- import { filter } from 'rxjs/operators';
8
- import { injectNgControl, updateErrorState, syncRequiredState, syncNgControlDisabled } from '@raintonic/formaui/cdk/form-field';
4
+ import { NgForm, FormGroupDirective, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
5
+ import { Subject } from 'rxjs';
6
+ import * as i1 from '@raintonic/formaui/cdk/form-field';
7
+ import { FuiPopupOverlayDirective, FuiFormControlSyncDirective, injectNgControl } from '@raintonic/formaui/cdk/form-field';
9
8
  import { FuiIntlBase, DefaultErrorStateMatcher, FUI_FORM_FIELD_CONTROL } from '@raintonic/formaui/core';
10
- import { FuiOverlayService } from '@raintonic/formaui/cdk/overlay';
9
+ import { FuiInputDirective } from '@raintonic/formaui/components/input';
11
10
  import { FuiIconComponent } from '@raintonic/formaui/components/icon';
12
11
 
12
+ /** Standard time arithmetic constants — avoid magic numbers in time parsing/generation. */
13
+ const HOURS_PER_DAY = 24;
14
+ const MINUTES_PER_HOUR = 60;
15
+ const SECONDS_PER_MINUTE = 60;
16
+ const NOON_HOUR = 12;
17
+ const MIDNIGHT_HOUR = 0;
13
18
  /**
14
- * Convert a TimeValue to total minutes for comparison purposes.
19
+ * @deprecated Use `timeToSeconds()` for integer-precise comparison. Will be removed in v0.6.0.
15
20
  */
16
21
  function timeToMinutes(time) {
17
- return time.hours * 60 + time.minutes + (time.seconds ? time.seconds / 60 : 0);
22
+ return time.hours * MINUTES_PER_HOUR + time.minutes + (time.seconds ? time.seconds / MINUTES_PER_HOUR : 0);
23
+ }
24
+ /**
25
+ * Converts a `TimeValue` to total seconds since 00:00:00.
26
+ * Integer-precise; safe for comparison without floating-point error.
27
+ */
28
+ function timeToSeconds(t) {
29
+ return t.hours * (MINUTES_PER_HOUR * SECONDS_PER_MINUTE) + t.minutes * SECONDS_PER_MINUTE + (t.seconds ?? 0);
18
30
  }
19
31
  /**
20
32
  * Compare two TimeValue objects.
21
- * Returns -1 if a < b, 0 if equal, 1 if a > b.
33
+ * Returns a negative number if a < b, 0 if equal, positive if a > b.
22
34
  */
23
35
  function compareTime(a, b) {
24
- const aMin = timeToMinutes(a);
25
- const bMin = timeToMinutes(b);
26
- if (aMin < bMin)
27
- return -1;
28
- if (aMin > bMin)
29
- return 1;
30
- return 0;
36
+ return timeToSeconds(a) - timeToSeconds(b);
31
37
  }
32
38
  /**
33
39
  * Check if a time is within an optional min/max range (inclusive).
@@ -56,12 +62,12 @@ function formatTime(value, format, showSeconds) {
56
62
  return showSeconds ? `${base}:${pad(s)}` : base;
57
63
  }
58
64
  // 12h format
59
- const period = h >= 12 ? 'PM' : 'AM';
65
+ const period = h >= NOON_HOUR ? 'PM' : 'AM';
60
66
  let displayHour = h;
61
- if (h === 0)
62
- displayHour = 12;
63
- else if (h > 12)
64
- displayHour = h - 12;
67
+ if (h === MIDNIGHT_HOUR)
68
+ displayHour = NOON_HOUR;
69
+ else if (h > NOON_HOUR)
70
+ displayHour = h - NOON_HOUR;
65
71
  const base = `${displayHour}:${pad(m)}`;
66
72
  return showSeconds ? `${base}:${pad(s)} ${period}` : `${base} ${period}`;
67
73
  }
@@ -83,14 +89,14 @@ function parseTimeString(str, format) {
83
89
  const minutes = parseInt(match12[2], 10);
84
90
  const seconds = match12[3] ? parseInt(match12[3], 10) : undefined;
85
91
  const period = match12[4].toUpperCase();
86
- if (hours < 1 || hours > 12 || minutes < 0 || minutes > 59)
92
+ if (hours < 1 || hours > NOON_HOUR || minutes < 0 || minutes > 59)
87
93
  return null;
88
94
  if (seconds !== undefined && (seconds < 0 || seconds > 59))
89
95
  return null;
90
- if (period === 'AM' && hours === 12)
91
- hours = 0;
92
- else if (period === 'PM' && hours !== 12)
93
- hours += 12;
96
+ if (period === 'AM' && hours === NOON_HOUR)
97
+ hours = MIDNIGHT_HOUR;
98
+ else if (period === 'PM' && hours !== NOON_HOUR)
99
+ hours += NOON_HOUR;
94
100
  return { hours, minutes, ...(seconds !== undefined ? { seconds } : {}) };
95
101
  }
96
102
  // 24h format: "HH:mm" or "HH:mm:ss"
@@ -100,7 +106,7 @@ function parseTimeString(str, format) {
100
106
  const hours = parseInt(match24[1], 10);
101
107
  const minutes = parseInt(match24[2], 10);
102
108
  const seconds = match24[3] ? parseInt(match24[3], 10) : undefined;
103
- if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59)
109
+ if (hours < 0 || hours > HOURS_PER_DAY - 1 || minutes < 0 || minutes > 59)
104
110
  return null;
105
111
  if (seconds !== undefined && (seconds < 0 || seconds > 59))
106
112
  return null;
@@ -111,9 +117,9 @@ function parseTimeString(str, format) {
111
117
  */
112
118
  function generateTimeOptions(format, minuteStep, showSeconds, min, max) {
113
119
  const options = [];
114
- const step = Math.max(1, Math.min(60, minuteStep));
115
- for (let h = 0; h < 24; h++) {
116
- for (let m = 0; m < 60; m += step) {
120
+ const step = Math.max(1, Math.min(MINUTES_PER_HOUR, minuteStep));
121
+ for (let h = 0; h < HOURS_PER_DAY; h++) {
122
+ for (let m = 0; m < MINUTES_PER_HOUR; m += step) {
117
123
  const value = { hours: h, minutes: m, ...(showSeconds ? { seconds: 0 } : {}) };
118
124
  const label = formatTime(value, format, showSeconds);
119
125
  const disabled = !isTimeInRange(value, min, max);
@@ -165,6 +171,23 @@ class FuiTimeListComponent {
165
171
  optionSelected = output();
166
172
  /** Index of the currently focused option for keyboard navigation. */
167
173
  _focusedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "_focusedIndex" }] : /* istanbul ignore next */ []));
174
+ /**
175
+ * Label of the currently active option for the aria-live announcement region.
176
+ * Resolves to the focused option during keyboard navigation, falling back to
177
+ * the selected option's label when no keyboard focus is active.
178
+ */
179
+ activeOptionLabel = computed(() => {
180
+ const opts = this.options();
181
+ const focusedIdx = this._focusedIndex();
182
+ if (focusedIdx >= 0 && focusedIdx < opts.length) {
183
+ return opts[focusedIdx].label;
184
+ }
185
+ const selected = this.selectedValue();
186
+ if (!selected)
187
+ return '';
188
+ const match = opts.find((o) => compareTime(o.value, selected) === 0);
189
+ return match?.label ?? '';
190
+ }, ...(ngDevMode ? [{ debugName: "activeOptionLabel" }] : /* istanbul ignore next */ []));
168
191
  listContainer;
169
192
  intl = inject(FuiTimePickerIntl);
170
193
  _cdr = inject(ChangeDetectorRef);
@@ -289,13 +312,13 @@ class FuiTimeListComponent {
289
312
  }
290
313
  }
291
314
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimeListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
292
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimeListComponent, isStandalone: true, selector: "fui-time-list", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, host: { classAttribute: "fui-time-list" }, viewQueries: [{ propertyName: "listContainer", first: true, predicate: ["listContainer"], descendants: true }], ngImport: i0, template: "<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-list{display:block}.fui-time-list__container{max-height:200px;overflow-y:auto;overflow-x:hidden;padding:var(--fui-spacing-02) 0}.fui-time-list__container::-webkit-scrollbar{width:8px}.fui-time-list__container::-webkit-scrollbar-track{background:var(--fui-surface-01);border-radius:var(--fui-border-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb{background:var(--fui-border-color);border-radius:var(--fui-border-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb:hover{background:var(--fui-text-secondary)}.fui-time-list__option{display:flex;align-items:center;padding:var(--fui-spacing-02) var(--fui-spacing-04);font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);line-height:var(--fui-line-height-02);color:var(--fui-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color var(--fui-duration-fast-01) var(--fui-ease-standard) 0ms}.fui-time-list__option:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-surface-02)}.fui-time-list__option--selected{background-color:var(--fui-primary-20);color:var(--fui-primary-80);font-weight:var(--fui-font-weight-medium)}.fui-time-list__option--selected:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-primary-30)}.fui-time-list__option--focused{outline:2px solid var(--fui-primary-60);outline-offset:-2px;border-radius:var(--fui-border-radius-sm)}.fui-time-list__option--disabled{color:var(--fui-text-disabled);cursor:not-allowed;opacity:.5}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
315
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimeListComponent, isStandalone: true, selector: "fui-time-list", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, host: { classAttribute: "fui-time-list" }, viewQueries: [{ propertyName: "listContainer", first: true, predicate: ["listContainer"], descendants: true }], ngImport: i0, template: "<span class=\"fui-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ activeOptionLabel() }}</span>\r\n<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-list{display:block}.fui-time-list__container{max-height:200px;overflow-y:auto;overflow-x:hidden;padding:var(--fui-spacing-2) 0}.fui-time-list__container::-webkit-scrollbar{width:8px}.fui-time-list__container::-webkit-scrollbar-track{background:var(--fui-bg-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb{background:var(--fui-border-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb:hover{background:var(--fui-text-secondary)}.fui-time-list__option{display:flex;align-items:center;padding:var(--fui-spacing-2) var(--fui-spacing-6);font-family:var(--fui-font-sans);font-size:var(--fui-text-base);line-height:var(--fui-leading-normal);color:var(--fui-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition-property:background-color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-list__option:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-bg-subtle)}.fui-time-list__option--selected{background-color:var(--fui-primary-20);color:var(--fui-primary-80);font-weight:var(--fui-weight-medium)}.fui-time-list__option--selected:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-primary-30)}.fui-time-list__option--focused{outline:2px solid var(--fui-primary-60);outline-offset:-2px;border-radius:var(--fui-radius-sm)}.fui-time-list__option--disabled{color:var(--fui-text-disabled);cursor:not-allowed;opacity:.5}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
293
316
  }
294
317
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimeListComponent, decorators: [{
295
318
  type: Component,
296
319
  args: [{ selector: 'fui-time-list', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
297
320
  class: 'fui-time-list',
298
- }, template: "<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-list{display:block}.fui-time-list__container{max-height:200px;overflow-y:auto;overflow-x:hidden;padding:var(--fui-spacing-02) 0}.fui-time-list__container::-webkit-scrollbar{width:8px}.fui-time-list__container::-webkit-scrollbar-track{background:var(--fui-surface-01);border-radius:var(--fui-border-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb{background:var(--fui-border-color);border-radius:var(--fui-border-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb:hover{background:var(--fui-text-secondary)}.fui-time-list__option{display:flex;align-items:center;padding:var(--fui-spacing-02) var(--fui-spacing-04);font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);line-height:var(--fui-line-height-02);color:var(--fui-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color var(--fui-duration-fast-01) var(--fui-ease-standard) 0ms}.fui-time-list__option:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-surface-02)}.fui-time-list__option--selected{background-color:var(--fui-primary-20);color:var(--fui-primary-80);font-weight:var(--fui-font-weight-medium)}.fui-time-list__option--selected:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-primary-30)}.fui-time-list__option--focused{outline:2px solid var(--fui-primary-60);outline-offset:-2px;border-radius:var(--fui-border-radius-sm)}.fui-time-list__option--disabled{color:var(--fui-text-disabled);cursor:not-allowed;opacity:.5}\n"] }]
321
+ }, template: "<span class=\"fui-sr-only\" aria-live=\"polite\" aria-atomic=\"true\">{{ activeOptionLabel() }}</span>\r\n<div\r\n #listContainer\r\n class=\"fui-time-list__container\"\r\n role=\"listbox\"\r\n tabindex=\"0\"\r\n [attr.aria-label]=\"intl.availableTimesAriaLabel\"\r\n (keydown)=\"onKeydown($event)\"\r\n>\r\n @for (option of options(); track option.label) {\r\n <div\r\n class=\"fui-time-list__option\"\r\n [class.fui-time-list__option--selected]=\"_isSelected(option)\"\r\n [class.fui-time-list__option--disabled]=\"option.disabled\"\r\n [class.fui-time-list__option--focused]=\"_focusedIndex() === $index\"\r\n [attr.id]=\"'fui-time-option-' + $index\"\r\n [attr.aria-selected]=\"_isSelected(option)\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n role=\"option\"\r\n (click)=\"_selectOption(option)\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-list{display:block}.fui-time-list__container{max-height:200px;overflow-y:auto;overflow-x:hidden;padding:var(--fui-spacing-2) 0}.fui-time-list__container::-webkit-scrollbar{width:8px}.fui-time-list__container::-webkit-scrollbar-track{background:var(--fui-bg-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb{background:var(--fui-border-default);border-radius:var(--fui-radius-sm)}.fui-time-list__container::-webkit-scrollbar-thumb:hover{background:var(--fui-text-secondary)}.fui-time-list__option{display:flex;align-items:center;padding:var(--fui-spacing-2) var(--fui-spacing-6);font-family:var(--fui-font-sans);font-size:var(--fui-text-base);line-height:var(--fui-leading-normal);color:var(--fui-text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;transition-property:background-color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-list__option:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-bg-subtle)}.fui-time-list__option--selected{background-color:var(--fui-primary-20);color:var(--fui-primary-80);font-weight:var(--fui-weight-medium)}.fui-time-list__option--selected:hover:not(.fui-time-list__option--disabled){background-color:var(--fui-primary-30)}.fui-time-list__option--focused{outline:2px solid var(--fui-primary-60);outline-offset:-2px;border-radius:var(--fui-radius-sm)}.fui-time-list__option--disabled{color:var(--fui-text-disabled);cursor:not-allowed;opacity:.5}\n"] }]
299
322
  }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], selectedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValue", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], listContainer: [{
300
323
  type: ViewChild,
301
324
  args: ['listContainer', { static: false }]
@@ -304,6 +327,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImpor
304
327
  class FuiTimePickerComponent {
305
328
  static nextId = 0;
306
329
  controlType = 'fui-time-picker';
330
+ // --- Injected dependencies ---
331
+ intl = inject(FuiTimePickerIntl);
332
+ _cdr = inject(ChangeDetectorRef);
333
+ _popup = inject(FuiPopupOverlayDirective);
334
+ _formSync = inject(FuiFormControlSyncDirective);
335
+ _elementRef = inject(ElementRef);
307
336
  // --- Inputs ---
308
337
  formatInput = input('24h', { ...(ngDevMode ? { debugName: "formatInput" } : /* istanbul ignore next */ {}), alias: 'format' });
309
338
  showSeconds = input(false, { ...(ngDevMode ? { debugName: "showSeconds" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
@@ -313,7 +342,8 @@ class FuiTimePickerComponent {
313
342
  placeholderInput = input('', { ...(ngDevMode ? { debugName: "placeholderInput" } : /* istanbul ignore next */ {}), alias: 'placeholder' });
314
343
  disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
315
344
  transform: booleanAttribute });
316
- readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
345
+ readonlyInput = input(false, { ...(ngDevMode ? { debugName: "readonlyInput" } : /* istanbul ignore next */ {}), alias: 'readonly',
346
+ transform: booleanAttribute });
317
347
  errorStateMatcher = input(null, ...(ngDevMode ? [{ debugName: "errorStateMatcher" }] : /* istanbul ignore next */ []));
318
348
  // --- Outputs ---
319
349
  timeChange = output();
@@ -324,8 +354,8 @@ class FuiTimePickerComponent {
324
354
  _ngControlDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_ngControlDisabled" }] : /* istanbul ignore next */ []));
325
355
  _required = signal(false, ...(ngDevMode ? [{ debugName: "_required" }] : /* istanbul ignore next */ []));
326
356
  _errorState = signal(false, ...(ngDevMode ? [{ debugName: "_errorState" }] : /* istanbul ignore next */ []));
327
- _readOnly = signal(false, ...(ngDevMode ? [{ debugName: "_readOnly" }] : /* istanbul ignore next */ []));
328
- _panelOpen = signal(false, ...(ngDevMode ? [{ debugName: "_panelOpen" }] : /* istanbul ignore next */ []));
357
+ /** Whether the panel is open projected from FuiPopupOverlayDirective. */
358
+ panelOpen = computed(() => this._popup.panelOpen(), ...(ngDevMode ? [{ debugName: "panelOpen" }] : /* istanbul ignore next */ []));
329
359
  // --- FuiFormFieldControl ---
330
360
  stateChanges = new Subject();
331
361
  _uid = `fui-time-picker-${FuiTimePickerComponent.nextId++}`;
@@ -347,6 +377,7 @@ class FuiTimePickerComponent {
347
377
  }, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
348
378
  focused = this._focused;
349
379
  disabled = computed(() => this._disabled() || this.disabledInput() || this._ngControlDisabled(), ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
380
+ readonly = computed(() => this.readonlyInput(), ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
350
381
  errorState = this._errorState;
351
382
  id = this._uid;
352
383
  empty = computed(() => {
@@ -364,19 +395,11 @@ class FuiTimePickerComponent {
364
395
  const maxVal = this.max() ? parseISOTimeString(this.max()) : undefined;
365
396
  return generateTimeOptions(this.formatInput(), this.minuteStep(), this.showSeconds(), minVal ?? undefined, maxVal ?? undefined);
366
397
  }, ...(ngDevMode ? [{ debugName: "_timeOptions" }] : /* istanbul ignore next */ []));
367
- // ViewChild
398
+ // ViewChild — static: true because the panel is always rendered via [hidden]
368
399
  inputElement;
369
400
  timeListPanel;
370
- // Overlay
371
- _overlayRef = null;
372
- _overlaySubscriptions = new Subscription();
373
- _overlayService = inject(FuiOverlayService);
374
- _elementRef = inject(ElementRef);
375
- _document = inject(DOCUMENT);
376
- _ngZone = inject(NgZone);
377
- intl = inject(FuiTimePickerIntl);
378
- _cdr = inject(ChangeDetectorRef);
379
- _outsideClickSub;
401
+ // ViewChild for fuiInput directive — delegates readonly/disabled/aria/state to it
402
+ _fuiInput;
380
403
  // CVA callbacks
381
404
  _onChange = () => {
382
405
  /* noop */
@@ -384,8 +407,12 @@ class FuiTimePickerComponent {
384
407
  _onTouched = () => {
385
408
  /* noop */
386
409
  };
410
+ // Validator callback — null until Angular calls registerOnValidatorChange
411
+ _validatorChange = null;
387
412
  constructor() {
388
- this.intl.changes.pipe(takeUntilDestroyed()).subscribe(() => { this._cdr.markForCheck(); });
413
+ this.intl.changes.pipe(takeUntilDestroyed()).subscribe(() => {
414
+ this._cdr.markForCheck();
415
+ });
389
416
  void Promise.resolve().then(() => {
390
417
  if (this._ngControlRef.ngControl) {
391
418
  this._ngControlRef.ngControl.valueAccessor = this;
@@ -410,20 +437,52 @@ class FuiTimePickerComponent {
410
437
  this._errorState();
411
438
  this.stateChanges.next();
412
439
  });
440
+ // Shift focus into the panel on open (a11y: moves ESC handling out of
441
+ // any ancestor focus-trap such as fui-dialog) and restore it on close.
442
+ // queueMicrotask defers until Angular has flushed the [hidden] binding,
443
+ // so the panel div is focusable when .focus() fires.
444
+ // _panelOpenInitialized skips the initial synchronous run so the input
445
+ // does not steal focus on first render.
446
+ let _panelOpenInitialized = false;
447
+ effect(() => {
448
+ const isOpen = this._popup.panelOpen();
449
+ if (!_panelOpenInitialized) {
450
+ _panelOpenInitialized = true;
451
+ return;
452
+ }
453
+ if (isOpen) {
454
+ queueMicrotask(() => this.timeListPanel?.nativeElement.focus());
455
+ }
456
+ else {
457
+ queueMicrotask(() => this.inputElement?.nativeElement.focus());
458
+ }
459
+ });
460
+ // Re-trigger validation when min/max signals change so Angular re-runs validate().
461
+ effect(() => {
462
+ this.min();
463
+ this.max();
464
+ this._validatorChange?.();
465
+ });
413
466
  }
414
- ngDoCheck() {
415
- if (this.ngControl) {
416
- updateErrorState(this.ngControl, this._errorState, this.errorStateMatcher(), this._defaultErrorStateMatcher, this._parentForm, this._parentFormGroup, this.stateChanges);
417
- syncRequiredState(this.ngControl, this._required, this.stateChanges);
418
- syncNgControlDisabled(this.ngControl, this._ngControlDisabled, this.stateChanges);
419
- }
467
+ ngAfterViewInit() {
468
+ // Wire popup-overlay directive — panel is always rendered via [hidden]
469
+ // so timeListPanel is available at this point.
470
+ this._popup.setTrigger(this._elementRef);
471
+ this._popup.setPanel(this.timeListPanel ?? null);
472
+ this._popup.panelClass.set(['fui-time-picker-overlay-panel']);
473
+ this._popup.backdropClass.set('fui-time-picker-backdrop');
474
+ // Wire form-control-sync directive signals
475
+ this._formSync.errorState.set(this._errorState);
476
+ this._formSync.errorStateMatcher.set(this.errorStateMatcher());
477
+ this._formSync.required.set(this._required);
478
+ this._formSync.ngControlDisabled.set(this._ngControlDisabled);
479
+ this._formSync.stateChanges.set(this.stateChanges);
420
480
  }
421
481
  ngOnDestroy() {
422
482
  this.stateChanges.complete();
423
- this._outsideClickSub?.unsubscribe();
424
- this._disposeOverlay();
425
483
  }
426
484
  // --- CVA ---
485
+ /** Sets the time value from the form model; accepts an ISO time string or null. */
427
486
  writeValue(value) {
428
487
  if (value) {
429
488
  const parsed = parseISOTimeString(value);
@@ -434,60 +493,84 @@ class FuiTimePickerComponent {
434
493
  }
435
494
  this.stateChanges.next();
436
495
  }
496
+ /** Registers the callback Angular calls when the value should propagate to the model. */
437
497
  registerOnChange(fn) {
438
498
  this._onChange = fn;
439
499
  }
500
+ /** Registers the callback Angular calls when the control should be marked as touched. */
440
501
  registerOnTouched(fn) {
441
502
  this._onTouched = fn;
442
503
  }
504
+ /** Enables or disables the control programmatically; mirrors the `disabled` form-control state. */
443
505
  setDisabledState(isDisabled) {
444
506
  this._disabled.set(isDisabled);
445
507
  this.stateChanges.next();
446
508
  }
509
+ // --- Validator ---
510
+ /** Stores the callback Angular calls when validator inputs (min/max) change so the form re-validates. */
511
+ registerOnValidatorChange(fn) {
512
+ this._validatorChange = fn;
513
+ }
514
+ /** Validates the current time value against min and max constraints. Returns null when valid. */
515
+ validate(control) {
516
+ const value = control.value;
517
+ if (value == null || value === '')
518
+ return null;
519
+ const parsed = parseISOTimeString(value);
520
+ if (!parsed)
521
+ return { timeInvalid: { value } };
522
+ const minStr = this.min();
523
+ if (minStr) {
524
+ const min = parseISOTimeString(minStr);
525
+ if (min && compareTime(parsed, min) < 0) {
526
+ return { minTime: { min: minStr, actual: value } };
527
+ }
528
+ }
529
+ const maxStr = this.max();
530
+ if (maxStr) {
531
+ const max = parseISOTimeString(maxStr);
532
+ if (max && compareTime(parsed, max) > 0) {
533
+ return { maxTime: { max: maxStr, actual: value } };
534
+ }
535
+ }
536
+ return null;
537
+ }
447
538
  // --- FuiFormFieldControl ---
539
+ /** Focuses the time input and opens the time panel when the form-field container is clicked, unless disabled. */
448
540
  onContainerClick(_event) {
449
541
  if (!this.disabled()) {
450
542
  this.inputElement?.nativeElement.focus();
451
- if (!this._panelOpen()) {
543
+ if (!this.panelOpen()) {
452
544
  this.open();
453
545
  }
454
546
  }
455
547
  }
548
+ /** Stores the space-separated list of IDs for the aria-describedby attribute. */
456
549
  setDescribedByIds(ids) {
457
550
  this._ariaDescribedby = ids.length ? ids.join(' ') : null;
458
- }
459
- setReadOnly(readOnly) {
460
- this._readOnly.set(readOnly);
551
+ this._fuiInput?.setDescribedByIds(ids);
461
552
  }
462
553
  // --- Panel open/close ---
554
+ /** Opens the time list panel. No-op if disabled, readonly, or already open. */
463
555
  open() {
464
- if (this.disabled() || this.readonly() || this._panelOpen())
556
+ if (this.disabled() || this.readonly() || this.panelOpen())
465
557
  return;
466
- requestAnimationFrame(() => {
467
- this._panelOpen.set(true);
468
- this._focused.set(true);
469
- setTimeout(() => {
470
- this._createOverlay();
471
- this._listenForOutsideClicks();
472
- });
473
- });
558
+ this._focused.set(true);
559
+ this._popup.open();
474
560
  }
561
+ /** Closes the time list panel. Focus is restored to the input by the constructor effect. No-op if already closed. */
475
562
  close() {
476
- if (!this._panelOpen())
563
+ if (!this.panelOpen())
477
564
  return;
478
- this._outsideClickSub?.unsubscribe();
479
- this._panelOpen.set(false);
480
565
  this._focused.set(false);
481
- this._disposeOverlay();
482
566
  this._onTouched();
483
- setTimeout(() => {
484
- this.inputElement?.nativeElement.focus();
485
- }, 0);
567
+ this._popup.close();
568
+ // Focus restore is handled by the effect in the constructor.
486
569
  }
487
570
  _togglePanel() {
488
571
  if (this.disabled() || this.readonly())
489
572
  return;
490
- if (this._panelOpen()) {
573
+ if (this.panelOpen()) {
491
574
  this.close();
492
575
  }
493
576
  else {
@@ -499,15 +582,16 @@ class FuiTimePickerComponent {
499
582
  // We handle parsing on blur, not on every keystroke
500
583
  }
501
584
  _onKeydown(event) {
502
- if (event.key === 'Escape' && this._panelOpen()) {
585
+ if (event.key === 'Escape' && this.panelOpen()) {
503
586
  event.preventDefault();
587
+ event.stopPropagation();
504
588
  this.close();
505
589
  }
506
- else if (event.key === 'ArrowDown' && !this._panelOpen()) {
590
+ else if (event.key === 'ArrowDown' && !this.panelOpen()) {
507
591
  event.preventDefault();
508
592
  this.open();
509
593
  }
510
- else if (event.key === 'Enter' && !this._panelOpen()) {
594
+ else if (event.key === 'Enter' && !this.panelOpen()) {
511
595
  event.preventDefault();
512
596
  this.open();
513
597
  }
@@ -532,7 +616,7 @@ class FuiTimePickerComponent {
532
616
  // Invalid input — revert to previous formatted value
533
617
  input.value = this._displayValue();
534
618
  }
535
- if (!this._panelOpen()) {
619
+ if (!this.panelOpen()) {
536
620
  this._onTouched();
537
621
  }
538
622
  }
@@ -541,26 +625,6 @@ class FuiTimePickerComponent {
541
625
  this._setValue(value);
542
626
  this.close();
543
627
  }
544
- // Start listening for outside clicks when panel opens
545
- _listenForOutsideClicks() {
546
- this._outsideClickSub?.unsubscribe();
547
- this._ngZone.runOutsideAngular(() => {
548
- setTimeout(() => {
549
- this._outsideClickSub = fromEvent(this._document, 'click')
550
- .pipe(filter(() => this._panelOpen()), filter((event) => {
551
- const target = event.target;
552
- const hostElement = this._elementRef.nativeElement;
553
- const overlayElement = this._overlayRef?.overlayElement;
554
- return !hostElement.contains(target) && !overlayElement?.contains(target);
555
- }))
556
- .subscribe(() => {
557
- this._ngZone.run(() => {
558
- this.close();
559
- });
560
- });
561
- });
562
- });
563
- }
564
628
  // --- Private helpers ---
565
629
  _setValue(value) {
566
630
  this._value.set(value);
@@ -569,95 +633,71 @@ class FuiTimePickerComponent {
569
633
  this.timeChange.emit(stringVal ?? '');
570
634
  this.stateChanges.next();
571
635
  }
572
- _createOverlay() {
573
- if (this._overlayRef || !this.timeListPanel)
574
- return;
575
- const triggerElement = this._elementRef.nativeElement;
576
- const triggerWidth = triggerElement.getBoundingClientRect().width;
577
- const positions = [
578
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
579
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
580
- ];
581
- const positionStrategy = this._overlayService
582
- .position()
583
- .connectedTo(triggerElement, positions)
584
- .withPush(true)
585
- .withViewportMargin(8);
586
- this._overlayRef = this._overlayService.create({
587
- positionStrategy,
588
- scrollStrategy: this._overlayService.scrollStrategies.reposition(),
589
- hasBackdrop: true,
590
- backdropClass: 'fui-time-picker-backdrop',
591
- backdropClickBehavior: 'close',
592
- panelClass: ['fui-time-picker-overlay-panel'],
593
- minWidth: Math.max(triggerWidth, 160),
594
- });
595
- // Track overlay subscriptions for proper cleanup
596
- this._overlaySubscriptions.unsubscribe();
597
- this._overlaySubscriptions = new Subscription();
598
- this._overlaySubscriptions.add(this._overlayRef.backdropClick.subscribe(() => {
599
- this.close();
600
- }));
601
- this._overlaySubscriptions.add(this._overlayRef.keydownEvents.subscribe((event) => {
602
- if (event.key === 'Escape') {
603
- this.close();
604
- }
605
- }));
606
- const panelElement = this.timeListPanel.nativeElement;
607
- this._overlayRef.attach(panelElement);
608
- }
609
- _disposeOverlay() {
610
- this._overlaySubscriptions.unsubscribe();
611
- if (this._overlayRef) {
612
- this._overlayRef.dispose();
613
- this._overlayRef = null;
614
- }
615
- }
616
636
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
617
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimePickerComponent, isStandalone: true, selector: "fui-time-picker", inputs: { formatInput: { classPropertyName: "formatInput", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", 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 }, placeholderInput: { classPropertyName: "placeholderInput", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timeChange: "timeChange" }, host: { properties: { "attr.id": "id", "class.fui-time-picker--open": "_panelOpen()", "class.fui-time-picker--disabled": "disabled()", "class.fui-time-picker--focused": "focused()", "class.fui-time-picker--error": "errorState()", "class.fui-time-picker--readonly": "_readOnly()" }, classAttribute: "fui-time-picker" }, providers: [
637
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.6", type: FuiTimePickerComponent, isStandalone: true, selector: "fui-time-picker", inputs: { formatInput: { classPropertyName: "formatInput", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, showSeconds: { classPropertyName: "showSeconds", publicName: "showSeconds", isSignal: true, isRequired: false, transformFunction: null }, minuteStep: { classPropertyName: "minuteStep", publicName: "minuteStep", 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 }, placeholderInput: { classPropertyName: "placeholderInput", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonlyInput: { classPropertyName: "readonlyInput", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timeChange: "timeChange" }, host: { properties: { "attr.id": "id", "class.fui-time-picker--open": "panelOpen()", "class.fui-time-picker--disabled": "disabled()", "class.fui-time-picker--focused": "focused()", "class.fui-time-picker--error": "errorState()", "class.fui-time-picker--readonly": "readonly()" }, classAttribute: "fui-time-picker" }, providers: [
618
638
  {
619
639
  provide: NG_VALUE_ACCESSOR,
620
640
  useExisting: FuiTimePickerComponent,
621
641
  multi: true,
622
642
  },
643
+ {
644
+ provide: NG_VALIDATORS,
645
+ useExisting: FuiTimePickerComponent,
646
+ multi: true,
647
+ },
623
648
  {
624
649
  provide: FUI_FORM_FIELD_CONTROL,
625
650
  useExisting: FuiTimePickerComponent,
626
651
  },
627
- ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "timeListPanel", first: true, predicate: ["timeListPanel"], descendants: true }], ngImport: i0, template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.disabled]=\"disabled() ? '' : null\"\r\n [attr.readonly]=\"readonly() ? '' : null\"\r\n [value]=\"_displayValue()\"\r\n [attr.aria-expanded]=\"_panelOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n [attr.aria-describedby]=\"_ariaDescribedby\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-invalid]=\"errorState()\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n (input)=\"_onManualInput($event)\"\r\n (keydown)=\"_onKeydown($event)\"\r\n (focus)=\"_onFocus()\"\r\n (blur)=\"_onBlur()\"\r\n />\r\n @if (!_readOnly()) {\r\n <button\r\n type=\"button\"\r\n class=\"fui-time-picker__toggle\"\r\n (click)=\"_togglePanel()\"\r\n [disabled]=\"disabled() || readonly()\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.toggleAriaLabel\"\r\n >\r\n <fui-icon name=\"clock\" size=\"sm\"></fui-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n@if (_panelOpen()) {\r\n <div #timeListPanel class=\"fui-time-picker__panel\" role=\"dialog\" [attr.aria-label]=\"intl.dialogAriaLabel\">\r\n <fui-time-list\r\n [options]=\"_timeOptions()\"\r\n [selectedValue]=\"_value()\"\r\n [format]=\"formatInput()\"\r\n (optionSelected)=\"_onOptionSelected($event)\"\r\n />\r\n </div>\r\n}\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-picker{--fui-time-picker-font-size: var(--fui-font-size-02);--fui-time-picker-panel-border-radius: var(--fui-border-radius-md);--fui-time-picker-panel-shadow: var(--fui-shadow-03);--fui-time-picker-panel-bg: var(--fui-surface-00);--fui-time-picker-toggle-size: 1.5rem;display:inline-block;width:100%;position:relative}.fui-time-picker__wrapper{width:100%;min-height:100%;display:flex;align-items:center;gap:var(--fui-spacing-01);cursor:pointer;background:transparent;border:none}.fui-time-picker__input{flex:1;min-width:0;border:none;outline:none;background:transparent;font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-regular);line-height:var(--fui-line-height-02);letter-spacing:var(--fui-letter-spacing-normal);color:var(--fui-text-primary);padding:0;cursor:text}.fui-time-picker__input::placeholder{color:var(--fui-text-disabled)}.fui-time-picker__input:disabled{cursor:not-allowed;opacity:.5}.fui-time-picker__toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-time-picker__toggle:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-time-picker__toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--fui-time-picker-toggle-size);height:var(--fui-time-picker-toggle-size);color:var(--fui-text-secondary);border-radius:var(--fui-border-radius-sm);transition:color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-time-picker__toggle:hover:not(:disabled){color:var(--fui-text-primary)}.fui-time-picker__toggle:disabled{color:var(--fui-text-disabled);cursor:not-allowed}.fui-time-picker__panel{background:var(--fui-time-picker-panel-bg);border:1px solid var(--fui-border-color);border-radius:var(--fui-time-picker-panel-border-radius);box-shadow:var(--fui-time-picker-panel-shadow);transition:opacity,transform var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-time-picker--disabled{pointer-events:none}.fui-time-picker--disabled .fui-time-picker__wrapper{cursor:not-allowed;opacity:.5}.fui-time-picker--readonly{pointer-events:none}.fui-time-picker--readonly .fui-time-picker__wrapper{cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input{color:var(--fui-text-primary)!important;-webkit-text-fill-color:var(--fui-text-primary)!important;cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input:disabled{opacity:1;cursor:default}.fui-time-picker-backdrop{background:transparent}@media(prefers-contrast:high){.fui-time-picker__panel{border-width:2px}}@media(prefers-reduced-motion:reduce){.fui-time-picker__panel{transition:none}}\n"], dependencies: [{ kind: "component", type: FuiIconComponent, selector: "fui-icon", inputs: ["name", "size", "weight", "color", "ariaLabel", "spin", "pulse"] }, { kind: "component", type: FuiTimeListComponent, selector: "fui-time-list", inputs: ["options", "selectedValue", "format"], outputs: ["optionSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
652
+ ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }, { propertyName: "timeListPanel", first: true, predicate: ["timeListPanel"], descendants: true, static: true }, { propertyName: "_fuiInput", first: true, predicate: FuiInputDirective, descendants: true }], hostDirectives: [{ directive: i1.FuiPopupOverlayDirective, inputs: ["positions", "positions", "panelClass", "panelClass", "backdropClass", "backdropClass", "scrollStrategy", "scrollStrategy", "minWidthFromTrigger", "minWidthFromTrigger"], outputs: ["openedChange", "openedChange", "escapeKey", "escapeKey"] }, { directive: i1.FuiFormControlSyncDirective }], ngImport: i0, template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n fuiInput\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [value]=\"_displayValue()\"\r\n [attr.aria-expanded]=\"panelOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n (input)=\"_onManualInput($event)\"\r\n (keydown)=\"_onKeydown($event)\"\r\n (focus)=\"_onFocus()\"\r\n (blur)=\"_onBlur()\"\r\n />\r\n @if (!disabled() && !readonly()) {\r\n <button\r\n type=\"button\"\r\n class=\"fui-time-picker__toggle\"\r\n (click)=\"_togglePanel()\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.toggleAriaLabel\"\r\n >\r\n <fui-icon name=\"clock\" size=\"sm\"></fui-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<div\r\n #timeListPanel\r\n class=\"fui-time-picker__panel\"\r\n role=\"dialog\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.dialogAriaLabel\"\r\n [hidden]=\"!panelOpen()\"\r\n>\r\n <fui-time-list\r\n [options]=\"_timeOptions()\"\r\n [selectedValue]=\"_value()\"\r\n [format]=\"formatInput()\"\r\n (optionSelected)=\"_onOptionSelected($event)\"\r\n />\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-picker{--fui-time-picker-font-size: var(--fui-text-base);--fui-time-picker-toggle-size: 1.5rem;display:inline-block;width:100%;position:relative}.fui-time-picker__wrapper{width:100%;min-height:100%;display:flex;align-items:center;gap:var(--fui-spacing-1);cursor:pointer;background:transparent;border:none}.fui-time-picker__input{flex:1;min-width:0;border:none;outline:none;background:transparent;font-family:var(--fui-font-sans);font-size:var(--fui-text-base);font-weight:var(--fui-weight-regular);line-height:var(--fui-leading-normal);letter-spacing:var(--fui-tracking-normal);color:var(--fui-text-primary);padding:0;cursor:text}.fui-time-picker__input::placeholder{color:var(--fui-text-disabled)}.fui-time-picker__input:disabled{cursor:not-allowed;opacity:.5}.fui-time-picker__toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-time-picker__toggle:focus-visible{outline:2px solid var(--fui-primary-10)}.fui-time-picker__toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--fui-time-picker-toggle-size);height:var(--fui-time-picker-toggle-size);color:var(--fui-text-secondary);border-radius:var(--fui-radius-sm);transition-property:color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker__toggle:hover:not(:disabled){color:var(--fui-text-primary)}.fui-time-picker__toggle:disabled{color:var(--fui-text-disabled);cursor:not-allowed}.fui-time-picker--disabled{pointer-events:none}.fui-time-picker--disabled .fui-time-picker__wrapper{cursor:not-allowed;opacity:.5}.fui-time-picker--readonly{pointer-events:none}.fui-time-picker--readonly .fui-time-picker__wrapper{cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input{color:var(--fui-text-primary)!important;-webkit-text-fill-color:var(--fui-text-primary)!important;cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input:disabled{opacity:1;cursor:default}.fui-time-picker__panel{--fui-time-picker-panel-border-radius: var(--fui-radius-md);--fui-time-picker-panel-shadow: var(--fui-shadow-md);--fui-time-picker-panel-bg: var(--fui-bg-default);--fui-time-picker-panel-border-color: var(--fui-border-default);background:var(--fui-time-picker-panel-bg);border:1px solid var(--fui-time-picker-panel-border-color);border-radius:var(--fui-time-picker-panel-border-radius);box-shadow:var(--fui-time-picker-panel-shadow);transition-property:opacity,transform;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker-backdrop{background:transparent}@media(prefers-contrast:high){.fui-time-picker__panel{border-width:2px}}@media(prefers-reduced-motion:reduce){.fui-time-picker__panel{transition:none}}\n"], dependencies: [{ kind: "directive", type: FuiInputDirective, selector: "input[fuiInput], textarea[fuiInput], select[fuiInput]", inputs: ["type", "placeholder", "readonly", "maxlength", "minlength", "pattern", "errorStateMatcher", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: FuiIconComponent, selector: "fui-icon", inputs: ["name", "size", "weight", "color", "ariaLabel", "spin", "pulse"] }, { kind: "component", type: FuiTimeListComponent, selector: "fui-time-list", inputs: ["options", "selectedValue", "format"], outputs: ["optionSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
628
653
  }
629
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.6", ngImport: i0, type: FuiTimePickerComponent, decorators: [{
630
655
  type: Component,
631
- args: [{ selector: 'fui-time-picker', standalone: true, imports: [FuiIconComponent, FuiTimeListComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
656
+ args: [{ selector: 'fui-time-picker', standalone: true, imports: [FuiInputDirective, FuiIconComponent, FuiTimeListComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, hostDirectives: [
657
+ {
658
+ directive: FuiPopupOverlayDirective,
659
+ inputs: ['positions', 'panelClass', 'backdropClass', 'scrollStrategy', 'minWidthFromTrigger'],
660
+ outputs: ['openedChange', 'escapeKey'],
661
+ },
662
+ FuiFormControlSyncDirective,
663
+ ], host: {
632
664
  class: 'fui-time-picker',
633
665
  '[attr.id]': 'id',
634
- '[class.fui-time-picker--open]': '_panelOpen()',
666
+ '[class.fui-time-picker--open]': 'panelOpen()',
635
667
  '[class.fui-time-picker--disabled]': 'disabled()',
636
668
  '[class.fui-time-picker--focused]': 'focused()',
637
669
  '[class.fui-time-picker--error]': 'errorState()',
638
- '[class.fui-time-picker--readonly]': '_readOnly()',
670
+ '[class.fui-time-picker--readonly]': 'readonly()',
639
671
  }, providers: [
640
672
  {
641
673
  provide: NG_VALUE_ACCESSOR,
642
674
  useExisting: FuiTimePickerComponent,
643
675
  multi: true,
644
676
  },
677
+ {
678
+ provide: NG_VALIDATORS,
679
+ useExisting: FuiTimePickerComponent,
680
+ multi: true,
681
+ },
645
682
  {
646
683
  provide: FUI_FORM_FIELD_CONTROL,
647
684
  useExisting: FuiTimePickerComponent,
648
685
  },
649
- ], template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [attr.placeholder]=\"placeholder()\"\r\n [attr.disabled]=\"disabled() ? '' : null\"\r\n [attr.readonly]=\"readonly() ? '' : null\"\r\n [value]=\"_displayValue()\"\r\n [attr.aria-expanded]=\"_panelOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n [attr.aria-describedby]=\"_ariaDescribedby\"\r\n [attr.aria-required]=\"required()\"\r\n [attr.aria-invalid]=\"errorState()\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n (input)=\"_onManualInput($event)\"\r\n (keydown)=\"_onKeydown($event)\"\r\n (focus)=\"_onFocus()\"\r\n (blur)=\"_onBlur()\"\r\n />\r\n @if (!_readOnly()) {\r\n <button\r\n type=\"button\"\r\n class=\"fui-time-picker__toggle\"\r\n (click)=\"_togglePanel()\"\r\n [disabled]=\"disabled() || readonly()\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.toggleAriaLabel\"\r\n >\r\n <fui-icon name=\"clock\" size=\"sm\"></fui-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n@if (_panelOpen()) {\r\n <div #timeListPanel class=\"fui-time-picker__panel\" role=\"dialog\" [attr.aria-label]=\"intl.dialogAriaLabel\">\r\n <fui-time-list\r\n [options]=\"_timeOptions()\"\r\n [selectedValue]=\"_value()\"\r\n [format]=\"formatInput()\"\r\n (optionSelected)=\"_onOptionSelected($event)\"\r\n />\r\n </div>\r\n}\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition:opacity var(--fui-duration-fast-02) var(--fui-ease-entrance) 0ms}.fui-motion-fade-out{transition:opacity var(--fui-duration-fast-01) var(--fui-ease-exit) 0ms}.fui-motion-slide-in-bottom{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition:transform var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition:transform,opacity var(--fui-duration-moderate-01) var(--fui-ease-entrance) 0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-moderate-01) var(--fui-ease-entrance)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-picker{--fui-time-picker-font-size: var(--fui-font-size-02);--fui-time-picker-panel-border-radius: var(--fui-border-radius-md);--fui-time-picker-panel-shadow: var(--fui-shadow-03);--fui-time-picker-panel-bg: var(--fui-surface-00);--fui-time-picker-toggle-size: 1.5rem;display:inline-block;width:100%;position:relative}.fui-time-picker__wrapper{width:100%;min-height:100%;display:flex;align-items:center;gap:var(--fui-spacing-01);cursor:pointer;background:transparent;border:none}.fui-time-picker__input{flex:1;min-width:0;border:none;outline:none;background:transparent;font-family:var(--fui-font-family-sans);font-size:var(--fui-font-size-02);font-weight:var(--fui-font-weight-regular);line-height:var(--fui-line-height-02);letter-spacing:var(--fui-letter-spacing-normal);color:var(--fui-text-primary);padding:0;cursor:text}.fui-time-picker__input::placeholder{color:var(--fui-text-disabled)}.fui-time-picker__input:disabled{cursor:not-allowed;opacity:.5}.fui-time-picker__toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-time-picker__toggle:focus-visible{outline:2px solid var(--fui-primary);outline-offset:2px}.fui-time-picker__toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--fui-time-picker-toggle-size);height:var(--fui-time-picker-toggle-size);color:var(--fui-text-secondary);border-radius:var(--fui-border-radius-sm);transition:color var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-time-picker__toggle:hover:not(:disabled){color:var(--fui-text-primary)}.fui-time-picker__toggle:disabled{color:var(--fui-text-disabled);cursor:not-allowed}.fui-time-picker__panel{background:var(--fui-time-picker-panel-bg);border:1px solid var(--fui-border-color);border-radius:var(--fui-time-picker-panel-border-radius);box-shadow:var(--fui-time-picker-panel-shadow);transition:opacity,transform var(--fui-duration-fast-02) var(--fui-ease-standard) 0ms}.fui-time-picker--disabled{pointer-events:none}.fui-time-picker--disabled .fui-time-picker__wrapper{cursor:not-allowed;opacity:.5}.fui-time-picker--readonly{pointer-events:none}.fui-time-picker--readonly .fui-time-picker__wrapper{cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input{color:var(--fui-text-primary)!important;-webkit-text-fill-color:var(--fui-text-primary)!important;cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input:disabled{opacity:1;cursor:default}.fui-time-picker-backdrop{background:transparent}@media(prefers-contrast:high){.fui-time-picker__panel{border-width:2px}}@media(prefers-reduced-motion:reduce){.fui-time-picker__panel{transition:none}}\n"] }]
650
- }], ctorParameters: () => [], propDecorators: { formatInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], placeholderInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], timeChange: [{ type: i0.Output, args: ["timeChange"] }], inputElement: [{
686
+ ], template: "<div class=\"fui-time-picker__wrapper\">\r\n <input\r\n #inputElement\r\n fuiInput\r\n class=\"fui-time-picker__input\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder()\"\r\n [disabled]=\"disabled()\"\r\n [readonly]=\"readonly()\"\r\n [value]=\"_displayValue()\"\r\n [attr.aria-expanded]=\"panelOpen()\"\r\n [attr.aria-haspopup]=\"'listbox'\"\r\n role=\"combobox\"\r\n autocomplete=\"off\"\r\n (input)=\"_onManualInput($event)\"\r\n (keydown)=\"_onKeydown($event)\"\r\n (focus)=\"_onFocus()\"\r\n (blur)=\"_onBlur()\"\r\n />\r\n @if (!disabled() && !readonly()) {\r\n <button\r\n type=\"button\"\r\n class=\"fui-time-picker__toggle\"\r\n (click)=\"_togglePanel()\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.toggleAriaLabel\"\r\n >\r\n <fui-icon name=\"clock\" size=\"sm\"></fui-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<div\r\n #timeListPanel\r\n class=\"fui-time-picker__panel\"\r\n role=\"dialog\"\r\n tabindex=\"-1\"\r\n [attr.aria-label]=\"intl.dialogAriaLabel\"\r\n [hidden]=\"!panelOpen()\"\r\n>\r\n <fui-time-list\r\n [options]=\"_timeOptions()\"\r\n [selectedValue]=\"_value()\"\r\n [format]=\"formatInput()\"\r\n (optionSelected)=\"_onOptionSelected($event)\"\r\n />\r\n</div>\r\n", styles: ["@keyframes fui-skeleton-pulse{0%{opacity:1}50%{opacity:.4}to{opacity:1}}@keyframes fui-spin{to{transform:rotate(360deg)}}@keyframes fui-shake{0%,to{transform:translate(0)}10%,30%,50%,70%,90%{transform:translate(-2px)}20%,40%,60%,80%{transform:translate(2px)}}.fui-motion-fade-in{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-out);transition-delay:0ms}.fui-motion-fade-out{transition-property:opacity;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in);transition-delay:0ms}.fui-motion-slide-in-bottom{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-bottom.fui-motion-entering{transform:translateY(1rem)}.fui-motion-slide-in-top{transition-property:transform;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:translateY(0)}.fui-motion-slide-in-top.fui-motion-entering{transform:translateY(-1rem)}.fui-motion-scale-in{transition-property:transform,opacity;transition-duration:var(--fui-duration-base);transition-timing-function:var(--fui-ease-out);transition-delay:0ms;transform:scale(1);opacity:1}.fui-motion-scale-in.fui-motion-entering{transform:scale(.95);opacity:0}.fui-no-motion{transition:none!important;animation:none!important}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes fui-pulse{0%{transform:scale(1);opacity:1}50%{transform:scale(1.05)}to{transform:scale(1);opacity:1}}@keyframes fui-slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.fui-slide-in{animation:fui-slide-in var(--fui-duration-base) var(--fui-ease-out)}@keyframes fui-popover-enter{0%{opacity:0;transform:translateY(-14px)}60%{opacity:1}to{opacity:1;transform:translateY(0)}}.fui-time-picker{--fui-time-picker-font-size: var(--fui-text-base);--fui-time-picker-toggle-size: 1.5rem;display:inline-block;width:100%;position:relative}.fui-time-picker__wrapper{width:100%;min-height:100%;display:flex;align-items:center;gap:var(--fui-spacing-1);cursor:pointer;background:transparent;border:none}.fui-time-picker__input{flex:1;min-width:0;border:none;outline:none;background:transparent;font-family:var(--fui-font-sans);font-size:var(--fui-text-base);font-weight:var(--fui-weight-regular);line-height:var(--fui-leading-normal);letter-spacing:var(--fui-tracking-normal);color:var(--fui-text-primary);padding:0;cursor:text}.fui-time-picker__input::placeholder{color:var(--fui-text-disabled)}.fui-time-picker__input:disabled{cursor:not-allowed;opacity:.5}.fui-time-picker__toggle{background:none;border:none;padding:0;margin:0;font:inherit;color:inherit;cursor:pointer;outline:none}.fui-time-picker__toggle:focus-visible{outline:2px solid var(--fui-primary-10)}.fui-time-picker__toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--fui-time-picker-toggle-size);height:var(--fui-time-picker-toggle-size);color:var(--fui-text-secondary);border-radius:var(--fui-radius-sm);transition-property:color;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker__toggle:hover:not(:disabled){color:var(--fui-text-primary)}.fui-time-picker__toggle:disabled{color:var(--fui-text-disabled);cursor:not-allowed}.fui-time-picker--disabled{pointer-events:none}.fui-time-picker--disabled .fui-time-picker__wrapper{cursor:not-allowed;opacity:.5}.fui-time-picker--readonly{pointer-events:none}.fui-time-picker--readonly .fui-time-picker__wrapper{cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input{color:var(--fui-text-primary)!important;-webkit-text-fill-color:var(--fui-text-primary)!important;cursor:default;opacity:1}.fui-time-picker--readonly .fui-time-picker__input:disabled{opacity:1;cursor:default}.fui-time-picker__panel{--fui-time-picker-panel-border-radius: var(--fui-radius-md);--fui-time-picker-panel-shadow: var(--fui-shadow-md);--fui-time-picker-panel-bg: var(--fui-bg-default);--fui-time-picker-panel-border-color: var(--fui-border-default);background:var(--fui-time-picker-panel-bg);border:1px solid var(--fui-time-picker-panel-border-color);border-radius:var(--fui-time-picker-panel-border-radius);box-shadow:var(--fui-time-picker-panel-shadow);transition-property:opacity,transform;transition-duration:var(--fui-duration-fast);transition-timing-function:var(--fui-ease-in-out);transition-delay:0ms}.fui-time-picker-backdrop{background:transparent}@media(prefers-contrast:high){.fui-time-picker__panel{border-width:2px}}@media(prefers-reduced-motion:reduce){.fui-time-picker__panel{transition:none}}\n"] }]
687
+ }], ctorParameters: () => [], propDecorators: { formatInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], showSeconds: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSeconds", required: false }] }], minuteStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "minuteStep", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], placeholderInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonlyInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], timeChange: [{ type: i0.Output, args: ["timeChange"] }], inputElement: [{
651
688
  type: ViewChild,
652
689
  args: ['inputElement', { static: false }]
653
690
  }], timeListPanel: [{
654
691
  type: ViewChild,
655
- args: ['timeListPanel', { static: false }]
692
+ args: ['timeListPanel', { static: true }]
693
+ }], _fuiInput: [{
694
+ type: ViewChild,
695
+ args: [FuiInputDirective, { static: false }]
656
696
  }] } });
657
697
 
658
698
  /**
659
699
  * Generated bundle index. Do not edit.
660
700
  */
661
701
 
662
- export { FuiTimeListComponent, FuiTimePickerComponent, FuiTimePickerIntl, compareTime, formatTime, generateTimeOptions, isTimeInRange, parseISOTimeString, parseTimeString, timeToMinutes, timeValueToString };
702
+ export { FuiTimeListComponent, FuiTimePickerComponent, FuiTimePickerIntl, compareTime, formatTime, generateTimeOptions, isTimeInRange, parseISOTimeString, parseTimeString, timeToMinutes, timeToSeconds, timeValueToString };
663
703
  //# sourceMappingURL=raintonic-formaui-components-time-picker.mjs.map