@allxsmith/bestax-bulma 5.27.0 → 5.27.2

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 (348) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/columns/Column.js +48 -0
  4. package/dist/esm/columns/Column.js.map +1 -0
  5. package/dist/esm/columns/Columns.js +53 -0
  6. package/dist/esm/columns/Columns.js.map +1 -0
  7. package/dist/esm/components/Avatar.js +299 -0
  8. package/dist/esm/components/Avatar.js.map +1 -0
  9. package/dist/esm/components/Avatars.js +81 -0
  10. package/dist/esm/components/Avatars.js.map +1 -0
  11. package/dist/esm/components/Badge.js +105 -0
  12. package/dist/esm/components/Badge.js.map +1 -0
  13. package/dist/esm/components/Breadcrumb.js +36 -0
  14. package/dist/esm/components/Breadcrumb.js.map +1 -0
  15. package/dist/esm/components/Card.js +252 -0
  16. package/dist/esm/components/Card.js.map +1 -0
  17. package/dist/esm/components/Carousel.js +351 -0
  18. package/dist/esm/components/Carousel.js.map +1 -0
  19. package/dist/esm/components/Collapse.js +193 -0
  20. package/dist/esm/components/Collapse.js.map +1 -0
  21. package/dist/esm/components/Collapses.js +99 -0
  22. package/dist/esm/components/Collapses.js.map +1 -0
  23. package/dist/esm/components/Dialog.js +230 -0
  24. package/dist/esm/components/Dialog.js.map +1 -0
  25. package/dist/esm/components/Dropdown.js +362 -0
  26. package/dist/esm/components/Dropdown.js.map +1 -0
  27. package/dist/esm/components/Loading.js +87 -0
  28. package/dist/esm/components/Loading.js.map +1 -0
  29. package/dist/esm/components/Menu.js +117 -0
  30. package/dist/esm/components/Menu.js.map +1 -0
  31. package/dist/esm/components/Message.js +56 -0
  32. package/dist/esm/components/Message.js.map +1 -0
  33. package/dist/esm/components/Modal.js +354 -0
  34. package/dist/esm/components/Modal.js.map +1 -0
  35. package/dist/esm/components/Navbar.js +287 -0
  36. package/dist/esm/components/Navbar.js.map +1 -0
  37. package/dist/esm/components/Pagination.js +191 -0
  38. package/dist/esm/components/Pagination.js.map +1 -0
  39. package/dist/esm/components/Panel.js +100 -0
  40. package/dist/esm/components/Panel.js.map +1 -0
  41. package/dist/esm/components/Popover.js +298 -0
  42. package/dist/esm/components/Popover.js.map +1 -0
  43. package/dist/esm/components/Reveal.js +157 -0
  44. package/dist/esm/components/Reveal.js.map +1 -0
  45. package/dist/esm/components/Sidebar.js +209 -0
  46. package/dist/esm/components/Sidebar.js.map +1 -0
  47. package/dist/esm/components/Steps.js +128 -0
  48. package/dist/esm/components/Steps.js.map +1 -0
  49. package/dist/esm/components/Tabs.js +335 -0
  50. package/dist/esm/components/Tabs.js.map +1 -0
  51. package/dist/esm/components/Toast.js +322 -0
  52. package/dist/esm/components/Toast.js.map +1 -0
  53. package/dist/esm/components/Tooltip.js +202 -0
  54. package/dist/esm/components/Tooltip.js.map +1 -0
  55. package/dist/esm/components/collapsesContext.js +12 -0
  56. package/dist/esm/components/collapsesContext.js.map +1 -0
  57. package/dist/esm/elements/Block.js +29 -0
  58. package/dist/esm/elements/Block.js.map +1 -0
  59. package/dist/esm/elements/Box.js +32 -0
  60. package/dist/esm/elements/Box.js.map +1 -0
  61. package/dist/esm/elements/Button.js +139 -0
  62. package/dist/esm/elements/Button.js.map +1 -0
  63. package/dist/esm/elements/Buttons.js +42 -0
  64. package/dist/esm/elements/Buttons.js.map +1 -0
  65. package/dist/esm/elements/Code.js +25 -0
  66. package/dist/esm/elements/Code.js.map +1 -0
  67. package/dist/esm/elements/Content.js +33 -0
  68. package/dist/esm/elements/Content.js.map +1 -0
  69. package/dist/esm/elements/Delete.js +35 -0
  70. package/dist/esm/elements/Delete.js.map +1 -0
  71. package/dist/esm/elements/Divider.js +27 -0
  72. package/dist/esm/elements/Divider.js.map +1 -0
  73. package/dist/esm/elements/Emphasis.js +25 -0
  74. package/dist/esm/elements/Emphasis.js.map +1 -0
  75. package/dist/esm/elements/Figure.js +58 -0
  76. package/dist/esm/elements/Figure.js.map +1 -0
  77. package/dist/esm/elements/Icon.js +256 -0
  78. package/dist/esm/elements/Icon.js.map +1 -0
  79. package/dist/esm/elements/IconText.js +36 -0
  80. package/dist/esm/elements/IconText.js.map +1 -0
  81. package/dist/esm/elements/Image.js +46 -0
  82. package/dist/esm/elements/Image.js.map +1 -0
  83. package/dist/esm/elements/Link.js +36 -0
  84. package/dist/esm/elements/Link.js.map +1 -0
  85. package/dist/esm/elements/LinkButton.js +37 -0
  86. package/dist/esm/elements/LinkButton.js.map +1 -0
  87. package/dist/esm/elements/ListItem.js +28 -0
  88. package/dist/esm/elements/ListItem.js.map +1 -0
  89. package/dist/esm/elements/Loader.js +37 -0
  90. package/dist/esm/elements/Loader.js.map +1 -0
  91. package/dist/esm/elements/Notification.js +316 -0
  92. package/dist/esm/elements/Notification.js.map +1 -0
  93. package/dist/esm/elements/OrderedList.js +31 -0
  94. package/dist/esm/elements/OrderedList.js.map +1 -0
  95. package/dist/esm/elements/Paragraph.js +28 -0
  96. package/dist/esm/elements/Paragraph.js.map +1 -0
  97. package/dist/esm/elements/Pre.js +25 -0
  98. package/dist/esm/elements/Pre.js.map +1 -0
  99. package/dist/esm/elements/Progress.js +33 -0
  100. package/dist/esm/elements/Progress.js.map +1 -0
  101. package/dist/esm/elements/Skeleton.js +30 -0
  102. package/dist/esm/elements/Skeleton.js.map +1 -0
  103. package/dist/esm/elements/Span.js +28 -0
  104. package/dist/esm/elements/Span.js.map +1 -0
  105. package/dist/esm/elements/Strong.js +25 -0
  106. package/dist/esm/elements/Strong.js.map +1 -0
  107. package/dist/esm/elements/SubTitle.js +51 -0
  108. package/dist/esm/elements/SubTitle.js.map +1 -0
  109. package/dist/esm/elements/Table.js +51 -0
  110. package/dist/esm/elements/Table.js.map +1 -0
  111. package/dist/esm/elements/Tag.js +49 -0
  112. package/dist/esm/elements/Tag.js.map +1 -0
  113. package/dist/esm/elements/Tags.js +34 -0
  114. package/dist/esm/elements/Tags.js.map +1 -0
  115. package/dist/esm/elements/Tbody.js +26 -0
  116. package/dist/esm/elements/Tbody.js.map +1 -0
  117. package/dist/esm/elements/Td.js +61 -0
  118. package/dist/esm/elements/Td.js.map +1 -0
  119. package/dist/esm/elements/Tfoot.js +26 -0
  120. package/dist/esm/elements/Tfoot.js.map +1 -0
  121. package/dist/esm/elements/Th.js +34 -0
  122. package/dist/esm/elements/Th.js.map +1 -0
  123. package/dist/esm/elements/Thead.js +26 -0
  124. package/dist/esm/elements/Thead.js.map +1 -0
  125. package/dist/esm/elements/Title.js +44 -0
  126. package/dist/esm/elements/Title.js.map +1 -0
  127. package/dist/esm/elements/Tr.js +31 -0
  128. package/dist/esm/elements/Tr.js.map +1 -0
  129. package/dist/esm/elements/UnorderedList.js +31 -0
  130. package/dist/esm/elements/UnorderedList.js.map +1 -0
  131. package/dist/esm/elements/iconProps.js +49 -0
  132. package/dist/esm/elements/iconProps.js.map +1 -0
  133. package/dist/esm/form/Autocomplete.js +279 -0
  134. package/dist/esm/form/Autocomplete.js.map +1 -0
  135. package/dist/esm/form/Checkbox.js +111 -0
  136. package/dist/esm/form/Checkbox.js.map +1 -0
  137. package/dist/esm/form/Checkboxes.js +90 -0
  138. package/dist/esm/form/Checkboxes.js.map +1 -0
  139. package/dist/esm/form/Control.js +79 -0
  140. package/dist/esm/form/Control.js.map +1 -0
  141. package/dist/esm/form/DateInput.js +88 -0
  142. package/dist/esm/form/DateInput.js.map +1 -0
  143. package/dist/esm/form/DateInputBase.js +263 -0
  144. package/dist/esm/form/DateInputBase.js.map +1 -0
  145. package/dist/esm/form/DateRangeInput.js +77 -0
  146. package/dist/esm/form/DateRangeInput.js.map +1 -0
  147. package/dist/esm/form/DateRangeInputBase.js +301 -0
  148. package/dist/esm/form/DateRangeInputBase.js.map +1 -0
  149. package/dist/esm/form/DateTimeInput.js +88 -0
  150. package/dist/esm/form/DateTimeInput.js.map +1 -0
  151. package/dist/esm/form/DateTimeInputBase.js +333 -0
  152. package/dist/esm/form/DateTimeInputBase.js.map +1 -0
  153. package/dist/esm/form/Field.js +167 -0
  154. package/dist/esm/form/Field.js.map +1 -0
  155. package/dist/esm/form/File.js +112 -0
  156. package/dist/esm/form/File.js.map +1 -0
  157. package/dist/esm/form/FormContext.js +194 -0
  158. package/dist/esm/form/FormContext.js.map +1 -0
  159. package/dist/esm/form/Input.js +88 -0
  160. package/dist/esm/form/Input.js.map +1 -0
  161. package/dist/esm/form/InputBase.js +40 -0
  162. package/dist/esm/form/InputBase.js.map +1 -0
  163. package/dist/esm/form/Numberinput.js +201 -0
  164. package/dist/esm/form/Numberinput.js.map +1 -0
  165. package/dist/esm/form/Radio.js +108 -0
  166. package/dist/esm/form/Radio.js.map +1 -0
  167. package/dist/esm/form/Radios.js +92 -0
  168. package/dist/esm/form/Radios.js.map +1 -0
  169. package/dist/esm/form/Rate.js +280 -0
  170. package/dist/esm/form/Rate.js.map +1 -0
  171. package/dist/esm/form/Select.js +81 -0
  172. package/dist/esm/form/Select.js.map +1 -0
  173. package/dist/esm/form/SelectBase.js +54 -0
  174. package/dist/esm/form/SelectBase.js.map +1 -0
  175. package/dist/esm/form/Slider.js +461 -0
  176. package/dist/esm/form/Slider.js.map +1 -0
  177. package/dist/esm/form/Switch.js +96 -0
  178. package/dist/esm/form/Switch.js.map +1 -0
  179. package/dist/esm/form/Taginput.js +351 -0
  180. package/dist/esm/form/Taginput.js.map +1 -0
  181. package/dist/esm/form/TextArea.js +77 -0
  182. package/dist/esm/form/TextArea.js.map +1 -0
  183. package/dist/esm/form/TextAreaBase.js +45 -0
  184. package/dist/esm/form/TextAreaBase.js.map +1 -0
  185. package/dist/esm/form/TimeInput.js +84 -0
  186. package/dist/esm/form/TimeInput.js.map +1 -0
  187. package/dist/esm/form/TimeInputBase.js +246 -0
  188. package/dist/esm/form/TimeInputBase.js.map +1 -0
  189. package/dist/esm/form/_pickerInternals/Calendar.js +760 -0
  190. package/dist/esm/form/_pickerInternals/Calendar.js.map +1 -0
  191. package/dist/esm/form/_pickerInternals/PickerPopover.js +95 -0
  192. package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -0
  193. package/dist/esm/form/_pickerInternals/TimeWheels.js +721 -0
  194. package/dist/esm/form/_pickerInternals/TimeWheels.js.map +1 -0
  195. package/dist/esm/form/_pickerInternals/audioTick.js +109 -0
  196. package/dist/esm/form/_pickerInternals/audioTick.js.map +1 -0
  197. package/dist/esm/form/_pickerInternals/dateUtils.js +267 -0
  198. package/dist/esm/form/_pickerInternals/dateUtils.js.map +1 -0
  199. package/dist/esm/form/_pickerInternals/formatters.js +322 -0
  200. package/dist/esm/form/_pickerInternals/formatters.js.map +1 -0
  201. package/dist/esm/form/_pickerInternals/haptics.js +38 -0
  202. package/dist/esm/form/_pickerInternals/haptics.js.map +1 -0
  203. package/dist/esm/form/_pickerInternals/nativeInputSupport.js +25 -0
  204. package/dist/esm/form/_pickerInternals/nativeInputSupport.js.map +1 -0
  205. package/dist/esm/form/_pickerInternals/pickerTypes.js +50 -0
  206. package/dist/esm/form/_pickerInternals/pickerTypes.js.map +1 -0
  207. package/dist/esm/form/_pickerInternals/segmentMap.js +315 -0
  208. package/dist/esm/form/_pickerInternals/segmentMap.js.map +1 -0
  209. package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js +49 -0
  210. package/dist/esm/form/_pickerInternals/useNativeMobilePicker.js.map +1 -0
  211. package/dist/esm/form/_pickerInternals/useSegmentedEntry.js +427 -0
  212. package/dist/esm/form/_pickerInternals/useSegmentedEntry.js.map +1 -0
  213. package/dist/esm/form/controlLoading.js +18 -0
  214. package/dist/esm/form/controlLoading.js.map +1 -0
  215. package/dist/esm/form/useAutoLabelId.js +88 -0
  216. package/dist/esm/form/useAutoLabelId.js.map +1 -0
  217. package/dist/esm/grid/Cell.js +35 -0
  218. package/dist/esm/grid/Cell.js.map +1 -0
  219. package/dist/esm/grid/Grid.js +51 -0
  220. package/dist/esm/grid/Grid.js.map +1 -0
  221. package/dist/esm/helpers/ClientOnly.js +33 -0
  222. package/dist/esm/helpers/ClientOnly.js.map +1 -0
  223. package/dist/esm/helpers/Config.js +60 -0
  224. package/dist/esm/helpers/Config.js.map +1 -0
  225. package/dist/esm/helpers/Theme.js +947 -0
  226. package/dist/esm/helpers/Theme.js.map +1 -0
  227. package/dist/esm/helpers/anchorAttrs.js +72 -0
  228. package/dist/esm/helpers/anchorAttrs.js.map +1 -0
  229. package/dist/esm/helpers/bulmaClassHelpers.js +415 -0
  230. package/dist/esm/helpers/bulmaClassHelpers.js.map +1 -0
  231. package/dist/esm/helpers/buttonType.js +24 -0
  232. package/dist/esm/helpers/buttonType.js.map +1 -0
  233. package/dist/esm/helpers/classNames.js +137 -0
  234. package/dist/esm/helpers/classNames.js.map +1 -0
  235. package/dist/esm/helpers/colorDeprecations.js +50 -0
  236. package/dist/esm/helpers/colorDeprecations.js.map +1 -0
  237. package/dist/esm/helpers/devWarnings.js +27 -0
  238. package/dist/esm/helpers/devWarnings.js.map +1 -0
  239. package/dist/esm/helpers/inertProps.js +27 -0
  240. package/dist/esm/helpers/inertProps.js.map +1 -0
  241. package/dist/esm/helpers/mergeBulmaStyles.js +20 -0
  242. package/dist/esm/helpers/mergeBulmaStyles.js.map +1 -0
  243. package/dist/esm/helpers/polymorphic.js +18 -0
  244. package/dist/esm/helpers/polymorphic.js.map +1 -0
  245. package/dist/esm/helpers/portal.js +76 -0
  246. package/dist/esm/helpers/portal.js.map +1 -0
  247. package/dist/esm/helpers/positionStacks.js +45 -0
  248. package/dist/esm/helpers/positionStacks.js.map +1 -0
  249. package/dist/esm/helpers/scrollLock.js +38 -0
  250. package/dist/esm/helpers/scrollLock.js.map +1 -0
  251. package/dist/esm/helpers/shadowDom.js +71 -0
  252. package/dist/esm/helpers/shadowDom.js.map +1 -0
  253. package/dist/esm/helpers/statusRegion.js +282 -0
  254. package/dist/esm/helpers/statusRegion.js.map +1 -0
  255. package/dist/esm/helpers/useAnchoredPosition.js +86 -0
  256. package/dist/esm/helpers/useAnchoredPosition.js.map +1 -0
  257. package/dist/esm/helpers/useBulmaClasses.js +158 -0
  258. package/dist/esm/helpers/useBulmaClasses.js.map +1 -0
  259. package/dist/esm/helpers/useClientLayoutEffect.js +13 -0
  260. package/dist/esm/helpers/useClientLayoutEffect.js.map +1 -0
  261. package/dist/esm/helpers/useColorClasses.js +84 -0
  262. package/dist/esm/helpers/useColorClasses.js.map +1 -0
  263. package/dist/esm/helpers/useFlexboxClasses.js +92 -0
  264. package/dist/esm/helpers/useFlexboxClasses.js.map +1 -0
  265. package/dist/esm/helpers/useFocusTrap.js +470 -0
  266. package/dist/esm/helpers/useFocusTrap.js.map +1 -0
  267. package/dist/esm/helpers/useIsHydrated.js +31 -0
  268. package/dist/esm/helpers/useIsHydrated.js.map +1 -0
  269. package/dist/esm/helpers/useOtherClasses.js +115 -0
  270. package/dist/esm/helpers/useOtherClasses.js.map +1 -0
  271. package/dist/esm/helpers/useSpacingClasses.js +82 -0
  272. package/dist/esm/helpers/useSpacingClasses.js.map +1 -0
  273. package/dist/esm/helpers/useTypographyClasses.js +93 -0
  274. package/dist/esm/helpers/useTypographyClasses.js.map +1 -0
  275. package/dist/esm/helpers/useVisibilityClasses.js +134 -0
  276. package/dist/esm/helpers/useVisibilityClasses.js.map +1 -0
  277. package/dist/esm/helpers/withSubComponents.js +24 -0
  278. package/dist/esm/helpers/withSubComponents.js.map +1 -0
  279. package/dist/esm/layout/Container.js +48 -0
  280. package/dist/esm/layout/Container.js.map +1 -0
  281. package/dist/esm/layout/Footer.js +33 -0
  282. package/dist/esm/layout/Footer.js.map +1 -0
  283. package/dist/esm/layout/Hero.js +90 -0
  284. package/dist/esm/layout/Hero.js.map +1 -0
  285. package/dist/esm/layout/Level.js +110 -0
  286. package/dist/esm/layout/Level.js.map +1 -0
  287. package/dist/esm/layout/Media.js +86 -0
  288. package/dist/esm/layout/Media.js.map +1 -0
  289. package/dist/esm/layout/Section.js +30 -0
  290. package/dist/esm/layout/Section.js.map +1 -0
  291. package/dist/extras.css +1 -1
  292. package/dist/extras.css.map +1 -1
  293. package/dist/index.cjs +187 -57
  294. package/dist/index.cjs.map +1 -1
  295. package/dist/index.esm.js +115 -17516
  296. package/dist/index.esm.js.map +1 -1
  297. package/dist/types/elements/Icon.d.ts +10 -7
  298. package/dist/types/form/Autocomplete.d.ts +1 -1
  299. package/dist/types/form/Checkboxes.d.ts +1 -1
  300. package/dist/types/form/DateInput.d.ts +1 -1
  301. package/dist/types/form/DateInputBase.d.ts +3 -4
  302. package/dist/types/form/DateRangeInput.d.ts +1 -1
  303. package/dist/types/form/DateTimeInput.d.ts +1 -1
  304. package/dist/types/form/DateTimeInputBase.d.ts +3 -4
  305. package/dist/types/form/Field.d.ts +2 -2
  306. package/dist/types/form/File.d.ts +4 -4
  307. package/dist/types/form/FormContext.d.ts +24 -2
  308. package/dist/types/form/Numberinput.d.ts +1 -1
  309. package/dist/types/form/Radios.d.ts +1 -1
  310. package/dist/types/form/Rate.d.ts +1 -1
  311. package/dist/types/form/Slider.d.ts +1 -1
  312. package/dist/types/form/Taginput.d.ts +1 -1
  313. package/dist/types/form/TimeInput.d.ts +1 -1
  314. package/dist/types/form/useAutoLabelId.d.ts +25 -4
  315. package/dist/types/helpers/Config.d.ts +3 -4
  316. package/dist/types/helpers/Theme.d.ts +44 -40
  317. package/dist/types-cjs/elements/Icon.d.ts +10 -7
  318. package/dist/types-cjs/form/Autocomplete.d.ts +1 -1
  319. package/dist/types-cjs/form/Checkboxes.d.ts +1 -1
  320. package/dist/types-cjs/form/DateInput.d.ts +1 -1
  321. package/dist/types-cjs/form/DateInputBase.d.ts +3 -4
  322. package/dist/types-cjs/form/DateRangeInput.d.ts +1 -1
  323. package/dist/types-cjs/form/DateTimeInput.d.ts +1 -1
  324. package/dist/types-cjs/form/DateTimeInputBase.d.ts +3 -4
  325. package/dist/types-cjs/form/Field.d.ts +2 -2
  326. package/dist/types-cjs/form/File.d.ts +4 -4
  327. package/dist/types-cjs/form/FormContext.d.ts +24 -2
  328. package/dist/types-cjs/form/Numberinput.d.ts +1 -1
  329. package/dist/types-cjs/form/Radios.d.ts +1 -1
  330. package/dist/types-cjs/form/Rate.d.ts +1 -1
  331. package/dist/types-cjs/form/Slider.d.ts +1 -1
  332. package/dist/types-cjs/form/Taginput.d.ts +1 -1
  333. package/dist/types-cjs/form/TimeInput.d.ts +1 -1
  334. package/dist/types-cjs/form/useAutoLabelId.d.ts +25 -4
  335. package/dist/types-cjs/helpers/Config.d.ts +3 -4
  336. package/dist/types-cjs/helpers/Theme.d.ts +44 -40
  337. package/dist/versions/bestax-no-dark-mode.css +1 -1
  338. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  339. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  340. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  341. package/dist/versions/bestax-no-helpers.css +1 -1
  342. package/dist/versions/bestax-no-helpers.css.map +1 -1
  343. package/dist/versions/bestax-prefixed.css +1 -1
  344. package/dist/versions/bestax-prefixed.css.map +1 -1
  345. package/package.json +1 -1
  346. package/src/scss/form/_dateinput.scss +37 -6
  347. package/src/scss/form/_datetimeinput.scss +7 -2
  348. package/src/scss/form/_picker-popover.scss +1 -4
@@ -0,0 +1,427 @@
1
+ /* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
2
+ import { useMemo, useState, useRef, useCallback, useLayoutEffect, useEffect } from 'react';
3
+ import { isWithin } from './dateUtils.js';
4
+ import { buildSegmentMap, segmentIndexAtCaret, setAmPm, setSegmentValue, incrementSegmentValue } from './segmentMap.js';
5
+
6
+ /**
7
+ * Single-character literals that, when typed, jump the caret to the next
8
+ * editable segment — the slash / colon / dash / dot / space separators that
9
+ * appear between date and time tokens. Lets a user type `12/25/2026` or
10
+ * `13:45` straight through, skipping over the format's punctuation.
11
+ */
12
+ const SEPARATOR_RE = /[/:\-.\s]/;
13
+ /**
14
+ * Segmented manual keyboard entry for a date / time picker input. Computes a
15
+ * segment map from the token format, tracks the active segment, and returns
16
+ * the full set of `<input>` event handlers — segment-mutating keys (arrows,
17
+ * digits, AM/PM, separators) are handled here, while popover open/close and
18
+ * free-form parse-on-blur/Enter are driven through the host-supplied
19
+ * `setOpen` / `tryParse` / `commitValue` callbacks. When the format is an
20
+ * `Intl` options object or contains variable-width tokens, the segment map is
21
+ * null and the input transparently falls back to free-form text entry.
22
+ */
23
+ function useSegmentedEntry(params) {
24
+ const { format, skipKinds, value, commitValue, formatFn, tryParse, text, setText, makeBaseDate, locale, min, max, isBlocked, isUnfinishedBlocked, disabled, readOnly, editable = true, popover = true, openOnFocus = true, closeOnSelect = false, isOpen, setOpen, inputRef, containerRef, onFocus, onClick, onKeyDown, onBlur, } = params;
25
+ const segmentMap = useMemo(() => {
26
+ const map = typeof format === 'string' ? buildSegmentMap(format) : null;
27
+ if (!map || !skipKinds)
28
+ return map;
29
+ const editable = map.editable.filter(i => !skipKinds.includes(map.segments[i].kind));
30
+ return editable.length > 0 ? { ...map, editable } : null;
31
+ }, [format, skipKinds]);
32
+ const [activeSegmentIdx, setActiveSegmentIdx] = useState(null);
33
+ // Buffer of digits typed within the current segment, cleared on segment
34
+ // change. Kept in a ref because key handlers read+update synchronously
35
+ // within the same event tick.
36
+ const typedDigitsRef = useRef('');
37
+ // The text focus seeded an empty picker with, until the user edits it or
38
+ // leaves.
39
+ const seedRef = useRef(null);
40
+ // Whether the text is still what the picker put there, so nothing in it was
41
+ // typed: the value's display, or an empty picker's seed until an edit drops
42
+ // it. Against a value it compares text alone, so typing that ends back on
43
+ // the display counts as untouched.
44
+ const isUntouched = useCallback((s) => value
45
+ ? s === formatFn(value, format, locale)
46
+ : seedRef.current !== null && s === seedRef.current, [value, formatFn, format, locale]);
47
+ // A seed the popover never turned into a value closes with it, wherever
48
+ // focus goes, so a dismissed empty field stays empty.
49
+ //
50
+ // As the popover closes, its focus trap hands focus back to the input. That
51
+ // focus is not the user arriving: it must not open the popover again under
52
+ // `openOnFocus`, or seed an empty picker. `closingRef` is set in the layout
53
+ // phase of the commit that closes it and cleared by that commit's effects,
54
+ // and React runs a commit's effect cleanups, the trap's among them, before
55
+ // any of its effects, so a focus in between is the one handed back.
56
+ const closingRef = useRef(false);
57
+ const wasOpenRef = useRef(isOpen);
58
+ useLayoutEffect(() => {
59
+ if (wasOpenRef.current && !isOpen) {
60
+ closingRef.current = true;
61
+ if (!value && isUntouched(text))
62
+ setText('');
63
+ seedRef.current = null;
64
+ }
65
+ wasOpenRef.current = isOpen;
66
+ }, [isOpen, value, isUntouched, text, setText]);
67
+ useEffect(() => {
68
+ closingRef.current = false;
69
+ }, [isOpen]);
70
+ // Segment mode is unavailable for disabled / read-only / non-editable
71
+ // pickers and for Intl-options / variable-width formats (segmentMap null).
72
+ const segmentEditable = !!segmentMap && !!editable && !disabled && !readOnly;
73
+ // The Date we edit when the user starts without a current value.
74
+ const segmentBaseDate = useCallback(() => value ?? makeBaseDate(), [value, makeBaseDate]);
75
+ // Single gate for every manual-entry commit: min/max bounds plus the
76
+ // host's blocking predicate.
77
+ const isAllowed = useCallback((d) => isWithin(d, min, max) && !isBlocked?.(d), [min, max, isBlocked]);
78
+ // Apply a segment-driven date update: commit through the host pipeline (so
79
+ // onChange / controlled mode keep working) and re-render the text so the
80
+ // active selection range stays valid.
81
+ const applyDateFromSegment = useCallback((next) => {
82
+ if (!isAllowed(next))
83
+ return;
84
+ commitValue(next);
85
+ setText(formatFn(next, format, locale));
86
+ }, [commitValue, isAllowed, formatFn, format, locale, setText]);
87
+ // After every segment-driven change, restore the system selection on the
88
+ // active segment's char range. useLayoutEffect runs synchronously before
89
+ // paint so the user never sees a stray caret position. We deliberately do
90
+ // NOT gate on document.activeElement: when openOnFocus is true the focus
91
+ // trap moves focus into the popover, but setSelectionRange on an unfocused
92
+ // input is well-defined and the selection becomes visible on refocus.
93
+ useLayoutEffect(() => {
94
+ if (activeSegmentIdx === null || !segmentMap || !inputRef.current)
95
+ return;
96
+ const seg = segmentMap.segments[activeSegmentIdx];
97
+ if (!seg)
98
+ return;
99
+ try {
100
+ inputRef.current.setSelectionRange(seg.start, seg.end);
101
+ }
102
+ catch {
103
+ // Some read-only inputs reject setSelectionRange; harmless.
104
+ }
105
+ }, [activeSegmentIdx, segmentMap, text, inputRef]);
106
+ const moveSegment = useCallback((delta) => {
107
+ if (!segmentMap || activeSegmentIdx === null)
108
+ return;
109
+ const pos = segmentMap.editable.indexOf(activeSegmentIdx);
110
+ if (pos < 0)
111
+ return;
112
+ const nextPos = Math.max(0, Math.min(segmentMap.editable.length - 1, pos + delta));
113
+ const nextIdx = segmentMap.editable[nextPos];
114
+ if (nextIdx !== activeSegmentIdx) {
115
+ typedDigitsRef.current = '';
116
+ setActiveSegmentIdx(nextIdx);
117
+ }
118
+ }, [segmentMap, activeSegmentIdx]);
119
+ const handleChange = useCallback((e) => {
120
+ // In segment mode the text is managed programmatically (every edit
121
+ // re-formats from the canonical Date). Browser-issued input events
122
+ // would otherwise clobber it. The free-form path runs only when
123
+ // segment mode is inactive.
124
+ if (activeSegmentIdx !== null && segmentEditable)
125
+ return;
126
+ seedRef.current = null;
127
+ setText(e.target.value);
128
+ }, [activeSegmentIdx, segmentEditable, setText]);
129
+ const handleBlur = useCallback((e) => {
130
+ // The popover opens on focus by default, and the focus trap moves focus
131
+ // into the popover panel — that fires this blur even though the user is
132
+ // still in the same picker. Only tear down segment state and re-parse
133
+ // when focus has truly left the picker's container.
134
+ const nextFocus = e.relatedTarget;
135
+ const stayingInPicker = nextFocus !== null &&
136
+ containerRef.current !== null &&
137
+ containerRef.current.contains(nextFocus);
138
+ if (stayingInPicker) {
139
+ onBlur?.(e);
140
+ return;
141
+ }
142
+ setActiveSegmentIdx(null);
143
+ typedDigitsRef.current = '';
144
+ // Leaving commits only what was typed. Re-parsing the value's display
145
+ // could drop what the format leaves out, such as seconds, and a seed
146
+ // nobody typed over is not the user's date.
147
+ const untouched = isUntouched(text);
148
+ // An open popover keeps an unused seed until it closes, so one portaled
149
+ // outside the picker, whose focus reads as leaving, keeps it as one
150
+ // inside does.
151
+ if (!isOpen)
152
+ seedRef.current = null;
153
+ if (untouched) {
154
+ if (!value && !isOpen)
155
+ setText('');
156
+ onBlur?.(e);
157
+ return;
158
+ }
159
+ const parsed = tryParse(text);
160
+ if (parsed && isAllowed(parsed)) {
161
+ commitValue(parsed);
162
+ }
163
+ else {
164
+ setText(value ? formatFn(value, format, locale) : '');
165
+ }
166
+ onBlur?.(e);
167
+ }, [
168
+ containerRef,
169
+ isUntouched,
170
+ isOpen,
171
+ tryParse,
172
+ text,
173
+ isAllowed,
174
+ commitValue,
175
+ value,
176
+ formatFn,
177
+ format,
178
+ locale,
179
+ setText,
180
+ onBlur,
181
+ ]);
182
+ const handleFocus = useCallback((e) => {
183
+ const handedBack = closingRef.current;
184
+ if (!handedBack && openOnFocus && popover && !disabled && !readOnly) {
185
+ setOpen(true);
186
+ }
187
+ // Enter segment mode: prime an initial value (so editing works even when
188
+ // empty), seed the text, and highlight the first editable segment. The
189
+ // segment base doesn't need the seed, so focus handed back skips it.
190
+ if (segmentEditable && segmentMap) {
191
+ if (!value && !handedBack) {
192
+ const seed = formatFn(makeBaseDate(), format, locale);
193
+ seedRef.current = seed;
194
+ setText(seed);
195
+ }
196
+ typedDigitsRef.current = '';
197
+ setActiveSegmentIdx(segmentMap.editable[0]);
198
+ }
199
+ onFocus?.(e);
200
+ }, [
201
+ openOnFocus,
202
+ popover,
203
+ disabled,
204
+ readOnly,
205
+ setOpen,
206
+ segmentEditable,
207
+ segmentMap,
208
+ value,
209
+ makeBaseDate,
210
+ formatFn,
211
+ format,
212
+ locale,
213
+ setText,
214
+ onFocus,
215
+ ]);
216
+ const handleClick = useCallback((e) => {
217
+ // Clicking the field opens the popover only when `openOnFocus` is on —
218
+ // a click is just a focus. With `openOnFocus={false}` (manual-entry
219
+ // mode) the click positions the caret for typing and the popover is
220
+ // opened explicitly via the right launcher button (or Alt+ArrowDown).
221
+ if (openOnFocus && popover && !disabled && !readOnly)
222
+ setOpen(true);
223
+ if (segmentEditable && segmentMap && inputRef.current) {
224
+ const caret = inputRef.current.selectionStart ?? 0;
225
+ const idx = segmentIndexAtCaret(segmentMap, caret);
226
+ if (idx !== null && idx !== activeSegmentIdx) {
227
+ typedDigitsRef.current = '';
228
+ setActiveSegmentIdx(idx);
229
+ }
230
+ }
231
+ onClick?.(e);
232
+ }, [
233
+ openOnFocus,
234
+ popover,
235
+ disabled,
236
+ readOnly,
237
+ setOpen,
238
+ segmentEditable,
239
+ segmentMap,
240
+ inputRef,
241
+ activeSegmentIdx,
242
+ onClick,
243
+ ]);
244
+ const inSegmentMode = segmentEditable && segmentMap !== null && activeSegmentIdx !== null;
245
+ const handleKeyDown = useCallback((e) => {
246
+ // Segment-mode key routing comes first. When segment mode isn't engaged
247
+ // (not editable, no map, or no active segment — e.g. after a typed-text
248
+ // fallback), unhandled keys fall through to the popover / free-form
249
+ // handling below.
250
+ if (inSegmentMode && segmentMap && activeSegmentIdx !== null) {
251
+ const seg = segmentMap.segments[activeSegmentIdx];
252
+ const base = segmentBaseDate();
253
+ const isPm = base.getHours() >= 12;
254
+ switch (e.key) {
255
+ case 'ArrowUp':
256
+ case 'ArrowDown':
257
+ e.preventDefault();
258
+ // The plain arrows step the segment, so the popover opens on
259
+ // Alt+ArrowDown and closes on Alt+ArrowUp, as a combobox's does,
260
+ // and neither steps it.
261
+ if (e.altKey) {
262
+ if (e.key === 'ArrowUp') {
263
+ if (isOpen)
264
+ setOpen(false);
265
+ }
266
+ else if (!isOpen && popover) {
267
+ setOpen(true);
268
+ }
269
+ return;
270
+ }
271
+ typedDigitsRef.current = '';
272
+ applyDateFromSegment(incrementSegmentValue(seg, base, e.key === 'ArrowUp' ? 1 : -1, isPm));
273
+ return;
274
+ case 'ArrowRight':
275
+ e.preventDefault();
276
+ moveSegment(1);
277
+ return;
278
+ case 'ArrowLeft':
279
+ e.preventDefault();
280
+ moveSegment(-1);
281
+ return;
282
+ case 'Backspace':
283
+ e.preventDefault();
284
+ if (typedDigitsRef.current.length > 0) {
285
+ typedDigitsRef.current = '';
286
+ }
287
+ else {
288
+ moveSegment(-1);
289
+ }
290
+ return;
291
+ case 'Escape':
292
+ if (isOpen) {
293
+ e.preventDefault();
294
+ setOpen(false);
295
+ }
296
+ return;
297
+ case 'Enter':
298
+ e.preventDefault();
299
+ if (closeOnSelect)
300
+ setOpen(false);
301
+ return;
302
+ case 'Tab':
303
+ setActiveSegmentIdx(null);
304
+ typedDigitsRef.current = '';
305
+ return;
306
+ default:
307
+ // AM/PM keys.
308
+ if (seg.kind === 'ampm') {
309
+ if (e.key === 'a' || e.key === 'A') {
310
+ e.preventDefault();
311
+ typedDigitsRef.current = '';
312
+ applyDateFromSegment(setAmPm(base, false));
313
+ moveSegment(1);
314
+ return;
315
+ }
316
+ if (e.key === 'p' || e.key === 'P') {
317
+ e.preventDefault();
318
+ typedDigitsRef.current = '';
319
+ applyDateFromSegment(setAmPm(base, true));
320
+ moveSegment(1);
321
+ return;
322
+ }
323
+ }
324
+ // Typing a format separator (/, :, -, ., space) jumps to the next
325
+ // segment, skipping over the literal punctuation.
326
+ if (e.key.length === 1 && SEPARATOR_RE.test(e.key)) {
327
+ e.preventDefault();
328
+ typedDigitsRef.current = '';
329
+ moveSegment(1);
330
+ return;
331
+ }
332
+ // Digit entry for numeric segments.
333
+ if (/^[0-9]$/.test(e.key) && seg.kind !== 'ampm') {
334
+ e.preventDefault();
335
+ const nextBuffer = typedDigitsRef.current + e.key;
336
+ typedDigitsRef.current = nextBuffer;
337
+ const { date, advance } = setSegmentValue(seg, base, nextBuffer, isPm);
338
+ if (!advance && isUnfinishedBlocked?.(date))
339
+ return;
340
+ applyDateFromSegment(date);
341
+ if (advance) {
342
+ typedDigitsRef.current = '';
343
+ moveSegment(1);
344
+ }
345
+ return;
346
+ }
347
+ }
348
+ }
349
+ // ----- free-form / popover key handling -----
350
+ // With no segment to step, ArrowDown opens the popover with or without
351
+ // Alt, unless the field is read-only or disabled, as focus and the
352
+ // launcher don't open it then either. Alt+ArrowUp closes it.
353
+ if (e.key === 'ArrowDown' &&
354
+ !isOpen &&
355
+ popover &&
356
+ !disabled &&
357
+ !readOnly) {
358
+ e.preventDefault();
359
+ setOpen(true);
360
+ return;
361
+ }
362
+ if (e.key === 'ArrowUp' && e.altKey && isOpen) {
363
+ e.preventDefault();
364
+ setOpen(false);
365
+ return;
366
+ }
367
+ if (e.key === 'Escape' && isOpen) {
368
+ e.preventDefault();
369
+ setOpen(false);
370
+ return;
371
+ }
372
+ if (e.key === 'Enter') {
373
+ e.preventDefault();
374
+ // Untouched text has nothing to commit, as on leaving, but Enter
375
+ // still confirms what the field shows.
376
+ if (isUntouched(text)) {
377
+ if (closeOnSelect)
378
+ setOpen(false);
379
+ }
380
+ else {
381
+ const parsed = tryParse(text);
382
+ if (parsed && isAllowed(parsed)) {
383
+ commitValue(parsed);
384
+ if (closeOnSelect)
385
+ setOpen(false);
386
+ }
387
+ }
388
+ }
389
+ onKeyDown?.(e);
390
+ }, [
391
+ inSegmentMode,
392
+ segmentMap,
393
+ activeSegmentIdx,
394
+ segmentBaseDate,
395
+ applyDateFromSegment,
396
+ isUnfinishedBlocked,
397
+ moveSegment,
398
+ isOpen,
399
+ setOpen,
400
+ closeOnSelect,
401
+ popover,
402
+ disabled,
403
+ readOnly,
404
+ isUntouched,
405
+ tryParse,
406
+ text,
407
+ isAllowed,
408
+ commitValue,
409
+ onKeyDown,
410
+ ]);
411
+ return {
412
+ segmentMap,
413
+ activeSegmentIdx,
414
+ segmentEditable,
415
+ inSegmentMode,
416
+ inputHandlers: {
417
+ onChange: handleChange,
418
+ onFocus: handleFocus,
419
+ onClick: handleClick,
420
+ onKeyDown: handleKeyDown,
421
+ onBlur: handleBlur,
422
+ },
423
+ };
424
+ }
425
+
426
+ export { useSegmentedEntry };
427
+ //# sourceMappingURL=useSegmentedEntry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSegmentedEntry.js","sources":["../../../../src/form/_pickerInternals/useSegmentedEntry.ts"],"sourcesContent":["import React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { DateFormatOption } from './formatters';\nimport { isWithin } from './dateUtils';\nimport {\n buildSegmentMap,\n incrementSegmentValue,\n setSegmentValue,\n setAmPm,\n segmentIndexAtCaret,\n SegmentKind,\n SegmentMap,\n} from './segmentMap';\n\n/**\n * Single-character literals that, when typed, jump the caret to the next\n * editable segment — the slash / colon / dash / dot / space separators that\n * appear between date and time tokens. Lets a user type `12/25/2026` or\n * `13:45` straight through, skipping over the format's punctuation.\n */\nconst SEPARATOR_RE = /[/:\\-.\\s]/;\n\nexport interface UseSegmentedEntryParams {\n /** Resolved format actually used to render (the host's default format). */\n format: DateFormatOption;\n /**\n * Segment kinds the caret never stops on: they still render, but arrows,\n * digits and separators pass over them. A month picker skips `'day'` so a\n * format such as `'YYYY-MM-DD'` edits only the year and month. Must be\n * referentially stable. A format left with no editable segment falls back\n * to free-form entry.\n */\n skipKinds?: readonly SegmentKind[];\n /** Current canonical value (controlled or internal). */\n value: Date | null;\n /** Commit a new Date through the host's onChange / internal pipeline. */\n commitValue: (next: Date | null) => void;\n /** Host formatter: formatDate | formatTime | formatDateTime. */\n formatFn: (\n d: Date,\n fmt: DateFormatOption | undefined,\n locale?: string\n ) => string;\n /** Host parser used by the free-form fallback (parseDate / parseTime / custom). */\n tryParse: (s: string) => Date | null;\n /** Host's displayed-text state and setter (the hook drives it during edits). */\n text: string;\n setText: (s: string) => void;\n /**\n * Seed for an empty value when the user starts typing: Time → today at noon,\n * Date → today at 00:00, DateTime → now. Should be referentially stable.\n */\n makeBaseDate: () => Date;\n locale?: string;\n min?: Date;\n max?: Date;\n /**\n * Host-supplied blocking predicate (composed from shouldDisableDate /\n * unselectableDates / unselectableTimes). Checked alongside min/max on\n * every manual-entry commit; return true to reject the candidate value.\n */\n isBlocked?: (d: Date) => boolean;\n /**\n * Rejects a candidate only while the digits typed into its segment leave\n * it unfinished, such as the first digits of a year. The digit stays in\n * the segment and the next one tries again, so a value passing through a\n * candidate it can't commit on the way to one it can costs nothing. A\n * finished segment, an arrow step and parsed text answer to `isBlocked`\n * alone.\n */\n isUnfinishedBlocked?: (d: Date) => boolean;\n disabled?: boolean;\n readOnly?: boolean;\n /** Allow segmented typing. When false, segment mode never engages. */\n editable?: boolean;\n /** Whether a popover exists; gates open-on-focus / click / Alt+ArrowDown. */\n popover?: boolean;\n openOnFocus?: boolean;\n closeOnSelect?: boolean;\n isOpen: boolean;\n /**\n * The host's open-state setter. The hook asks it to open on focus, click\n * or Alt+ArrowDown, but not on the focus the popover's focus trap hands\n * back to the input as it closes, which is not the user arriving.\n */\n setOpen: (next: boolean) => void;\n inputRef: React.RefObject<HTMLInputElement | null>;\n containerRef: React.RefObject<HTMLElement | null>;\n onFocus?: React.FocusEventHandler<HTMLInputElement>;\n onClick?: React.MouseEventHandler<HTMLInputElement>;\n onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;\n onBlur?: React.FocusEventHandler<HTMLInputElement>;\n}\n\nexport interface UseSegmentedEntryResult {\n segmentMap: SegmentMap | null;\n activeSegmentIdx: number | null;\n /** True when segment typing is available (map + editable + not disabled/readOnly). */\n segmentEditable: boolean;\n /** True when a segment is actively selected. */\n inSegmentMode: boolean;\n /** Spread onto the combobox `<input>`. */\n inputHandlers: {\n onChange: React.ChangeEventHandler<HTMLInputElement>;\n onFocus: React.FocusEventHandler<HTMLInputElement>;\n onClick: React.MouseEventHandler<HTMLInputElement>;\n onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;\n onBlur: React.FocusEventHandler<HTMLInputElement>;\n };\n}\n\n/**\n * Segmented manual keyboard entry for a date / time picker input. Computes a\n * segment map from the token format, tracks the active segment, and returns\n * the full set of `<input>` event handlers — segment-mutating keys (arrows,\n * digits, AM/PM, separators) are handled here, while popover open/close and\n * free-form parse-on-blur/Enter are driven through the host-supplied\n * `setOpen` / `tryParse` / `commitValue` callbacks. When the format is an\n * `Intl` options object or contains variable-width tokens, the segment map is\n * null and the input transparently falls back to free-form text entry.\n */\nexport function useSegmentedEntry(\n params: UseSegmentedEntryParams\n): UseSegmentedEntryResult {\n const {\n format,\n skipKinds,\n value,\n commitValue,\n formatFn,\n tryParse,\n text,\n setText,\n makeBaseDate,\n locale,\n min,\n max,\n isBlocked,\n isUnfinishedBlocked,\n disabled,\n readOnly,\n editable = true,\n popover = true,\n openOnFocus = true,\n closeOnSelect = false,\n isOpen,\n setOpen,\n inputRef,\n containerRef,\n onFocus,\n onClick,\n onKeyDown,\n onBlur,\n } = params;\n\n const segmentMap: SegmentMap | null = useMemo(() => {\n const map = typeof format === 'string' ? buildSegmentMap(format) : null;\n if (!map || !skipKinds) return map;\n const editable = map.editable.filter(\n i => !skipKinds.includes(map.segments[i].kind)\n );\n return editable.length > 0 ? { ...map, editable } : null;\n }, [format, skipKinds]);\n const [activeSegmentIdx, setActiveSegmentIdx] = useState<number | null>(null);\n // Buffer of digits typed within the current segment, cleared on segment\n // change. Kept in a ref because key handlers read+update synchronously\n // within the same event tick.\n const typedDigitsRef = useRef<string>('');\n // The text focus seeded an empty picker with, until the user edits it or\n // leaves.\n const seedRef = useRef<string | null>(null);\n\n // Whether the text is still what the picker put there, so nothing in it was\n // typed: the value's display, or an empty picker's seed until an edit drops\n // it. Against a value it compares text alone, so typing that ends back on\n // the display counts as untouched.\n const isUntouched = useCallback(\n (s: string) =>\n value\n ? s === formatFn(value, format, locale)\n : seedRef.current !== null && s === seedRef.current,\n [value, formatFn, format, locale]\n );\n\n // A seed the popover never turned into a value closes with it, wherever\n // focus goes, so a dismissed empty field stays empty.\n //\n // As the popover closes, its focus trap hands focus back to the input. That\n // focus is not the user arriving: it must not open the popover again under\n // `openOnFocus`, or seed an empty picker. `closingRef` is set in the layout\n // phase of the commit that closes it and cleared by that commit's effects,\n // and React runs a commit's effect cleanups, the trap's among them, before\n // any of its effects, so a focus in between is the one handed back.\n const closingRef = useRef(false);\n const wasOpenRef = useRef(isOpen);\n useLayoutEffect(() => {\n if (wasOpenRef.current && !isOpen) {\n closingRef.current = true;\n if (!value && isUntouched(text)) setText('');\n seedRef.current = null;\n }\n wasOpenRef.current = isOpen;\n }, [isOpen, value, isUntouched, text, setText]);\n useEffect(() => {\n closingRef.current = false;\n }, [isOpen]);\n\n // Segment mode is unavailable for disabled / read-only / non-editable\n // pickers and for Intl-options / variable-width formats (segmentMap null).\n const segmentEditable = !!segmentMap && !!editable && !disabled && !readOnly;\n\n // The Date we edit when the user starts without a current value.\n const segmentBaseDate = useCallback(\n (): Date => value ?? makeBaseDate(),\n [value, makeBaseDate]\n );\n\n // Single gate for every manual-entry commit: min/max bounds plus the\n // host's blocking predicate.\n const isAllowed = useCallback(\n (d: Date) => isWithin(d, min, max) && !isBlocked?.(d),\n [min, max, isBlocked]\n );\n\n // Apply a segment-driven date update: commit through the host pipeline (so\n // onChange / controlled mode keep working) and re-render the text so the\n // active selection range stays valid.\n const applyDateFromSegment = useCallback(\n (next: Date) => {\n if (!isAllowed(next)) return;\n commitValue(next);\n setText(formatFn(next, format, locale));\n },\n [commitValue, isAllowed, formatFn, format, locale, setText]\n );\n\n // After every segment-driven change, restore the system selection on the\n // active segment's char range. useLayoutEffect runs synchronously before\n // paint so the user never sees a stray caret position. We deliberately do\n // NOT gate on document.activeElement: when openOnFocus is true the focus\n // trap moves focus into the popover, but setSelectionRange on an unfocused\n // input is well-defined and the selection becomes visible on refocus.\n useLayoutEffect(() => {\n if (activeSegmentIdx === null || !segmentMap || !inputRef.current) return;\n const seg = segmentMap.segments[activeSegmentIdx];\n if (!seg) return;\n try {\n inputRef.current.setSelectionRange(seg.start, seg.end);\n } catch {\n // Some read-only inputs reject setSelectionRange; harmless.\n }\n }, [activeSegmentIdx, segmentMap, text, inputRef]);\n\n const moveSegment = useCallback(\n (delta: 1 | -1) => {\n if (!segmentMap || activeSegmentIdx === null) return;\n const pos = segmentMap.editable.indexOf(activeSegmentIdx);\n if (pos < 0) return;\n const nextPos = Math.max(\n 0,\n Math.min(segmentMap.editable.length - 1, pos + delta)\n );\n const nextIdx = segmentMap.editable[nextPos];\n if (nextIdx !== activeSegmentIdx) {\n typedDigitsRef.current = '';\n setActiveSegmentIdx(nextIdx);\n }\n },\n [segmentMap, activeSegmentIdx]\n );\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n // In segment mode the text is managed programmatically (every edit\n // re-formats from the canonical Date). Browser-issued input events\n // would otherwise clobber it. The free-form path runs only when\n // segment mode is inactive.\n if (activeSegmentIdx !== null && segmentEditable) return;\n seedRef.current = null;\n setText(e.target.value);\n },\n [activeSegmentIdx, segmentEditable, setText]\n );\n\n const handleBlur = useCallback(\n (e: React.FocusEvent<HTMLInputElement>) => {\n // The popover opens on focus by default, and the focus trap moves focus\n // into the popover panel — that fires this blur even though the user is\n // still in the same picker. Only tear down segment state and re-parse\n // when focus has truly left the picker's container.\n const nextFocus = e.relatedTarget as HTMLElement | null;\n const stayingInPicker =\n nextFocus !== null &&\n containerRef.current !== null &&\n containerRef.current.contains(nextFocus);\n if (stayingInPicker) {\n onBlur?.(e);\n return;\n }\n setActiveSegmentIdx(null);\n typedDigitsRef.current = '';\n // Leaving commits only what was typed. Re-parsing the value's display\n // could drop what the format leaves out, such as seconds, and a seed\n // nobody typed over is not the user's date.\n const untouched = isUntouched(text);\n // An open popover keeps an unused seed until it closes, so one portaled\n // outside the picker, whose focus reads as leaving, keeps it as one\n // inside does.\n if (!isOpen) seedRef.current = null;\n if (untouched) {\n if (!value && !isOpen) setText('');\n onBlur?.(e);\n return;\n }\n const parsed = tryParse(text);\n if (parsed && isAllowed(parsed)) {\n commitValue(parsed);\n } else {\n setText(value ? formatFn(value, format, locale) : '');\n }\n onBlur?.(e);\n },\n [\n containerRef,\n isUntouched,\n isOpen,\n tryParse,\n text,\n isAllowed,\n commitValue,\n value,\n formatFn,\n format,\n locale,\n setText,\n onBlur,\n ]\n );\n\n const handleFocus = useCallback(\n (e: React.FocusEvent<HTMLInputElement>) => {\n const handedBack = closingRef.current;\n if (!handedBack && openOnFocus && popover && !disabled && !readOnly) {\n setOpen(true);\n }\n // Enter segment mode: prime an initial value (so editing works even when\n // empty), seed the text, and highlight the first editable segment. The\n // segment base doesn't need the seed, so focus handed back skips it.\n if (segmentEditable && segmentMap) {\n if (!value && !handedBack) {\n const seed = formatFn(makeBaseDate(), format, locale);\n seedRef.current = seed;\n setText(seed);\n }\n typedDigitsRef.current = '';\n setActiveSegmentIdx(segmentMap.editable[0]);\n }\n onFocus?.(e);\n },\n [\n openOnFocus,\n popover,\n disabled,\n readOnly,\n setOpen,\n segmentEditable,\n segmentMap,\n value,\n makeBaseDate,\n formatFn,\n format,\n locale,\n setText,\n onFocus,\n ]\n );\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLInputElement>) => {\n // Clicking the field opens the popover only when `openOnFocus` is on —\n // a click is just a focus. With `openOnFocus={false}` (manual-entry\n // mode) the click positions the caret for typing and the popover is\n // opened explicitly via the right launcher button (or Alt+ArrowDown).\n if (openOnFocus && popover && !disabled && !readOnly) setOpen(true);\n if (segmentEditable && segmentMap && inputRef.current) {\n const caret = inputRef.current.selectionStart ?? 0;\n const idx = segmentIndexAtCaret(segmentMap, caret);\n if (idx !== null && idx !== activeSegmentIdx) {\n typedDigitsRef.current = '';\n setActiveSegmentIdx(idx);\n }\n }\n onClick?.(e);\n },\n [\n openOnFocus,\n popover,\n disabled,\n readOnly,\n setOpen,\n segmentEditable,\n segmentMap,\n inputRef,\n activeSegmentIdx,\n onClick,\n ]\n );\n\n const inSegmentMode =\n segmentEditable && segmentMap !== null && activeSegmentIdx !== null;\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n // Segment-mode key routing comes first. When segment mode isn't engaged\n // (not editable, no map, or no active segment — e.g. after a typed-text\n // fallback), unhandled keys fall through to the popover / free-form\n // handling below.\n if (inSegmentMode && segmentMap && activeSegmentIdx !== null) {\n const seg = segmentMap.segments[activeSegmentIdx];\n const base = segmentBaseDate();\n const isPm = base.getHours() >= 12;\n switch (e.key) {\n case 'ArrowUp':\n case 'ArrowDown':\n e.preventDefault();\n // The plain arrows step the segment, so the popover opens on\n // Alt+ArrowDown and closes on Alt+ArrowUp, as a combobox's does,\n // and neither steps it.\n if (e.altKey) {\n if (e.key === 'ArrowUp') {\n if (isOpen) setOpen(false);\n } else if (!isOpen && popover) {\n setOpen(true);\n }\n return;\n }\n typedDigitsRef.current = '';\n applyDateFromSegment(\n incrementSegmentValue(\n seg,\n base,\n e.key === 'ArrowUp' ? 1 : -1,\n isPm\n )\n );\n return;\n case 'ArrowRight':\n e.preventDefault();\n moveSegment(1);\n return;\n case 'ArrowLeft':\n e.preventDefault();\n moveSegment(-1);\n return;\n case 'Backspace':\n e.preventDefault();\n if (typedDigitsRef.current.length > 0) {\n typedDigitsRef.current = '';\n } else {\n moveSegment(-1);\n }\n return;\n case 'Escape':\n if (isOpen) {\n e.preventDefault();\n setOpen(false);\n }\n return;\n case 'Enter':\n e.preventDefault();\n if (closeOnSelect) setOpen(false);\n return;\n case 'Tab':\n setActiveSegmentIdx(null);\n typedDigitsRef.current = '';\n return;\n default:\n // AM/PM keys.\n if (seg.kind === 'ampm') {\n if (e.key === 'a' || e.key === 'A') {\n e.preventDefault();\n typedDigitsRef.current = '';\n applyDateFromSegment(setAmPm(base, false));\n moveSegment(1);\n return;\n }\n if (e.key === 'p' || e.key === 'P') {\n e.preventDefault();\n typedDigitsRef.current = '';\n applyDateFromSegment(setAmPm(base, true));\n moveSegment(1);\n return;\n }\n }\n // Typing a format separator (/, :, -, ., space) jumps to the next\n // segment, skipping over the literal punctuation.\n if (e.key.length === 1 && SEPARATOR_RE.test(e.key)) {\n e.preventDefault();\n typedDigitsRef.current = '';\n moveSegment(1);\n return;\n }\n // Digit entry for numeric segments.\n if (/^[0-9]$/.test(e.key) && seg.kind !== 'ampm') {\n e.preventDefault();\n const nextBuffer = typedDigitsRef.current + e.key;\n typedDigitsRef.current = nextBuffer;\n const { date, advance } = setSegmentValue(\n seg,\n base,\n nextBuffer,\n isPm\n );\n if (!advance && isUnfinishedBlocked?.(date)) return;\n applyDateFromSegment(date);\n if (advance) {\n typedDigitsRef.current = '';\n moveSegment(1);\n }\n return;\n }\n }\n }\n // ----- free-form / popover key handling -----\n // With no segment to step, ArrowDown opens the popover with or without\n // Alt, unless the field is read-only or disabled, as focus and the\n // launcher don't open it then either. Alt+ArrowUp closes it.\n if (\n e.key === 'ArrowDown' &&\n !isOpen &&\n popover &&\n !disabled &&\n !readOnly\n ) {\n e.preventDefault();\n setOpen(true);\n return;\n }\n if (e.key === 'ArrowUp' && e.altKey && isOpen) {\n e.preventDefault();\n setOpen(false);\n return;\n }\n if (e.key === 'Escape' && isOpen) {\n e.preventDefault();\n setOpen(false);\n return;\n }\n if (e.key === 'Enter') {\n e.preventDefault();\n // Untouched text has nothing to commit, as on leaving, but Enter\n // still confirms what the field shows.\n if (isUntouched(text)) {\n if (closeOnSelect) setOpen(false);\n } else {\n const parsed = tryParse(text);\n if (parsed && isAllowed(parsed)) {\n commitValue(parsed);\n if (closeOnSelect) setOpen(false);\n }\n }\n }\n onKeyDown?.(e);\n },\n [\n inSegmentMode,\n segmentMap,\n activeSegmentIdx,\n segmentBaseDate,\n applyDateFromSegment,\n isUnfinishedBlocked,\n moveSegment,\n isOpen,\n setOpen,\n closeOnSelect,\n popover,\n disabled,\n readOnly,\n isUntouched,\n tryParse,\n text,\n isAllowed,\n commitValue,\n onKeyDown,\n ]\n );\n\n return {\n segmentMap,\n activeSegmentIdx,\n segmentEditable,\n inSegmentMode,\n inputHandlers: {\n onChange: handleChange,\n onFocus: handleFocus,\n onClick: handleClick,\n onKeyDown: handleKeyDown,\n onBlur: handleBlur,\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
2
+ import { createContext, useContext } from 'react';
3
+
4
+ // Whether the nearest enclosing `Control` shows its `isLoading` spinner. The
5
+ // picker bases read it so their right-side launcher gives way to that
6
+ // spinner, which sits at the same edge, whether the `Control` is the one a
7
+ // convenience input renders or one the caller wrapped around the input.
8
+ // Autocomplete reads it for its clear button, which sits there too.
9
+ // Kept apart from `FormContext`, whose contexts only report whether a `Field`
10
+ // or `Control` is present. Internal; not part of the public API.
11
+ const ControlLoadingContext = createContext(false);
12
+ /** Provider for the loading state, used internally by `Control`. */
13
+ const ControlLoadingProvider = ControlLoadingContext.Provider;
14
+ /** Whether the nearest enclosing `Control` is loading; `false` outside one. */
15
+ const useControlLoading = () => useContext(ControlLoadingContext);
16
+
17
+ export { ControlLoadingProvider, useControlLoading };
18
+ //# sourceMappingURL=controlLoading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlLoading.js","sources":["../../../src/form/controlLoading.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\n\n// Whether the nearest enclosing `Control` shows its `isLoading` spinner. The\n// picker bases read it so their right-side launcher gives way to that\n// spinner, which sits at the same edge, whether the `Control` is the one a\n// convenience input renders or one the caller wrapped around the input.\n// Autocomplete reads it for its clear button, which sits there too.\n// Kept apart from `FormContext`, whose contexts only report whether a `Field`\n// or `Control` is present. Internal; not part of the public API.\nconst ControlLoadingContext = createContext(false);\n\n/** Provider for the loading state, used internally by `Control`. */\nexport const ControlLoadingProvider = ControlLoadingContext.Provider;\n\n/** Whether the nearest enclosing `Control` is loading; `false` outside one. */\nexport const useControlLoading = () => useContext(ControlLoadingContext);\n"],"names":[],"mappings":";;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM,qBAAqB,GAAG,aAAa,CAAC,KAAK,CAAC;AAElD;AACO,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D;AACO,MAAM,iBAAiB,GAAG,MAAM,UAAU,CAAC,qBAAqB;;"}
@@ -0,0 +1,88 @@
1
+ /* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
2
+ import { useId } from 'react';
3
+ import { useFieldLabelElementId, useFieldLabelId, useFieldLabelFor } from './FormContext.js';
4
+
5
+ /**
6
+ * Associates the convenience `label` prop with its control (#368): generates
7
+ * an id for the control and returns labelProps carrying a matching `htmlFor`.
8
+ * A user-supplied `id` is used as the target instead of the generated one, and
9
+ * an explicit `htmlFor` key in labelProps — even set to `undefined` — disables
10
+ * generation entirely: the user has taken over the association (#495 presence
11
+ * semantics). When the control renders no label of its own, it adopts the id
12
+ * a labeled Field around it offers, the way the bases do, so that Field's
13
+ * label names it (#939); a user `id` still wins there.
14
+ * Internal; not part of the public API.
15
+ */
16
+ function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true, }) {
17
+ // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
18
+ const generatedId = useId();
19
+ // Read outside the control's own Field, so it is set only when an outer
20
+ // Field holds the control, which then renders no label of its own.
21
+ const fieldLabelId = useFieldLabelId();
22
+ // What an outer Field's label points at, even when wired by hand.
23
+ const fieldLabelFor = useFieldLabelFor();
24
+ // Truthiness mirrors Field's own `if (label)` render gate.
25
+ const active = !!label && rendersLabel;
26
+ // Presence, not truthiness: `htmlFor: undefined` is an explicit opt-out and
27
+ // must not leave an orphan generated id on the control.
28
+ const userWired = !!labelProps && 'htmlFor' in labelProps;
29
+ const adopted = !active && hasInput ? fieldLabelId : undefined;
30
+ const controlId = id ?? (active && !userWired ? generatedId : adopted);
31
+ const ownLabelProps = { htmlFor: controlId, ...labelProps };
32
+ // Inactive with a label still means an own Field may render it (pickers'
33
+ // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`
34
+ // tells Field the association is owned here, so it must not generate one
35
+ // that would dangle (#495 presence semantics).
36
+ const fieldLabelProps = active
37
+ ? ownLabelProps
38
+ : label
39
+ ? { htmlFor: undefined, ...labelProps }
40
+ : labelProps;
41
+ const labelTarget = active ? ownLabelProps.htmlFor : fieldLabelFor;
42
+ return {
43
+ controlId,
44
+ fieldLabelProps,
45
+ labelled: !!controlId && labelTarget === controlId,
46
+ };
47
+ }
48
+ /**
49
+ * Group-input counterpart of {@link useAutoLabelId} (#494): a group of
50
+ * controls cannot take a single `htmlFor`, so instead the rendered `<label>`
51
+ * gets a generated id and the group container points at it with
52
+ * `aria-labelledby`. A user-supplied `labelProps.id` is used as the target
53
+ * instead of generating one. Any caller `htmlFor` is stripped — a group label
54
+ * names the group, never a single control — so the merged labelProps always
55
+ * carry an explicit `htmlFor: undefined`. When the group renders no label of
56
+ * its own, it points at the label of a labeled Field around it instead
57
+ * (#939). A caller who named the group with `aria-label` or
58
+ * `aria-labelledby` gets no `aria-labelledby` from either label, so their
59
+ * name wins wherever the label sits.
60
+ * Internal; not part of the public API.
61
+ */
62
+ function useAutoLabelledBy({ label, labelProps, rendersLabel, callerProps, }) {
63
+ // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
64
+ const generatedId = useId();
65
+ // Set only when an outer Field holds the group (see useAutoLabelId).
66
+ const fieldLabelElementId = useFieldLabelElementId();
67
+ const active = !!label && rendersLabel;
68
+ const labelId = labelProps?.id ?? (active ? generatedId : undefined);
69
+ const fieldLabelProps = active
70
+ ? { ...labelProps, id: labelId, htmlFor: undefined }
71
+ : labelProps;
72
+ const aria = callerProps;
73
+ // Presence for aria-labelledby: the caller's key replaces the attribute
74
+ // through the spread whatever its value, and pointing it at a label would
75
+ // only take a Rate's fallback name away. Checked before either label, so
76
+ // the caller's name wins over the group's own `label` as it does over a
77
+ // Field's, the way an aria-label beats a `<label for>` on a single input.
78
+ const callerNamed = !!aria['aria-label'] || 'aria-labelledby' in aria;
79
+ const ariaLabelledBy = callerNamed
80
+ ? undefined
81
+ : active
82
+ ? labelId
83
+ : fieldLabelElementId;
84
+ return { ariaLabelledBy, fieldLabelProps };
85
+ }
86
+
87
+ export { useAutoLabelId, useAutoLabelledBy };
88
+ //# sourceMappingURL=useAutoLabelId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAutoLabelId.js","sources":["../../../src/form/useAutoLabelId.ts"],"sourcesContent":["import React, { useId } from 'react';\nimport type { FieldProps } from './Field';\nimport {\n useFieldLabelElementId,\n useFieldLabelFor,\n useFieldLabelId,\n} from './FormContext';\n\ninterface UseAutoLabelIdOptions {\n /** The convenience `label` prop as passed by the caller. */\n label: React.ReactNode;\n /** User-supplied id for the control, if any. */\n id?: string;\n /** User-supplied labelProps, if any. */\n labelProps?: FieldProps['labelProps'];\n /**\n * True when this render actually outputs the label wired to a control —\n * false inside an outer Field (label is dropped), in variants that render\n * no Field, and in modes with no labellable control (e.g. inline pickers).\n */\n rendersLabel: boolean;\n /**\n * False when this render has no input to label at all (an inline picker),\n * so it takes no id from a surrounding Field either. Defaults to true.\n */\n hasInput?: boolean;\n}\n\n/**\n * Associates the convenience `label` prop with its control (#368): generates\n * an id for the control and returns labelProps carrying a matching `htmlFor`.\n * A user-supplied `id` is used as the target instead of the generated one, and\n * an explicit `htmlFor` key in labelProps — even set to `undefined` — disables\n * generation entirely: the user has taken over the association (#495 presence\n * semantics). When the control renders no label of its own, it adopts the id\n * a labeled Field around it offers, the way the bases do, so that Field's\n * label names it (#939); a user `id` still wins there.\n * Internal; not part of the public API.\n */\nexport function useAutoLabelId({\n label,\n id,\n labelProps,\n rendersLabel,\n hasInput = true,\n}: UseAutoLabelIdOptions): {\n controlId: string | undefined;\n fieldLabelProps: FieldProps['labelProps'] | undefined;\n /** Whether a rendered label, its own or a surrounding Field's, points at `controlId`. */\n labelled: boolean;\n} {\n // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.\n const generatedId = useId();\n // Read outside the control's own Field, so it is set only when an outer\n // Field holds the control, which then renders no label of its own.\n const fieldLabelId = useFieldLabelId();\n // What an outer Field's label points at, even when wired by hand.\n const fieldLabelFor = useFieldLabelFor();\n // Truthiness mirrors Field's own `if (label)` render gate.\n const active = !!label && rendersLabel;\n // Presence, not truthiness: `htmlFor: undefined` is an explicit opt-out and\n // must not leave an orphan generated id on the control.\n const userWired = !!labelProps && 'htmlFor' in labelProps;\n const adopted = !active && hasInput ? fieldLabelId : undefined;\n const controlId = id ?? (active && !userWired ? generatedId : adopted);\n const ownLabelProps = { htmlFor: controlId, ...labelProps };\n // Inactive with a label still means an own Field may render it (pickers'\n // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`\n // tells Field the association is owned here, so it must not generate one\n // that would dangle (#495 presence semantics).\n const fieldLabelProps = active\n ? ownLabelProps\n : label\n ? { htmlFor: undefined, ...labelProps }\n : labelProps;\n const labelTarget = active ? ownLabelProps.htmlFor : fieldLabelFor;\n return {\n controlId,\n fieldLabelProps,\n labelled: !!controlId && labelTarget === controlId,\n };\n}\n\ninterface UseAutoLabelledByOptions {\n /** The convenience `label` prop as passed by the caller. */\n label: React.ReactNode;\n /** User-supplied labelProps, if any. */\n labelProps?: FieldProps['labelProps'];\n /** True when this render actually outputs the label naming the group. */\n rendersLabel: boolean;\n /**\n * The caller's remaining props. A non-empty `aria-label` among them names\n * the group, and so does any `aria-labelledby` key, even an undefined one,\n * since the group spreads them after its own attribute. Either keeps every\n * label off the group, its own and a surrounding Field's alike.\n */\n callerProps: object;\n}\n\n/**\n * Group-input counterpart of {@link useAutoLabelId} (#494): a group of\n * controls cannot take a single `htmlFor`, so instead the rendered `<label>`\n * gets a generated id and the group container points at it with\n * `aria-labelledby`. A user-supplied `labelProps.id` is used as the target\n * instead of generating one. Any caller `htmlFor` is stripped — a group label\n * names the group, never a single control — so the merged labelProps always\n * carry an explicit `htmlFor: undefined`. When the group renders no label of\n * its own, it points at the label of a labeled Field around it instead\n * (#939). A caller who named the group with `aria-label` or\n * `aria-labelledby` gets no `aria-labelledby` from either label, so their\n * name wins wherever the label sits.\n * Internal; not part of the public API.\n */\nexport function useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel,\n callerProps,\n}: UseAutoLabelledByOptions): {\n ariaLabelledBy: string | undefined;\n fieldLabelProps: FieldProps['labelProps'] | undefined;\n} {\n // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.\n const generatedId = useId();\n // Set only when an outer Field holds the group (see useAutoLabelId).\n const fieldLabelElementId = useFieldLabelElementId();\n const active = !!label && rendersLabel;\n const labelId = labelProps?.id ?? (active ? generatedId : undefined);\n const fieldLabelProps = active\n ? { ...labelProps, id: labelId, htmlFor: undefined }\n : labelProps;\n const aria = callerProps as React.AriaAttributes;\n // Presence for aria-labelledby: the caller's key replaces the attribute\n // through the spread whatever its value, and pointing it at a label would\n // only take a Rate's fallback name away. Checked before either label, so\n // the caller's name wins over the group's own `label` as it does over a\n // Field's, the way an aria-label beats a `<label for>` on a single input.\n const callerNamed = !!aria['aria-label'] || 'aria-labelledby' in aria;\n const ariaLabelledBy = callerNamed\n ? undefined\n : active\n ? labelId\n : fieldLabelElementId;\n return { ariaLabelledBy, fieldLabelProps };\n}\n"],"names":[],"mappings":";;;;AA4BA;;;;;;;;;;AAUG;AACG,SAAU,cAAc,CAAC,EAC7B,KAAK,EACL,EAAE,EACF,UAAU,EACV,YAAY,EACZ,QAAQ,GAAG,IAAI,GACO,EAAA;;AAOtB,IAAA,MAAM,WAAW,GAAG,KAAK,EAAE;;;AAG3B,IAAA,MAAM,YAAY,GAAG,eAAe,EAAE;;AAEtC,IAAA,MAAM,aAAa,GAAG,gBAAgB,EAAE;;AAExC,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,YAAY;;;IAGtC,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,IAAI,SAAS,IAAI,UAAU;AACzD,IAAA,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,QAAQ,GAAG,YAAY,GAAG,SAAS;AAC9D,IAAA,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,IAAI,CAAC,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IACtE,MAAM,aAAa,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE;;;;;IAK3D,MAAM,eAAe,GAAG;AACtB,UAAE;AACF,UAAE;cACE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU;cACnC,UAAU;AAChB,IAAA,MAAM,WAAW,GAAG,MAAM,GAAG,aAAa,CAAC,OAAO,GAAG,aAAa;IAClE,OAAO;QACL,SAAS;QACT,eAAe;AACf,QAAA,QAAQ,EAAE,CAAC,CAAC,SAAS,IAAI,WAAW,KAAK,SAAS;KACnD;AACH;AAkBA;;;;;;;;;;;;;AAaG;AACG,SAAU,iBAAiB,CAAC,EAChC,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,GACc,EAAA;;AAKzB,IAAA,MAAM,WAAW,GAAG,KAAK,EAAE;;AAE3B,IAAA,MAAM,mBAAmB,GAAG,sBAAsB,EAAE;AACpD,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,YAAY;AACtC,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,EAAE,KAAK,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACpE,MAAM,eAAe,GAAG;AACtB,UAAE,EAAE,GAAG,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS;UAChD,UAAU;IACd,MAAM,IAAI,GAAG,WAAmC;;;;;;AAMhD,IAAA,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,iBAAiB,IAAI,IAAI;IACrE,MAAM,cAAc,GAAG;AACrB,UAAE;AACF,UAAE;AACA,cAAE;cACA,mBAAmB;AACzB,IAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE;AAC5C;;"}