@comitor/ui 2.0.0 → 3.0.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 (439) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +67 -24
  3. package/dist/chart/chart.d.ts +6 -1
  4. package/dist/chart/chart.js +3 -1
  5. package/dist/chart/chart.js.map +1 -1
  6. package/dist/chart/comitor-chart.d.ts +4 -1
  7. package/dist/chart/comitor-chart.js +3 -1
  8. package/dist/chart/comitor-chart.js.map +1 -1
  9. package/dist/components/composite/avatar-group.d.ts +9 -6
  10. package/dist/components/composite/avatar-group.js +6 -2
  11. package/dist/components/composite/avatar-group.js.map +1 -1
  12. package/dist/components/composite/combobox.d.ts +11 -11
  13. package/dist/components/composite/combobox.js +25 -11
  14. package/dist/components/composite/combobox.js.map +1 -1
  15. package/dist/components/composite/command-palette-constants.d.ts +15 -5
  16. package/dist/components/composite/command-palette-constants.js +3 -2
  17. package/dist/components/composite/command-palette-constants.js.map +1 -1
  18. package/dist/components/composite/command-palette.d.ts +2 -2
  19. package/dist/components/composite/command-palette.js +8 -5
  20. package/dist/components/composite/command-palette.js.map +1 -1
  21. package/dist/components/composite/confirm-dialog.d.ts +8 -8
  22. package/dist/components/composite/confirm-dialog.js +8 -3
  23. package/dist/components/composite/confirm-dialog.js.map +1 -1
  24. package/dist/components/composite/copy-button.d.ts +7 -3
  25. package/dist/components/composite/copy-button.js +7 -3
  26. package/dist/components/composite/copy-button.js.map +1 -1
  27. package/dist/components/composite/data-table-constants.d.ts +7 -3
  28. package/dist/components/composite/data-table-constants.js +2 -9
  29. package/dist/components/composite/data-table-constants.js.map +1 -1
  30. package/dist/components/composite/data-table.js +3 -3
  31. package/dist/components/composite/data-table.js.map +1 -1
  32. package/dist/components/composite/date-picker-constants.d.ts +32 -9
  33. package/dist/components/composite/date-picker-constants.js +16 -11
  34. package/dist/components/composite/date-picker-constants.js.map +1 -1
  35. package/dist/components/composite/date-picker.d.ts +19 -10
  36. package/dist/components/composite/date-picker.js +33 -9
  37. package/dist/components/composite/date-picker.js.map +1 -1
  38. package/dist/components/composite/filter-chips.d.ts +4 -3
  39. package/dist/components/composite/filter-chips.js +6 -2
  40. package/dist/components/composite/filter-chips.js.map +1 -1
  41. package/dist/components/composite/inline-edit.d.ts +14 -3
  42. package/dist/components/composite/inline-edit.js +8 -4
  43. package/dist/components/composite/inline-edit.js.map +1 -1
  44. package/dist/components/composite/relative-time.d.ts +4 -4
  45. package/dist/components/composite/relative-time.js +4 -2
  46. package/dist/components/composite/relative-time.js.map +1 -1
  47. package/dist/components/composite/route-tabs-nav.d.ts +9 -0
  48. package/dist/components/composite/route-tabs-nav.js +11 -0
  49. package/dist/components/composite/route-tabs-nav.js.map +1 -0
  50. package/dist/components/composite/route-tabs.d.ts +4 -1
  51. package/dist/components/composite/route-tabs.js +3 -2
  52. package/dist/components/composite/route-tabs.js.map +1 -1
  53. package/dist/components/composite/search-filter-bar-constants.d.ts +18 -8
  54. package/dist/components/composite/search-filter-bar-constants.js +2 -14
  55. package/dist/components/composite/search-filter-bar-constants.js.map +1 -1
  56. package/dist/components/composite/search-filter-bar.d.ts +5 -2
  57. package/dist/components/composite/search-filter-bar.js +2 -2
  58. package/dist/components/composite/search-filter-bar.js.map +1 -1
  59. package/dist/components/composite/stat-card.d.ts +1 -1
  60. package/dist/components/composite/table-pagination-constants.d.ts +16 -11
  61. package/dist/components/composite/table-pagination-constants.js +2 -12
  62. package/dist/components/composite/table-pagination-constants.js.map +1 -1
  63. package/dist/components/composite/table-pagination.d.ts +6 -3
  64. package/dist/components/composite/table-pagination.js +3 -5
  65. package/dist/components/composite/table-pagination.js.map +1 -1
  66. package/dist/components/composite/toast.d.ts +4 -2
  67. package/dist/components/composite/toast.js +9 -1
  68. package/dist/components/composite/toast.js.map +1 -1
  69. package/dist/components/ui/action-bar-constants.d.ts +35 -0
  70. package/dist/components/ui/action-bar-constants.js +6 -0
  71. package/dist/components/ui/action-bar-constants.js.map +1 -0
  72. package/dist/components/ui/action-bar.d.ts +68 -0
  73. package/dist/components/ui/action-bar.js +522 -0
  74. package/dist/components/ui/action-bar.js.map +1 -0
  75. package/dist/components/ui/alert.d.ts +1 -1
  76. package/dist/components/ui/badge-overflow.d.ts +30 -0
  77. package/dist/components/ui/badge-overflow.js +181 -0
  78. package/dist/components/ui/badge-overflow.js.map +1 -0
  79. package/dist/components/ui/badge.d.ts +1 -1
  80. package/dist/components/ui/banner.d.ts +80 -0
  81. package/dist/components/ui/banner.js +520 -0
  82. package/dist/components/ui/banner.js.map +1 -0
  83. package/dist/components/ui/breadcrumb.d.ts +11 -4
  84. package/dist/components/ui/breadcrumb.js +7 -2
  85. package/dist/components/ui/breadcrumb.js.map +1 -1
  86. package/dist/components/ui/button.d.ts +2 -2
  87. package/dist/components/ui/calendar.d.ts +12 -5
  88. package/dist/components/ui/calendar.js +38 -3
  89. package/dist/components/ui/calendar.js.map +1 -1
  90. package/dist/components/ui/carousel.d.ts +4 -3
  91. package/dist/components/ui/carousel.js +11 -4
  92. package/dist/components/ui/carousel.js.map +1 -1
  93. package/dist/components/ui/circular-progress.d.ts +50 -0
  94. package/dist/components/ui/circular-progress.js +266 -0
  95. package/dist/components/ui/circular-progress.js.map +1 -0
  96. package/dist/components/ui/command.d.ts +5 -4
  97. package/dist/components/ui/command.js +6 -2
  98. package/dist/components/ui/command.js.map +1 -1
  99. package/dist/components/ui/cropper-constants.d.ts +39 -0
  100. package/dist/components/ui/cropper-constants.js +6 -0
  101. package/dist/components/ui/cropper-constants.js.map +1 -0
  102. package/dist/components/ui/cropper.d.ts +107 -0
  103. package/dist/components/ui/cropper.js +1269 -0
  104. package/dist/components/ui/cropper.js.map +1 -0
  105. package/dist/components/ui/dialog.d.ts +2 -2
  106. package/dist/components/ui/dialog.js +4 -1
  107. package/dist/components/ui/dialog.js.map +1 -1
  108. package/dist/components/ui/drawer.d.ts +1 -1
  109. package/dist/components/ui/drawer.js +2 -2
  110. package/dist/components/ui/drawer.js.map +1 -1
  111. package/dist/components/ui/item.d.ts +2 -2
  112. package/dist/components/ui/kanban-constants.d.ts +67 -0
  113. package/dist/components/ui/kanban-constants.js +6 -0
  114. package/dist/components/ui/kanban-constants.js.map +1 -0
  115. package/dist/components/ui/kanban.d.ts +72 -0
  116. package/dist/components/ui/kanban.js +787 -0
  117. package/dist/components/ui/kanban.js.map +1 -0
  118. package/dist/components/ui/key-value-constants.d.ts +37 -0
  119. package/dist/components/ui/key-value-constants.js +6 -0
  120. package/dist/components/ui/key-value-constants.js.map +1 -0
  121. package/dist/components/ui/key-value.d.ts +96 -0
  122. package/dist/components/ui/key-value.js +641 -0
  123. package/dist/components/ui/key-value.js.map +1 -0
  124. package/dist/components/ui/mask-input-constants.d.ts +98 -0
  125. package/dist/components/ui/mask-input-constants.js +600 -0
  126. package/dist/components/ui/mask-input-constants.js.map +1 -0
  127. package/dist/components/ui/mask-input.d.ts +39 -0
  128. package/dist/components/ui/mask-input.js +560 -0
  129. package/dist/components/ui/mask-input.js.map +1 -0
  130. package/dist/components/ui/mention.d.ts +32 -0
  131. package/dist/components/ui/mention.js +126 -0
  132. package/dist/components/ui/mention.js.map +1 -0
  133. package/dist/components/ui/pagination.d.ts +13 -9
  134. package/dist/components/ui/pagination.js +15 -6
  135. package/dist/components/ui/pagination.js.map +1 -1
  136. package/dist/components/ui/phone-input-constants.d.ts +65 -0
  137. package/dist/components/ui/phone-input-constants.js +6 -0
  138. package/dist/components/ui/phone-input-constants.js.map +1 -0
  139. package/dist/components/ui/phone-input.d.ts +92 -0
  140. package/dist/components/ui/phone-input.js +742 -0
  141. package/dist/components/ui/phone-input.js.map +1 -0
  142. package/dist/components/ui/progress.d.ts +2 -2
  143. package/dist/components/ui/rating.d.ts +57 -0
  144. package/dist/components/ui/rating.js +648 -0
  145. package/dist/components/ui/rating.js.map +1 -0
  146. package/dist/components/ui/responsive-dialog.d.ts +35 -0
  147. package/dist/components/ui/responsive-dialog.js +193 -0
  148. package/dist/components/ui/responsive-dialog.js.map +1 -0
  149. package/dist/components/ui/scroll-spy.d.ts +42 -0
  150. package/dist/components/ui/scroll-spy.js +336 -0
  151. package/dist/components/ui/scroll-spy.js.map +1 -0
  152. package/dist/components/ui/sheet.d.ts +2 -2
  153. package/dist/components/ui/sheet.js +10 -1
  154. package/dist/components/ui/sheet.js.map +1 -1
  155. package/dist/components/ui/spinner.js +4 -1
  156. package/dist/components/ui/spinner.js.map +1 -1
  157. package/dist/components/ui/stepper-constants.d.ts +34 -0
  158. package/dist/components/ui/stepper-constants.js +6 -0
  159. package/dist/components/ui/stepper-constants.js.map +1 -0
  160. package/dist/components/ui/stepper.d.ts +81 -0
  161. package/dist/components/ui/stepper.js +902 -0
  162. package/dist/components/ui/stepper.js.map +1 -0
  163. package/dist/components/ui/tags-input.d.ts +42 -0
  164. package/dist/components/ui/tags-input.js +129 -0
  165. package/dist/components/ui/tags-input.js.map +1 -0
  166. package/dist/components/ui/time-picker-constants.d.ts +51 -0
  167. package/dist/components/ui/time-picker-constants.js +6 -0
  168. package/dist/components/ui/time-picker-constants.js.map +1 -0
  169. package/dist/components/ui/time-picker.d.ts +120 -0
  170. package/dist/components/ui/time-picker.js +1618 -0
  171. package/dist/components/ui/time-picker.js.map +1 -0
  172. package/dist/components/ui/timeline.d.ts +32 -0
  173. package/dist/components/ui/timeline.js +559 -0
  174. package/dist/components/ui/timeline.js.map +1 -0
  175. package/dist/components/ui/toggle.d.ts +1 -1
  176. package/dist/components/ui/tour-constants.d.ts +44 -0
  177. package/dist/components/ui/tour-constants.js +6 -0
  178. package/dist/components/ui/tour-constants.js.map +1 -0
  179. package/dist/components/ui/tour.d.ts +125 -0
  180. package/dist/components/ui/tour.js +1207 -0
  181. package/dist/components/ui/tour.js.map +1 -0
  182. package/dist/data-grid/data-grid-cell-variants.js +4 -4
  183. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  184. package/dist/data-grid/data-grid-cell-wrapper.js +1 -1
  185. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
  186. package/dist/data-grid/data-grid-column-header.js +1 -1
  187. package/dist/data-grid/data-grid-column-header.js.map +1 -1
  188. package/dist/data-grid/data-grid-context-menu.js +1 -1
  189. package/dist/data-grid/data-grid-context-menu.js.map +1 -1
  190. package/dist/data-grid/data-grid-filter-menu.d.ts +5 -4
  191. package/dist/data-grid/data-grid-filter-menu.js +13 -8
  192. package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
  193. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +4 -4
  194. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
  195. package/dist/data-grid/data-grid-locale.d.ts +58 -6
  196. package/dist/data-grid/data-grid-locale.js +41 -9
  197. package/dist/data-grid/data-grid-locale.js.map +1 -1
  198. package/dist/data-grid/data-grid-paste-dialog.js +1 -1
  199. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
  200. package/dist/data-grid/data-grid-row-height-menu.d.ts +4 -3
  201. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
  202. package/dist/data-grid/data-grid-row.js +1 -1
  203. package/dist/data-grid/data-grid-row.js.map +1 -1
  204. package/dist/data-grid/data-grid-search.js +1 -1
  205. package/dist/data-grid/data-grid-search.js.map +1 -1
  206. package/dist/data-grid/data-grid-sort-menu.d.ts +4 -4
  207. package/dist/data-grid/data-grid-sort-menu.js +1 -1
  208. package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
  209. package/dist/data-grid/data-grid-view-menu.d.ts +4 -4
  210. package/dist/data-grid/data-grid-view-menu.js.map +1 -1
  211. package/dist/data-grid/data-grid.d.ts +4 -3
  212. package/dist/data-grid/data-grid.js +3 -4
  213. package/dist/data-grid/data-grid.js.map +1 -1
  214. package/dist/data-grid/index.d.ts +1 -0
  215. package/dist/data-grid/index.js.map +1 -1
  216. package/dist/data-grid/messages-en.d.ts +23 -4
  217. package/dist/data-grid/messages-en.js +20 -0
  218. package/dist/data-grid/messages-en.js.map +1 -1
  219. package/dist/data-grid/messages-vi.d.ts +3 -1
  220. package/dist/data-grid/messages-vi.js +14 -0
  221. package/dist/data-grid/messages-vi.js.map +1 -1
  222. package/dist/data-grid/messages.d.ts +29 -3
  223. package/dist/data-grid/messages.js.map +1 -1
  224. package/dist/data-grid/sortable.js +17 -20
  225. package/dist/data-grid/sortable.js.map +1 -1
  226. package/dist/data-grid/types.d.ts +1 -1
  227. package/dist/data-grid/use-data-grid-undo-redo.d.ts +5 -4
  228. package/dist/data-grid/use-data-grid-undo-redo.js +4 -4
  229. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  230. package/dist/data-grid/use-data-grid.d.ts +21 -6
  231. package/dist/data-grid/use-data-grid.js +11 -9
  232. package/dist/data-grid/use-data-grid.js.map +1 -1
  233. package/dist/data-grid/utils.d.ts +24 -4
  234. package/dist/data-grid/utils.js +15 -5
  235. package/dist/data-grid/utils.js.map +1 -1
  236. package/dist/i18n/locale-provider.d.ts +109 -0
  237. package/dist/i18n/locale-provider.js +47 -0
  238. package/dist/i18n/locale-provider.js.map +1 -0
  239. package/dist/i18n/locale.d.ts +27 -0
  240. package/dist/i18n/locale.js +10 -0
  241. package/dist/i18n/locale.js.map +1 -0
  242. package/dist/i18n/messages.d.ts +116 -0
  243. package/dist/i18n/messages.js +149 -0
  244. package/dist/i18n/messages.js.map +1 -0
  245. package/dist/i18n/namespaces/action-bar.d.ts +7 -0
  246. package/dist/i18n/namespaces/action-bar.js +15 -0
  247. package/dist/i18n/namespaces/action-bar.js.map +1 -0
  248. package/dist/i18n/namespaces/avatar-group.d.ts +14 -0
  249. package/dist/i18n/namespaces/avatar-group.js +13 -0
  250. package/dist/i18n/namespaces/avatar-group.js.map +1 -0
  251. package/dist/i18n/namespaces/badge-overflow.d.ts +12 -0
  252. package/dist/i18n/namespaces/badge-overflow.js +11 -0
  253. package/dist/i18n/namespaces/badge-overflow.js.map +1 -0
  254. package/dist/i18n/namespaces/banner.d.ts +9 -0
  255. package/dist/i18n/namespaces/banner.js +7 -0
  256. package/dist/i18n/namespaces/banner.js.map +1 -0
  257. package/dist/i18n/namespaces/breadcrumb.d.ts +11 -0
  258. package/dist/i18n/namespaces/breadcrumb.js +13 -0
  259. package/dist/i18n/namespaces/breadcrumb.js.map +1 -0
  260. package/dist/i18n/namespaces/calendar.d.ts +40 -0
  261. package/dist/i18n/namespaces/calendar.js +25 -0
  262. package/dist/i18n/namespaces/calendar.js.map +1 -0
  263. package/dist/i18n/namespaces/carousel.d.ts +15 -0
  264. package/dist/i18n/namespaces/carousel.js +17 -0
  265. package/dist/i18n/namespaces/carousel.js.map +1 -0
  266. package/dist/i18n/namespaces/combobox.d.ts +21 -0
  267. package/dist/i18n/namespaces/combobox.js +23 -0
  268. package/dist/i18n/namespaces/combobox.js.map +1 -0
  269. package/dist/i18n/namespaces/command-palette.d.ts +20 -0
  270. package/dist/i18n/namespaces/command-palette.js +19 -0
  271. package/dist/i18n/namespaces/command-palette.js.map +1 -0
  272. package/dist/i18n/namespaces/command.d.ts +11 -0
  273. package/dist/i18n/namespaces/command.js +13 -0
  274. package/dist/i18n/namespaces/command.js.map +1 -0
  275. package/dist/i18n/namespaces/confirm-dialog.d.ts +13 -0
  276. package/dist/i18n/namespaces/confirm-dialog.js +15 -0
  277. package/dist/i18n/namespaces/confirm-dialog.js.map +1 -0
  278. package/dist/i18n/namespaces/contrast.d.ts +7 -0
  279. package/dist/i18n/namespaces/contrast.js +13 -0
  280. package/dist/i18n/namespaces/contrast.js.map +1 -0
  281. package/dist/i18n/namespaces/copy-button.d.ts +11 -0
  282. package/dist/i18n/namespaces/copy-button.js +13 -0
  283. package/dist/i18n/namespaces/copy-button.js.map +1 -0
  284. package/dist/i18n/namespaces/cropper.d.ts +7 -0
  285. package/dist/i18n/namespaces/cropper.js +17 -0
  286. package/dist/i18n/namespaces/cropper.js.map +1 -0
  287. package/dist/i18n/namespaces/data-table.d.ts +7 -0
  288. package/dist/i18n/namespaces/data-table.js +23 -0
  289. package/dist/i18n/namespaces/data-table.js.map +1 -0
  290. package/dist/i18n/namespaces/date-picker.d.ts +42 -0
  291. package/dist/i18n/namespaces/date-picker.js +35 -0
  292. package/dist/i18n/namespaces/date-picker.js.map +1 -0
  293. package/dist/i18n/namespaces/dialog.d.ts +9 -0
  294. package/dist/i18n/namespaces/dialog.js +7 -0
  295. package/dist/i18n/namespaces/dialog.js.map +1 -0
  296. package/dist/i18n/namespaces/display-axis.d.ts +25 -0
  297. package/dist/i18n/namespaces/display-axis.js +21 -0
  298. package/dist/i18n/namespaces/display-axis.js.map +1 -0
  299. package/dist/i18n/namespaces/file-upload.d.ts +11 -0
  300. package/dist/i18n/namespaces/file-upload.js +19 -0
  301. package/dist/i18n/namespaces/file-upload.js.map +1 -0
  302. package/dist/i18n/namespaces/filter-chips.d.ts +11 -0
  303. package/dist/i18n/namespaces/filter-chips.js +13 -0
  304. package/dist/i18n/namespaces/filter-chips.js.map +1 -0
  305. package/dist/i18n/namespaces/image-upload-field.d.ts +12 -0
  306. package/dist/i18n/namespaces/image-upload-field.js +23 -0
  307. package/dist/i18n/namespaces/image-upload-field.js.map +1 -0
  308. package/dist/i18n/namespaces/inline-edit.d.ts +15 -0
  309. package/dist/i18n/namespaces/inline-edit.js +17 -0
  310. package/dist/i18n/namespaces/inline-edit.js.map +1 -0
  311. package/dist/i18n/namespaces/kanban.d.ts +11 -0
  312. package/dist/i18n/namespaces/kanban.js +39 -0
  313. package/dist/i18n/namespaces/kanban.js.map +1 -0
  314. package/dist/i18n/namespaces/key-value.d.ts +10 -0
  315. package/dist/i18n/namespaces/key-value.js +19 -0
  316. package/dist/i18n/namespaces/key-value.js.map +1 -0
  317. package/dist/i18n/namespaces/mention.d.ts +9 -0
  318. package/dist/i18n/namespaces/mention.js +7 -0
  319. package/dist/i18n/namespaces/mention.js.map +1 -0
  320. package/dist/i18n/namespaces/pagination.d.ts +19 -0
  321. package/dist/i18n/namespaces/pagination.js +21 -0
  322. package/dist/i18n/namespaces/pagination.js.map +1 -0
  323. package/dist/i18n/namespaces/phone-input.d.ts +10 -0
  324. package/dist/i18n/namespaces/phone-input.js +17 -0
  325. package/dist/i18n/namespaces/phone-input.js.map +1 -0
  326. package/dist/i18n/namespaces/rating.d.ts +12 -0
  327. package/dist/i18n/namespaces/rating.js +11 -0
  328. package/dist/i18n/namespaces/rating.js.map +1 -0
  329. package/dist/i18n/namespaces/route-tabs.d.ts +9 -0
  330. package/dist/i18n/namespaces/route-tabs.js +7 -0
  331. package/dist/i18n/namespaces/route-tabs.js.map +1 -0
  332. package/dist/i18n/namespaces/scroll-spy.d.ts +9 -0
  333. package/dist/i18n/namespaces/scroll-spy.js +7 -0
  334. package/dist/i18n/namespaces/scroll-spy.js.map +1 -0
  335. package/dist/i18n/namespaces/search-filter-bar.d.ts +15 -0
  336. package/dist/i18n/namespaces/search-filter-bar.js +32 -0
  337. package/dist/i18n/namespaces/search-filter-bar.js.map +1 -0
  338. package/dist/i18n/namespaces/sheet.d.ts +9 -0
  339. package/dist/i18n/namespaces/sheet.js +7 -0
  340. package/dist/i18n/namespaces/sheet.js.map +1 -0
  341. package/dist/i18n/namespaces/shell.d.ts +17 -0
  342. package/dist/i18n/namespaces/shell.js +75 -0
  343. package/dist/i18n/namespaces/shell.js.map +1 -0
  344. package/dist/i18n/namespaces/spinner.d.ts +9 -0
  345. package/dist/i18n/namespaces/spinner.js +7 -0
  346. package/dist/i18n/namespaces/spinner.js.map +1 -0
  347. package/dist/i18n/namespaces/stepper.d.ts +7 -0
  348. package/dist/i18n/namespaces/stepper.js +15 -0
  349. package/dist/i18n/namespaces/stepper.js.map +1 -0
  350. package/dist/i18n/namespaces/table-pagination.d.ts +7 -0
  351. package/dist/i18n/namespaces/table-pagination.js +29 -0
  352. package/dist/i18n/namespaces/table-pagination.js.map +1 -0
  353. package/dist/i18n/namespaces/tags-input.d.ts +12 -0
  354. package/dist/i18n/namespaces/tags-input.js +11 -0
  355. package/dist/i18n/namespaces/tags-input.js.map +1 -0
  356. package/dist/i18n/namespaces/time-picker.d.ts +15 -0
  357. package/dist/i18n/namespaces/time-picker.js +25 -0
  358. package/dist/i18n/namespaces/time-picker.js.map +1 -0
  359. package/dist/i18n/namespaces/toast.d.ts +15 -0
  360. package/dist/i18n/namespaces/toast.js +7 -0
  361. package/dist/i18n/namespaces/toast.js.map +1 -0
  362. package/dist/i18n/namespaces/tour.d.ts +7 -0
  363. package/dist/i18n/namespaces/tour.js +21 -0
  364. package/dist/i18n/namespaces/tour.js.map +1 -0
  365. package/dist/index.d.ts +63 -0
  366. package/dist/index.js +66 -1
  367. package/dist/index.js.map +1 -1
  368. package/dist/lib/compose-refs.js.map +1 -0
  369. package/dist/lib/use-as-ref.js.map +1 -0
  370. package/dist/lib/use-is-mobile.d.ts +10 -0
  371. package/dist/lib/use-is-mobile.js +21 -0
  372. package/dist/lib/use-is-mobile.js.map +1 -0
  373. package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
  374. package/dist/lib/use-lazy-ref.js.map +1 -0
  375. package/dist/lib/visually-hidden-input.d.ts +12 -0
  376. package/dist/lib/visually-hidden-input.js +105 -0
  377. package/dist/lib/visually-hidden-input.js.map +1 -0
  378. package/dist/shell/app-header.js.map +1 -1
  379. package/dist/shell/app-shell.d.ts +20 -2
  380. package/dist/shell/app-shell.js +4 -1
  381. package/dist/shell/app-shell.js.map +1 -1
  382. package/dist/shell/contrast-constants.d.ts +9 -1
  383. package/dist/shell/contrast-constants.js +2 -4
  384. package/dist/shell/contrast-constants.js.map +1 -1
  385. package/dist/shell/contrast-toggle.d.ts +4 -1
  386. package/dist/shell/contrast-toggle.js +2 -2
  387. package/dist/shell/contrast-toggle.js.map +1 -1
  388. package/dist/shell/display-axis-constants.d.ts +2 -1
  389. package/dist/shell/display-axis-constants.js.map +1 -1
  390. package/dist/shell/display-axis-controls.d.ts +15 -6
  391. package/dist/shell/display-axis-controls.js +11 -9
  392. package/dist/shell/display-axis-controls.js.map +1 -1
  393. package/dist/shell/mobile-menu.js.map +1 -1
  394. package/dist/shell/notifications-button.d.ts +1 -1
  395. package/dist/shell/notifications-button.js +3 -2
  396. package/dist/shell/notifications-button.js.map +1 -1
  397. package/dist/shell/shell-context.d.ts +4 -1
  398. package/dist/shell/shell-context.js +2 -4
  399. package/dist/shell/shell-context.js.map +1 -1
  400. package/dist/shell/sidebar.d.ts +1 -1
  401. package/dist/shell/sidebar.js.map +1 -1
  402. package/dist/shell/theme-toggle.d.ts +6 -3
  403. package/dist/shell/theme-toggle.js +2 -2
  404. package/dist/shell/theme-toggle.js.map +1 -1
  405. package/dist/shell/types.d.ts +14 -8
  406. package/dist/shell/types.js +2 -35
  407. package/dist/shell/types.js.map +1 -1
  408. package/dist/shell/user-menu.d.ts +6 -6
  409. package/dist/shell/user-menu.js.map +1 -1
  410. package/dist/shell/utils.d.ts +1 -1
  411. package/dist/shell/utils.js.map +1 -1
  412. package/dist/shell/workspace-switcher.js +1 -1
  413. package/dist/shell/workspace-switcher.js.map +1 -1
  414. package/dist/uploader/file-upload-constants.d.ts +13 -0
  415. package/dist/uploader/file-upload-constants.js +4 -16
  416. package/dist/uploader/file-upload-constants.js.map +1 -1
  417. package/dist/uploader/file-upload.d.ts +4 -1
  418. package/dist/uploader/file-upload.js +2 -2
  419. package/dist/uploader/file-upload.js.map +1 -1
  420. package/dist/uploader/image-upload-field.d.ts +5 -1
  421. package/dist/uploader/image-upload-field.js +2 -2
  422. package/dist/uploader/image-upload-field.js.map +1 -1
  423. package/dist/uploader/use-file-upload.d.ts +4 -0
  424. package/dist/uploader/use-file-upload.js +6 -2
  425. package/dist/uploader/use-file-upload.js.map +1 -1
  426. package/package.json +8 -3
  427. package/styles.css +19 -0
  428. package/dist/data-grid/compose-refs.js.map +0 -1
  429. package/dist/data-grid/use-as-ref.js.map +0 -1
  430. package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
  431. package/dist/data-grid/use-lazy-ref.js.map +0 -1
  432. /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
  433. /package/dist/{data-grid → lib}/compose-refs.js +0 -0
  434. /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
  435. /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
  436. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
  437. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
  438. /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
  439. /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
@@ -0,0 +1,1618 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { Slot } from "@radix-ui/react-slot";
4
+ import { Clock } from "lucide-react";
5
+ import * as React from "react";
6
+ import { useComitorLabels, useComitorLocale } from "../../i18n/locale-provider.js";
7
+ import { cn } from "../../lib/cn.js";
8
+ import { useComposedRefs } from "../../lib/compose-refs.js";
9
+ import { useAsRef } from "../../lib/use-as-ref.js";
10
+ import { useIsomorphicLayoutEffect } from "../../lib/use-isomorphic-layout-effect.js";
11
+ import { useLazyRef } from "../../lib/use-lazy-ref.js";
12
+ import { VisuallyHiddenInput } from "../../lib/visually-hidden-input.js";
13
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./popover.js";
14
+ const ROOT_NAME = "TimePicker";
15
+ const LABEL_NAME = "TimePickerLabel";
16
+ const INPUT_GROUP_NAME = "TimePickerInputGroup";
17
+ const INPUT_NAME = "TimePickerInput";
18
+ const TRIGGER_NAME = "TimePickerTrigger";
19
+ const CONTENT_NAME = "TimePickerContent";
20
+ const COLUMN_NAME = "TimePickerColumn";
21
+ const COLUMN_ITEM_NAME = "TimePickerColumnItem";
22
+ const HOUR_NAME = "TimePickerHour";
23
+ const MINUTE_NAME = "TimePickerMinute";
24
+ const SECOND_NAME = "TimePickerSecond";
25
+ const PERIOD_NAME = "TimePickerPeriod";
26
+ const CLEAR_NAME = "TimePickerClear";
27
+ const DEFAULT_STEP = 1;
28
+ const DEFAULT_SEGMENT_PLACEHOLDER = "--";
29
+ const SEGMENTS = ["hour", "minute", "second", "period"];
30
+ const PERIODS = ["AM", "PM"];
31
+ function focusFirst(candidates, preventScroll = false) {
32
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
33
+ for (const candidateRef of candidates) {
34
+ const candidate = candidateRef.current;
35
+ if (!candidate) continue;
36
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
37
+ candidate.focus({ preventScroll });
38
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
39
+ }
40
+ }
41
+ function sortNodes(items) {
42
+ return items.sort((a, b) => {
43
+ const elementA = a.ref.current;
44
+ const elementB = b.ref.current;
45
+ if (!elementA || !elementB) return 0;
46
+ const position = elementA.compareDocumentPosition(elementB);
47
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
48
+ return -1;
49
+ }
50
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
51
+ return 1;
52
+ }
53
+ return 0;
54
+ });
55
+ }
56
+ function getIs12Hour(locale) {
57
+ const testDate = new Date(2e3, 0, 1, 13, 0, 0);
58
+ const formatted = new Intl.DateTimeFormat(locale, {
59
+ hour: "numeric"
60
+ }).format(testDate);
61
+ return /am|pm/i.test(formatted) || !formatted.includes("13");
62
+ }
63
+ function parseTimeString(timeString) {
64
+ if (!timeString) return null;
65
+ const parts = timeString.split(":");
66
+ if (parts.length < 2) return null;
67
+ const result = {};
68
+ if (parts[0] && parts[0] !== DEFAULT_SEGMENT_PLACEHOLDER) {
69
+ const hour = Number.parseInt(parts[0], 10);
70
+ if (!Number.isNaN(hour) && hour >= 0 && hour <= 23) {
71
+ result.hour = hour;
72
+ }
73
+ }
74
+ if (parts[1] && parts[1] !== DEFAULT_SEGMENT_PLACEHOLDER) {
75
+ const minute = Number.parseInt(parts[1], 10);
76
+ if (!Number.isNaN(minute) && minute >= 0 && minute <= 59) {
77
+ result.minute = minute;
78
+ }
79
+ }
80
+ if (parts[2] && parts[2] !== DEFAULT_SEGMENT_PLACEHOLDER) {
81
+ const second = Number.parseInt(parts[2], 10);
82
+ if (!Number.isNaN(second) && second >= 0 && second <= 59) {
83
+ result.second = second;
84
+ }
85
+ }
86
+ if (result.hour === void 0 && result.minute === void 0 && result.second === void 0) {
87
+ return null;
88
+ }
89
+ return result;
90
+ }
91
+ function formatTimeValue(value, showSeconds) {
92
+ const hourStr = value.hour !== void 0 ? value.hour.toString().padStart(2, "0") : DEFAULT_SEGMENT_PLACEHOLDER;
93
+ const minuteStr = value.minute !== void 0 ? value.minute.toString().padStart(2, "0") : DEFAULT_SEGMENT_PLACEHOLDER;
94
+ const secondStr = value.second !== void 0 ? value.second.toString().padStart(2, "0") : DEFAULT_SEGMENT_PLACEHOLDER;
95
+ if (showSeconds) {
96
+ return `${hourStr}:${minuteStr}:${secondStr}`;
97
+ }
98
+ return `${hourStr}:${minuteStr}`;
99
+ }
100
+ function to12Hour(hour24) {
101
+ const period = hour24 >= 12 ? "PM" : "AM";
102
+ const hour = hour24 % 12 || 12;
103
+ return { hour, period };
104
+ }
105
+ function to24Hour(hour12, period) {
106
+ if (hour12 === 12) {
107
+ return period === "PM" ? 12 : 0;
108
+ }
109
+ return period === "PM" ? hour12 + 12 : hour12;
110
+ }
111
+ function clamp(value, min, max) {
112
+ return Math.min(Math.max(value, min), max);
113
+ }
114
+ function getPeriodLabel(value, labels) {
115
+ if (value === "AM") return labels.am;
116
+ if (value === "PM") return labels.pm;
117
+ return value;
118
+ }
119
+ function getPeriodFromKey(key, labels) {
120
+ const lowerKey = key.toLowerCase();
121
+ if (lowerKey === "a" || lowerKey === "1") return "AM";
122
+ if (lowerKey === "p" || lowerKey === "2") return "PM";
123
+ if (lowerKey.length !== 1) return null;
124
+ if (lowerKey === labels.am.charAt(0).toLowerCase()) return "AM";
125
+ if (lowerKey === labels.pm.charAt(0).toLowerCase()) return "PM";
126
+ return null;
127
+ }
128
+ const StoreContext = React.createContext(null);
129
+ function useStoreContext(consumerName) {
130
+ const context = React.useContext(StoreContext);
131
+ if (!context) {
132
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ROOT_NAME}\``);
133
+ }
134
+ return context;
135
+ }
136
+ function useStore(selector, ogStore) {
137
+ const contextStore = React.useContext(StoreContext);
138
+ const store = ogStore ?? contextStore;
139
+ if (!store) {
140
+ throw new Error(`\`useTimePicker\` ph\u1EA3i \u0111\u01B0\u1EE3c g\u1ECDi b\xEAn trong \`${ROOT_NAME}\``);
141
+ }
142
+ const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);
143
+ return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
144
+ }
145
+ const TimePickerContext = React.createContext(null);
146
+ function useTimePickerContext(consumerName) {
147
+ const context = React.useContext(TimePickerContext);
148
+ if (!context) {
149
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${ROOT_NAME}\``);
150
+ }
151
+ return context;
152
+ }
153
+ function TimePicker(props) {
154
+ const {
155
+ value: valueProp,
156
+ defaultValue,
157
+ onValueChange,
158
+ open,
159
+ defaultOpen,
160
+ onOpenChange,
161
+ openOnFocus = false,
162
+ inputGroupClickAction = "focus",
163
+ min,
164
+ max,
165
+ hourStep = DEFAULT_STEP,
166
+ minuteStep = DEFAULT_STEP,
167
+ secondStep = DEFAULT_STEP,
168
+ segmentPlaceholder = DEFAULT_SEGMENT_PLACEHOLDER,
169
+ locale: localeProp,
170
+ labels: labelsProp,
171
+ name,
172
+ asChild,
173
+ disabled = false,
174
+ invalid = false,
175
+ readOnly = false,
176
+ required = false,
177
+ showSeconds = false,
178
+ className,
179
+ children,
180
+ id,
181
+ ...rootProps
182
+ } = props;
183
+ const { locale: providerLocale } = useComitorLocale();
184
+ const locale = localeProp ?? providerLocale;
185
+ const instanceId = React.useId();
186
+ const rootId = id ?? instanceId;
187
+ const inputGroupId = React.useId();
188
+ const labelId = React.useId();
189
+ const triggerId = React.useId();
190
+ const inputGroupRef = React.useRef(null);
191
+ const triggerRef = React.useRef(null);
192
+ const [inputGroup, setInputGroup] = React.useState(null);
193
+ const isFormControl = inputGroup ? !!inputGroup.closest("form") : true;
194
+ const listenersRef = useLazyRef(() => /* @__PURE__ */ new Set());
195
+ const stateRef = useLazyRef(() => ({
196
+ value: valueProp ?? defaultValue ?? "",
197
+ open: open ?? defaultOpen ?? false,
198
+ openedViaFocus: false
199
+ }));
200
+ const propsRef = useAsRef({ onValueChange, onOpenChange });
201
+ const store = React.useMemo(() => {
202
+ return {
203
+ subscribe: (cb) => {
204
+ listenersRef.current.add(cb);
205
+ return () => listenersRef.current.delete(cb);
206
+ },
207
+ getState: () => stateRef.current,
208
+ setState: (key, value2) => {
209
+ if (Object.is(stateRef.current[key], value2)) return;
210
+ if (key === "value" && typeof value2 === "string") {
211
+ stateRef.current.value = value2;
212
+ propsRef.current.onValueChange?.(value2);
213
+ } else if (key === "open" && typeof value2 === "boolean") {
214
+ stateRef.current.open = value2;
215
+ propsRef.current.onOpenChange?.(value2);
216
+ if (!value2) {
217
+ stateRef.current.openedViaFocus = false;
218
+ }
219
+ } else {
220
+ stateRef.current[key] = value2;
221
+ }
222
+ store.notify();
223
+ },
224
+ notify: () => {
225
+ for (const cb of listenersRef.current) {
226
+ cb();
227
+ }
228
+ }
229
+ };
230
+ }, [listenersRef, stateRef, propsRef]);
231
+ const value = useStore((state) => state.value, store);
232
+ useIsomorphicLayoutEffect(() => {
233
+ if (valueProp !== void 0) {
234
+ store.setState("value", valueProp);
235
+ }
236
+ }, [valueProp]);
237
+ useIsomorphicLayoutEffect(() => {
238
+ if (open !== void 0) {
239
+ store.setState("open", open);
240
+ }
241
+ }, [open]);
242
+ const storeOpen = useStore((state) => state.open, store);
243
+ const onPopoverOpenChange = React.useCallback((newOpen) => store.setState("open", newOpen), [store]);
244
+ const is12Hour = React.useMemo(() => getIs12Hour(locale), [locale]);
245
+ const normalizedPlaceholder = React.useMemo(() => {
246
+ if (typeof segmentPlaceholder === "string") {
247
+ return {
248
+ hour: segmentPlaceholder,
249
+ minute: segmentPlaceholder,
250
+ second: segmentPlaceholder,
251
+ period: segmentPlaceholder
252
+ };
253
+ }
254
+ return {
255
+ hour: segmentPlaceholder.hour ?? DEFAULT_SEGMENT_PLACEHOLDER,
256
+ minute: segmentPlaceholder.minute ?? DEFAULT_SEGMENT_PLACEHOLDER,
257
+ second: segmentPlaceholder.second ?? DEFAULT_SEGMENT_PLACEHOLDER,
258
+ period: segmentPlaceholder.period ?? DEFAULT_SEGMENT_PLACEHOLDER
259
+ };
260
+ }, [segmentPlaceholder]);
261
+ const labels = useComitorLabels("timePicker", labelsProp);
262
+ const rootContext = React.useMemo(
263
+ () => ({
264
+ id: rootId,
265
+ inputGroupId,
266
+ labelId,
267
+ triggerId,
268
+ inputGroupRef,
269
+ triggerRef,
270
+ openOnFocus,
271
+ inputGroupClickAction,
272
+ onInputGroupChange: setInputGroup,
273
+ disabled,
274
+ readOnly,
275
+ required,
276
+ invalid,
277
+ showSeconds,
278
+ is12Hour,
279
+ minuteStep,
280
+ secondStep,
281
+ hourStep,
282
+ segmentPlaceholder: normalizedPlaceholder,
283
+ labels,
284
+ min,
285
+ max
286
+ }),
287
+ [
288
+ rootId,
289
+ inputGroupId,
290
+ labelId,
291
+ triggerId,
292
+ openOnFocus,
293
+ inputGroupClickAction,
294
+ disabled,
295
+ readOnly,
296
+ required,
297
+ invalid,
298
+ showSeconds,
299
+ is12Hour,
300
+ minuteStep,
301
+ secondStep,
302
+ hourStep,
303
+ normalizedPlaceholder,
304
+ labels,
305
+ min,
306
+ max
307
+ ]
308
+ );
309
+ const RootPrimitive = asChild ? Slot : "div";
310
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
311
+ /* @__PURE__ */ jsx(StoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx(TimePickerContext.Provider, { value: rootContext, children: /* @__PURE__ */ jsx(Popover, { open: storeOpen, onOpenChange: onPopoverOpenChange, children: /* @__PURE__ */ jsx(
312
+ RootPrimitive,
313
+ {
314
+ "data-slot": "time-picker",
315
+ "data-disabled": disabled ? "" : void 0,
316
+ "data-invalid": invalid ? "" : void 0,
317
+ ...rootProps,
318
+ className: cn("relative", className),
319
+ children
320
+ }
321
+ ) }) }) }),
322
+ isFormControl && /* @__PURE__ */ jsx(
323
+ VisuallyHiddenInput,
324
+ {
325
+ type: "hidden",
326
+ control: inputGroup,
327
+ name,
328
+ value,
329
+ disabled,
330
+ readOnly,
331
+ required
332
+ }
333
+ )
334
+ ] });
335
+ }
336
+ function TimePickerLabel(props) {
337
+ const { asChild, className, ...labelProps } = props;
338
+ const { labelId } = useTimePickerContext(LABEL_NAME);
339
+ const LabelPrimitive = asChild ? Slot : "label";
340
+ return /* @__PURE__ */ jsx(
341
+ LabelPrimitive,
342
+ {
343
+ "data-slot": "time-picker-label",
344
+ ...labelProps,
345
+ id: labelId,
346
+ className: cn(
347
+ "text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
348
+ className
349
+ )
350
+ }
351
+ );
352
+ }
353
+ const TimePickerInputGroupContext = React.createContext(null);
354
+ function useTimePickerInputGroupContext(consumerName) {
355
+ const context = React.useContext(TimePickerInputGroupContext);
356
+ if (!context) {
357
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${INPUT_GROUP_NAME}\``);
358
+ }
359
+ return context;
360
+ }
361
+ function TimePickerInputGroup(props) {
362
+ const {
363
+ onPointerDown: onPointerDownProp,
364
+ onClick: onClickProp,
365
+ asChild,
366
+ className,
367
+ style,
368
+ ref,
369
+ ...inputGroupProps
370
+ } = props;
371
+ const {
372
+ inputGroupId,
373
+ labelId,
374
+ onInputGroupChange,
375
+ disabled,
376
+ readOnly,
377
+ invalid,
378
+ segmentPlaceholder,
379
+ labels,
380
+ inputGroupRef,
381
+ triggerRef,
382
+ inputGroupClickAction
383
+ } = useTimePickerContext(INPUT_GROUP_NAME);
384
+ const store = useStoreContext(INPUT_GROUP_NAME);
385
+ const composedRef = useComposedRefs(ref, inputGroupRef, onInputGroupChange);
386
+ const inputRefsMap = React.useRef(/* @__PURE__ */ new Map());
387
+ const onInputRegister = React.useCallback((segment, ref2) => {
388
+ inputRefsMap.current.set(segment, ref2);
389
+ }, []);
390
+ const onInputUnregister = React.useCallback((segment) => {
391
+ inputRefsMap.current.delete(segment);
392
+ }, []);
393
+ const getNextInput = React.useCallback((currentSegment) => {
394
+ const segmentOrder = ["hour", "minute", "second", "period"];
395
+ const currentIndex = segmentOrder.indexOf(currentSegment);
396
+ if (currentIndex === -1 || currentIndex === segmentOrder.length - 1) {
397
+ return null;
398
+ }
399
+ for (let i = currentIndex + 1; i < segmentOrder.length; i++) {
400
+ const nextSegment = segmentOrder[i];
401
+ if (nextSegment) {
402
+ const nextRef = inputRefsMap.current.get(nextSegment);
403
+ if (nextRef?.current) {
404
+ return nextRef;
405
+ }
406
+ }
407
+ }
408
+ return null;
409
+ }, []);
410
+ const onPointerDown = React.useCallback(
411
+ (event) => {
412
+ onPointerDownProp?.(event);
413
+ if (disabled || readOnly || event.defaultPrevented) return;
414
+ const target = event.target;
415
+ if (target.tagName === "INPUT" || target.closest("input")) {
416
+ return;
417
+ }
418
+ if (triggerRef.current?.contains(target)) {
419
+ return;
420
+ }
421
+ event.preventDefault();
422
+ },
423
+ [onPointerDownProp, disabled, readOnly, triggerRef]
424
+ );
425
+ const onClick = React.useCallback(
426
+ (event) => {
427
+ onClickProp?.(event);
428
+ if (disabled || readOnly || event.defaultPrevented) return;
429
+ const target = event.target;
430
+ if (target.tagName === "INPUT" || target.closest("input")) {
431
+ return;
432
+ }
433
+ if (triggerRef.current?.contains(target)) {
434
+ return;
435
+ }
436
+ if (inputGroupClickAction === "open") {
437
+ store.setState("open", true);
438
+ } else {
439
+ const activeElement = document.activeElement;
440
+ const isInputAlreadyFocused = activeElement && activeElement.tagName === "INPUT" && inputGroupRef.current?.contains(activeElement);
441
+ if (!isInputAlreadyFocused) {
442
+ for (const segment of SEGMENTS) {
443
+ const inputRef = inputRefsMap.current.get(segment);
444
+ if (inputRef?.current) {
445
+ inputRef.current.focus();
446
+ inputRef.current.select();
447
+ break;
448
+ }
449
+ }
450
+ }
451
+ }
452
+ },
453
+ [onClickProp, disabled, readOnly, inputGroupClickAction, store, triggerRef, inputGroupRef]
454
+ );
455
+ const inputGroupContextValue = React.useMemo(
456
+ () => ({
457
+ onInputRegister,
458
+ onInputUnregister,
459
+ getNextInput
460
+ }),
461
+ [onInputRegister, onInputUnregister, getNextInput]
462
+ );
463
+ const InputGroupPrimitive = asChild ? Slot : "div";
464
+ return /* @__PURE__ */ jsx(TimePickerInputGroupContext.Provider, { value: inputGroupContextValue, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx(
465
+ InputGroupPrimitive,
466
+ {
467
+ role: "group",
468
+ id: inputGroupId,
469
+ "aria-labelledby": labelId,
470
+ "data-slot": "time-picker-input-group",
471
+ "data-disabled": disabled ? "" : void 0,
472
+ "data-invalid": invalid ? "" : void 0,
473
+ ...inputGroupProps,
474
+ className: cn(
475
+ /*
476
+ * ⚠ Khác thượng nguồn: khung ô đi theo `Input` của gói (ô ngày của `DatePicker` là
477
+ * một `Input`, và hai thứ này hay đứng chung một hàng biểu mẫu): `h-9` thay `h-10`,
478
+ * bỏ `shadow-xs`, hover đậm viền `--control-edge-strong`, focus viền
479
+ * `--primary-ink` + quầng `ring-2 ring-ring/50` KHÔNG offset (quy ước họ ô nhập —
480
+ * xem `InputGroup`), lỗi viền `--destructive-ink`. Số đo ở chú thích đầu `input.tsx`.
481
+ */
482
+ "flex h-9 w-full cursor-text items-center gap-0.5 rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground transition-colors duration-150 ease-out outline-none hover:border-control-edge-strong",
483
+ "has-[input:focus]:border-primary-ink has-[input:focus]:ring-2 has-[input:focus]:ring-ring/50",
484
+ invalid && "border-destructive-ink hover:border-destructive-ink has-[input:focus]:border-destructive-ink has-[input:focus]:ring-destructive/30",
485
+ /*
486
+ * ⚠ Khác thượng nguồn: bản gốc ghép `disabled && "cursor-not-allowed opacity-50"` —
487
+ * lớp gắn VÔ ĐIỀU KIỆN vào chuỗi, nên `pnpm lint:a11y` đo viền/chữ lúc NGHỈ đã nhân
488
+ * 50% (viền 1,75:1, chữ 3,49:1 ✗). Neo vào `:disabled` gốc của các ô con (cổng miễn
489
+ * trừ đúng nhánh này — WCAG 1.4.3/1.4.11 không đòi tương phản cho control vô hiệu);
490
+ * `disabled` ở gốc khoá MỌI ô nên hai cách cho cùng kết quả. Thêm `bg-muted` theo `Input`.
491
+ */
492
+ "has-[input:disabled]:cursor-not-allowed has-[input:disabled]:bg-muted has-[input:disabled]:opacity-50",
493
+ className
494
+ ),
495
+ style: {
496
+ "--time-picker-hour-input-width": `${segmentPlaceholder.hour.length}ch`,
497
+ "--time-picker-minute-input-width": `${segmentPlaceholder.minute.length}ch`,
498
+ "--time-picker-second-input-width": `${segmentPlaceholder.second.length}ch`,
499
+ // ⚠ Khác thượng nguồn: tính cả độ dài nhãn buổi (`labels.am`/`labels.pm`) — bản gốc
500
+ // chỉ có "AM"/"PM" 2 ký tự nên ghim sàn 2; nhãn do app đặt có thể dài hơn.
501
+ "--time-picker-period-input-width": `${Math.max(segmentPlaceholder.period.length, labels.am.length, labels.pm.length, 2) + 0.5}ch`,
502
+ ...style
503
+ },
504
+ ref: composedRef,
505
+ onPointerDown,
506
+ onClick
507
+ }
508
+ ) }) });
509
+ }
510
+ function TimePickerInput(props) {
511
+ const {
512
+ segment,
513
+ onBlur: onBlurProp,
514
+ onChange: onChangeProp,
515
+ onClick: onClickProp,
516
+ onFocus: onFocusProp,
517
+ onKeyDown: onKeyDownProp,
518
+ disabled: disabledProp,
519
+ readOnly: readOnlyProp,
520
+ className,
521
+ style,
522
+ ref,
523
+ ...inputProps
524
+ } = props;
525
+ const { is12Hour, showSeconds, disabled, readOnly, invalid, segmentPlaceholder, labels, openOnFocus } = useTimePickerContext(INPUT_NAME);
526
+ const store = useStoreContext(INPUT_NAME);
527
+ const inputGroupContext = useTimePickerInputGroupContext(INPUT_NAME);
528
+ const isDisabled = disabledProp || disabled;
529
+ const isReadOnly = readOnlyProp || readOnly;
530
+ const value = useStore((state) => state.value);
531
+ const timeValue = parseTimeString(value);
532
+ const inputRef = React.useRef(null);
533
+ const composedRef = useComposedRefs(ref, inputRef);
534
+ useIsomorphicLayoutEffect(() => {
535
+ if (segment) {
536
+ inputGroupContext.onInputRegister(segment, inputRef);
537
+ return () => inputGroupContext.onInputUnregister(segment);
538
+ }
539
+ }, [inputGroupContext, segment]);
540
+ const getSegmentValue = React.useCallback(() => {
541
+ if (!timeValue) {
542
+ if (!segment) return "";
543
+ return segmentPlaceholder[segment];
544
+ }
545
+ switch (segment) {
546
+ case "hour": {
547
+ if (timeValue.hour === void 0) return segmentPlaceholder.hour;
548
+ if (is12Hour) {
549
+ return to12Hour(timeValue.hour).hour.toString().padStart(2, "0");
550
+ }
551
+ return timeValue.hour.toString().padStart(2, "0");
552
+ }
553
+ case "minute":
554
+ if (timeValue.minute === void 0) return segmentPlaceholder.minute;
555
+ return timeValue.minute.toString().padStart(2, "0");
556
+ case "second":
557
+ if (timeValue.second === void 0) return segmentPlaceholder.second;
558
+ return timeValue.second.toString().padStart(2, "0");
559
+ case "period": {
560
+ if (!timeValue || timeValue.hour === void 0) return segmentPlaceholder.period;
561
+ return to12Hour(timeValue.hour).period;
562
+ }
563
+ default:
564
+ return "";
565
+ }
566
+ }, [timeValue, segment, is12Hour, segmentPlaceholder]);
567
+ const [editValue, setEditValue] = React.useState(getSegmentValue());
568
+ const [isEditing, setIsEditing] = React.useState(false);
569
+ const [pendingDigit, setPendingDigit] = React.useState(null);
570
+ React.useEffect(() => {
571
+ if (!isEditing) {
572
+ setEditValue(getSegmentValue());
573
+ setPendingDigit(null);
574
+ }
575
+ }, [getSegmentValue, isEditing]);
576
+ const updateTimeValue = React.useCallback(
577
+ (newSegmentValue, shouldCreateIfEmpty = false) => {
578
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
579
+ if (!newSegmentValue || newSegmentValue === placeholder) return;
580
+ if (!timeValue && !shouldCreateIfEmpty) return;
581
+ const currentTime = timeValue ?? {};
582
+ const newTime = { ...currentTime };
583
+ switch (segment) {
584
+ case "hour": {
585
+ const displayHour = Number.parseInt(newSegmentValue, 10);
586
+ if (!Number.isNaN(displayHour)) {
587
+ if (is12Hour) {
588
+ const clampedHour = clamp(displayHour, 1, 12);
589
+ let currentPeriod;
590
+ if (timeValue?.period !== void 0) {
591
+ currentPeriod = timeValue.period;
592
+ } else if (timeValue?.hour !== void 0) {
593
+ currentPeriod = to12Hour(timeValue.hour).period;
594
+ } else {
595
+ const now = /* @__PURE__ */ new Date();
596
+ currentPeriod = to12Hour(now.getHours()).period;
597
+ }
598
+ const hour24 = to24Hour(clampedHour, currentPeriod);
599
+ newTime.hour = hour24;
600
+ if (timeValue?.period !== void 0) {
601
+ newTime.period = timeValue.period;
602
+ }
603
+ } else {
604
+ newTime.hour = clamp(displayHour, 0, 23);
605
+ }
606
+ }
607
+ break;
608
+ }
609
+ case "minute": {
610
+ const minute = Number.parseInt(newSegmentValue, 10);
611
+ if (!Number.isNaN(minute)) {
612
+ newTime.minute = clamp(minute, 0, 59);
613
+ }
614
+ break;
615
+ }
616
+ case "second": {
617
+ const second = Number.parseInt(newSegmentValue, 10);
618
+ if (!Number.isNaN(second)) {
619
+ newTime.second = clamp(second, 0, 59);
620
+ }
621
+ break;
622
+ }
623
+ case "period": {
624
+ if (newSegmentValue === "AM" || newSegmentValue === "PM") {
625
+ newTime.period = newSegmentValue;
626
+ if (timeValue && timeValue.hour !== void 0) {
627
+ const currentDisplay = to12Hour(timeValue.hour);
628
+ newTime.hour = to24Hour(currentDisplay.hour, newSegmentValue);
629
+ }
630
+ }
631
+ break;
632
+ }
633
+ }
634
+ const newValue = formatTimeValue(newTime, showSeconds);
635
+ store.setState("value", newValue);
636
+ },
637
+ [timeValue, segment, is12Hour, showSeconds, store, segmentPlaceholder]
638
+ );
639
+ const onBlur = React.useCallback(
640
+ (event) => {
641
+ onBlurProp?.(event);
642
+ if (event.defaultPrevented) return;
643
+ setIsEditing(false);
644
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
645
+ if (editValue && editValue !== placeholder && editValue.length > 0) {
646
+ let valueToUpdate = editValue;
647
+ if (segment !== "period") {
648
+ if (editValue.length === 2) {
649
+ valueToUpdate = editValue;
650
+ } else if (editValue.length === 1) {
651
+ const numValue = Number.parseInt(editValue, 10);
652
+ if (!Number.isNaN(numValue)) {
653
+ valueToUpdate = numValue.toString().padStart(2, "0");
654
+ }
655
+ }
656
+ }
657
+ updateTimeValue(valueToUpdate, true);
658
+ queueMicrotask(() => {
659
+ const currentTimeValue = parseTimeString(store.getState().value);
660
+ if (currentTimeValue) {
661
+ const now = /* @__PURE__ */ new Date();
662
+ const newTime = { ...currentTimeValue };
663
+ let needsUpdate = false;
664
+ if (newTime.hour === void 0) {
665
+ newTime.hour = now.getHours();
666
+ needsUpdate = true;
667
+ }
668
+ if (newTime.minute === void 0) {
669
+ newTime.minute = now.getMinutes();
670
+ needsUpdate = true;
671
+ }
672
+ if (showSeconds && newTime.second === void 0) {
673
+ newTime.second = now.getSeconds();
674
+ needsUpdate = true;
675
+ }
676
+ if (needsUpdate) {
677
+ const newValue = formatTimeValue(newTime, showSeconds);
678
+ store.setState("value", newValue);
679
+ }
680
+ }
681
+ });
682
+ }
683
+ setEditValue(getSegmentValue());
684
+ setPendingDigit(null);
685
+ },
686
+ [onBlurProp, editValue, updateTimeValue, getSegmentValue, segment, segmentPlaceholder, showSeconds, store]
687
+ );
688
+ const onChange = React.useCallback(
689
+ (event) => {
690
+ onChangeProp?.(event);
691
+ if (event.defaultPrevented) return;
692
+ let newValue = event.target.value;
693
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
694
+ if (editValue === placeholder && newValue.length > 0 && newValue !== placeholder) {
695
+ newValue = newValue.replace(new RegExp(`^${placeholder}`), "");
696
+ }
697
+ if (segment === "period") {
698
+ const newPeriod = getPeriodFromKey(newValue.charAt(0), labels);
699
+ if (newPeriod) {
700
+ setEditValue(newPeriod);
701
+ updateTimeValue(newPeriod, true);
702
+ queueMicrotask(() => {
703
+ inputRef.current?.select();
704
+ });
705
+ }
706
+ return;
707
+ }
708
+ if (segment === "hour" || segment === "minute" || segment === "second") {
709
+ newValue = newValue.replace(/\D/g, "");
710
+ }
711
+ if (newValue.length > 2) {
712
+ newValue = newValue.slice(0, 2);
713
+ }
714
+ if (segment === "hour" || segment === "minute" || segment === "second") {
715
+ const numValue = Number.parseInt(newValue, 10);
716
+ if (!Number.isNaN(numValue) && newValue.length > 0) {
717
+ if (pendingDigit !== null && newValue.length === 1) {
718
+ const twoDigitValue = pendingDigit + newValue;
719
+ const combinedNum = Number.parseInt(twoDigitValue, 10);
720
+ if (!Number.isNaN(combinedNum)) {
721
+ const paddedValue = combinedNum.toString().padStart(2, "0");
722
+ setEditValue(paddedValue);
723
+ updateTimeValue(paddedValue, true);
724
+ setPendingDigit(null);
725
+ queueMicrotask(() => {
726
+ if (segment) {
727
+ const nextInputRef = inputGroupContext.getNextInput(segment);
728
+ if (nextInputRef?.current) {
729
+ nextInputRef.current.focus();
730
+ nextInputRef.current.select();
731
+ }
732
+ }
733
+ });
734
+ return;
735
+ }
736
+ }
737
+ const maxFirstDigit = segment === "hour" ? is12Hour ? 1 : 2 : 5;
738
+ const firstDigit = Number.parseInt(newValue[0] ?? "0", 10);
739
+ const shouldAutoAdvance = firstDigit > maxFirstDigit;
740
+ if (newValue.length === 1) {
741
+ if (shouldAutoAdvance) {
742
+ const paddedValue = numValue.toString().padStart(2, "0");
743
+ setEditValue(paddedValue);
744
+ updateTimeValue(paddedValue, true);
745
+ setPendingDigit(null);
746
+ queueMicrotask(() => {
747
+ if (segment) {
748
+ const nextInputRef = inputGroupContext.getNextInput(segment);
749
+ if (nextInputRef?.current) {
750
+ nextInputRef.current.focus();
751
+ nextInputRef.current.select();
752
+ }
753
+ }
754
+ });
755
+ } else {
756
+ const paddedValue = numValue.toString().padStart(2, "0");
757
+ setEditValue(paddedValue);
758
+ setPendingDigit(newValue);
759
+ queueMicrotask(() => {
760
+ inputRef.current?.select();
761
+ });
762
+ }
763
+ } else if (newValue.length === 2) {
764
+ const paddedValue = numValue.toString().padStart(2, "0");
765
+ setEditValue(paddedValue);
766
+ updateTimeValue(paddedValue, true);
767
+ setPendingDigit(null);
768
+ queueMicrotask(() => {
769
+ if (segment) {
770
+ const nextInputRef = inputGroupContext.getNextInput(segment);
771
+ if (nextInputRef?.current) {
772
+ nextInputRef.current.focus();
773
+ nextInputRef.current.select();
774
+ }
775
+ }
776
+ });
777
+ }
778
+ } else if (newValue.length === 0) {
779
+ setEditValue("");
780
+ setPendingDigit(null);
781
+ }
782
+ }
783
+ },
784
+ [
785
+ segment,
786
+ updateTimeValue,
787
+ onChangeProp,
788
+ editValue,
789
+ is12Hour,
790
+ inputGroupContext,
791
+ pendingDigit,
792
+ segmentPlaceholder,
793
+ labels
794
+ ]
795
+ );
796
+ const onClick = React.useCallback(
797
+ (event) => {
798
+ onClickProp?.(event);
799
+ if (event.defaultPrevented) return;
800
+ event.currentTarget.select();
801
+ },
802
+ [onClickProp]
803
+ );
804
+ const onFocus = React.useCallback(
805
+ (event) => {
806
+ onFocusProp?.(event);
807
+ if (event.defaultPrevented) return;
808
+ setIsEditing(true);
809
+ setPendingDigit(null);
810
+ if (openOnFocus && !store.getState().open) {
811
+ store.setState("openedViaFocus", true);
812
+ store.setState("open", true);
813
+ }
814
+ queueMicrotask(() => event.target.select());
815
+ },
816
+ [onFocusProp, openOnFocus, store]
817
+ );
818
+ const onKeyDown = React.useCallback(
819
+ (event) => {
820
+ onKeyDownProp?.(event);
821
+ if (event.defaultPrevented) return;
822
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
823
+ event.preventDefault();
824
+ const goToPrevious = event.key === "ArrowLeft";
825
+ const inputGroup = inputRef.current?.closest('[data-slot="time-picker-input-group"]');
826
+ if (inputGroup && inputRef.current) {
827
+ const allInputs = Array.from(inputGroup.querySelectorAll('input[type="text"]'));
828
+ const currentIdx = allInputs.indexOf(inputRef.current);
829
+ if (currentIdx !== -1) {
830
+ const targetIdx = goToPrevious ? Math.max(0, currentIdx - 1) : Math.min(allInputs.length - 1, currentIdx + 1);
831
+ const targetInput = allInputs[targetIdx];
832
+ if (targetInput && targetInput !== inputRef.current) {
833
+ targetInput.focus();
834
+ targetInput.select();
835
+ }
836
+ }
837
+ }
838
+ return;
839
+ }
840
+ if (event.key === "Backspace" || event.key === "Delete") {
841
+ const input = inputRef.current;
842
+ if (input && input.selectionStart === 0 && input.selectionEnd === input.value.length) {
843
+ event.preventDefault();
844
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
845
+ setEditValue(placeholder);
846
+ setPendingDigit(null);
847
+ if (timeValue) {
848
+ const newTime = { ...timeValue };
849
+ switch (segment) {
850
+ case "hour":
851
+ delete newTime.hour;
852
+ break;
853
+ case "minute":
854
+ delete newTime.minute;
855
+ break;
856
+ case "second":
857
+ delete newTime.second;
858
+ break;
859
+ case "period":
860
+ delete newTime.period;
861
+ break;
862
+ }
863
+ if (newTime.hour !== void 0 || newTime.minute !== void 0 || newTime.second !== void 0 || newTime.period !== void 0) {
864
+ const newValue = formatTimeValue(newTime, showSeconds);
865
+ store.setState("value", newValue);
866
+ } else {
867
+ store.setState("value", "");
868
+ }
869
+ } else {
870
+ store.setState("value", "");
871
+ }
872
+ queueMicrotask(() => {
873
+ inputRef.current?.select();
874
+ });
875
+ return;
876
+ }
877
+ }
878
+ if (segment === "period") {
879
+ const newPeriodFromKey = getPeriodFromKey(event.key, labels);
880
+ if (newPeriodFromKey) {
881
+ event.preventDefault();
882
+ setEditValue(newPeriodFromKey);
883
+ updateTimeValue(newPeriodFromKey, true);
884
+ queueMicrotask(() => {
885
+ inputRef.current?.select();
886
+ });
887
+ } else if (event.key === "ArrowUp" || event.key === "ArrowDown") {
888
+ event.preventDefault();
889
+ const placeholder = segmentPlaceholder.period;
890
+ const currentPeriod = editValue === placeholder || editValue === "" ? "AM" : editValue;
891
+ const newPeriod = currentPeriod === "AM" || currentPeriod === "A" ? "PM" : "AM";
892
+ setEditValue(newPeriod);
893
+ updateTimeValue(newPeriod, true);
894
+ queueMicrotask(() => {
895
+ inputRef.current?.select();
896
+ });
897
+ }
898
+ return;
899
+ }
900
+ if (event.key === "Tab") {
901
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
902
+ if (editValue && editValue.length > 0 && editValue !== placeholder) {
903
+ if (editValue.length === 2) {
904
+ updateTimeValue(editValue, true);
905
+ } else if (editValue.length === 1) {
906
+ const numValue = Number.parseInt(editValue, 10);
907
+ if (!Number.isNaN(numValue)) {
908
+ const paddedValue = numValue.toString().padStart(2, "0");
909
+ updateTimeValue(paddedValue, true);
910
+ }
911
+ }
912
+ }
913
+ return;
914
+ }
915
+ if (event.key === "Enter") {
916
+ event.preventDefault();
917
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
918
+ if (editValue && editValue.length > 0 && editValue !== placeholder) {
919
+ if (editValue.length === 2) {
920
+ updateTimeValue(editValue, true);
921
+ } else if (editValue.length === 1) {
922
+ const numValue = Number.parseInt(editValue, 10);
923
+ if (!Number.isNaN(numValue)) {
924
+ const paddedValue = numValue.toString().padStart(2, "0");
925
+ updateTimeValue(paddedValue, true);
926
+ }
927
+ }
928
+ }
929
+ queueMicrotask(() => {
930
+ inputRef.current?.select();
931
+ });
932
+ }
933
+ if (event.key === "Escape") {
934
+ event.preventDefault();
935
+ setEditValue(getSegmentValue());
936
+ inputRef.current?.blur();
937
+ }
938
+ if (event.key === "ArrowUp") {
939
+ event.preventDefault();
940
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
941
+ if (editValue === placeholder || editValue === "") {
942
+ const defaultValue = segment === "hour" ? is12Hour ? 12 : 0 : 0;
943
+ const formattedValue = defaultValue.toString().padStart(2, "0");
944
+ setEditValue(formattedValue);
945
+ updateTimeValue(formattedValue, true);
946
+ queueMicrotask(() => {
947
+ inputRef.current?.select();
948
+ });
949
+ return;
950
+ }
951
+ const currentValue = Number.parseInt(editValue, 10);
952
+ if (!Number.isNaN(currentValue)) {
953
+ let newValue;
954
+ switch (segment) {
955
+ case "hour":
956
+ if (is12Hour) {
957
+ newValue = currentValue === 12 ? 1 : currentValue + 1;
958
+ } else {
959
+ newValue = currentValue === 23 ? 0 : currentValue + 1;
960
+ }
961
+ break;
962
+ case "minute":
963
+ case "second":
964
+ newValue = currentValue === 59 ? 0 : currentValue + 1;
965
+ break;
966
+ default:
967
+ return;
968
+ }
969
+ const formattedValue = newValue.toString().padStart(2, "0");
970
+ setEditValue(formattedValue);
971
+ updateTimeValue(formattedValue, true);
972
+ queueMicrotask(() => {
973
+ inputRef.current?.select();
974
+ });
975
+ }
976
+ }
977
+ if (event.key === "ArrowDown") {
978
+ event.preventDefault();
979
+ const placeholder = segment ? segmentPlaceholder[segment] : DEFAULT_SEGMENT_PLACEHOLDER;
980
+ if (editValue === placeholder || editValue === "") {
981
+ const defaultValue = segment === "hour" ? is12Hour ? 12 : 23 : 59;
982
+ const formattedValue = defaultValue.toString().padStart(2, "0");
983
+ setEditValue(formattedValue);
984
+ updateTimeValue(formattedValue, true);
985
+ queueMicrotask(() => {
986
+ inputRef.current?.select();
987
+ });
988
+ return;
989
+ }
990
+ const currentValue = Number.parseInt(editValue, 10);
991
+ if (!Number.isNaN(currentValue)) {
992
+ let newValue;
993
+ switch (segment) {
994
+ case "hour":
995
+ if (is12Hour) {
996
+ newValue = currentValue === 1 ? 12 : currentValue - 1;
997
+ } else {
998
+ newValue = currentValue === 0 ? 23 : currentValue - 1;
999
+ }
1000
+ break;
1001
+ case "minute":
1002
+ case "second":
1003
+ newValue = currentValue === 0 ? 59 : currentValue - 1;
1004
+ break;
1005
+ default:
1006
+ return;
1007
+ }
1008
+ const formattedValue = newValue.toString().padStart(2, "0");
1009
+ setEditValue(formattedValue);
1010
+ updateTimeValue(formattedValue, true);
1011
+ queueMicrotask(() => {
1012
+ inputRef.current?.select();
1013
+ });
1014
+ }
1015
+ }
1016
+ },
1017
+ [
1018
+ onKeyDownProp,
1019
+ editValue,
1020
+ segment,
1021
+ is12Hour,
1022
+ getSegmentValue,
1023
+ updateTimeValue,
1024
+ showSeconds,
1025
+ timeValue,
1026
+ store,
1027
+ segmentPlaceholder,
1028
+ labels
1029
+ ]
1030
+ );
1031
+ if (segment === "period" && !is12Hour) return null;
1032
+ const displayValue = isEditing ? editValue : getSegmentValue();
1033
+ const segmentWidth = segment ? `var(--time-picker-${segment}-input-width)` : "2ch";
1034
+ return /* @__PURE__ */ jsx(
1035
+ "input",
1036
+ {
1037
+ type: "text",
1038
+ inputMode: segment === "period" ? "text" : "numeric",
1039
+ autoComplete: "off",
1040
+ autoCorrect: "off",
1041
+ autoCapitalize: "off",
1042
+ spellCheck: false,
1043
+ translate: "no",
1044
+ "aria-label": labels[segment],
1045
+ "aria-invalid": invalid || void 0,
1046
+ ...inputProps,
1047
+ disabled: isDisabled,
1048
+ readOnly: isReadOnly,
1049
+ className: cn(
1050
+ "inline-flex h-full items-center justify-center border-0 bg-transparent text-center text-sm tabular-nums transition-colors outline-none focus:bg-transparent disabled:cursor-not-allowed disabled:opacity-50",
1051
+ className
1052
+ ),
1053
+ style: { width: segmentWidth, ...style },
1054
+ ref: composedRef,
1055
+ value: segment === "period" ? getPeriodLabel(displayValue, labels) : displayValue,
1056
+ onBlur,
1057
+ onChange,
1058
+ onClick,
1059
+ onFocus,
1060
+ onKeyDown
1061
+ }
1062
+ );
1063
+ }
1064
+ function TimePickerTrigger(props) {
1065
+ const { className, children, disabled: disabledProp, ref, ...triggerProps } = props;
1066
+ const { triggerId, disabled, labels, triggerRef } = useTimePickerContext(TRIGGER_NAME);
1067
+ const isDisabled = disabledProp || disabled;
1068
+ const composedRef = useComposedRefs(ref, triggerRef);
1069
+ return /* @__PURE__ */ jsx(
1070
+ PopoverTrigger,
1071
+ {
1072
+ type: "button",
1073
+ id: triggerId,
1074
+ "data-slot": "time-picker-trigger",
1075
+ disabled: isDisabled,
1076
+ ref: composedRef,
1077
+ ...triggerProps,
1078
+ className: cn(
1079
+ // ⚠ Khác thượng nguồn: thêm viền focus theo khuôn `Button` của gói — bản gốc không có kiểu
1080
+ // focus nào, và viền mặc định của trình duyệt bị lớp nền `outline-ring/50` pha còn gold 50%.
1081
+ "ml-auto flex items-center rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:pointer-events-none [&>svg:not([class*='size-'])]:size-4",
1082
+ className
1083
+ ),
1084
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
1085
+ /* @__PURE__ */ jsx(Clock, { "aria-hidden": "true" }),
1086
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: labels.openPicker })
1087
+ ] })
1088
+ }
1089
+ );
1090
+ }
1091
+ const TimePickerGroupContext = React.createContext(null);
1092
+ function useTimePickerGroupContext(consumerName) {
1093
+ const context = React.useContext(TimePickerGroupContext);
1094
+ if (!context) {
1095
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong \`${CONTENT_NAME}\``);
1096
+ }
1097
+ return context;
1098
+ }
1099
+ function TimePickerContent(props) {
1100
+ const {
1101
+ side = "bottom",
1102
+ align = "start",
1103
+ sideOffset = 6,
1104
+ className,
1105
+ onOpenAutoFocus: onOpenAutoFocusProp,
1106
+ onInteractOutside: onInteractOutsideProp,
1107
+ ...contentProps
1108
+ } = props;
1109
+ const store = useStoreContext(CONTENT_NAME);
1110
+ const { openOnFocus, inputGroupRef } = useTimePickerContext(CONTENT_NAME);
1111
+ const columnsRef = React.useRef(/* @__PURE__ */ new Map());
1112
+ const onColumnRegister = React.useCallback((column) => {
1113
+ columnsRef.current.set(column.id, column);
1114
+ }, []);
1115
+ const onColumnUnregister = React.useCallback((id) => {
1116
+ columnsRef.current.delete(id);
1117
+ }, []);
1118
+ const getColumns = React.useCallback(() => {
1119
+ const columns = Array.from(columnsRef.current.entries()).map(([id, { ref, getSelectedItemRef, getItems }]) => ({
1120
+ id,
1121
+ ref,
1122
+ getSelectedItemRef,
1123
+ getItems
1124
+ })).filter((c) => c.ref.current !== null);
1125
+ return sortNodes(columns);
1126
+ }, []);
1127
+ const groupContextValue = React.useMemo(
1128
+ () => ({
1129
+ getColumns,
1130
+ onColumnRegister,
1131
+ onColumnUnregister
1132
+ }),
1133
+ [getColumns, onColumnRegister, onColumnUnregister]
1134
+ );
1135
+ const onOpenAutoFocus = React.useCallback(
1136
+ (event) => {
1137
+ onOpenAutoFocusProp?.(event);
1138
+ if (event.defaultPrevented) return;
1139
+ event.preventDefault();
1140
+ const { openedViaFocus } = store.getState();
1141
+ if (openedViaFocus) {
1142
+ store.setState("openedViaFocus", false);
1143
+ return;
1144
+ }
1145
+ const columns = getColumns();
1146
+ const firstColumn = columns[0];
1147
+ if (!firstColumn) return;
1148
+ const items = firstColumn.getItems();
1149
+ const selectedItem = items.find((item) => item.selected);
1150
+ const candidateRefs = selectedItem ? [selectedItem.ref, ...items.map((item) => item.ref)] : items.map((item) => item.ref);
1151
+ focusFirst(candidateRefs, false);
1152
+ },
1153
+ [onOpenAutoFocusProp, getColumns, store]
1154
+ );
1155
+ const onInteractOutside = React.useCallback(
1156
+ (event) => {
1157
+ onInteractOutsideProp?.(event);
1158
+ if (event.defaultPrevented) return;
1159
+ if (openOnFocus && inputGroupRef.current) {
1160
+ const target = event.target;
1161
+ if (!(target instanceof Node)) return;
1162
+ const isInsideInputGroup = inputGroupRef.current.contains(target);
1163
+ if (isInsideInputGroup) {
1164
+ event.preventDefault();
1165
+ }
1166
+ }
1167
+ },
1168
+ [onInteractOutsideProp, openOnFocus, inputGroupRef]
1169
+ );
1170
+ return /* @__PURE__ */ jsx(TimePickerGroupContext.Provider, { value: groupContextValue, children: /* @__PURE__ */ jsx(
1171
+ PopoverContent,
1172
+ {
1173
+ "data-slot": "time-picker-content",
1174
+ side,
1175
+ align,
1176
+ sideOffset,
1177
+ ...contentProps,
1178
+ className: cn("flex w-auto max-w-(--radix-popover-trigger-width) p-0", className),
1179
+ onOpenAutoFocus,
1180
+ onInteractOutside
1181
+ }
1182
+ ) });
1183
+ }
1184
+ const TimePickerColumnContext = React.createContext(null);
1185
+ function useTimePickerColumnContext(consumerName) {
1186
+ const context = React.useContext(TimePickerColumnContext);
1187
+ if (!context) {
1188
+ throw new Error(`\`${consumerName}\` ph\u1EA3i n\u1EB1m b\xEAn trong m\u1ED9t c\u1ED9t (\`${COLUMN_NAME}\`)`);
1189
+ }
1190
+ return context;
1191
+ }
1192
+ function TimePickerColumn(props) {
1193
+ const { children, className, ref, ...columnProps } = props;
1194
+ const columnId = React.useId();
1195
+ const columnRef = React.useRef(null);
1196
+ const composedRef = useComposedRefs(ref, columnRef);
1197
+ const itemsRef = React.useRef(/* @__PURE__ */ new Map());
1198
+ const groupContext = useTimePickerGroupContext(COLUMN_NAME);
1199
+ const onItemRegister = React.useCallback(
1200
+ (value, ref2, selected) => {
1201
+ itemsRef.current.set(value, { ref: ref2, selected });
1202
+ },
1203
+ []
1204
+ );
1205
+ const onItemUnregister = React.useCallback((value) => {
1206
+ itemsRef.current.delete(value);
1207
+ }, []);
1208
+ const getItems = React.useCallback(() => {
1209
+ const items = Array.from(itemsRef.current.entries()).map(([value, { ref: ref2, selected }]) => ({
1210
+ value,
1211
+ ref: ref2,
1212
+ selected
1213
+ })).filter((item) => item.ref.current);
1214
+ return sortNodes(items);
1215
+ }, []);
1216
+ const getSelectedItemRef = React.useCallback(() => {
1217
+ const items = getItems();
1218
+ return items.find((item) => item.selected)?.ref ?? null;
1219
+ }, [getItems]);
1220
+ useIsomorphicLayoutEffect(() => {
1221
+ groupContext.onColumnRegister({
1222
+ id: columnId,
1223
+ ref: columnRef,
1224
+ getSelectedItemRef,
1225
+ getItems
1226
+ });
1227
+ return () => groupContext.onColumnUnregister(columnId);
1228
+ }, [groupContext, columnId, getSelectedItemRef, getItems]);
1229
+ const columnContextValue = React.useMemo(
1230
+ () => ({
1231
+ getItems,
1232
+ onItemRegister,
1233
+ onItemUnregister
1234
+ }),
1235
+ [getItems, onItemRegister, onItemUnregister]
1236
+ );
1237
+ return /* @__PURE__ */ jsx(TimePickerColumnContext.Provider, { value: columnContextValue, children: /* @__PURE__ */ jsx(
1238
+ "div",
1239
+ {
1240
+ role: "listbox",
1241
+ ref: composedRef,
1242
+ "data-slot": "time-picker-column",
1243
+ ...columnProps,
1244
+ className: cn("flex flex-col gap-1 p-1 not-last:border-r", className),
1245
+ children
1246
+ }
1247
+ ) });
1248
+ }
1249
+ function TimePickerColumnItem(props) {
1250
+ const { value, selected = false, format = "numeric", className, children, ref, ...itemProps } = props;
1251
+ const itemRef = React.useRef(null);
1252
+ const composedRef = useComposedRefs(ref, itemRef);
1253
+ const columnContext = useTimePickerColumnContext(COLUMN_ITEM_NAME);
1254
+ const groupContext = useTimePickerGroupContext(COLUMN_ITEM_NAME);
1255
+ useIsomorphicLayoutEffect(() => {
1256
+ columnContext.onItemRegister(value, itemRef, selected);
1257
+ return () => columnContext.onItemUnregister(value);
1258
+ }, [columnContext, value, selected]);
1259
+ useIsomorphicLayoutEffect(() => {
1260
+ if (selected && itemRef.current) {
1261
+ itemRef.current.scrollIntoView({ block: "nearest" });
1262
+ }
1263
+ }, [selected]);
1264
+ const onClick = React.useCallback(
1265
+ (event) => {
1266
+ itemProps.onClick?.(event);
1267
+ if (event.defaultPrevented) return;
1268
+ itemRef.current?.focus();
1269
+ },
1270
+ [itemProps.onClick]
1271
+ );
1272
+ const onKeyDown = React.useCallback(
1273
+ (event) => {
1274
+ itemProps.onKeyDown?.(event);
1275
+ if (event.defaultPrevented) return;
1276
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
1277
+ event.preventDefault();
1278
+ const items = columnContext.getItems().sort((a, b) => {
1279
+ if (typeof a.value === "number" && typeof b.value === "number") {
1280
+ return a.value - b.value;
1281
+ }
1282
+ return 0;
1283
+ });
1284
+ const currentIndex = items.findIndex((item) => item.value === value);
1285
+ let nextIndex;
1286
+ if (event.key === "ArrowUp") {
1287
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
1288
+ } else {
1289
+ nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
1290
+ }
1291
+ const nextItem = items[nextIndex];
1292
+ nextItem?.ref.current?.focus();
1293
+ nextItem?.ref.current?.click();
1294
+ } else if ((event.key === "Tab" || event.key === "ArrowLeft" || event.key === "ArrowRight") && groupContext) {
1295
+ event.preventDefault();
1296
+ queueMicrotask(() => {
1297
+ const columns = groupContext.getColumns();
1298
+ if (columns.length === 0) return;
1299
+ const currentColumnIndex = columns.findIndex((c) => c.ref.current?.contains(itemRef.current) ?? false);
1300
+ if (currentColumnIndex === -1) return;
1301
+ const goToPrevious = event.key === "ArrowLeft" || event.key === "Tab" && event.shiftKey;
1302
+ const nextColumnIndex = goToPrevious ? currentColumnIndex > 0 ? currentColumnIndex - 1 : columns.length - 1 : currentColumnIndex < columns.length - 1 ? currentColumnIndex + 1 : 0;
1303
+ const nextColumn = columns[nextColumnIndex];
1304
+ if (nextColumn?.ref.current) {
1305
+ const items = nextColumn.getItems();
1306
+ const selectedItem = items.find((item) => item.selected);
1307
+ const candidateRefs = selectedItem ? [selectedItem.ref, ...items.map((item) => item.ref)] : items.map((item) => item.ref);
1308
+ focusFirst(candidateRefs, false);
1309
+ }
1310
+ });
1311
+ }
1312
+ },
1313
+ [itemProps.onKeyDown, columnContext, groupContext, value]
1314
+ );
1315
+ const formattedValue = typeof value === "number" && format === "2-digit" ? value.toString().padStart(2, "0") : value.toString();
1316
+ return /* @__PURE__ */ jsx(
1317
+ "button",
1318
+ {
1319
+ type: "button",
1320
+ role: "option",
1321
+ "aria-selected": selected,
1322
+ ...itemProps,
1323
+ ref: composedRef,
1324
+ "data-selected": selected ? "" : void 0,
1325
+ className: cn(
1326
+ /*
1327
+ * ⚠ Khác thượng nguồn: focus đi theo khuôn `Checkbox`/`Calendar` của gói (`focus-visible`,
1328
+ * có offset — mục đang chọn tô nền vàng, quầng vàng dính sát nền vàng thì không thấy);
1329
+ * mục đang chọn dùng cặp token của control BẬT `--control-on` / `--control-check` thay
1330
+ * `bg-primary text-primary-foreground` — cùng lý do và số đo với ô ngày đang chọn của
1331
+ * `Calendar` (chú thích đầu `calendar.tsx`). Hover chỉ áp cho mục CHƯA chọn
1332
+ * (`not-data-selected:`) thay vì để `data-selected:hover:` ghi đè lại: cùng kết quả trên
1333
+ * màn hình, nhưng cách cũ khiến `pnpm lint:a11y` ghép chữ hover `--accent-foreground`
1334
+ * với nền mục đang chọn (1,39:1 ở dark) — một cặp không bao giờ xuất hiện.
1335
+ */
1336
+ "w-full rounded px-3 py-1.5 text-left text-sm not-data-selected:hover:bg-accent not-data-selected:hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1337
+ "data-selected:bg-control-on data-selected:text-control-check",
1338
+ className
1339
+ ),
1340
+ onClick,
1341
+ onKeyDown,
1342
+ children: children ?? formattedValue
1343
+ }
1344
+ );
1345
+ }
1346
+ function TimePickerHour(props) {
1347
+ const { asChild, format = "numeric", className, ...hourProps } = props;
1348
+ const { is12Hour, hourStep, showSeconds, disabled, readOnly, labels } = useTimePickerContext(HOUR_NAME);
1349
+ const store = useStoreContext(HOUR_NAME);
1350
+ const value = useStore((state) => state.value);
1351
+ const timeValue = parseTimeString(value);
1352
+ const hours = Array.from(
1353
+ {
1354
+ length: is12Hour ? Math.ceil(12 / hourStep) : Math.ceil(24 / hourStep)
1355
+ },
1356
+ (_, i) => {
1357
+ if (is12Hour) {
1358
+ const hour = i * hourStep % 12;
1359
+ return hour === 0 ? 12 : hour;
1360
+ }
1361
+ return i * hourStep;
1362
+ }
1363
+ );
1364
+ const onHourSelect = React.useCallback(
1365
+ (displayHour2) => {
1366
+ if (disabled || readOnly) return;
1367
+ const now2 = /* @__PURE__ */ new Date();
1368
+ const currentTime = timeValue ?? {};
1369
+ let hour24 = displayHour2;
1370
+ if (is12Hour) {
1371
+ let currentPeriod;
1372
+ if (timeValue?.period !== void 0) {
1373
+ currentPeriod = timeValue.period;
1374
+ } else if (timeValue?.hour !== void 0) {
1375
+ currentPeriod = to12Hour(timeValue.hour).period;
1376
+ } else {
1377
+ currentPeriod = to12Hour(now2.getHours()).period;
1378
+ }
1379
+ hour24 = to24Hour(displayHour2, currentPeriod);
1380
+ }
1381
+ const newTime = { ...currentTime, hour: hour24 };
1382
+ if (timeValue && timeValue.period !== void 0) {
1383
+ newTime.period = timeValue.period;
1384
+ }
1385
+ if (newTime.minute === void 0) {
1386
+ newTime.minute = now2.getMinutes();
1387
+ }
1388
+ if (showSeconds && newTime.second === void 0) {
1389
+ newTime.second = now2.getSeconds();
1390
+ }
1391
+ const newValue = formatTimeValue(newTime, showSeconds);
1392
+ store.setState("value", newValue);
1393
+ },
1394
+ [timeValue, showSeconds, is12Hour, disabled, readOnly, store]
1395
+ );
1396
+ const now = /* @__PURE__ */ new Date();
1397
+ const referenceHour = timeValue?.hour ?? now.getHours();
1398
+ const displayHour = is12Hour ? to12Hour(referenceHour).hour : referenceHour;
1399
+ const HourPrimitive = asChild ? Slot : TimePickerColumn;
1400
+ return /* @__PURE__ */ jsx(
1401
+ HourPrimitive,
1402
+ {
1403
+ "data-slot": "time-picker-hour",
1404
+ "aria-label": labels.hour,
1405
+ ...hourProps,
1406
+ className: cn("scrollbar-none flex max-h-[200px] flex-col gap-1 overflow-y-auto p-1", className),
1407
+ children: hours.map((hour) => /* @__PURE__ */ jsx(
1408
+ TimePickerColumnItem,
1409
+ {
1410
+ value: hour,
1411
+ selected: displayHour === hour,
1412
+ format,
1413
+ onClick: () => onHourSelect(hour)
1414
+ },
1415
+ hour
1416
+ ))
1417
+ }
1418
+ );
1419
+ }
1420
+ function TimePickerMinute(props) {
1421
+ const { asChild, format = "2-digit", className, ...minuteProps } = props;
1422
+ const { minuteStep, showSeconds, disabled, readOnly, labels } = useTimePickerContext(MINUTE_NAME);
1423
+ const store = useStoreContext(MINUTE_NAME);
1424
+ const value = useStore((state) => state.value);
1425
+ const timeValue = parseTimeString(value);
1426
+ const minutes = Array.from({ length: Math.ceil(60 / minuteStep) }, (_, i) => i * minuteStep);
1427
+ const onMinuteSelect = React.useCallback(
1428
+ (minute) => {
1429
+ if (disabled || readOnly) return;
1430
+ const now2 = /* @__PURE__ */ new Date();
1431
+ const currentTime = timeValue ?? {};
1432
+ const newTime = { ...currentTime, minute };
1433
+ if (newTime.hour === void 0) {
1434
+ newTime.hour = now2.getHours();
1435
+ }
1436
+ if (showSeconds && newTime.second === void 0) {
1437
+ newTime.second = now2.getSeconds();
1438
+ }
1439
+ const newValue = formatTimeValue(newTime, showSeconds);
1440
+ store.setState("value", newValue);
1441
+ },
1442
+ [timeValue, showSeconds, disabled, readOnly, store]
1443
+ );
1444
+ const MinutePrimitive = asChild ? Slot : TimePickerColumn;
1445
+ const now = /* @__PURE__ */ new Date();
1446
+ const referenceMinute = timeValue?.minute ?? now.getMinutes();
1447
+ return /* @__PURE__ */ jsx(
1448
+ MinutePrimitive,
1449
+ {
1450
+ "data-slot": "time-picker-minute",
1451
+ "aria-label": labels.minute,
1452
+ ...minuteProps,
1453
+ className: cn("scrollbar-none flex max-h-[200px] flex-col gap-1 overflow-y-auto p-1", className),
1454
+ children: minutes.map((minute) => /* @__PURE__ */ jsx(
1455
+ TimePickerColumnItem,
1456
+ {
1457
+ value: minute,
1458
+ selected: referenceMinute === minute,
1459
+ format,
1460
+ onClick: () => onMinuteSelect(minute)
1461
+ },
1462
+ minute
1463
+ ))
1464
+ }
1465
+ );
1466
+ }
1467
+ function TimePickerSecond(props) {
1468
+ const { asChild, format = "2-digit", className, ...secondProps } = props;
1469
+ const { secondStep, disabled, readOnly, labels } = useTimePickerContext(SECOND_NAME);
1470
+ const store = useStoreContext(SECOND_NAME);
1471
+ const value = useStore((state) => state.value);
1472
+ const timeValue = parseTimeString(value);
1473
+ const seconds = Array.from({ length: Math.ceil(60 / secondStep) }, (_, i) => i * secondStep);
1474
+ const onSecondSelect = React.useCallback(
1475
+ (second) => {
1476
+ if (disabled || readOnly) return;
1477
+ const now2 = /* @__PURE__ */ new Date();
1478
+ const currentTime = timeValue ?? {};
1479
+ const newTime = { ...currentTime, second };
1480
+ if (newTime.hour === void 0) {
1481
+ newTime.hour = now2.getHours();
1482
+ }
1483
+ if (newTime.minute === void 0) {
1484
+ newTime.minute = now2.getMinutes();
1485
+ }
1486
+ const newValue = formatTimeValue(newTime, true);
1487
+ store.setState("value", newValue);
1488
+ },
1489
+ [timeValue, disabled, readOnly, store]
1490
+ );
1491
+ const SecondPrimitive = asChild ? Slot : TimePickerColumn;
1492
+ const now = /* @__PURE__ */ new Date();
1493
+ const referenceSecond = timeValue?.second ?? now.getSeconds();
1494
+ return /* @__PURE__ */ jsx(
1495
+ SecondPrimitive,
1496
+ {
1497
+ "data-slot": "time-picker-second",
1498
+ "aria-label": labels.second,
1499
+ ...secondProps,
1500
+ className: cn("scrollbar-none flex max-h-[200px] flex-col gap-1 overflow-y-auto p-1", className),
1501
+ children: seconds.map((second) => /* @__PURE__ */ jsx(
1502
+ TimePickerColumnItem,
1503
+ {
1504
+ value: second,
1505
+ selected: referenceSecond === second,
1506
+ format,
1507
+ onClick: () => onSecondSelect(second)
1508
+ },
1509
+ second
1510
+ ))
1511
+ }
1512
+ );
1513
+ }
1514
+ function TimePickerPeriod(props) {
1515
+ const { asChild, className, ...periodProps } = props;
1516
+ const { is12Hour, showSeconds, disabled, readOnly, labels } = useTimePickerContext(PERIOD_NAME);
1517
+ const store = useStoreContext(PERIOD_NAME);
1518
+ const value = useStore((state) => state.value);
1519
+ const timeValue = parseTimeString(value);
1520
+ const onPeriodToggle = React.useCallback(
1521
+ (period) => {
1522
+ if (disabled || readOnly) return;
1523
+ const now2 = /* @__PURE__ */ new Date();
1524
+ const currentTime = timeValue ?? {};
1525
+ const currentHour = currentTime.hour !== void 0 ? currentTime.hour : now2.getHours();
1526
+ const currentDisplay = to12Hour(currentHour);
1527
+ const new24Hour = to24Hour(currentDisplay.hour, period);
1528
+ const newTime = { ...currentTime, hour: new24Hour };
1529
+ if (newTime.minute === void 0) {
1530
+ newTime.minute = now2.getMinutes();
1531
+ }
1532
+ if (showSeconds && newTime.second === void 0) {
1533
+ newTime.second = now2.getSeconds();
1534
+ }
1535
+ const newValue = formatTimeValue(newTime, showSeconds);
1536
+ store.setState("value", newValue);
1537
+ },
1538
+ [timeValue, showSeconds, disabled, readOnly, store]
1539
+ );
1540
+ if (!is12Hour) return null;
1541
+ const now = /* @__PURE__ */ new Date();
1542
+ const referenceHour = timeValue?.hour ?? now.getHours();
1543
+ const currentPeriod = to12Hour(referenceHour).period;
1544
+ const PeriodPrimitive = asChild ? Slot : TimePickerColumn;
1545
+ return /* @__PURE__ */ jsx(
1546
+ PeriodPrimitive,
1547
+ {
1548
+ "data-slot": "time-picker-period",
1549
+ "aria-label": labels.period,
1550
+ ...periodProps,
1551
+ className: cn("flex flex-col gap-1 p-1", className),
1552
+ children: PERIODS.map((period) => /* @__PURE__ */ jsx(
1553
+ TimePickerColumnItem,
1554
+ {
1555
+ value: period,
1556
+ selected: currentPeriod === period,
1557
+ onClick: () => onPeriodToggle(period),
1558
+ children: getPeriodLabel(period, labels)
1559
+ },
1560
+ period
1561
+ ))
1562
+ }
1563
+ );
1564
+ }
1565
+ function TimePickerSeparator(props) {
1566
+ const { asChild, children, ...separatorProps } = props;
1567
+ const SeparatorPrimitive = asChild ? Slot : "span";
1568
+ return /* @__PURE__ */ jsx(SeparatorPrimitive, { "aria-hidden": "true", "data-slot": "time-picker-separator", ...separatorProps, children: children ?? ":" });
1569
+ }
1570
+ function TimePickerClear(props) {
1571
+ const { asChild, className, children, disabled: disabledProp, ...clearProps } = props;
1572
+ const { disabled, readOnly, labels } = useTimePickerContext(CLEAR_NAME);
1573
+ const store = useStoreContext(CLEAR_NAME);
1574
+ const isDisabled = disabledProp || disabled;
1575
+ const onClick = React.useCallback(
1576
+ (event) => {
1577
+ clearProps.onClick?.(event);
1578
+ if (event.defaultPrevented) return;
1579
+ event.preventDefault();
1580
+ if (disabled || readOnly) return;
1581
+ store.setState("value", "");
1582
+ },
1583
+ [clearProps.onClick, disabled, readOnly, store]
1584
+ );
1585
+ const ClearPrimitive = asChild ? Slot : "button";
1586
+ return /* @__PURE__ */ jsx(
1587
+ ClearPrimitive,
1588
+ {
1589
+ type: "button",
1590
+ "data-slot": "time-picker-clear",
1591
+ disabled: isDisabled,
1592
+ ...clearProps,
1593
+ className: cn(
1594
+ // Viền focus theo khuôn `Button` — xem `TimePickerTrigger`.
1595
+ "inline-flex items-center justify-center rounded-sm text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50",
1596
+ className
1597
+ ),
1598
+ onClick,
1599
+ children: children ?? labels.clear
1600
+ }
1601
+ );
1602
+ }
1603
+ export {
1604
+ TimePicker,
1605
+ TimePickerClear,
1606
+ TimePickerContent,
1607
+ TimePickerHour,
1608
+ TimePickerInput,
1609
+ TimePickerInputGroup,
1610
+ TimePickerLabel,
1611
+ TimePickerMinute,
1612
+ TimePickerPeriod,
1613
+ TimePickerSecond,
1614
+ TimePickerSeparator,
1615
+ TimePickerTrigger,
1616
+ useStore as useTimePicker
1617
+ };
1618
+ //# sourceMappingURL=time-picker.js.map