@eifi1/ui-kit 0.51.6 → 0.52.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 (805) hide show
  1. package/README.md +146 -26
  2. package/dist/account/account-parts.d.ts +10 -8
  3. package/dist/account/account-parts.js +1 -18
  4. package/dist/account/account-parts.js.map +1 -1
  5. package/dist/account/data-export-setting-labels.js +1 -1
  6. package/dist/account/data-export-setting-labels.js.map +1 -1
  7. package/dist/account/delete-account-setting-labels.js +1 -1
  8. package/dist/account/delete-account-setting-labels.js.map +1 -1
  9. package/dist/account/email-change-setting-labels.js +1 -1
  10. package/dist/account/email-change-setting-labels.js.map +1 -1
  11. package/dist/account/passkeys-setting.d.ts +9 -2
  12. package/dist/account/passkeys-setting.js +41 -29
  13. package/dist/account/passkeys-setting.js.map +1 -1
  14. package/dist/account/sessions-setting.d.ts +3 -2
  15. package/dist/account/sessions-setting.js +10 -8
  16. package/dist/account/sessions-setting.js.map +1 -1
  17. package/dist/admin/admin-action-confirm.js +3 -7
  18. package/dist/admin/admin-action-confirm.js.map +1 -1
  19. package/dist/admin/admin-parts.d.ts +6 -3
  20. package/dist/admin/admin-parts.js +6 -21
  21. package/dist/admin/admin-parts.js.map +1 -1
  22. package/dist/admin/mail-test-card-labels.js +1 -1
  23. package/dist/admin/mail-test-card-labels.js.map +1 -1
  24. package/dist/admin/plan-change-labels.d.ts +2 -2
  25. package/dist/admin/plan-change-labels.js +1 -17
  26. package/dist/admin/plan-change-labels.js.map +1 -1
  27. package/dist/admin/plan-change.d.ts +3 -42
  28. package/dist/admin/plan-change.js +1 -1
  29. package/dist/admin/plan-change.js.map +1 -1
  30. package/dist/admin/reviewer-scope-editor.js +3 -3
  31. package/dist/admin/reviewer-scope-editor.js.map +1 -1
  32. package/dist/auth/auth-errors-labels.d.ts +11 -0
  33. package/dist/auth/auth-errors-labels.js +13 -0
  34. package/dist/auth/auth-errors-labels.js.map +1 -0
  35. package/dist/auth/auth-errors.d.ts +7 -23
  36. package/dist/auth/auth-errors.js +2 -15
  37. package/dist/auth/auth-errors.js.map +1 -1
  38. package/dist/auth/complete-name-dialog.d.ts +3 -2
  39. package/dist/auth/complete-name-dialog.js +3 -5
  40. package/dist/auth/complete-name-dialog.js.map +1 -1
  41. package/dist/auth/forgot-password-form-labels.js +1 -1
  42. package/dist/auth/forgot-password-form-labels.js.map +1 -1
  43. package/dist/auth/forgot-password-form.js +7 -2
  44. package/dist/auth/forgot-password-form.js.map +1 -1
  45. package/dist/auth/register-form-labels.js +1 -1
  46. package/dist/auth/register-form-labels.js.map +1 -1
  47. package/dist/auth/reset-password-form-labels.js +1 -1
  48. package/dist/auth/reset-password-form-labels.js.map +1 -1
  49. package/dist/auth/reset-password-form.js +5 -1
  50. package/dist/auth/reset-password-form.js.map +1 -1
  51. package/dist/auth/sign-in-form-labels.js +1 -1
  52. package/dist/auth/sign-in-form-labels.js.map +1 -1
  53. package/dist/auth/status-parts.d.ts +0 -3
  54. package/dist/auth/status-parts.js +0 -4
  55. package/dist/auth/status-parts.js.map +1 -1
  56. package/dist/auth/verify-email-labels.js +1 -1
  57. package/dist/auth/verify-email-labels.js.map +1 -1
  58. package/dist/auth/verify-email.js +7 -2
  59. package/dist/auth/verify-email.js.map +1 -1
  60. package/dist/billing/cancel-request.d.ts +11 -2
  61. package/dist/billing/cancel-request.js +15 -3
  62. package/dist/billing/cancel-request.js.map +1 -1
  63. package/dist/billing/cancel-subscription.d.ts +22 -10
  64. package/dist/billing/cancel-subscription.js +8 -11
  65. package/dist/billing/cancel-subscription.js.map +1 -1
  66. package/dist/billing/dialog-close.d.ts +29 -0
  67. package/dist/billing/dialog-close.js +8 -0
  68. package/dist/billing/dialog-close.js.map +1 -0
  69. package/dist/billing/plan-card.d.ts +14 -4
  70. package/dist/billing/plan-card.js +15 -8
  71. package/dist/billing/plan-card.js.map +1 -1
  72. package/dist/billing/plan-change-labels.d.ts +42 -0
  73. package/dist/billing/plan-change-labels.js +22 -0
  74. package/dist/billing/plan-change-labels.js.map +1 -0
  75. package/dist/billing/plan-switch.d.ts +21 -9
  76. package/dist/billing/plan-switch.js +10 -8
  77. package/dist/billing/plan-switch.js.map +1 -1
  78. package/dist/chart.d.ts +12 -0
  79. package/dist/chart.js +16 -1
  80. package/dist/chart.js.map +1 -1
  81. package/dist/components/account-chips.d.ts +5 -5
  82. package/dist/components/account-chips.js +11 -13
  83. package/dist/components/account-chips.js.map +1 -1
  84. package/dist/components/alert-banner.d.ts +16 -5
  85. package/dist/components/alert-banner.js +46 -1
  86. package/dist/components/alert-banner.js.map +1 -1
  87. package/dist/components/amount-input.d.ts +22 -4
  88. package/dist/components/amount-input.js +3 -3
  89. package/dist/components/amount-input.js.map +1 -1
  90. package/dist/components/autocomplete.js +57 -65
  91. package/dist/components/autocomplete.js.map +1 -1
  92. package/dist/components/bulk-action-bar.d.ts +12 -2
  93. package/dist/components/bulk-action-bar.js +65 -21
  94. package/dist/components/bulk-action-bar.js.map +1 -1
  95. package/dist/components/button-look.d.ts +5 -5
  96. package/dist/components/button-look.js.map +1 -1
  97. package/dist/components/button.d.ts +2 -2
  98. package/dist/components/button.js.map +1 -1
  99. package/dist/components/calculator.d.ts +21 -2
  100. package/dist/components/calculator.js +53 -14
  101. package/dist/components/calculator.js.map +1 -1
  102. package/dist/components/chart-edge.d.ts +16 -0
  103. package/dist/components/chart-edge.js +14 -1
  104. package/dist/components/chart-edge.js.map +1 -1
  105. package/dist/components/chart-figure.d.ts +13 -0
  106. package/dist/components/chart-figure.js +15 -0
  107. package/dist/components/chart-figure.js.map +1 -0
  108. package/dist/components/chart-zoom-core.js +1 -4
  109. package/dist/components/chart-zoom-core.js.map +1 -1
  110. package/dist/components/chart.d.ts +8 -1
  111. package/dist/components/chart.js +19 -10
  112. package/dist/components/chart.js.map +1 -1
  113. package/dist/components/combobox-core.d.ts +119 -7
  114. package/dist/components/combobox-core.js +151 -46
  115. package/dist/components/combobox-core.js.map +1 -1
  116. package/dist/components/combobox.d.ts +1 -1
  117. package/dist/components/combobox.js +142 -131
  118. package/dist/components/combobox.js.map +1 -1
  119. package/dist/components/copy-button.d.ts +6 -5
  120. package/dist/components/copy-button.js +5 -3
  121. package/dist/components/copy-button.js.map +1 -1
  122. package/dist/components/country-select.js +2 -2
  123. package/dist/components/country-select.js.map +1 -1
  124. package/dist/components/data-table-column-settings.d.ts +1 -1
  125. package/dist/components/data-table-column-settings.js +33 -7
  126. package/dist/components/data-table-column-settings.js.map +1 -1
  127. package/dist/components/data-table-filters.d.ts +4 -2
  128. package/dist/components/data-table-filters.js +2 -2
  129. package/dist/components/data-table-filters.js.map +1 -1
  130. package/dist/components/data-table-fit.d.ts +6 -4
  131. package/dist/components/data-table-fit.js +4 -1
  132. package/dist/components/data-table-fit.js.map +1 -1
  133. package/dist/components/data-table-groups.d.ts +1 -1
  134. package/dist/components/data-table-groups.js.map +1 -1
  135. package/dist/components/data-table-head.d.ts +1 -1
  136. package/dist/components/data-table-head.js +15 -12
  137. package/dist/components/data-table-head.js.map +1 -1
  138. package/dist/components/data-table-mobile.d.ts +36 -7
  139. package/dist/components/data-table-mobile.js +63 -31
  140. package/dist/components/data-table-mobile.js.map +1 -1
  141. package/dist/components/data-table-pagination.js +11 -6
  142. package/dist/components/data-table-pagination.js.map +1 -1
  143. package/dist/components/data-table-resize.d.ts +1 -1
  144. package/dist/components/data-table-resize.js.map +1 -1
  145. package/dist/components/data-table-row-actions.d.ts +2 -2
  146. package/dist/components/data-table-row-actions.js.map +1 -1
  147. package/dist/components/data-table-row.d.ts +6 -2
  148. package/dist/components/data-table-row.js +17 -10
  149. package/dist/components/data-table-row.js.map +1 -1
  150. package/dist/components/data-table-selection.d.ts +1 -1
  151. package/dist/components/data-table-selection.js.map +1 -1
  152. package/dist/components/data-table-shared.d.ts +10 -2
  153. package/dist/components/data-table-shared.js +18 -0
  154. package/dist/components/data-table-shared.js.map +1 -1
  155. package/dist/components/data-table-sort.d.ts +21 -2
  156. package/dist/components/data-table-sort.js +24 -0
  157. package/dist/components/data-table-sort.js.map +1 -1
  158. package/dist/components/data-table-virtual.d.ts +1 -1
  159. package/dist/components/data-table-virtual.js +18 -6
  160. package/dist/components/data-table-virtual.js.map +1 -1
  161. package/dist/components/data-table.d.ts +25 -8
  162. package/dist/components/data-table.js +154 -25
  163. package/dist/components/data-table.js.map +1 -1
  164. package/dist/components/date-field.d.ts +24 -2
  165. package/dist/components/date-field.js +33 -8
  166. package/dist/components/date-field.js.map +1 -1
  167. package/dist/components/date-picker.d.ts +15 -2
  168. package/dist/components/date-picker.js +55 -26
  169. package/dist/components/date-picker.js.map +1 -1
  170. package/dist/components/date-range-picker.d.ts +47 -20
  171. package/dist/components/date-range-picker.js +23 -7
  172. package/dist/components/date-range-picker.js.map +1 -1
  173. package/dist/components/dialog-frame.d.ts +2 -1
  174. package/dist/components/dialog-frame.js.map +1 -1
  175. package/dist/components/entity-combobox.js +4 -2
  176. package/dist/components/entity-combobox.js.map +1 -1
  177. package/dist/components/error-boundary.js +37 -30
  178. package/dist/components/error-boundary.js.map +1 -1
  179. package/dist/components/{ui-labels.d.ts → field-base-labels.d.ts} +0 -2
  180. package/dist/components/{ui-labels.js → field-base-labels.js} +2 -9
  181. package/dist/components/field-base-labels.js.map +1 -0
  182. package/dist/components/field-base.d.ts +4 -3
  183. package/dist/components/field-base.js +3 -3
  184. package/dist/components/field-base.js.map +1 -1
  185. package/dist/components/field-sync-labels.d.ts +1 -0
  186. package/dist/components/field-sync-labels.js.map +1 -1
  187. package/dist/components/field-sync.d.ts +23 -1
  188. package/dist/components/field-sync.js.map +1 -1
  189. package/dist/components/form-actions.js +11 -5
  190. package/dist/components/form-actions.js.map +1 -1
  191. package/dist/components/hover-menu.d.ts +3 -0
  192. package/dist/components/hover-menu.js +5 -3
  193. package/dist/components/hover-menu.js.map +1 -1
  194. package/dist/components/icon-button.js +14 -9
  195. package/dist/components/icon-button.js.map +1 -1
  196. package/dist/components/inline-confirm.d.ts +43 -0
  197. package/dist/components/inline-confirm.js +67 -0
  198. package/dist/components/inline-confirm.js.map +1 -0
  199. package/dist/components/inline-entity-combobox.d.ts +4 -3
  200. package/dist/components/inline-entity-combobox.js +150 -141
  201. package/dist/components/inline-entity-combobox.js.map +1 -1
  202. package/dist/components/input.js +1 -1
  203. package/dist/components/input.js.map +1 -1
  204. package/dist/components/jump-calendar.js +28 -112
  205. package/dist/components/jump-calendar.js.map +1 -1
  206. package/dist/components/legal-page.d.ts +3 -1
  207. package/dist/components/legal-page.js +3 -2
  208. package/dist/components/legal-page.js.map +1 -1
  209. package/dist/components/legal.d.ts +4 -2
  210. package/dist/components/legal.js.map +1 -1
  211. package/dist/components/lightbox.d.ts +2 -2
  212. package/dist/components/lightbox.js +17 -6
  213. package/dist/components/lightbox.js.map +1 -1
  214. package/dist/components/list.d.ts +6 -2
  215. package/dist/components/list.js +4 -3
  216. package/dist/components/list.js.map +1 -1
  217. package/dist/components/measured-grid.d.ts +1 -1
  218. package/dist/components/measured-grid.js +2 -1
  219. package/dist/components/measured-grid.js.map +1 -1
  220. package/dist/components/mini-calendar.js +3 -5
  221. package/dist/components/mini-calendar.js.map +1 -1
  222. package/dist/components/modal.d.ts +24 -0
  223. package/dist/components/modal.js +12 -4
  224. package/dist/components/modal.js.map +1 -1
  225. package/dist/components/money-draft.js +7 -5
  226. package/dist/components/money-draft.js.map +1 -1
  227. package/dist/components/money-field.d.ts +2 -2
  228. package/dist/components/money-field.js.map +1 -1
  229. package/dist/components/month-grid.d.ts +55 -0
  230. package/dist/components/month-grid.js +148 -0
  231. package/dist/components/month-grid.js.map +1 -0
  232. package/dist/components/month-picker.d.ts +36 -4
  233. package/dist/components/month-picker.js +123 -146
  234. package/dist/components/month-picker.js.map +1 -1
  235. package/dist/components/multi-entity-combobox.js +2 -1
  236. package/dist/components/multi-entity-combobox.js.map +1 -1
  237. package/dist/components/multi-select.d.ts +2 -2
  238. package/dist/components/multi-select.js +11 -6
  239. package/dist/components/multi-select.js.map +1 -1
  240. package/dist/components/number-field.d.ts +6 -4
  241. package/dist/components/number-field.js +3 -2
  242. package/dist/components/number-field.js.map +1 -1
  243. package/dist/components/number-input.d.ts +4 -1
  244. package/dist/components/number-input.js +3 -2
  245. package/dist/components/number-input.js.map +1 -1
  246. package/dist/components/numpad-sheet.js.map +1 -1
  247. package/dist/components/picker-sheet.js +2 -1
  248. package/dist/components/picker-sheet.js.map +1 -1
  249. package/dist/components/pie-chart-labels.d.ts +7 -0
  250. package/dist/components/pie-chart-labels.js +1 -0
  251. package/dist/components/pie-chart-labels.js.map +1 -1
  252. package/dist/components/pie-chart.d.ts +5 -3
  253. package/dist/components/pie-chart.js +7 -22
  254. package/dist/components/pie-chart.js.map +1 -1
  255. package/dist/components/popover.d.ts +3 -0
  256. package/dist/components/popover.js +7 -1
  257. package/dist/components/popover.js.map +1 -1
  258. package/dist/components/reauth-dialog.d.ts +4 -5
  259. package/dist/components/reauth-dialog.js +1 -4
  260. package/dist/components/reauth-dialog.js.map +1 -1
  261. package/dist/components/row-actions.js +2 -1
  262. package/dist/components/row-actions.js.map +1 -1
  263. package/dist/components/section-nav.d.ts +7 -0
  264. package/dist/components/section-nav.js.map +1 -1
  265. package/dist/components/series-chart-axes.d.ts +10 -3
  266. package/dist/components/series-chart-axes.js +1 -0
  267. package/dist/components/series-chart-axes.js.map +1 -1
  268. package/dist/components/series-chart-labels.d.ts +7 -4
  269. package/dist/components/series-chart-labels.js.map +1 -1
  270. package/dist/components/series-chart.d.ts +31 -7
  271. package/dist/components/series-chart.js +72 -29
  272. package/dist/components/series-chart.js.map +1 -1
  273. package/dist/components/share-card.js +3 -3
  274. package/dist/components/share-card.js.map +1 -1
  275. package/dist/components/sign-chip-labels.d.ts +1 -0
  276. package/dist/components/sign-chip-labels.js.map +1 -1
  277. package/dist/components/sign-chip.d.ts +10 -1
  278. package/dist/components/sign-chip.js.map +1 -1
  279. package/dist/components/tabs-labels.d.ts +2 -0
  280. package/dist/components/tabs-labels.js +11 -0
  281. package/dist/components/tabs-labels.js.map +1 -0
  282. package/dist/components/tabs.d.ts +1 -1
  283. package/dist/components/tabs.js +13 -11
  284. package/dist/components/tabs.js.map +1 -1
  285. package/dist/components/toggle-legend.d.ts +2 -2
  286. package/dist/components/toggle-legend.js +6 -6
  287. package/dist/components/toggle-legend.js.map +1 -1
  288. package/dist/components/translation-review-editor.d.ts +4 -2
  289. package/dist/components/translation-review-editor.js +2 -3
  290. package/dist/components/translation-review-editor.js.map +1 -1
  291. package/dist/components/translation-review.d.ts +2 -1
  292. package/dist/components/translation-review.js +2 -2
  293. package/dist/components/translation-review.js.map +1 -1
  294. package/dist/components/treemap-labels.d.ts +26 -0
  295. package/dist/components/treemap-labels.js +10 -0
  296. package/dist/components/treemap-labels.js.map +1 -0
  297. package/dist/components/treemap-layout.d.ts +62 -0
  298. package/dist/components/treemap-layout.js +126 -0
  299. package/dist/components/treemap-layout.js.map +1 -0
  300. package/dist/components/treemap.d.ts +140 -17
  301. package/dist/components/treemap.js +324 -97
  302. package/dist/components/treemap.js.map +1 -1
  303. package/dist/components/use-table-state.d.ts +5 -5
  304. package/dist/components/use-table-state.js +4 -1
  305. package/dist/components/use-table-state.js.map +1 -1
  306. package/dist/data-table.d.ts +6 -2
  307. package/dist/data-table.js +7 -2
  308. package/dist/data-table.js.map +1 -1
  309. package/dist/eslint/index.js +1 -1
  310. package/dist/eslint/role-utilities.d.ts +36 -7
  311. package/dist/eslint/role-utilities.js +42 -9
  312. package/dist/eslint/role-utilities.js.map +1 -1
  313. package/dist/feedback/feedback-dialog.d.ts +25 -16
  314. package/dist/feedback/feedback-dialog.js +6 -2
  315. package/dist/feedback/feedback-dialog.js.map +1 -1
  316. package/dist/feedback/feedback-mutation.d.ts +30 -0
  317. package/dist/feedback/feedback-mutation.js +16 -0
  318. package/dist/feedback/feedback-mutation.js.map +1 -0
  319. package/dist/feedback/feedback-page-link.d.ts +53 -0
  320. package/dist/feedback/feedback-page-link.js +37 -0
  321. package/dist/feedback/feedback-page-link.js.map +1 -0
  322. package/dist/feedback/feedback-public-list.d.ts +12 -3
  323. package/dist/feedback/feedback-public-list.js +2 -1
  324. package/dist/feedback/feedback-public-list.js.map +1 -1
  325. package/dist/feedback/feedback-replies.d.ts +3 -1
  326. package/dist/feedback/feedback-replies.js +5 -4
  327. package/dist/feedback/feedback-replies.js.map +1 -1
  328. package/dist/feedback/feedback-row-detail-labels.js +5 -0
  329. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  330. package/dist/feedback/feedback-row-detail.d.ts +64 -28
  331. package/dist/feedback/feedback-row-detail.js +30 -28
  332. package/dist/feedback/feedback-row-detail.js.map +1 -1
  333. package/dist/feedback/feedback-status-undo.d.ts +30 -11
  334. package/dist/feedback/feedback-status-undo.js +10 -15
  335. package/dist/feedback/feedback-status-undo.js.map +1 -1
  336. package/dist/feedback/feedback-submit-dialog.d.ts +1 -1
  337. package/dist/feedback/feedback-submit-dialog.js +1 -1
  338. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  339. package/dist/feedback/feedback-submit.js +9 -3
  340. package/dist/feedback/feedback-submit.js.map +1 -1
  341. package/dist/feedback/feedback-swipe.d.ts +1 -1
  342. package/dist/feedback/feedback-swipe.js.map +1 -1
  343. package/dist/feedback/feedback-table.d.ts +21 -9
  344. package/dist/feedback/feedback-table.js +45 -25
  345. package/dist/feedback/feedback-table.js.map +1 -1
  346. package/dist/feedback/feedback-thread-labels.d.ts +2 -2
  347. package/dist/feedback/feedback-thread-labels.js.map +1 -1
  348. package/dist/feedback/feedback-thread.d.ts +24 -23
  349. package/dist/feedback/feedback-thread.js +2 -1
  350. package/dist/feedback/feedback-thread.js.map +1 -1
  351. package/dist/feedback.d.ts +34 -0
  352. package/dist/feedback.js +42 -0
  353. package/dist/feedback.js.map +1 -1
  354. package/dist/hooks/use-dismiss.js.map +1 -1
  355. package/dist/hooks/use-focus-trap.d.ts +35 -0
  356. package/dist/hooks/use-focus-trap.js +6 -4
  357. package/dist/hooks/use-focus-trap.js.map +1 -1
  358. package/dist/hooks/use-hotkey.d.ts +5 -2
  359. package/dist/hooks/use-hotkey.js +5 -4
  360. package/dist/hooks/use-hotkey.js.map +1 -1
  361. package/dist/hooks/use-long-press.d.ts +7 -4
  362. package/dist/hooks/use-long-press.js +3 -0
  363. package/dist/hooks/use-long-press.js.map +1 -1
  364. package/dist/hooks/use-media-query.js +12 -4
  365. package/dist/hooks/use-media-query.js.map +1 -1
  366. package/dist/hooks/use-mounted.d.ts +22 -0
  367. package/dist/hooks/use-mounted.js +24 -0
  368. package/dist/hooks/use-mounted.js.map +1 -0
  369. package/dist/hooks/use-overlay-history.d.ts +7 -1
  370. package/dist/hooks/use-overlay-history.js +35 -5
  371. package/dist/hooks/use-overlay-history.js.map +1 -1
  372. package/dist/i18n/defaults.js +4 -5
  373. package/dist/i18n/defaults.js.map +1 -1
  374. package/dist/i18n/german.js +7 -1
  375. package/dist/i18n/german.js.map +1 -1
  376. package/dist/i18n/kit-labels.d.ts +9 -2
  377. package/dist/i18n/kit-labels.js.map +1 -1
  378. package/dist/i18n/locales/en.js +20 -41
  379. package/dist/i18n/locales/en.js.map +3 -3
  380. package/dist/i18n/locales/es.js +7 -1
  381. package/dist/i18n/locales/es.js.map +1 -1
  382. package/dist/i18n/locales/fr.js +20 -10
  383. package/dist/i18n/locales/fr.js.map +1 -1
  384. package/dist/i18n/locales/hu.js +9 -1
  385. package/dist/i18n/locales/hu.js.map +1 -1
  386. package/dist/i18n/locales/it.js +7 -1
  387. package/dist/i18n/locales/it.js.map +1 -1
  388. package/dist/i18n/locales/zh.js +8 -1
  389. package/dist/i18n/locales/zh.js.map +1 -1
  390. package/dist/i18n/review.js +5 -0
  391. package/dist/i18n/review.js.map +1 -1
  392. package/dist/index.d.ts +64 -17
  393. package/dist/index.js +77 -12
  394. package/dist/index.js.map +1 -1
  395. package/dist/landing/routing.d.ts +3 -1
  396. package/dist/landing/routing.js +4 -7
  397. package/dist/landing/routing.js.map +1 -1
  398. package/dist/lib/broadcast.d.ts +17 -0
  399. package/dist/lib/broadcast.js +30 -0
  400. package/dist/lib/broadcast.js.map +1 -0
  401. package/dist/lib/calc.d.ts +7 -1
  402. package/dist/lib/calc.js +3 -1
  403. package/dist/lib/calc.js.map +1 -1
  404. package/dist/lib/clipboard-minus.d.ts +2 -2
  405. package/dist/lib/clipboard-minus.js +1 -1
  406. package/dist/lib/clipboard-minus.js.map +1 -1
  407. package/dist/lib/date-format.d.ts +37 -2
  408. package/dist/lib/date-format.js +34 -8
  409. package/dist/lib/date-format.js.map +1 -1
  410. package/dist/lib/day-format.d.ts +46 -0
  411. package/dist/lib/day-format.js +46 -0
  412. package/dist/lib/day-format.js.map +1 -0
  413. package/dist/lib/decimal-marks.d.ts +7 -1
  414. package/dist/lib/decimal-marks.js +5 -2
  415. package/dist/lib/decimal-marks.js.map +1 -1
  416. package/dist/lib/describe-failure.d.ts +14 -0
  417. package/dist/lib/describe-failure.js +9 -0
  418. package/dist/lib/describe-failure.js.map +1 -0
  419. package/dist/lib/direction.d.ts +16 -0
  420. package/dist/lib/direction.js +9 -1
  421. package/dist/lib/direction.js.map +1 -1
  422. package/dist/lib/focus-left.d.ts +63 -0
  423. package/dist/lib/focus-left.js +67 -0
  424. package/dist/lib/focus-left.js.map +1 -0
  425. package/dist/lib/format.d.ts +5 -3
  426. package/dist/lib/format.js +16 -13
  427. package/dist/lib/format.js.map +1 -1
  428. package/dist/lib/http-status.d.ts +24 -0
  429. package/dist/lib/http-status.js +14 -0
  430. package/dist/lib/http-status.js.map +1 -0
  431. package/dist/lib/intl-cache.d.ts +14 -0
  432. package/dist/lib/intl-cache.js +15 -0
  433. package/dist/lib/intl-cache.js.map +1 -0
  434. package/dist/lib/isin.d.ts +10 -0
  435. package/dist/lib/isin.js.map +1 -1
  436. package/dist/lib/iso-day.d.ts +11 -0
  437. package/dist/lib/iso-day.js +8 -0
  438. package/dist/lib/iso-day.js.map +1 -0
  439. package/dist/lib/lazy-with-fallback.d.ts +31 -0
  440. package/dist/lib/lazy-with-fallback.js +40 -0
  441. package/dist/lib/lazy-with-fallback.js.map +1 -0
  442. package/dist/lib/path-pattern.d.ts +34 -0
  443. package/dist/lib/path-pattern.js +24 -0
  444. package/dist/lib/path-pattern.js.map +1 -0
  445. package/dist/lib/reduced-motion.d.ts +2 -1
  446. package/dist/lib/reduced-motion.js.map +1 -1
  447. package/dist/lib/round.d.ts +2 -1
  448. package/dist/lib/round.js +2 -1
  449. package/dist/lib/round.js.map +1 -1
  450. package/dist/lib/safe-storage.d.ts +24 -5
  451. package/dist/lib/safe-storage.js +27 -2
  452. package/dist/lib/safe-storage.js.map +1 -1
  453. package/dist/lib/session-refresh.js +1 -1
  454. package/dist/lib/session-refresh.js.map +1 -1
  455. package/dist/lib/share-format.d.ts +13 -0
  456. package/dist/lib/share-format.js +10 -0
  457. package/dist/lib/share-format.js.map +1 -0
  458. package/dist/lib/text-case.d.ts +17 -0
  459. package/dist/lib/text-case.js +8 -0
  460. package/dist/lib/text-case.js.map +1 -0
  461. package/dist/lib/text-match.d.ts +38 -0
  462. package/dist/lib/text-match.js +24 -2
  463. package/dist/lib/text-match.js.map +1 -1
  464. package/dist/lib/thenable.d.ts +13 -0
  465. package/dist/lib/thenable.js +8 -0
  466. package/dist/lib/thenable.js.map +1 -0
  467. package/dist/lib/translation-review.d.ts +7 -2
  468. package/dist/lib/translation-review.js +4 -4
  469. package/dist/lib/translation-review.js.map +1 -1
  470. package/dist/notifications/device-zone-report.js +1 -1
  471. package/dist/notifications/device-zone-report.js.map +1 -1
  472. package/dist/notifications/notification-bell.d.ts +2 -1
  473. package/dist/notifications/notification-bell.js +3 -3
  474. package/dist/notifications/notification-bell.js.map +1 -1
  475. package/dist/notifications/notification-inbox.d.ts +10 -6
  476. package/dist/notifications/notification-inbox.js +26 -48
  477. package/dist/notifications/notification-inbox.js.map +1 -1
  478. package/dist/notifications/notification-parts.d.ts +3 -2
  479. package/dist/notifications/notification-parts.js +1 -3
  480. package/dist/notifications/notification-parts.js.map +1 -1
  481. package/dist/notifications/notification-types-setting.js +1 -1
  482. package/dist/notifications/notification-types-setting.js.map +1 -1
  483. package/dist/notifications/notifications-labels.d.ts +6 -0
  484. package/dist/notifications/notifications-labels.js +1 -0
  485. package/dist/notifications/notifications-labels.js.map +1 -1
  486. package/dist/notifications/push-device-setting.js +1 -1
  487. package/dist/notifications/push-device-setting.js.map +1 -1
  488. package/dist/notifications/push-devices-setting.d.ts +2 -1
  489. package/dist/notifications/push-devices-setting.js +27 -49
  490. package/dist/notifications/push-devices-setting.js.map +1 -1
  491. package/dist/notifications/quiet-hours-setting.d.ts +10 -3
  492. package/dist/notifications/quiet-hours-setting.js +65 -26
  493. package/dist/notifications/quiet-hours-setting.js.map +1 -1
  494. package/dist/notifications/unsubscribe-panel.js +4 -7
  495. package/dist/notifications/unsubscribe-panel.js.map +1 -1
  496. package/dist/notifications/use-push-device.d.ts +39 -1
  497. package/dist/notifications/use-push-device.js +28 -7
  498. package/dist/notifications/use-push-device.js.map +1 -1
  499. package/dist/offline/query-persistence.d.ts +3 -1
  500. package/dist/offline/query-persistence.js +19 -2
  501. package/dist/offline/query-persistence.js.map +1 -1
  502. package/dist/rhf/fields.d.ts +11 -4
  503. package/dist/rhf/fields.js.map +1 -1
  504. package/dist/search/command-palette.js +1 -1
  505. package/dist/search/command-palette.js.map +1 -1
  506. package/dist/search/global-search.js +19 -9
  507. package/dist/search/global-search.js.map +1 -1
  508. package/dist/search/use-command-key.d.ts +12 -1
  509. package/dist/search/use-command-key.js +3 -12
  510. package/dist/search/use-command-key.js.map +1 -1
  511. package/dist/search.d.ts +7 -0
  512. package/dist/search.js +10 -0
  513. package/dist/search.js.map +1 -1
  514. package/dist/settings/use-settings-focus.js +1 -0
  515. package/dist/settings/use-settings-focus.js.map +1 -1
  516. package/dist/shell/account-menu-language.d.ts +21 -5
  517. package/dist/shell/account-menu-language.js +3 -1
  518. package/dist/shell/account-menu-language.js.map +1 -1
  519. package/dist/shell/account-menu-text-size.d.ts +26 -9
  520. package/dist/shell/account-menu-text-size.js +3 -2
  521. package/dist/shell/account-menu-text-size.js.map +1 -1
  522. package/dist/shell/dev-menu.js +5 -18
  523. package/dist/shell/dev-menu.js.map +1 -1
  524. package/dist/shell/role-switcher.d.ts +7 -0
  525. package/dist/shell/role-switcher.js.map +1 -1
  526. package/dist/shell/top-bar-actions.d.ts +1 -1
  527. package/dist/shell/top-bar-actions.js.map +1 -1
  528. package/dist/shell/top-bar-parts-labels.d.ts +0 -2
  529. package/dist/shell/top-bar-parts-labels.js +0 -1
  530. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  531. package/dist/shell.d.ts +27 -8
  532. package/dist/shell.js +28 -7
  533. package/dist/shell.js.map +1 -1
  534. package/dist/sync/engine.d.ts +30 -4
  535. package/dist/sync/engine.js +172 -52
  536. package/dist/sync/engine.js.map +1 -1
  537. package/dist/sync.d.ts +6 -2
  538. package/dist/sync.js +6 -1
  539. package/dist/sync.js.map +1 -1
  540. package/dist/testing/css-variables.js +3 -8
  541. package/dist/testing/css-variables.js.map +1 -1
  542. package/dist/theme/contrast.d.ts +2 -1
  543. package/dist/theme/contrast.js +3 -8
  544. package/dist/theme/contrast.js.map +1 -1
  545. package/dist/theme/palette-store.js +6 -6
  546. package/dist/theme/palette-store.js.map +1 -1
  547. package/dist/theme/text-size.js +3 -8
  548. package/dist/theme/text-size.js.map +1 -1
  549. package/dist/theme/theme-store.js +3 -6
  550. package/dist/theme/theme-store.js.map +1 -1
  551. package/dist/tour/tour-catalogue.d.ts +7 -4
  552. package/dist/tour/tour-catalogue.js +12 -10
  553. package/dist/tour/tour-catalogue.js.map +1 -1
  554. package/dist/tour/tour-dock.d.ts +0 -2
  555. package/dist/tour/tour-dock.js +0 -2
  556. package/dist/tour/tour-dock.js.map +1 -1
  557. package/dist/tour/tour-gates.d.ts +4 -0
  558. package/dist/tour/tour-gates.js +14 -0
  559. package/dist/tour/tour-gates.js.map +1 -1
  560. package/dist/tour/tour-overlay.d.ts +27 -0
  561. package/dist/tour/tour-overlay.js +741 -0
  562. package/dist/tour/tour-overlay.js.map +1 -0
  563. package/dist/tour/tour.d.ts +16 -4
  564. package/dist/tour/tour.js +62 -778
  565. package/dist/tour/tour.js.map +1 -1
  566. package/dist/tour/use-tour-progress.js +1 -1
  567. package/dist/tour/use-tour-progress.js.map +1 -1
  568. package/dist/tour/use-tour-url-sync.js +2 -2
  569. package/dist/tour/use-tour-url-sync.js.map +1 -1
  570. package/dist/wizard/stepper-nav.d.ts +5 -1
  571. package/dist/wizard/stepper-nav.js.map +1 -1
  572. package/dist/wizard/types.d.ts +6 -2
  573. package/dist/wizard/types.js.map +1 -1
  574. package/dist/wizard/use-wizard.d.ts +3 -1
  575. package/dist/wizard/use-wizard.js.map +1 -1
  576. package/dist/wizard/validation.d.ts +3 -1
  577. package/dist/wizard/validation.js.map +1 -1
  578. package/package.json +12 -8
  579. package/src/account/account-parts.ts +5 -25
  580. package/src/account/data-export-setting-labels.ts +1 -1
  581. package/src/account/delete-account-setting-labels.ts +1 -1
  582. package/src/account/email-change-setting-labels.ts +1 -1
  583. package/src/account/passkeys-setting.tsx +60 -38
  584. package/src/account/sessions-setting.tsx +18 -12
  585. package/src/admin/admin-action-confirm.tsx +5 -10
  586. package/src/admin/admin-parts.ts +11 -25
  587. package/src/admin/mail-test-card-labels.ts +1 -1
  588. package/src/admin/plan-change-labels.ts +5 -23
  589. package/src/admin/plan-change.tsx +7 -44
  590. package/src/admin/reviewer-scope-editor.tsx +3 -3
  591. package/src/auth/auth-errors-labels.ts +23 -0
  592. package/src/auth/auth-errors.ts +11 -38
  593. package/src/auth/complete-name-dialog.tsx +9 -8
  594. package/src/auth/forgot-password-form-labels.ts +1 -1
  595. package/src/auth/forgot-password-form.tsx +11 -6
  596. package/src/auth/register-form-labels.ts +1 -1
  597. package/src/auth/reset-password-form-labels.ts +1 -1
  598. package/src/auth/reset-password-form.tsx +8 -4
  599. package/src/auth/sign-in-form-labels.ts +1 -1
  600. package/src/auth/status-parts.tsx +0 -6
  601. package/src/auth/verify-email-labels.ts +1 -1
  602. package/src/auth/verify-email.tsx +11 -6
  603. package/src/billing/cancel-request.ts +28 -6
  604. package/src/billing/cancel-subscription.tsx +29 -22
  605. package/src/billing/dialog-close.ts +34 -0
  606. package/src/billing/plan-card.tsx +36 -11
  607. package/src/billing/plan-change-labels.ts +66 -0
  608. package/src/billing/plan-switch.tsx +31 -20
  609. package/src/chart.ts +21 -3
  610. package/src/components/account-chips.tsx +20 -23
  611. package/src/components/alert-banner.tsx +82 -5
  612. package/src/components/amount-input.tsx +32 -14
  613. package/src/components/autocomplete.tsx +64 -70
  614. package/src/components/bulk-action-bar.tsx +70 -2
  615. package/src/components/button-look.ts +5 -5
  616. package/src/components/button.tsx +2 -2
  617. package/src/components/calculator.tsx +98 -17
  618. package/src/components/chart-edge.ts +34 -1
  619. package/src/components/chart-figure.tsx +35 -0
  620. package/src/components/chart-zoom-core.tsx +6 -5
  621. package/src/components/chart.tsx +26 -5
  622. package/src/components/combobox-core.tsx +303 -69
  623. package/src/components/combobox.tsx +184 -155
  624. package/src/components/copy-button.tsx +34 -23
  625. package/src/components/country-select.tsx +16 -11
  626. package/src/components/data-table-column-settings.tsx +52 -10
  627. package/src/components/data-table-filters.ts +6 -4
  628. package/src/components/data-table-fit.ts +19 -5
  629. package/src/components/data-table-groups.tsx +1 -1
  630. package/src/components/data-table-head.tsx +32 -22
  631. package/src/components/data-table-mobile.tsx +110 -27
  632. package/src/components/data-table-pagination.tsx +21 -6
  633. package/src/components/data-table-resize.ts +1 -1
  634. package/src/components/data-table-row-actions.tsx +3 -3
  635. package/src/components/data-table-row.tsx +25 -14
  636. package/src/components/data-table-selection.ts +1 -1
  637. package/src/components/data-table-shared.tsx +33 -1
  638. package/src/components/data-table-sort.ts +57 -2
  639. package/src/components/data-table-virtual.ts +32 -8
  640. package/src/components/data-table.tsx +267 -41
  641. package/src/components/date-field.tsx +65 -8
  642. package/src/components/date-picker.tsx +125 -48
  643. package/src/components/date-range-picker.tsx +82 -31
  644. package/src/components/dialog-frame.tsx +2 -1
  645. package/src/components/entity-combobox.tsx +4 -1
  646. package/src/components/error-boundary.tsx +53 -39
  647. package/src/components/field-base-labels.ts +20 -0
  648. package/src/components/field-base.tsx +13 -7
  649. package/src/components/field-sync-labels.ts +1 -0
  650. package/src/components/field-sync.tsx +23 -1
  651. package/src/components/form-actions.tsx +20 -5
  652. package/src/components/hover-menu.tsx +18 -8
  653. package/src/components/icon-button.tsx +14 -9
  654. package/src/components/inline-confirm.tsx +116 -0
  655. package/src/components/inline-entity-combobox.tsx +198 -152
  656. package/src/components/input.tsx +1 -1
  657. package/src/components/jump-calendar.tsx +30 -146
  658. package/src/components/legal-page.tsx +10 -7
  659. package/src/components/legal.tsx +4 -2
  660. package/src/components/lightbox.tsx +21 -8
  661. package/src/components/list.tsx +14 -5
  662. package/src/components/measured-grid.tsx +8 -4
  663. package/src/components/mini-calendar.tsx +6 -5
  664. package/src/components/modal.tsx +45 -4
  665. package/src/components/money-draft.ts +16 -5
  666. package/src/components/money-field.tsx +2 -2
  667. package/src/components/month-grid.tsx +229 -0
  668. package/src/components/month-picker.tsx +192 -200
  669. package/src/components/multi-entity-combobox.tsx +1 -0
  670. package/src/components/multi-select.tsx +21 -9
  671. package/src/components/number-field.tsx +14 -7
  672. package/src/components/number-input.tsx +9 -3
  673. package/src/components/numpad-sheet.tsx +4 -3
  674. package/src/components/picker-sheet.tsx +12 -9
  675. package/src/components/pie-chart-labels.ts +8 -0
  676. package/src/components/pie-chart.tsx +22 -28
  677. package/src/components/popover.tsx +18 -1
  678. package/src/components/reauth-dialog.tsx +6 -10
  679. package/src/components/row-actions.tsx +2 -1
  680. package/src/components/section-nav.tsx +7 -0
  681. package/src/components/series-chart-axes.ts +11 -3
  682. package/src/components/series-chart-labels.ts +7 -4
  683. package/src/components/series-chart.tsx +178 -69
  684. package/src/components/share-card.tsx +6 -6
  685. package/src/components/sign-chip-labels.ts +1 -0
  686. package/src/components/sign-chip.tsx +10 -1
  687. package/src/components/tabs-labels.ts +13 -0
  688. package/src/components/tabs.tsx +35 -19
  689. package/src/components/toggle-legend.tsx +16 -11
  690. package/src/components/translation-review-editor.tsx +6 -5
  691. package/src/components/translation-review.tsx +4 -3
  692. package/src/components/treemap-labels.ts +39 -0
  693. package/src/components/treemap-layout.ts +210 -0
  694. package/src/components/treemap.tsx +722 -133
  695. package/src/components/use-table-state.ts +12 -9
  696. package/src/data-table.ts +9 -2
  697. package/src/eslint/role-utilities.ts +97 -19
  698. package/src/feedback/feedback-dialog.tsx +30 -19
  699. package/src/feedback/feedback-mutation.ts +41 -0
  700. package/src/feedback/feedback-page-link.ts +90 -0
  701. package/src/feedback/feedback-public-list.tsx +17 -5
  702. package/src/feedback/feedback-replies.tsx +13 -6
  703. package/src/feedback/feedback-row-detail-labels.ts +5 -0
  704. package/src/feedback/feedback-row-detail.tsx +103 -67
  705. package/src/feedback/feedback-status-undo.ts +41 -27
  706. package/src/feedback/feedback-submit-dialog.tsx +2 -2
  707. package/src/feedback/feedback-submit.tsx +21 -7
  708. package/src/feedback/feedback-swipe.tsx +1 -1
  709. package/src/feedback/feedback-table.tsx +86 -38
  710. package/src/feedback/feedback-thread-labels.ts +2 -2
  711. package/src/feedback/feedback-thread.tsx +27 -25
  712. package/src/feedback.ts +42 -0
  713. package/src/hooks/use-dismiss.ts +3 -2
  714. package/src/hooks/use-focus-trap.ts +8 -24
  715. package/src/hooks/use-hotkey.ts +24 -7
  716. package/src/hooks/use-long-press.ts +16 -2
  717. package/src/hooks/use-media-query.ts +20 -4
  718. package/src/hooks/use-mounted.ts +39 -0
  719. package/src/hooks/use-overlay-history.ts +70 -5
  720. package/src/i18n/defaults.ts +4 -5
  721. package/src/i18n/german.ts +8 -2
  722. package/src/i18n/kit-labels.tsx +9 -2
  723. package/src/i18n/locales/es.ts +8 -2
  724. package/src/i18n/locales/fr.ts +21 -13
  725. package/src/i18n/locales/hu.ts +10 -2
  726. package/src/i18n/locales/it.ts +8 -2
  727. package/src/i18n/locales/zh.ts +9 -2
  728. package/src/i18n/review.ts +5 -0
  729. package/src/index.ts +103 -13
  730. package/src/landing/routing.tsx +14 -10
  731. package/src/lib/broadcast.ts +52 -0
  732. package/src/lib/calc.ts +13 -2
  733. package/src/lib/clipboard-minus.ts +14 -4
  734. package/src/lib/date-format.ts +89 -13
  735. package/src/lib/day-format.ts +96 -0
  736. package/src/lib/decimal-marks.ts +12 -3
  737. package/src/lib/describe-failure.ts +18 -0
  738. package/src/lib/direction.ts +39 -0
  739. package/src/lib/focus-left.ts +156 -0
  740. package/src/lib/format.ts +34 -19
  741. package/src/lib/http-status.ts +38 -0
  742. package/src/lib/intl-cache.ts +25 -0
  743. package/src/lib/isin.ts +10 -0
  744. package/src/lib/iso-day.ts +13 -0
  745. package/src/lib/lazy-with-fallback.ts +82 -0
  746. package/src/lib/path-pattern.ts +55 -0
  747. package/src/lib/reduced-motion.ts +2 -1
  748. package/src/lib/round.ts +7 -2
  749. package/src/lib/safe-storage.ts +48 -6
  750. package/src/lib/session-refresh.ts +1 -1
  751. package/src/lib/share-format.ts +18 -0
  752. package/src/lib/text-case.ts +19 -0
  753. package/src/lib/text-match.ts +69 -2
  754. package/src/lib/thenable.ts +19 -0
  755. package/src/lib/translation-review.ts +13 -5
  756. package/src/notifications/device-zone-report.ts +1 -1
  757. package/src/notifications/notification-bell.tsx +7 -4
  758. package/src/notifications/notification-inbox.tsx +40 -58
  759. package/src/notifications/notification-parts.tsx +3 -4
  760. package/src/notifications/notification-types-setting.tsx +1 -1
  761. package/src/notifications/notifications-labels.ts +7 -0
  762. package/src/notifications/push-device-setting.tsx +1 -1
  763. package/src/notifications/push-devices-setting.tsx +30 -55
  764. package/src/notifications/quiet-hours-setting.tsx +93 -31
  765. package/src/notifications/unsubscribe-panel.tsx +4 -7
  766. package/src/notifications/use-push-device.ts +76 -10
  767. package/src/offline/query-persistence.ts +35 -3
  768. package/src/rhf/fields.tsx +11 -4
  769. package/src/search/command-palette.tsx +2 -4
  770. package/src/search/global-search.tsx +36 -15
  771. package/src/search/use-command-key.ts +13 -12
  772. package/src/search.ts +9 -0
  773. package/src/settings/use-settings-focus.ts +3 -0
  774. package/src/shell/account-menu-language.tsx +27 -5
  775. package/src/shell/account-menu-text-size.tsx +32 -10
  776. package/src/shell/dev-menu.tsx +7 -21
  777. package/src/shell/role-switcher.tsx +7 -0
  778. package/src/shell/top-bar-actions.tsx +1 -1
  779. package/src/shell/top-bar-parts-labels.ts +0 -3
  780. package/src/shell.ts +40 -9
  781. package/src/sync/engine.ts +339 -61
  782. package/src/sync.ts +8 -1
  783. package/src/testing/css-variables.ts +8 -9
  784. package/src/theme/contrast.ts +8 -9
  785. package/src/theme/palette-store.ts +9 -9
  786. package/src/theme/text-size.ts +6 -8
  787. package/src/theme/theme-store.ts +7 -9
  788. package/src/tour/tour-catalogue.tsx +26 -15
  789. package/src/tour/tour-dock.ts +0 -2
  790. package/src/tour/tour-gates.ts +23 -0
  791. package/src/tour/tour-overlay.tsx +1170 -0
  792. package/src/tour/tour.tsx +116 -1193
  793. package/src/tour/use-tour-progress.ts +1 -1
  794. package/src/tour/use-tour-url-sync.ts +7 -3
  795. package/src/wizard/stepper-nav.tsx +3 -1
  796. package/src/wizard/types.ts +11 -2
  797. package/src/wizard/use-wizard.ts +2 -1
  798. package/src/wizard/validation.ts +8 -2
  799. package/tokens.css +2 -1
  800. package/dist/components/ui-labels.js.map +0 -1
  801. package/dist/shell/dev-switchers.d.ts +0 -6
  802. package/dist/shell/dev-switchers.js +0 -12
  803. package/dist/shell/dev-switchers.js.map +0 -1
  804. package/src/components/ui-labels.ts +0 -24
  805. package/src/shell/dev-switchers.tsx +0 -18
@@ -1,6 +1,6 @@
1
1
  import type { ComponentPropsWithoutRef, ReactNode, Ref, RefObject } from "react";
2
2
  import { type DatePickerLabels, type KitDateFormatContext } from "../i18n/kit-labels.js";
3
- import { type FieldHelpProps } from "./field-parts.js";
3
+ import { type DisabledReasonDisplay, type FieldHelpProps } from "./field-parts.js";
4
4
  import type { RemLength } from "../hooks/use-breakpoint.js";
5
5
  import type { MiniCalendarProps } from "./mini-calendar.js";
6
6
  /**
@@ -28,6 +28,22 @@ export interface DatePickerBaseProps extends Omit<ComponentPropsWithoutRef<"div"
28
28
  * `<UiKitProvider labels>`; kept as a prop because it predates the provider. */
29
29
  clearLabel?: string;
30
30
  disabled?: boolean;
31
+ /**
32
+ * Why the date cannot be changed — {@link Select}'s `disabledReason`, for a picker that
33
+ * SAVES on change (a report's period kept per user, a filter persisted as it moves).
34
+ * The trigger stays focusable and `aria-disabled`, the calendar does not open, the ‹ ›
35
+ * and Today buttons refuse, the clear is not offered, and `onChange` never fires; the
36
+ * reason is a line under the field at Large and on a touch screen, else in the kit
37
+ * {@link Tooltip} (`disabledReasonDisplay`), and on the trigger's `aria-describedby`.
38
+ * Wins over `disabled`.
39
+ *
40
+ * 0.52 (refactor audit 2026-10-30, API-3): the date pickers and MonthPicker were the
41
+ * only value fields with no lock opt-in.
42
+ */
43
+ disabledReason?: ReactNode;
44
+ /** Where the reason shows — {@link Button}'s `disabledReasonDisplay`: a line under the
45
+ * field at Large and on a touch screen by default, the tooltip otherwise. */
46
+ disabledReasonDisplay?: DisabledReasonDisplay;
31
47
  /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */
32
48
  invalid?: boolean;
33
49
  /**
@@ -144,6 +160,9 @@ export interface DateTriggerAttributes {
144
160
  /** The field's required state — `Field required` hands it down with the id pair. */
145
161
  "aria-required"?: boolean | "true" | "false";
146
162
  disabled?: boolean;
163
+ /** Locked by the write lock (0.52, `commit` / `disabledReason`): focusable, and the
164
+ * click opens nothing. Dress a custom trigger for it as for `disabled`. */
165
+ "aria-disabled"?: true;
147
166
  onClick: () => void;
148
167
  className: string;
149
168
  }
@@ -158,7 +177,7 @@ type TriggerRenderer = (attrs: DateTriggerAttributes, valueId: string) => ReactN
158
177
  export type PanelFrame = (body: ReactNode, actions?: ReactNode) => ReactNode;
159
178
  /** A field-styled trigger that opens a portalled MiniCalendar. Shared by the
160
179
  * single- and range-date pickers (date-picker.tsx, date-range-picker.tsx). */
161
- export declare function DateField({ triggerText, hasValue, label, disabled, clearable, clearLabel, panelLabel, className, width, onClear, invalid, sheet, sheetBackCloses, renderTrigger, labelHint, children, "aria-required": ariaRequired, fieldRef, ...rest }: Omit<ComponentPropsWithoutRef<"div">, "children"> & {
180
+ export declare function DateField({ triggerText, hasValue, label, disabled, locked, clearable, clearLabel, panelLabel, className, width, onClear, invalid, sheet, sheetBackCloses, renderTrigger, labelHint, children, "aria-required": ariaRequired, fieldRef, ...rest }: Omit<ComponentPropsWithoutRef<"div">, "children"> & {
162
181
  /** The picker's `ref` prop: the trigger (API-3). */
163
182
  fieldRef?: Ref<HTMLButtonElement>;
164
183
  triggerText: string;
@@ -167,6 +186,9 @@ export declare function DateField({ triggerText, hasValue, label, disabled, clea
167
186
  /** A `FieldHint` for the label line, beside the label. */
168
187
  labelHint?: ReactNode;
169
188
  disabled?: boolean;
189
+ /** The write lock is on: the trigger is focusable and opens nothing, no clear. The
190
+ * picker draws the reason's frame around the field (or its step row). */
191
+ locked?: boolean;
170
192
  clearable?: boolean;
171
193
  /** Already resolved by the picker (prop > provider > English). */
172
194
  clearLabel: string;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
4
4
  import { Calendar, X } from "lucide-react";
5
5
  import { cn } from "../lib/cn.js";
6
6
  import { dirOf } from "../lib/direction.js";
@@ -13,7 +13,11 @@ import {
13
13
  } from "../i18n/kit-labels.js";
14
14
  import { splitTriggerAria } from "./trigger-aria.js";
15
15
  import { FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID } from "./field-base.js";
16
- import { assignRef, FieldLabelLine, LABEL_ABOVE_BOX } from "./field-parts.js";
16
+ import {
17
+ assignRef,
18
+ FieldLabelLine,
19
+ LABEL_ABOVE_BOX
20
+ } from "./field-parts.js";
17
21
  import { FullBleedDialog } from "./full-bleed-dialog.js";
18
22
  import { Popover } from "./popover.js";
19
23
  function useTriggerFormat(source, locale, formatOptions, formatValue) {
@@ -45,6 +49,7 @@ function DateFieldTrigger({
45
49
  valueId,
46
50
  padded,
47
51
  disabled,
52
+ locked,
48
53
  invalid,
49
54
  required,
50
55
  panelId,
@@ -69,8 +74,15 @@ function DateFieldTrigger({
69
74
  const attrs = {
70
75
  ref: triggerRef,
71
76
  type: "button",
72
- disabled,
73
- onClick: toggle,
77
+ // The focusable kind of disabled while locked (Button's rule): native `disabled`
78
+ // would take the field out of the tab order, and the reason with it.
79
+ disabled: locked ? void 0 : disabled,
80
+ "aria-disabled": locked || void 0,
81
+ // Locked, the calendar does not open. One open when the lock arrives closes: the
82
+ // picker moves the field into the reason's frame, which remounts it, as Select's.
83
+ onClick: () => {
84
+ if (!locked) toggle();
85
+ },
74
86
  // The name has to carry the VALUE. A bare `aria-label={label}` — which is what
75
87
  // this was — replaces the button's text outright, so the one field in the form
76
88
  // whose entire job is to show a date announced "Due date" and stopped there,
@@ -104,7 +116,7 @@ function DateFieldTrigger({
104
116
  FIELD_TRIGGER,
105
117
  "pe-9",
106
118
  padded && FIELD_FLOATING_PAD,
107
- disabled && "cursor-not-allowed opacity-50",
119
+ (disabled || locked) && "cursor-not-allowed opacity-50",
108
120
  // An `aria-invalid` from a Field spread paints too, as on Input and AmountInput.
109
121
  isInvalid && FIELD_INVALID
110
122
  )
@@ -121,6 +133,7 @@ function DateField({
121
133
  hasValue,
122
134
  label,
123
135
  disabled,
136
+ locked = false,
124
137
  clearable,
125
138
  clearLabel,
126
139
  panelLabel,
@@ -140,8 +153,16 @@ function DateField({
140
153
  fieldRef,
141
154
  ...rest
142
155
  }) {
143
- const showClear = Boolean(clearable && hasValue && !disabled);
156
+ const showClear = Boolean(clearable && hasValue && !disabled && !locked);
144
157
  const [aria, wrapperRest] = splitTriggerAria(rest);
158
+ const triggerEl = useRef(null);
159
+ const toTrigger = useCallback(
160
+ (node) => {
161
+ triggerEl.current = node;
162
+ assignRef(fieldRef, node);
163
+ },
164
+ [fieldRef]
165
+ );
145
166
  const rootRef = useRef(null);
146
167
  const [dir, setDir] = useState("ltr");
147
168
  const id = useId();
@@ -194,10 +215,11 @@ function DateField({
194
215
  valueId,
195
216
  padded: label !== void 0,
196
217
  disabled,
218
+ locked,
197
219
  invalid,
198
220
  required: ariaRequired,
199
221
  renderTrigger,
200
- fieldRef
222
+ fieldRef: toTrigger
201
223
  }
202
224
  );
203
225
  return (
@@ -227,7 +249,10 @@ function DateField({
227
249
  {
228
250
  type: "button",
229
251
  "aria-label": clearLabel,
230
- onClick: onClear,
252
+ onClick: () => {
253
+ onClear();
254
+ triggerEl.current?.focus();
255
+ },
231
256
  className: "absolute end-1.5 top-1/2 -translate-y-1/2 rounded p-1 text-placeholder hover:text-secondary",
232
257
  children: /* @__PURE__ */ jsx(X, { className: "size-4" })
233
258
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/date-field.tsx"],"sourcesContent":["// The field both date pickers draw: the trigger, the popover or phone sheet it opens,\n// the clear button — and the props, label hook and trigger format the two share.\n// Moved out of date-picker.tsx as it was (MOD-9, refactor audit 2026-10); date-picker\n// still exports the one public name here, `DateTriggerAttributes`.\nimport { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode, Ref, RefObject } from \"react\";\nimport { Calendar, X } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { dirOf, type Direction } from \"../lib/direction\";\nimport { formatIsoDate } from \"../lib/dates\";\nimport {\n DEFAULT_DATE_PICKER_LABELS,\n DEFAULT_PICKER_SHEET_LABELS,\n useKitDateFormatter,\n useKitLabels,\n type DatePickerLabels,\n type KitDateFormatContext,\n} from \"../i18n/kit-labels\";\nimport { splitTriggerAria } from \"./trigger-aria\";\nimport type { TriggerAria } from \"./trigger-aria\";\nimport { FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID } from \"./field-base\";\nimport { assignRef, FieldLabelLine, LABEL_ABOVE_BOX, type FieldHelpProps } from \"./field-parts\";\nimport { FullBleedDialog } from \"./full-bleed-dialog\";\nimport type { RemLength } from \"../hooks/use-breakpoint\";\nimport type { MiniCalendarProps } from \"./mini-calendar\";\nimport { Popover } from \"./popover\";\n\n/**\n * What both pickers share — and, through `ComponentPropsWithoutRef<\"div\">`, everything a\n * `<div>` takes, so a `data-tour` anchor, a test id or an `aria-describedby` reaches the\n * field's root element instead of being dropped (audit §api-design).\n *\n * `onChange` is omitted from those attributes: the DOM event of that name carries a\n * `FormEvent`, while both pickers answer with ISO date strings. Each declares its own\n * (date-picker.tsx, date-range-picker.tsx).\n */\nexport interface DatePickerBaseProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n /** BCP 47 tag for the trigger's date and the calendar. Falls back to\n * `<UiKitProvider locale>`, then to the runtime's default. */\n locale?: string;\n /** Embedded top-boundary label, matching the native Input/Select fields. */\n label?: ReactNode;\n placeholder?: string;\n /** Inclusive ISO bounds for selectable days. */\n min?: string;\n max?: string;\n /** Show an X to clear the value (emits `\"\"`). */\n clearable?: boolean;\n /** Accessible name of the clear button. Wins over `datePicker.clear` from\n * `<UiKitProvider labels>`; kept as a prop because it predates the provider. */\n clearLabel?: string;\n disabled?: boolean;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /**\n * Standing advice for the field (keksdose K4, \"uniform field anatomy\"), as on a\n * {@link Select}: plain TEXT is a caption under the field, attached with\n * `aria-describedby`; a {@link FieldHint} rides the label line (or, unlabelled, sits\n * at the field's end, outside the box).\n */\n hint?: ReactNode;\n /**\n * What is wrong with the date, in the caller's words (\"The due date is before the\n * invoice date\") — {@link Input}'s `error` (keksdose K4: DateField errors had no slot).\n * Rendered under the field (under the whole row, with `step` / `today`), attached to\n * the trigger with `aria-describedby` — merged with any the caller passed, after the\n * caption — and implies `invalid`. Passing the key at all keeps the field's box while\n * there is no message, so the trigger is never remounted as one comes and goes.\n */\n error?: ReactNode;\n className?: string;\n /** Intl options for the trigger's formatted date (default: locale short date). Win\n * over the provider's `formatDate`. */\n formatOptions?: Intl.DateTimeFormatOptions;\n /** Render the trigger's text for an ISO date yourself; wins over `formatOptions` and\n * over the provider's `formatDate`.\n * For a host whose date rendering is more than one `Intl` call can say — Keksdose\n * puts the weekday's name in the UI language beside digits ordered by a separate\n * format preference, two locales in one string. `locale` still drives the calendar.\n * Set that rendering once with `<UiKitProvider formatDate>` (keksdose K12) and this\n * is for the one field that differs. */\n formatValue?: (iso: string) => string;\n /** Passed through to the {@link MiniCalendar} this opens. Its month arrows are\n * icon-only, so their `aria-label` is the only name they have — and the package\n * ships English defaults, which a translating host has to be able to replace.\n * Usually unnecessary now: the calendar reads `miniCalendar` from the provider. */\n calendarLabels?: MiniCalendarProps[\"labels\"];\n /**\n * Make the panel's caption (\"July 2026\") a button that opens the year's twelve\n * months, with ‹ › stepping a year; picking a month returns to its days. Far dates\n * stop being a march of month arrows.\n *\n * kastlan is why: its service-charge period runs July to June, and with one month on\n * screen and ‹ › the only way across, the end of the period was eleven clicks past\n * the start — so kastlan kept two date fields instead of `RhfDateRangePicker`. With\n * the jump the whole period is open, caption, Jul, 1, caption, next year, Jun, 30.\n *\n * What it adds, for the keyboard and a screen reader:\n * - the caption is a disclosure button (`aria-expanded`). Its name is the month it\n * shows, so speech input can say what it sees; it is `aria-live`, as the\n * calendar's caption was, so paging months still says where you landed; and it is\n * described by `monthPicker.panel` (\"Choose a month\").\n * - the month grid is `MonthPicker`'s grid: one tab stop, arrows walk the months\n * (across the year's edge), Home/End the row, PageUp/PageDown a year, Enter or a\n * click picks. Focus opens on the month on show and comes back on its days — the\n * selected day if it is in that month, else the 1st. Months outside `min`/`max`\n * are `aria-disabled`, and a year arrow is once the whole year beyond is.\n * - Escape in the month grid goes back to the days without closing the panel (focus\n * on the caption); in the days, Escape closes the panel as before.\n *\n * No new strings: the month arrows keep `miniCalendar.previousMonth` / `nextMonth`\n * (`calendarLabels`), and the month grid speaks with `MonthPicker`'s words —\n * `monthPicker.previousYear` / `nextYear` / `month` / `panel` — because it is that\n * grid, and a host that translated it once should not have to again.\n *\n * Off by default: the caption becomes one more stop in the panel's tab order, which a\n * 0.x minor does not spring on a form. Works in the popover and in\n * `DateRangePicker`'s phone sheet alike, beside presets and in `commit=\"apply\"`\n * (a preset picked while the months are open goes back to the days, on its start).\n *\n * Why not a two-month panel (`months={2}`) instead, or beside it: for a twelve-month\n * period it still pages six times where this takes one year step, and two\n * `MiniCalendar`s side by side each follow the value on their own — picking the end\n * in the right-hand month would pull the left one onto it — so the calendar would\n * have to learn to share a month first. Not worth that for a saving of one arrow on a\n * range across a single month boundary.\n */\n monthJump?: boolean;\n /**\n * The TRIGGER — the focusable button that opens the calendar, a `renderTrigger` one\n * too — as a React 19 ref prop (0.34, API-3, refactor audit 2026-10). The field took\n * none, so react-hook-form's focus-on-error reached it only through /rhf's id lookup,\n * and an app not at all. Not the wrapper, which takes the rest of the props.\n */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * The trigger's date renderer, in the kit's order of precedence: the picker's own\n * `formatValue`, then its `formatOptions` (an explicit `Intl` look for this field),\n * then the provider's `formatDate` (keksdose K12), then the locale's short date. A\n * provider formatter that answers `\"\"` falls through to the default for that date.\n */\nexport function useTriggerFormat(\n source: KitDateFormatContext[\"source\"],\n locale: string | undefined,\n formatOptions: Intl.DateTimeFormatOptions | undefined,\n formatValue: ((iso: string) => string) | undefined,\n): (iso: string) => string {\n const fromProvider = useKitDateFormatter();\n if (formatValue) return formatValue;\n return (iso) => {\n if (formatOptions === undefined && fromProvider) {\n // A single date stands alone, where the weekday helps; a range's two ends do not.\n const text = fromProvider(iso, { unit: \"day\", source, locale, weekday: source === \"datePicker\" });\n if (text) return text;\n }\n // No prop and no provider leaves `locale` undefined: the runtime's default (DUP-11).\n return formatIsoDate(iso, locale, formatOptions);\n };\n}\n\n/**\n * Everything the kit's own trigger button carries, handed to a custom one\n * ({@link DateRangePickerProps.renderTrigger}) to spread onto its `<button>`. Spread\n * it WHOLE: the ref is how focus comes back when the panel closes, the id pair and\n * `aria-labelledby` are the 0.5.1 naming (an external `<label htmlFor>`, a form\n * library's `aria-describedby`), and `role`/`aria-haspopup`/`aria-controls`/\n * `aria-expanded` are what tell a screen reader this opens a calendar and whether it\n * is open. `className` is the kit's field look (42px with a label, `pe-9` for the\n * glyph the field draws at its end) — merge yours after it with `cn`.\n */\nexport interface DateTriggerAttributes {\n ref: RefObject<HTMLButtonElement | null>;\n type: \"button\";\n id?: string;\n role: \"combobox\";\n \"aria-haspopup\": \"dialog\";\n \"aria-controls\": string;\n \"aria-expanded\": boolean;\n \"aria-labelledby\"?: string;\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n \"aria-invalid\"?: true;\n /** The field's required state — `Field required` hands it down with the id pair. */\n \"aria-required\"?: boolean | \"true\" | \"false\";\n disabled?: boolean;\n onClick: () => void;\n className: string;\n}\n\n/** A custom trigger, as `DateField` calls it; each picker adapts it to its own props. */\ntype TriggerRenderer = (attrs: DateTriggerAttributes, valueId: string) => ReactNode;\n\n/** A caller's trigger, as an element of its own: the attributes carry the trigger's\n * ref, and a ref is handed to a child as a prop, not passed to a function in the\n * middle of the parent's render. */\nfunction CustomTrigger({\n render,\n attrs,\n valueId,\n}: {\n render: TriggerRenderer;\n attrs: DateTriggerAttributes;\n valueId: string;\n}) {\n return render(attrs, valueId);\n}\n\n/**\n * The trigger's value wraps instead of truncating at Large and Extra large (0.32.1, the\n * 360 px Extra-large sweep; docs/text-size-harmonization.md §4 \"nothing truncates\"). A\n * range (\"01/09/2026 – 30/09/2026\") is 312 px at 150 %, and on a 360 px phone it read\n * \"01/09/2026 – 30/…\" — its end, the half a reader needs, was the half cut off. A\n * `large:` class, so Normal keeps its one-line field: since 0.33 (§10.17) the kit's one\n * `truncate-until-large` (tokens.css) in place of this file's own `truncate` +\n * `large:whitespace-normal large:break-words`.\n */\nconst VALUE_TRUNCATE = \"truncate-until-large\";\n\n/**\n * The field-shaped button that opens the calendar.\n *\n * A component of its own rather than JSX inline in `Popover`'s `trigger` render prop,\n * because handing focus back to it is an effect and a render prop is not somewhere a\n * hook can go.\n */\nfunction DateFieldTrigger({\n open,\n toggle,\n triggerRef,\n triggerText,\n hasValue,\n labelledBy,\n valueId,\n padded,\n disabled,\n invalid,\n required,\n panelId,\n aria,\n renderTrigger,\n fieldRef,\n}: {\n /** The caller's naming/description attributes, routed here from the field. */\n aria: TriggerAria;\n /** Draw the button yourself, with these attributes — see {@link DateTriggerAttributes}. */\n renderTrigger?: TriggerRenderer;\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n /** The picker's own `ref` prop (API-3) — the same element as `triggerRef`. */\n fieldRef?: Ref<HTMLButtonElement>;\n /** The panel's id — `role=\"combobox\"` requires `aria-controls`. */\n panelId: string;\n triggerText: string;\n hasValue: boolean;\n /** ids of the hidden label twin + the value span, in the order they are spoken. */\n labelledBy?: string;\n valueId: string;\n padded: boolean;\n disabled?: boolean;\n invalid?: boolean;\n /** `aria-required`, on the trigger with the rest of the field's ARIA. */\n required?: boolean | \"true\" | \"false\";\n}) {\n const wasOpen = useRef(false);\n useEffect(() => {\n const justClosed = wasOpen.current && !open;\n wasOpen.current = open;\n if (!justClosed) return;\n // Only when the panel still HAD focus. The day the user was standing on has just\n // been unmounted with the panel, which drops focus to <body> and leaves a\n // keyboard user back at the top of the document with no idea where they were.\n // But an outside click closes this panel too, and that click has already given\n // focus to whatever it landed on — dragging it back here would be worse than the\n // bug, so a focus that is somewhere real is left alone.\n const active = document.activeElement;\n if (!active || active === document.body) triggerRef.current?.focus();\n // Here rather than through `useFocusTrap`'s `restoreFocus`, and not only because\n // `Popover` runs no trap to carry it: that option restores to whatever held focus\n // when the panel opened, and on Safari — and on Firefox on macOS — clicking a\n // button does not focus it, so \"whatever held focus\" is the page. The requirement\n // is this trigger, every time, however the panel was opened.\n }, [open, triggerRef]);\n // The caller's `ref` (API-3, refactor audit 2026-10) is handed the trigger once it is\n // mounted, rather than merged into `attrs.ref`: that one is a `RefObject` by contract\n // (`DateTriggerAttributes`), which a `renderTrigger` button may read, and this way a\n // caller's own trigger is covered as well as the kit's.\n useLayoutEffect(() => {\n if (!fieldRef) return;\n assignRef(fieldRef, triggerRef.current);\n return () => assignRef(fieldRef, null);\n }, [fieldRef, triggerRef]);\n\n // One attribute object, worn either by the kit's own button below or by a caller's\n // (`renderTrigger`) — so a custom trigger cannot quietly fall out of step with the\n // naming and state this one announces.\n const isInvalid = Boolean(invalid) || aria[\"aria-invalid\"] === true || aria[\"aria-invalid\"] === \"true\";\n const attrs: DateTriggerAttributes = {\n ref: triggerRef,\n type: \"button\",\n disabled,\n onClick: toggle,\n // The name has to carry the VALUE. A bare `aria-label={label}` — which is what\n // this was — replaces the button's text outright, so the one field in the form\n // whose entire job is to show a date announced \"Due date\" and stopped there,\n // and a screen-reader user could not read back what they had picked without\n // opening the calendar and hunting for the selected day.\n //\n // Two ids rather than one composed string (`common.fieldValue`): \"label: value\"\n // is a sentence, and a name reference is spoken as the two texts in order with\n // no punctuation to translate at all. The hidden twin of the FieldLabel lives\n // in DateField, next to the label it copies.\n id: aria.id,\n \"aria-labelledby\": aria[\"aria-label\"] && !aria[\"aria-labelledby\"] ? undefined : labelledBy,\n \"aria-label\": aria[\"aria-label\"],\n \"aria-describedby\": aria[\"aria-describedby\"],\n // `role=\"combobox\"` on a button that opens a calendar is the APG date-picker\n // shape, and it is what makes the next two lines legal: `button` supports\n // neither `aria-expanded` nor `aria-invalid`, so the previous markup set an\n // invalid state that announced nothing (ESLint's `role-supports-aria-props`).\n //\n // `aria-haspopup=\"dialog\"` is now honest — it was not when this comment first\n // said so. `Popover` gained a real `role=\"dialog\"` in the same wave, so the\n // trigger's promise and the panel's role finally agree. The phone sheet is a\n // `role=\"dialog\"` too, and carries the same id.\n role: \"combobox\",\n \"aria-haspopup\": \"dialog\",\n \"aria-controls\": panelId,\n \"aria-expanded\": open,\n \"aria-invalid\": isInvalid || undefined,\n \"aria-required\": required,\n className: cn(\n FIELD_TRIGGER,\n \"pe-9\",\n padded && FIELD_FLOATING_PAD,\n disabled && \"cursor-not-allowed opacity-50\",\n // An `aria-invalid` from a Field spread paints too, as on Input and AmountInput.\n isInvalid && FIELD_INVALID,\n ),\n };\n if (renderTrigger) return <CustomTrigger render={renderTrigger} attrs={attrs} valueId={valueId} />;\n\n return (\n <button {...attrs}>\n <span id={valueId} className={cn(VALUE_TRUNCATE, !hasValue && \"text-placeholder\")}>\n {/* `|| \" \"` (a NON-BREAKING space) — triggerText is \"\" when there is no value and no\n placeholder was passed. An empty span has no line box, so the\n trigger collapses to its padding and sits shorter than every\n other field beside it.\n\n It has to be a NON-BREAKING space. This read `|| \" \"` first, and\n that fix did nothing at all: an ordinary space is collapsible\n white space, and white space at the start and end of a line is\n removed — so the span rendered with height 0 and the trigger\n stayed 22px against the 42px of the fields either side of it\n (Keksdose live #294, measured in the browser: `span.textContent`\n WAS `\" \"` and `getBoundingClientRect().height` was 0). U+00A0 is\n not collapsible, so it holds the line. */}\n {triggerText || \" \"}\n </span>\n </button>\n );\n}\n\n/**\n * How a panel's body and its action row are put on screen — the popover stacks them,\n * the phone sheet pins the actions in its footer. Handed to the panel rather than\n * applied around it because the actions belong to the panel's own draft state\n * (`RangePanel`), while the frame they sit in belongs to the field.\n */\nexport type PanelFrame = (body: ReactNode, actions?: ReactNode) => ReactNode;\n\nconst popoverFrame: PanelFrame = (body, actions) =>\n actions ? (\n <div className=\"flex flex-col gap-2\">\n {body}\n <div className=\"flex items-center justify-end gap-2 border-t border-subtle pt-2\">{actions}</div>\n </div>\n ) : (\n body\n );\n\n/** A field-styled trigger that opens a portalled MiniCalendar. Shared by the\n * single- and range-date pickers (date-picker.tsx, date-range-picker.tsx). */\nexport function DateField({\n triggerText,\n hasValue,\n label,\n disabled,\n clearable,\n clearLabel,\n panelLabel,\n className,\n width,\n onClear,\n invalid,\n sheet = false,\n sheetBackCloses = true,\n renderTrigger,\n labelHint,\n children,\n // The trigger's, not the wrapper's: `Field`'s render-prop spreads it with the id\n // pair, and a required date read as optional while it sat on the role-less div.\n // Taken here rather than in `splitTriggerAria`, which MonthPicker shares.\n \"aria-required\": ariaRequired,\n fieldRef,\n ...rest\n}: Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> & {\n /** The picker's `ref` prop: the trigger (API-3). */\n fieldRef?: Ref<HTMLButtonElement>;\n triggerText: string;\n hasValue: boolean;\n label?: ReactNode;\n /** A `FieldHint` for the label line, beside the label. */\n labelHint?: ReactNode;\n disabled?: boolean;\n clearable?: boolean;\n /** Already resolved by the picker (prop > provider > English). */\n clearLabel: string;\n /** The popover panel's accessible name, resolved likewise. */\n panelLabel: string;\n invalid?: boolean;\n className?: string;\n /** Popover panel width; omit for the default (a bare calendar). In rem so it grows\n * with the text size, like the Popover's own default (§3.2). */\n width?: number | RemLength;\n onClear: () => void;\n /** Open as a full-screen sheet instead of the popover — the picker decides when\n * (in the phone layout, `usePhoneLayout()`). */\n sheet?: boolean;\n /** The sheet's `FullBleedDialog backCloses`. */\n sheetBackCloses?: boolean;\n renderTrigger?: TriggerRenderer;\n children: (close: () => void, frame: PanelFrame) => ReactNode;\n}) {\n const showClear = Boolean(clearable && hasValue && !disabled);\n const [aria, wrapperRest] = splitTriggerAria(rest);\n // The panel is portalled to <body> and leaves the subtree whose `dir` it inherited,\n // so a calendar in an RTL form opened LTR — the grid's columns, its arrow keys and\n // its chevrons all the wrong way round. Read at the moment of opening (the only way\n // the popover opens is through `toggle`) and put back on the panel.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n const id = useId();\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const panelId = `${id}-panel`;\n // Only a STRING label can be reused as a name; a ReactNode may be a whole row with\n // its own interactive \"?\" hint in it, and naming a button with that reads the hint\n // out as part of the field's name.\n const named = typeof label === \"string\";\n // The sheet's open flag. Only the phone branch owns one: on desktop `Popover` owns\n // it, so the two can never disagree about what is on screen.\n const [sheetOpen, setSheetOpen] = useState(false);\n const sheetTriggerRef = useRef<HTMLButtonElement>(null);\n const closeSheet = () => setSheetOpen(false);\n const sheetLabels = useKitLabels(\"pickerSheet\", DEFAULT_PICKER_SHEET_LABELS);\n const sheetFrame: PanelFrame = (body, actions) => (\n <FullBleedDialog\n open\n onClose={closeSheet}\n // The same id the popover panel wears, so the trigger's `aria-controls` holds in\n // both presentations; named the way the popover is.\n id={panelId}\n aria-label={panelLabel}\n header={panelLabel}\n // `pickerSheet.close`: the one \"close this full-screen picker\" string the kit\n // already translates, rather than a second key saying the same thing.\n closeLabel={sheetLabels.close}\n backCloses={sheetBackCloses}\n dir={dir}\n // Portalled to <body>, the sheet is not inside the field's wrapper, so a\n // document-level \"outside click\" listener — a Popover or dropdown this field\n // sits in — would read a tap on a day as outside and unmount the cell before\n // its click (Keksdose dev#477, the fix `PickerSheet` carries).\n onMouseDown={(e) => e.stopPropagation()}\n footer={actions}\n >\n {body}\n </FullBleedDialog>\n );\n const fieldTrigger = ({\n open,\n toggle,\n ref,\n }: {\n open: boolean;\n toggle: () => void;\n ref: RefObject<HTMLButtonElement | null>;\n }) => (\n <DateFieldTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n triggerText={triggerText}\n hasValue={hasValue}\n // ALWAYS a name reference, never `undefined`. A `<button>` takes its\n // accessible name from its contents, so an unlabelled trigger used to\n // announce its own date text for free. `role=\"combobox\"` does not —\n // content is excluded from name computation for that role — so leaving\n // this undefined made an unlabelled date field announce nothing at all.\n // Named: \"label, value\". Unnamed: the value alone, which is what the\n // button was saying before.\n //\n // A caller's own reference comes first. And when the caller gives the field\n // an `id` (so an external `<label htmlFor>` can point at it), the trigger\n // lists ITSELF first: a self-reference in `aria-labelledby` is resolved\n // from the element's native label (accname 2B → 2D), so \"Due date\" from\n // that <label> is spoken before the value, instead of being overridden.\n labelledBy={[\n aria[\"aria-labelledby\"],\n named ? labelId : !aria[\"aria-labelledby\"] && aria.id ? aria.id : undefined,\n valueId,\n ]\n .filter(Boolean)\n .join(\" \")}\n aria={aria}\n panelId={panelId}\n valueId={valueId}\n padded={label !== undefined}\n disabled={disabled}\n invalid={invalid}\n required={ariaRequired}\n renderTrigger={renderTrigger}\n fieldRef={fieldRef}\n />\n );\n return (\n // The caller's attributes land here, on the field's own box — the trigger inside is\n // named by `aria-labelledby` and must keep the id pair it is given.\n // At Large a labelled box is a grid: the label above, the calendar glyph and the\n // clear × measured against the trigger's row (LABEL_ABOVE_BOX, 0.33 §10.17).\n <div {...wrapperRest} ref={rootRef} className={cn(\"relative\", label !== undefined && LABEL_ABOVE_BOX, className)}>\n {label !== undefined && <FieldLabelLine label={label} hint={labelHint} />}\n {/* The visible FieldLabel is a plain span, not a `<label htmlFor>`, so it names\n nothing on its own — this hidden twin is what the trigger is named by.\n `aria-hidden` because a name reference reads a hidden element deliberately\n (accname §4.3.1), and without it the label would be announced twice: once as\n loose text before the button, once inside the button's own name.\n `sr-only-fixed` rather than `sr-only` — see the note on the class in\n tokens.css. */}\n {named && (\n <span id={labelId} aria-hidden className=\"sr-only-fixed\">\n {label}\n </span>\n )}\n {sheet ? (\n <>\n {fieldTrigger({ open: sheetOpen, toggle: () => setSheetOpen((v) => !v), ref: sheetTriggerRef })}\n {/* Mounted only while open, like the popover's children: the panel's draft is\n born with it, so every open starts again from the committed value. */}\n {sheetOpen && children(closeSheet, sheetFrame)}\n </>\n ) : (\n <Popover\n width={width}\n panelId={panelId}\n // Named for what it is. Unnamed, it fell back to `popover.panel`, and a date\n // field announced its calendar as \"Popover\" — in English, in every language.\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={fieldTrigger}\n >\n {(close) => children(close, popoverFrame)}\n </Popover>\n )}\n {showClear ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={onClear}\n className=\"absolute end-1.5 top-1/2 -translate-y-1/2 rounded p-1 text-placeholder hover:text-secondary\"\n >\n <X className=\"size-4\" />\n </button>\n ) : (\n <Calendar\n aria-hidden\n className=\"pointer-events-none absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-placeholder\"\n />\n )}\n </div>\n );\n}\n\n/**\n * The picker's own label props, folded into one `datePicker` override so they sit at\n * the top of the same precedence chain as everything else: prop > provider > English.\n * They predate the provider and stay public API, which is why they are not simply\n * replaced by a `labels` prop.\n */\nexport function useDatePickerLabels(\n clearLabel: string | undefined,\n stepLabels: { prev: string; next: string } | undefined,\n todayLabel: string | undefined,\n): DatePickerLabels {\n const prev = stepLabels?.prev;\n const next = stepLabels?.next;\n const fromProps = useMemo(() => {\n const out: Partial<DatePickerLabels> = {};\n if (clearLabel !== undefined) out.clear = clearLabel;\n if (prev !== undefined) out.previousDay = prev;\n if (next !== undefined) out.nextDay = next;\n if (todayLabel !== undefined) out.today = todayLabel;\n return out;\n }, [clearLabel, prev, next, todayLabel]);\n return useKitLabels(\"datePicker\", DEFAULT_DATE_PICKER_LABELS, fromProps);\n}\n\n/**\n * The caller's `hint` when it has no label line to ride: at the end of the field,\n * outside the box, as an unlabelled {@link Select} places its FieldHint (0.15.5 P8).\n */\nexport function withEndHint(field: ReactNode, hint: ReactNode): ReactNode {\n if (hint === undefined) return field;\n return (\n <div className=\"flex items-center gap-1.5\">\n <div className=\"min-w-0 flex-1\">{field}</div>\n <span className=\"flex shrink-0 items-center\">{hint}</span>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/date-field.tsx"],"sourcesContent":["// The field both date pickers draw: the trigger, the popover or phone sheet it opens,\n// the clear button — and the props, label hook and trigger format the two share.\n// Moved out of date-picker.tsx as it was (MOD-9, refactor audit 2026-10); date-picker\n// still exports the one public name here, `DateTriggerAttributes`.\nimport { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode, Ref, RefObject } from \"react\";\nimport { Calendar, X } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { dirOf, type Direction } from \"../lib/direction\";\nimport { formatIsoDate } from \"../lib/dates\";\nimport {\n DEFAULT_DATE_PICKER_LABELS,\n DEFAULT_PICKER_SHEET_LABELS,\n useKitDateFormatter,\n useKitLabels,\n type DatePickerLabels,\n type KitDateFormatContext,\n} from \"../i18n/kit-labels\";\nimport { splitTriggerAria } from \"./trigger-aria\";\nimport type { TriggerAria } from \"./trigger-aria\";\nimport { FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID } from \"./field-base\";\nimport {\n assignRef,\n FieldLabelLine,\n LABEL_ABOVE_BOX,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport { FullBleedDialog } from \"./full-bleed-dialog\";\nimport type { RemLength } from \"../hooks/use-breakpoint\";\nimport type { MiniCalendarProps } from \"./mini-calendar\";\nimport { Popover } from \"./popover\";\n\n/**\n * What both pickers share — and, through `ComponentPropsWithoutRef<\"div\">`, everything a\n * `<div>` takes, so a `data-tour` anchor, a test id or an `aria-describedby` reaches the\n * field's root element instead of being dropped (audit §api-design).\n *\n * `onChange` is omitted from those attributes: the DOM event of that name carries a\n * `FormEvent`, while both pickers answer with ISO date strings. Each declares its own\n * (date-picker.tsx, date-range-picker.tsx).\n */\nexport interface DatePickerBaseProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n /** BCP 47 tag for the trigger's date and the calendar. Falls back to\n * `<UiKitProvider locale>`, then to the runtime's default. */\n locale?: string;\n /** Embedded top-boundary label, matching the native Input/Select fields. */\n label?: ReactNode;\n placeholder?: string;\n /** Inclusive ISO bounds for selectable days. */\n min?: string;\n max?: string;\n /** Show an X to clear the value (emits `\"\"`). */\n clearable?: boolean;\n /** Accessible name of the clear button. Wins over `datePicker.clear` from\n * `<UiKitProvider labels>`; kept as a prop because it predates the provider. */\n clearLabel?: string;\n disabled?: boolean;\n /**\n * Why the date cannot be changed — {@link Select}'s `disabledReason`, for a picker that\n * SAVES on change (a report's period kept per user, a filter persisted as it moves).\n * The trigger stays focusable and `aria-disabled`, the calendar does not open, the ‹ ›\n * and Today buttons refuse, the clear is not offered, and `onChange` never fires; the\n * reason is a line under the field at Large and on a touch screen, else in the kit\n * {@link Tooltip} (`disabledReasonDisplay`), and on the trigger's `aria-describedby`.\n * Wins over `disabled`.\n *\n * 0.52 (refactor audit 2026-10-30, API-3): the date pickers and MonthPicker were the\n * only value fields with no lock opt-in.\n */\n disabledReason?: ReactNode;\n /** Where the reason shows — {@link Button}'s `disabledReasonDisplay`: a line under the\n * field at Large and on a touch screen by default, the tooltip otherwise. */\n disabledReasonDisplay?: DisabledReasonDisplay;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /**\n * Standing advice for the field (keksdose K4, \"uniform field anatomy\"), as on a\n * {@link Select}: plain TEXT is a caption under the field, attached with\n * `aria-describedby`; a {@link FieldHint} rides the label line (or, unlabelled, sits\n * at the field's end, outside the box).\n */\n hint?: ReactNode;\n /**\n * What is wrong with the date, in the caller's words (\"The due date is before the\n * invoice date\") — {@link Input}'s `error` (keksdose K4: DateField errors had no slot).\n * Rendered under the field (under the whole row, with `step` / `today`), attached to\n * the trigger with `aria-describedby` — merged with any the caller passed, after the\n * caption — and implies `invalid`. Passing the key at all keeps the field's box while\n * there is no message, so the trigger is never remounted as one comes and goes.\n */\n error?: ReactNode;\n className?: string;\n /** Intl options for the trigger's formatted date (default: locale short date). Win\n * over the provider's `formatDate`. */\n formatOptions?: Intl.DateTimeFormatOptions;\n /** Render the trigger's text for an ISO date yourself; wins over `formatOptions` and\n * over the provider's `formatDate`.\n * For a host whose date rendering is more than one `Intl` call can say — Keksdose\n * puts the weekday's name in the UI language beside digits ordered by a separate\n * format preference, two locales in one string. `locale` still drives the calendar.\n * Set that rendering once with `<UiKitProvider formatDate>` (keksdose K12) and this\n * is for the one field that differs. */\n formatValue?: (iso: string) => string;\n /** Passed through to the {@link MiniCalendar} this opens. Its month arrows are\n * icon-only, so their `aria-label` is the only name they have — and the package\n * ships English defaults, which a translating host has to be able to replace.\n * Usually unnecessary now: the calendar reads `miniCalendar` from the provider. */\n calendarLabels?: MiniCalendarProps[\"labels\"];\n /**\n * Make the panel's caption (\"July 2026\") a button that opens the year's twelve\n * months, with ‹ › stepping a year; picking a month returns to its days. Far dates\n * stop being a march of month arrows.\n *\n * kastlan is why: its service-charge period runs July to June, and with one month on\n * screen and ‹ › the only way across, the end of the period was eleven clicks past\n * the start — so kastlan kept two date fields instead of `RhfDateRangePicker`. With\n * the jump the whole period is open, caption, Jul, 1, caption, next year, Jun, 30.\n *\n * What it adds, for the keyboard and a screen reader:\n * - the caption is a disclosure button (`aria-expanded`). Its name is the month it\n * shows, so speech input can say what it sees; it is `aria-live`, as the\n * calendar's caption was, so paging months still says where you landed; and it is\n * described by `monthPicker.panel` (\"Choose a month\").\n * - the month grid is `MonthPicker`'s grid: one tab stop, arrows walk the months\n * (across the year's edge), Home/End the row, PageUp/PageDown a year, Enter or a\n * click picks. Focus opens on the month on show and comes back on its days — the\n * selected day if it is in that month, else the 1st. Months outside `min`/`max`\n * are `aria-disabled`, and a year arrow is once the whole year beyond is.\n * - Escape in the month grid goes back to the days without closing the panel (focus\n * on the caption); in the days, Escape closes the panel as before.\n *\n * No new strings: the month arrows keep `miniCalendar.previousMonth` / `nextMonth`\n * (`calendarLabels`), and the month grid speaks with `MonthPicker`'s words —\n * `monthPicker.previousYear` / `nextYear` / `month` / `panel` — because it is that\n * grid, and a host that translated it once should not have to again.\n *\n * Off by default: the caption becomes one more stop in the panel's tab order, which a\n * 0.x minor does not spring on a form. Works in the popover and in\n * `DateRangePicker`'s phone sheet alike, beside presets and in `commit=\"apply\"`\n * (a preset picked while the months are open goes back to the days, on its start).\n *\n * Why not a two-month panel (`months={2}`) instead, or beside it: for a twelve-month\n * period it still pages six times where this takes one year step, and two\n * `MiniCalendar`s side by side each follow the value on their own — picking the end\n * in the right-hand month would pull the left one onto it — so the calendar would\n * have to learn to share a month first. Not worth that for a saving of one arrow on a\n * range across a single month boundary.\n */\n monthJump?: boolean;\n /**\n * The TRIGGER — the focusable button that opens the calendar, a `renderTrigger` one\n * too — as a React 19 ref prop (0.34, API-3, refactor audit 2026-10). The field took\n * none, so react-hook-form's focus-on-error reached it only through /rhf's id lookup,\n * and an app not at all. Not the wrapper, which takes the rest of the props.\n */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * The trigger's date renderer, in the kit's order of precedence: the picker's own\n * `formatValue`, then its `formatOptions` (an explicit `Intl` look for this field),\n * then the provider's `formatDate` (keksdose K12), then the locale's short date. A\n * provider formatter that answers `\"\"` falls through to the default for that date.\n */\nexport function useTriggerFormat(\n source: KitDateFormatContext[\"source\"],\n locale: string | undefined,\n formatOptions: Intl.DateTimeFormatOptions | undefined,\n formatValue: ((iso: string) => string) | undefined,\n): (iso: string) => string {\n const fromProvider = useKitDateFormatter();\n if (formatValue) return formatValue;\n return (iso) => {\n if (formatOptions === undefined && fromProvider) {\n // A single date stands alone, where the weekday helps; a range's two ends do not.\n const text = fromProvider(iso, { unit: \"day\", source, locale, weekday: source === \"datePicker\" });\n if (text) return text;\n }\n // No prop and no provider leaves `locale` undefined: the runtime's default (DUP-11).\n return formatIsoDate(iso, locale, formatOptions);\n };\n}\n\n/**\n * Everything the kit's own trigger button carries, handed to a custom one\n * ({@link DateRangePickerProps.renderTrigger}) to spread onto its `<button>`. Spread\n * it WHOLE: the ref is how focus comes back when the panel closes, the id pair and\n * `aria-labelledby` are the 0.5.1 naming (an external `<label htmlFor>`, a form\n * library's `aria-describedby`), and `role`/`aria-haspopup`/`aria-controls`/\n * `aria-expanded` are what tell a screen reader this opens a calendar and whether it\n * is open. `className` is the kit's field look (42px with a label, `pe-9` for the\n * glyph the field draws at its end) — merge yours after it with `cn`.\n */\nexport interface DateTriggerAttributes {\n ref: RefObject<HTMLButtonElement | null>;\n type: \"button\";\n id?: string;\n role: \"combobox\";\n \"aria-haspopup\": \"dialog\";\n \"aria-controls\": string;\n \"aria-expanded\": boolean;\n \"aria-labelledby\"?: string;\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n \"aria-invalid\"?: true;\n /** The field's required state — `Field required` hands it down with the id pair. */\n \"aria-required\"?: boolean | \"true\" | \"false\";\n disabled?: boolean;\n /** Locked by the write lock (0.52, `commit` / `disabledReason`): focusable, and the\n * click opens nothing. Dress a custom trigger for it as for `disabled`. */\n \"aria-disabled\"?: true;\n onClick: () => void;\n className: string;\n}\n\n/** A custom trigger, as `DateField` calls it; each picker adapts it to its own props. */\ntype TriggerRenderer = (attrs: DateTriggerAttributes, valueId: string) => ReactNode;\n\n/** A caller's trigger, as an element of its own: the attributes carry the trigger's\n * ref, and a ref is handed to a child as a prop, not passed to a function in the\n * middle of the parent's render. */\nfunction CustomTrigger({\n render,\n attrs,\n valueId,\n}: {\n render: TriggerRenderer;\n attrs: DateTriggerAttributes;\n valueId: string;\n}) {\n return render(attrs, valueId);\n}\n\n/**\n * The trigger's value wraps instead of truncating at Large and Extra large (0.32.1, the\n * 360 px Extra-large sweep; docs/text-size-harmonization.md §4 \"nothing truncates\"). A\n * range (\"01/09/2026 – 30/09/2026\") is 312 px at 150 %, and on a 360 px phone it read\n * \"01/09/2026 – 30/…\" — its end, the half a reader needs, was the half cut off. A\n * `large:` class, so Normal keeps its one-line field: since 0.33 (§10.17) the kit's one\n * `truncate-until-large` (tokens.css) in place of this file's own `truncate` +\n * `large:whitespace-normal large:break-words`.\n */\nconst VALUE_TRUNCATE = \"truncate-until-large\";\n\n/**\n * The field-shaped button that opens the calendar.\n *\n * A component of its own rather than JSX inline in `Popover`'s `trigger` render prop,\n * because handing focus back to it is an effect and a render prop is not somewhere a\n * hook can go.\n */\nfunction DateFieldTrigger({\n open,\n toggle,\n triggerRef,\n triggerText,\n hasValue,\n labelledBy,\n valueId,\n padded,\n disabled,\n locked,\n invalid,\n required,\n panelId,\n aria,\n renderTrigger,\n fieldRef,\n}: {\n /** The caller's naming/description attributes, routed here from the field. */\n aria: TriggerAria;\n /** Draw the button yourself, with these attributes — see {@link DateTriggerAttributes}. */\n renderTrigger?: TriggerRenderer;\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n /** The picker's own `ref` prop (API-3) — the same element as `triggerRef`. */\n fieldRef?: Ref<HTMLButtonElement>;\n /** The panel's id — `role=\"combobox\"` requires `aria-controls`. */\n panelId: string;\n triggerText: string;\n hasValue: boolean;\n /** ids of the hidden label twin + the value span, in the order they are spoken. */\n labelledBy?: string;\n valueId: string;\n padded: boolean;\n disabled?: boolean;\n /** The write lock: the focusable kind of disabled (see `disabledReason`). */\n locked: boolean;\n invalid?: boolean;\n /** `aria-required`, on the trigger with the rest of the field's ARIA. */\n required?: boolean | \"true\" | \"false\";\n}) {\n const wasOpen = useRef(false);\n useEffect(() => {\n const justClosed = wasOpen.current && !open;\n wasOpen.current = open;\n if (!justClosed) return;\n // Only when the panel still HAD focus. The day the user was standing on has just\n // been unmounted with the panel, which drops focus to <body> and leaves a\n // keyboard user back at the top of the document with no idea where they were.\n // But an outside click closes this panel too, and that click has already given\n // focus to whatever it landed on — dragging it back here would be worse than the\n // bug, so a focus that is somewhere real is left alone.\n const active = document.activeElement;\n if (!active || active === document.body) triggerRef.current?.focus();\n // Here rather than through `useFocusTrap`'s `restoreFocus`, and not only because\n // `Popover` runs no trap to carry it: that option restores to whatever held focus\n // when the panel opened, and on Safari — and on Firefox on macOS — clicking a\n // button does not focus it, so \"whatever held focus\" is the page. The requirement\n // is this trigger, every time, however the panel was opened.\n }, [open, triggerRef]);\n // The caller's `ref` (API-3, refactor audit 2026-10) is handed the trigger once it is\n // mounted, rather than merged into `attrs.ref`: that one is a `RefObject` by contract\n // (`DateTriggerAttributes`), which a `renderTrigger` button may read, and this way a\n // caller's own trigger is covered as well as the kit's.\n useLayoutEffect(() => {\n if (!fieldRef) return;\n assignRef(fieldRef, triggerRef.current);\n return () => assignRef(fieldRef, null);\n }, [fieldRef, triggerRef]);\n\n // One attribute object, worn either by the kit's own button below or by a caller's\n // (`renderTrigger`) — so a custom trigger cannot quietly fall out of step with the\n // naming and state this one announces.\n const isInvalid = Boolean(invalid) || aria[\"aria-invalid\"] === true || aria[\"aria-invalid\"] === \"true\";\n const attrs: DateTriggerAttributes = {\n ref: triggerRef,\n type: \"button\",\n // The focusable kind of disabled while locked (Button's rule): native `disabled`\n // would take the field out of the tab order, and the reason with it.\n disabled: locked ? undefined : disabled,\n \"aria-disabled\": locked || undefined,\n // Locked, the calendar does not open. One open when the lock arrives closes: the\n // picker moves the field into the reason's frame, which remounts it, as Select's.\n onClick: () => {\n if (!locked) toggle();\n },\n // The name has to carry the VALUE. A bare `aria-label={label}` — which is what\n // this was — replaces the button's text outright, so the one field in the form\n // whose entire job is to show a date announced \"Due date\" and stopped there,\n // and a screen-reader user could not read back what they had picked without\n // opening the calendar and hunting for the selected day.\n //\n // Two ids rather than one composed string (`common.fieldValue`): \"label: value\"\n // is a sentence, and a name reference is spoken as the two texts in order with\n // no punctuation to translate at all. The hidden twin of the FieldLabel lives\n // in DateField, next to the label it copies.\n id: aria.id,\n \"aria-labelledby\": aria[\"aria-label\"] && !aria[\"aria-labelledby\"] ? undefined : labelledBy,\n \"aria-label\": aria[\"aria-label\"],\n \"aria-describedby\": aria[\"aria-describedby\"],\n // `role=\"combobox\"` on a button that opens a calendar is the APG date-picker\n // shape, and it is what makes the next two lines legal: `button` supports\n // neither `aria-expanded` nor `aria-invalid`, so the previous markup set an\n // invalid state that announced nothing (ESLint's `role-supports-aria-props`).\n //\n // `aria-haspopup=\"dialog\"` is now honest — it was not when this comment first\n // said so. `Popover` gained a real `role=\"dialog\"` in the same wave, so the\n // trigger's promise and the panel's role finally agree. The phone sheet is a\n // `role=\"dialog\"` too, and carries the same id.\n role: \"combobox\",\n \"aria-haspopup\": \"dialog\",\n \"aria-controls\": panelId,\n \"aria-expanded\": open,\n \"aria-invalid\": isInvalid || undefined,\n \"aria-required\": required,\n className: cn(\n FIELD_TRIGGER,\n \"pe-9\",\n padded && FIELD_FLOATING_PAD,\n (disabled || locked) && \"cursor-not-allowed opacity-50\",\n // An `aria-invalid` from a Field spread paints too, as on Input and AmountInput.\n isInvalid && FIELD_INVALID,\n ),\n };\n if (renderTrigger) return <CustomTrigger render={renderTrigger} attrs={attrs} valueId={valueId} />;\n\n return (\n <button {...attrs}>\n <span id={valueId} className={cn(VALUE_TRUNCATE, !hasValue && \"text-placeholder\")}>\n {/* `|| \" \"` (a NON-BREAKING space) — triggerText is \"\" when there is no value and no\n placeholder was passed. An empty span has no line box, so the\n trigger collapses to its padding and sits shorter than every\n other field beside it.\n\n It has to be a NON-BREAKING space. This read `|| \" \"` first, and\n that fix did nothing at all: an ordinary space is collapsible\n white space, and white space at the start and end of a line is\n removed — so the span rendered with height 0 and the trigger\n stayed 22px against the 42px of the fields either side of it\n (Keksdose live #294, measured in the browser: `span.textContent`\n WAS `\" \"` and `getBoundingClientRect().height` was 0). U+00A0 is\n not collapsible, so it holds the line. */}\n {triggerText || \" \"}\n </span>\n </button>\n );\n}\n\n/**\n * How a panel's body and its action row are put on screen — the popover stacks them,\n * the phone sheet pins the actions in its footer. Handed to the panel rather than\n * applied around it because the actions belong to the panel's own draft state\n * (`RangePanel`), while the frame they sit in belongs to the field.\n */\nexport type PanelFrame = (body: ReactNode, actions?: ReactNode) => ReactNode;\n\nconst popoverFrame: PanelFrame = (body, actions) =>\n actions ? (\n <div className=\"flex flex-col gap-2\">\n {body}\n <div className=\"flex items-center justify-end gap-2 border-t border-subtle pt-2\">{actions}</div>\n </div>\n ) : (\n body\n );\n\n/** A field-styled trigger that opens a portalled MiniCalendar. Shared by the\n * single- and range-date pickers (date-picker.tsx, date-range-picker.tsx). */\nexport function DateField({\n triggerText,\n hasValue,\n label,\n disabled,\n locked = false,\n clearable,\n clearLabel,\n panelLabel,\n className,\n width,\n onClear,\n invalid,\n sheet = false,\n sheetBackCloses = true,\n renderTrigger,\n labelHint,\n children,\n // The trigger's, not the wrapper's: `Field`'s render-prop spreads it with the id\n // pair, and a required date read as optional while it sat on the role-less div.\n // Taken here rather than in `splitTriggerAria`, which MonthPicker shares.\n \"aria-required\": ariaRequired,\n fieldRef,\n ...rest\n}: Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> & {\n /** The picker's `ref` prop: the trigger (API-3). */\n fieldRef?: Ref<HTMLButtonElement>;\n triggerText: string;\n hasValue: boolean;\n label?: ReactNode;\n /** A `FieldHint` for the label line, beside the label. */\n labelHint?: ReactNode;\n disabled?: boolean;\n /** The write lock is on: the trigger is focusable and opens nothing, no clear. The\n * picker draws the reason's frame around the field (or its step row). */\n locked?: boolean;\n clearable?: boolean;\n /** Already resolved by the picker (prop > provider > English). */\n clearLabel: string;\n /** The popover panel's accessible name, resolved likewise. */\n panelLabel: string;\n invalid?: boolean;\n className?: string;\n /** Popover panel width; omit for the default (a bare calendar). In rem so it grows\n * with the text size, like the Popover's own default (§3.2). */\n width?: number | RemLength;\n onClear: () => void;\n /** Open as a full-screen sheet instead of the popover — the picker decides when\n * (in the phone layout, `usePhoneLayout()`). */\n sheet?: boolean;\n /** The sheet's `FullBleedDialog backCloses`. */\n sheetBackCloses?: boolean;\n renderTrigger?: TriggerRenderer;\n children: (close: () => void, frame: PanelFrame) => ReactNode;\n}) {\n // A clear is a change like any other: not offered while locked.\n const showClear = Boolean(clearable && hasValue && !disabled && !locked);\n const [aria, wrapperRest] = splitTriggerAria(rest);\n // The trigger, for the clear to hand the focus to (INP-10, below), beside the picker's\n // own `ref`.\n const triggerEl = useRef<HTMLButtonElement | null>(null);\n const toTrigger = useCallback(\n (node: HTMLButtonElement | null) => {\n triggerEl.current = node;\n assignRef(fieldRef, node);\n },\n [fieldRef],\n );\n // The panel is portalled to <body> and leaves the subtree whose `dir` it inherited,\n // so a calendar in an RTL form opened LTR — the grid's columns, its arrow keys and\n // its chevrons all the wrong way round. Read at the moment of opening (the only way\n // the popover opens is through `toggle`) and put back on the panel.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n const id = useId();\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const panelId = `${id}-panel`;\n // Only a STRING label can be reused as a name; a ReactNode may be a whole row with\n // its own interactive \"?\" hint in it, and naming a button with that reads the hint\n // out as part of the field's name.\n const named = typeof label === \"string\";\n // The sheet's open flag. Only the phone branch owns one: on desktop `Popover` owns\n // it, so the two can never disagree about what is on screen.\n const [sheetOpen, setSheetOpen] = useState(false);\n const sheetTriggerRef = useRef<HTMLButtonElement>(null);\n const closeSheet = () => setSheetOpen(false);\n const sheetLabels = useKitLabels(\"pickerSheet\", DEFAULT_PICKER_SHEET_LABELS);\n const sheetFrame: PanelFrame = (body, actions) => (\n <FullBleedDialog\n open\n onClose={closeSheet}\n // The same id the popover panel wears, so the trigger's `aria-controls` holds in\n // both presentations; named the way the popover is.\n id={panelId}\n aria-label={panelLabel}\n header={panelLabel}\n // `pickerSheet.close`: the one \"close this full-screen picker\" string the kit\n // already translates, rather than a second key saying the same thing.\n closeLabel={sheetLabels.close}\n backCloses={sheetBackCloses}\n dir={dir}\n // Portalled to <body>, the sheet is not inside the field's wrapper, so a\n // document-level \"outside click\" listener — a Popover or dropdown this field\n // sits in — would read a tap on a day as outside and unmount the cell before\n // its click (Keksdose dev#477, the fix `PickerSheet` carries).\n onMouseDown={(e) => e.stopPropagation()}\n footer={actions}\n >\n {body}\n </FullBleedDialog>\n );\n const fieldTrigger = ({\n open,\n toggle,\n ref,\n }: {\n open: boolean;\n toggle: () => void;\n ref: RefObject<HTMLButtonElement | null>;\n }) => (\n <DateFieldTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n triggerText={triggerText}\n hasValue={hasValue}\n // ALWAYS a name reference, never `undefined`. A `<button>` takes its\n // accessible name from its contents, so an unlabelled trigger used to\n // announce its own date text for free. `role=\"combobox\"` does not —\n // content is excluded from name computation for that role — so leaving\n // this undefined made an unlabelled date field announce nothing at all.\n // Named: \"label, value\". Unnamed: the value alone, which is what the\n // button was saying before.\n //\n // A caller's own reference comes first. And when the caller gives the field\n // an `id` (so an external `<label htmlFor>` can point at it), the trigger\n // lists ITSELF first: a self-reference in `aria-labelledby` is resolved\n // from the element's native label (accname 2B → 2D), so \"Due date\" from\n // that <label> is spoken before the value, instead of being overridden.\n labelledBy={[\n aria[\"aria-labelledby\"],\n named ? labelId : !aria[\"aria-labelledby\"] && aria.id ? aria.id : undefined,\n valueId,\n ]\n .filter(Boolean)\n .join(\" \")}\n aria={aria}\n panelId={panelId}\n valueId={valueId}\n padded={label !== undefined}\n disabled={disabled}\n locked={locked}\n invalid={invalid}\n required={ariaRequired}\n renderTrigger={renderTrigger}\n fieldRef={toTrigger}\n />\n );\n return (\n // The caller's attributes land here, on the field's own box — the trigger inside is\n // named by `aria-labelledby` and must keep the id pair it is given.\n // At Large a labelled box is a grid: the label above, the calendar glyph and the\n // clear × measured against the trigger's row (LABEL_ABOVE_BOX, 0.33 §10.17).\n <div {...wrapperRest} ref={rootRef} className={cn(\"relative\", label !== undefined && LABEL_ABOVE_BOX, className)}>\n {label !== undefined && <FieldLabelLine label={label} hint={labelHint} />}\n {/* The visible FieldLabel is a plain span, not a `<label htmlFor>`, so it names\n nothing on its own — this hidden twin is what the trigger is named by.\n `aria-hidden` because a name reference reads a hidden element deliberately\n (accname §4.3.1), and without it the label would be announced twice: once as\n loose text before the button, once inside the button's own name.\n `sr-only-fixed` rather than `sr-only` — see the note on the class in\n tokens.css. */}\n {named && (\n <span id={labelId} aria-hidden className=\"sr-only-fixed\">\n {label}\n </span>\n )}\n {sheet ? (\n <>\n {fieldTrigger({ open: sheetOpen, toggle: () => setSheetOpen((v) => !v), ref: sheetTriggerRef })}\n {/* Mounted only while open, like the popover's children: the panel's draft is\n born with it, so every open starts again from the committed value. */}\n {sheetOpen && children(closeSheet, sheetFrame)}\n </>\n ) : (\n <Popover\n width={width}\n panelId={panelId}\n // Named for what it is. Unnamed, it fell back to `popover.panel`, and a date\n // field announced its calendar as \"Popover\" — in English, in every language.\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={fieldTrigger}\n >\n {(close) => children(close, popoverFrame)}\n </Popover>\n )}\n {showClear ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={() => {\n onClear();\n // The × goes with the value, so a keyboard clear left the focus on a button\n // that was about to unmount — and it fell to <body> (refactor audit\n // 2026-10-30, INP-10). The trigger, as SearchField's clear does.\n triggerEl.current?.focus();\n }}\n className=\"absolute end-1.5 top-1/2 -translate-y-1/2 rounded p-1 text-placeholder hover:text-secondary\"\n >\n <X className=\"size-4\" />\n </button>\n ) : (\n <Calendar\n aria-hidden\n className=\"pointer-events-none absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-placeholder\"\n />\n )}\n </div>\n );\n}\n\n/**\n * The picker's own label props, folded into one `datePicker` override so they sit at\n * the top of the same precedence chain as everything else: prop > provider > English.\n * They predate the provider and stay public API, which is why they are not simply\n * replaced by a `labels` prop.\n */\nexport function useDatePickerLabels(\n clearLabel: string | undefined,\n stepLabels: { prev: string; next: string } | undefined,\n todayLabel: string | undefined,\n): DatePickerLabels {\n const prev = stepLabels?.prev;\n const next = stepLabels?.next;\n const fromProps = useMemo(() => {\n const out: Partial<DatePickerLabels> = {};\n if (clearLabel !== undefined) out.clear = clearLabel;\n if (prev !== undefined) out.previousDay = prev;\n if (next !== undefined) out.nextDay = next;\n if (todayLabel !== undefined) out.today = todayLabel;\n return out;\n }, [clearLabel, prev, next, todayLabel]);\n return useKitLabels(\"datePicker\", DEFAULT_DATE_PICKER_LABELS, fromProps);\n}\n\n/**\n * The caller's `hint` when it has no label line to ride: at the end of the field,\n * outside the box, as an unlabelled {@link Select} places its FieldHint (0.15.5 P8).\n */\nexport function withEndHint(field: ReactNode, hint: ReactNode): ReactNode {\n if (hint === undefined) return field;\n return (\n <div className=\"flex items-center gap-1.5\">\n <div className=\"min-w-0 flex-1\">{field}</div>\n <span className=\"flex shrink-0 items-center\">{hint}</span>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,4 @@
1
+ import type { CommitScope } from "./write-lock.js";
1
2
  import { type DatePickerBaseProps } from "./date-field.js";
2
3
  export type { DateTriggerAttributes } from "./date-field.js";
3
4
  export { DateRangePicker } from "./date-range-picker.js";
@@ -9,7 +10,8 @@ export interface DatePickerProps extends DatePickerBaseProps {
9
10
  /**
10
11
  * Flank the field with ‹ › buttons that move the value one day. For fields whose
11
12
  * typical edit is a day or two — opening a calendar to go "yesterday" is three
12
- * interactions for what should be one.
13
+ * interactions for what should be one. A button whose day is outside `min` / `max` is
14
+ * refused (`aria-disabled`, still focusable — 0.52); with no value both are disabled.
13
15
  *
14
16
  * Off by default: on a filter or a far-away date the buttons are dead weight, and
15
17
  * they cost ~5rem of width that a narrow layout may not have.
@@ -25,7 +27,9 @@ export interface DatePickerProps extends DatePickerBaseProps {
25
27
  };
26
28
  /**
27
29
  * Today's date as ISO "YYYY-MM-DD". Adds a jump-to-today button beside the field,
28
- * disabled once the value already is that day.
30
+ * refused once the value already is that day — `aria-disabled`, so the focus stays on
31
+ * the button that was just pressed (0.52) — and disabled while that day is outside
32
+ * `min` / `max`.
29
33
  *
30
34
  * Passed in rather than read from a clock here: a component that decides what "today"
31
35
  * is cannot be tested at a fixed date, and the consumer already has one definition of
@@ -36,6 +40,15 @@ export interface DatePickerProps extends DatePickerBaseProps {
36
40
  today?: string;
37
41
  /** Accessible name for the today button; wins over `datePicker.today`. */
38
42
  todayLabel?: string;
43
+ /**
44
+ * This picker COMMITS — picking a date saves it (a row's date edited in place, a
45
+ * filter kept as it moves). Under a locked {@link WriteLockProvider} it is locked the
46
+ * `disabledReason` way, with the lock's reason; a {@link CommitScope}, as on every kit
47
+ * control. A date field inside a form with its own Save stays editable under the lock —
48
+ * leave this off there, and put `commit` on the Save. 0.52 (refactor audit 2026-10-30,
49
+ * API-3).
50
+ */
51
+ commit?: CommitScope;
39
52
  }
40
53
  /** Single-date picker: a field showing the formatted date, opening a calendar. */
41
54
  export declare function DatePicker(props: DatePickerProps): import("react").JSX.Element;
@@ -6,7 +6,7 @@ import { addDaysIso } from "../lib/dates.js";
6
6
  import { useKitLocale } from "../i18n/kit-labels.js";
7
7
  import { splitTriggerAria } from "./trigger-aria.js";
8
8
  import { FIELD_BASE } from "./field-base.js";
9
- import { FieldBox, useFieldMessages } from "./field-parts.js";
9
+ import { FieldBox, LockedReason, useFieldMessages, useLockReason } from "./field-parts.js";
10
10
  import { Tooltip } from "./tooltip.js";
11
11
  import { DateField, useDatePickerLabels, useTriggerFormat, withEndHint } from "./date-field.js";
12
12
  import { PanelCalendar } from "./jump-calendar.js";
@@ -21,18 +21,24 @@ const STEP_ROW_STACK_AT_LARGE = cn(
21
21
  function StepButton({
22
22
  icon: Icon,
23
23
  label,
24
- disabled,
24
+ off,
25
+ refused,
26
+ reasonId,
25
27
  onClick,
26
28
  mirror,
27
29
  className
28
30
  }) {
29
- return /* @__PURE__ */ jsx(Tooltip, { label, portal: true, children: /* @__PURE__ */ jsx(
31
+ const button = /* @__PURE__ */ jsx(
30
32
  "button",
31
33
  {
32
34
  type: "button",
33
35
  "aria-label": label,
34
- disabled,
35
- onClick,
36
+ disabled: off,
37
+ "aria-disabled": !off && refused || void 0,
38
+ "aria-describedby": reasonId,
39
+ onClick: () => {
40
+ if (!refused) onClick();
41
+ },
36
42
  className: cn(
37
43
  FIELD_BASE,
38
44
  // FIELD_BASE is `block w-full` for text inputs; a step button is neither, and
@@ -40,15 +46,19 @@ function StepButton({
40
46
  "relative flex h-full w-10 shrink-0 items-center justify-center px-0 text-muted",
41
47
  "hover:bg-hover hover:text-secondary",
42
48
  // Dimmed ICON, not a dimmed box: at 40% opacity the whole button — border
43
- // included — faded out of the group and read as a broken, detached control.
49
+ // included — faded out of the group and read as a broken, detached control. The
50
+ // same look for the focusable kind (INP-4).
44
51
  "disabled:cursor-not-allowed disabled:text-placeholder disabled:opacity-60 disabled:hover:bg-surface",
52
+ "aria-disabled:cursor-not-allowed aria-disabled:text-placeholder aria-disabled:opacity-60 aria-disabled:hover:bg-surface",
45
53
  // A part of a joined row: FIELD_BASE's 2px focus line, whole at 125 % (K5, the
46
54
  // note above this function).
47
55
  className
48
56
  ),
49
57
  children: /* @__PURE__ */ jsx(Icon, { className: cn("size-4", mirror && "rtl:-scale-x-100") })
50
58
  }
51
- ) });
59
+ );
60
+ if (reasonId) return /* @__PURE__ */ jsx("span", { className: "relative inline-flex", children: button });
61
+ return /* @__PURE__ */ jsx(Tooltip, { label, portal: true, children: button });
52
62
  }
53
63
  function DatePicker(props) {
54
64
  const {
@@ -71,6 +81,9 @@ function DatePicker(props) {
71
81
  clearable,
72
82
  clearLabel,
73
83
  disabled,
84
+ disabledReason,
85
+ disabledReasonDisplay,
86
+ commit,
74
87
  invalid,
75
88
  hint,
76
89
  help,
@@ -85,6 +98,11 @@ function DatePicker(props) {
85
98
  const locale = useKitLocale(localeProp);
86
99
  const text = useDatePickerLabels(clearLabel, stepLabels, todayLabel);
87
100
  const render = useTriggerFormat("datePicker", locale, formatOptions, formatValue);
101
+ const lock = useLockReason(commit, disabledReason);
102
+ const locked = lock.locked;
103
+ const change = (iso) => {
104
+ if (!locked) onChange(iso);
105
+ };
88
106
  const messages = useFieldMessages({
89
107
  hint,
90
108
  help,
@@ -92,7 +110,8 @@ function DatePicker(props) {
92
110
  error,
93
111
  invalid,
94
112
  describedBy: rest["aria-describedby"],
95
- ariaInvalid: rest["aria-invalid"]
113
+ ariaInvalid: rest["aria-invalid"],
114
+ reasonId: locked && lock.reasonId
96
115
  });
97
116
  const wrapped = Boolean(step || today);
98
117
  const [triggerAria, wrapperRest] = splitTriggerAria(rest);
@@ -109,12 +128,13 @@ function DatePicker(props) {
109
128
  clearLabel: text.clear,
110
129
  panelLabel: text.panel,
111
130
  disabled,
131
+ locked,
112
132
  invalid: messages.isInvalid,
113
- className: step ? "min-w-0 flex-1" : className,
133
+ className: wrapped ? "min-w-0 flex-1" : locked ? void 0 : className,
114
134
  ...wrapped ? { "data-slot": "date-step-field" } : {},
115
135
  hasValue: Boolean(value),
116
136
  triggerText: value ? render(value) : placeholder ?? "",
117
- onClear: () => onChange(""),
137
+ onClear: () => change(""),
118
138
  children: (close) => /* @__PURE__ */ jsx(
119
139
  PanelCalendar,
120
140
  {
@@ -128,7 +148,7 @@ function DatePicker(props) {
128
148
  max,
129
149
  labels: calendarLabels,
130
150
  onSelect: (iso) => {
131
- onChange(iso);
151
+ change(iso);
132
152
  close();
133
153
  }
134
154
  }
@@ -137,16 +157,14 @@ function DatePicker(props) {
137
157
  );
138
158
  const reserve = "error" in props || "hint" in props;
139
159
  const endHint = label === void 0 ? messages.labelHint : void 0;
160
+ const withReason = (box) => locked ? /* @__PURE__ */ jsx(LockedReason, { lock, display: disabledReasonDisplay, className: cn("block", className), children: box }) : box;
140
161
  if (!wrapped) {
141
- return /* @__PURE__ */ jsx(FieldBox, { below: messages.below, reserve, children: withEndHint(field, endHint) });
162
+ return /* @__PURE__ */ jsx(FieldBox, { below: messages.below, reserve, children: withReason(withEndHint(field, endHint)) });
142
163
  }
143
164
  const target = (days) => value ? addDaysIso(value, days) : "";
144
165
  const outOfBounds = (iso) => Boolean(min && iso < min || max && iso > max);
145
- const blocked = (days) => {
146
- const next = target(days);
147
- if (!next || disabled) return true;
148
- return outOfBounds(next);
149
- };
166
+ const fieldOff = Boolean(disabled) && !locked;
167
+ const reasonId = locked ? lock.reasonId : void 0;
150
168
  const row = (
151
169
  // ONE joined control, not three boxes in a row: the buttons and the field share
152
170
  // their borders (each piece overlaps the previous by a pixel) and only the outer
@@ -177,7 +195,7 @@ function DatePicker(props) {
177
195
  // …and its line is 2px, so no side of it reads half-strength at 125 % (live #394,
178
196
  // fourth round): FIELD_BASE's inset ring, which every part of the row carries.
179
197
  STEP_ROW_STACK_AT_LARGE,
180
- className
198
+ !locked && className
181
199
  ),
182
200
  children: [
183
201
  step && /* @__PURE__ */ jsx(
@@ -186,8 +204,10 @@ function DatePicker(props) {
186
204
  icon: ChevronLeft,
187
205
  mirror: true,
188
206
  label: text.previousDay,
189
- disabled: blocked(-1),
190
- onClick: () => onChange(target(-1)),
207
+ off: fieldOff || !value,
208
+ refused: locked || outOfBounds(target(-1)),
209
+ reasonId,
210
+ onClick: () => change(target(-1)),
191
211
  className: "rounded-e-none"
192
212
  }
193
213
  ),
@@ -198,18 +218,27 @@ function DatePicker(props) {
198
218
  icon: ChevronRight,
199
219
  mirror: true,
200
220
  label: text.nextDay,
201
- disabled: blocked(1),
202
- onClick: () => onChange(target(1)),
221
+ off: fieldOff || !value,
222
+ refused: locked || outOfBounds(target(1)),
223
+ reasonId,
224
+ onClick: () => change(target(1)),
203
225
  className: cn("rounded-s-none", today && "rounded-e-none")
204
226
  }
205
227
  ),
206
- today && /* @__PURE__ */ jsx(
228
+ today && // Refused once the value is today: pressable as a no-op would keep the focus too
229
+ // (INP-4), and this also says there is nothing to jump to. Today outside the
230
+ // bounds is `off`: no press here gets there, as MonthPicker's stepper has it — it
231
+ // was refused, a tab stop that could never do anything (0.52 code review,
232
+ // date-picker.tsx). A lock wins, focusable, to say why.
233
+ /* @__PURE__ */ jsx(
207
234
  StepButton,
208
235
  {
209
236
  icon: CalendarClock,
210
237
  label: text.today,
211
- disabled: Boolean(disabled) || value === today || outOfBounds(today),
212
- onClick: () => onChange(today),
238
+ off: fieldOff || !locked && outOfBounds(today),
239
+ refused: locked || value === today,
240
+ reasonId,
241
+ onClick: () => change(today),
213
242
  className: "rounded-s-none"
214
243
  }
215
244
  )
@@ -217,7 +246,7 @@ function DatePicker(props) {
217
246
  }
218
247
  )
219
248
  );
220
- return /* @__PURE__ */ jsx(FieldBox, { below: messages.below, reserve, children: withEndHint(row, endHint) });
249
+ return /* @__PURE__ */ jsx(FieldBox, { below: messages.below, reserve, children: withReason(withEndHint(row, endHint)) });
221
250
  }
222
251
  export {
223
252
  DatePicker,