@eifi1/ui-kit 0.54.0 → 0.56.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 (847) hide show
  1. package/README.md +90 -76
  2. package/dist/admin/plan-change.js +2 -2
  3. package/dist/admin/plan-change.js.map +1 -1
  4. package/dist/admin/user-roster.d.ts +6 -0
  5. package/dist/admin/user-roster.js +4 -0
  6. package/dist/admin/user-roster.js.map +1 -1
  7. package/dist/billing/billing-labels.d.ts +26 -0
  8. package/dist/billing/billing-labels.js +9 -0
  9. package/dist/billing/billing-labels.js.map +1 -1
  10. package/dist/billing/subscription-standing.d.ts +61 -0
  11. package/dist/billing/subscription-standing.js +51 -0
  12. package/dist/billing/subscription-standing.js.map +1 -0
  13. package/dist/billing/usage-meter.d.ts +52 -0
  14. package/dist/billing/usage-meter.js +52 -0
  15. package/dist/billing/usage-meter.js.map +1 -0
  16. package/dist/chart.d.ts +1 -0
  17. package/dist/chart.js +4 -0
  18. package/dist/chart.js.map +1 -1
  19. package/dist/components/alert-banner.js +2 -3
  20. package/dist/components/alert-banner.js.map +1 -1
  21. package/dist/components/amount-input.js +3 -15
  22. package/dist/components/amount-input.js.map +1 -1
  23. package/dist/components/autocomplete.js +2 -2
  24. package/dist/components/autocomplete.js.map +1 -1
  25. package/dist/components/bulk-action-bar.js +2 -1
  26. package/dist/components/bulk-action-bar.js.map +1 -1
  27. package/dist/components/button.d.ts +35 -1
  28. package/dist/components/button.js +6 -9
  29. package/dist/components/button.js.map +1 -1
  30. package/dist/components/calendar-heatmap.js +2 -2
  31. package/dist/components/calendar-heatmap.js.map +1 -1
  32. package/dist/components/camera-button.d.ts +89 -0
  33. package/dist/components/camera-button.js +122 -0
  34. package/dist/components/camera-button.js.map +1 -0
  35. package/dist/components/camera-capture.d.ts +66 -0
  36. package/dist/components/camera-capture.js +190 -0
  37. package/dist/components/camera-capture.js.map +1 -0
  38. package/dist/components/camera-labels.d.ts +20 -0
  39. package/dist/components/camera-labels.js +15 -0
  40. package/dist/components/camera-labels.js.map +1 -0
  41. package/dist/components/chart-edge.d.ts +9 -0
  42. package/dist/components/chart-edge.js +14 -3
  43. package/dist/components/chart-edge.js.map +1 -1
  44. package/dist/components/chart-zoom-core.js +34 -29
  45. package/dist/components/chart-zoom-core.js.map +1 -1
  46. package/dist/components/checkbox-group.js +1 -1
  47. package/dist/components/checkbox-group.js.map +1 -1
  48. package/dist/components/combobox-core.js +3 -2
  49. package/dist/components/combobox-core.js.map +1 -1
  50. package/dist/components/combobox.js +2 -2
  51. package/dist/components/combobox.js.map +1 -1
  52. package/dist/components/consent-again-notice-labels.d.ts +25 -0
  53. package/dist/components/consent-again-notice-labels.js +15 -0
  54. package/dist/components/consent-again-notice-labels.js.map +1 -0
  55. package/dist/components/consent-again-notice.d.ts +85 -0
  56. package/dist/components/consent-again-notice.js +94 -0
  57. package/dist/components/consent-again-notice.js.map +1 -0
  58. package/dist/components/country-select.js +2 -7
  59. package/dist/components/country-select.js.map +1 -1
  60. package/dist/components/currency-select.js +2 -2
  61. package/dist/components/currency-select.js.map +1 -1
  62. package/dist/components/danger-confirm.js +2 -2
  63. package/dist/components/danger-confirm.js.map +1 -1
  64. package/dist/components/data-table-column-settings.js +2 -2
  65. package/dist/components/data-table-column-settings.js.map +1 -1
  66. package/dist/components/data-table-fit.js.map +1 -1
  67. package/dist/components/data-table-virtual.js +13 -3
  68. package/dist/components/data-table-virtual.js.map +1 -1
  69. package/dist/components/date-field.js +2 -2
  70. package/dist/components/date-field.js.map +1 -1
  71. package/dist/components/date-picker.js +2 -2
  72. package/dist/components/date-picker.js.map +1 -1
  73. package/dist/components/date-range-picker.js +2 -6
  74. package/dist/components/date-range-picker.js.map +1 -1
  75. package/dist/components/dialog-frame.d.ts +3 -1
  76. package/dist/components/dialog-frame.js.map +1 -1
  77. package/dist/components/dropdown.js +7 -3
  78. package/dist/components/dropdown.js.map +1 -1
  79. package/dist/components/entity-combobox.js +2 -2
  80. package/dist/components/entity-combobox.js.map +1 -1
  81. package/dist/components/field-parts.d.ts +108 -6
  82. package/dist/components/field-parts.js +59 -6
  83. package/dist/components/field-parts.js.map +1 -1
  84. package/dist/components/field-suggestion-labels.d.ts +2 -0
  85. package/dist/components/field-suggestion-labels.js +10 -0
  86. package/dist/components/field-suggestion-labels.js.map +1 -0
  87. package/dist/components/field.js +1 -1
  88. package/dist/components/field.js.map +1 -1
  89. package/dist/components/file-button.d.ts +1 -1
  90. package/dist/components/file-button.js.map +1 -1
  91. package/dist/components/floating-panel.d.ts +7 -6
  92. package/dist/components/floating-panel.js +3 -17
  93. package/dist/components/floating-panel.js.map +1 -1
  94. package/dist/components/form-actions.d.ts +3 -2
  95. package/dist/components/form-actions.js.map +1 -1
  96. package/dist/components/hover-menu.js +3 -2
  97. package/dist/components/hover-menu.js.map +1 -1
  98. package/dist/components/icon-button.d.ts +23 -5
  99. package/dist/components/icon-button.js +7 -8
  100. package/dist/components/icon-button.js.map +1 -1
  101. package/dist/components/inline-confirm.js +2 -1
  102. package/dist/components/inline-confirm.js.map +1 -1
  103. package/dist/components/inline-entity-combobox.js +2 -2
  104. package/dist/components/inline-entity-combobox.js.map +1 -1
  105. package/dist/components/input.js +2 -2
  106. package/dist/components/input.js.map +1 -1
  107. package/dist/components/jump-calendar.js +3 -3
  108. package/dist/components/jump-calendar.js.map +1 -1
  109. package/dist/components/legal.d.ts +13 -1
  110. package/dist/components/legal.js +12 -0
  111. package/dist/components/legal.js.map +1 -1
  112. package/dist/components/legended-series-chart.d.ts +83 -0
  113. package/dist/components/legended-series-chart.js +77 -0
  114. package/dist/components/legended-series-chart.js.map +1 -0
  115. package/dist/components/lightbox.js +2 -1
  116. package/dist/components/lightbox.js.map +1 -1
  117. package/dist/components/mini-calendar.js +13 -5
  118. package/dist/components/mini-calendar.js.map +1 -1
  119. package/dist/components/modal.d.ts +23 -2
  120. package/dist/components/modal.js +24 -6
  121. package/dist/components/modal.js.map +1 -1
  122. package/dist/components/month-picker.d.ts +1 -1
  123. package/dist/components/month-picker.js +6 -6
  124. package/dist/components/month-picker.js.map +1 -1
  125. package/dist/components/multi-entity-combobox.js +2 -2
  126. package/dist/components/multi-entity-combobox.js.map +1 -1
  127. package/dist/components/multi-select.js +2 -6
  128. package/dist/components/multi-select.js.map +1 -1
  129. package/dist/components/number-input.js +2 -2
  130. package/dist/components/number-input.js.map +1 -1
  131. package/dist/components/page-header-layout.d.ts +18 -0
  132. package/dist/components/page-header-layout.js +23 -0
  133. package/dist/components/page-header-layout.js.map +1 -0
  134. package/dist/components/page-header-skeleton.d.ts +41 -0
  135. package/dist/components/page-header-skeleton.js +52 -0
  136. package/dist/components/page-header-skeleton.js.map +1 -0
  137. package/dist/components/page-header.js +4 -17
  138. package/dist/components/page-header.js.map +1 -1
  139. package/dist/components/popover.js +7 -2
  140. package/dist/components/popover.js.map +1 -1
  141. package/dist/components/release-notes-labels.d.ts +16 -0
  142. package/dist/components/release-notes-labels.js +18 -0
  143. package/dist/components/release-notes-labels.js.map +1 -0
  144. package/dist/components/release-notes.d.ts +65 -0
  145. package/dist/components/release-notes.js +65 -0
  146. package/dist/components/release-notes.js.map +1 -0
  147. package/dist/components/select.js +2 -2
  148. package/dist/components/select.js.map +1 -1
  149. package/dist/components/series-chart-labels.d.ts +9 -0
  150. package/dist/components/series-chart-labels.js +3 -1
  151. package/dist/components/series-chart-labels.js.map +1 -1
  152. package/dist/components/series-chart.d.ts +5 -0
  153. package/dist/components/series-chart.js +89 -36
  154. package/dist/components/series-chart.js.map +1 -1
  155. package/dist/components/toggle-group.js +3 -2
  156. package/dist/components/toggle-group.js.map +1 -1
  157. package/dist/components/treemap-layout.js.map +1 -1
  158. package/dist/components/treemap.d.ts +17 -15
  159. package/dist/components/treemap.js +167 -85
  160. package/dist/components/treemap.js.map +1 -1
  161. package/dist/eslint/index.js +1 -1
  162. package/dist/eslint/role-utilities.d.ts +2 -2
  163. package/dist/eslint/role-utilities.js.map +1 -1
  164. package/dist/feedback/feedback-inbox-page.d.ts +260 -0
  165. package/dist/feedback/feedback-inbox-page.js +207 -0
  166. package/dist/feedback/feedback-inbox-page.js.map +1 -0
  167. package/dist/feedback/feedback-vocabulary.js +2 -2
  168. package/dist/feedback/feedback-vocabulary.js.map +1 -1
  169. package/dist/feedback.d.ts +1 -0
  170. package/dist/feedback.js +6 -0
  171. package/dist/feedback.js.map +1 -1
  172. package/dist/hooks/use-debounce.d.ts +32 -5
  173. package/dist/hooks/use-debounce.js +6 -5
  174. package/dist/hooks/use-debounce.js.map +1 -1
  175. package/dist/hooks/use-file-picker.d.ts +3 -1
  176. package/dist/hooks/use-file-picker.js.map +1 -1
  177. package/dist/hooks/use-focus-trap.d.ts +19 -3
  178. package/dist/hooks/use-focus-trap.js +15 -5
  179. package/dist/hooks/use-focus-trap.js.map +1 -1
  180. package/dist/hooks/use-has-camera.d.ts +36 -0
  181. package/dist/hooks/use-has-camera.js +53 -0
  182. package/dist/hooks/use-has-camera.js.map +1 -0
  183. package/dist/hooks/use-hotkey.js +1 -10
  184. package/dist/hooks/use-hotkey.js.map +1 -1
  185. package/dist/hooks/use-overlay-history.d.ts +6 -1
  186. package/dist/hooks/use-overlay-history.js +10 -2
  187. package/dist/hooks/use-overlay-history.js.map +1 -1
  188. package/dist/i18n/defaults.js +11 -1
  189. package/dist/i18n/defaults.js.map +1 -1
  190. package/dist/i18n/german.js +75 -3
  191. package/dist/i18n/german.js.map +1 -1
  192. package/dist/i18n/kit-labels.d.ts +20 -0
  193. package/dist/i18n/kit-labels.js.map +1 -1
  194. package/dist/i18n/locales/en.js +92 -4
  195. package/dist/i18n/locales/en.js.map +3 -3
  196. package/dist/i18n/locales/es.js +71 -3
  197. package/dist/i18n/locales/es.js.map +1 -1
  198. package/dist/i18n/locales/fr.js +72 -2
  199. package/dist/i18n/locales/fr.js.map +1 -1
  200. package/dist/i18n/locales/hu.js +74 -3
  201. package/dist/i18n/locales/hu.js.map +1 -1
  202. package/dist/i18n/locales/it.js +74 -3
  203. package/dist/i18n/locales/it.js.map +1 -1
  204. package/dist/i18n/locales/zh.js +71 -3
  205. package/dist/i18n/locales/zh.js.map +1 -1
  206. package/dist/i18n/review.js +22 -1
  207. package/dist/i18n/review.js.map +1 -1
  208. package/dist/index.d.ts +24 -5
  209. package/dist/index.js +35 -1
  210. package/dist/index.js.map +1 -1
  211. package/dist/landing/landing-labels.d.ts +8 -0
  212. package/dist/landing/landing-labels.js +2 -1
  213. package/dist/landing/landing-labels.js.map +1 -1
  214. package/dist/lib/changelog.d.ts +130 -0
  215. package/dist/lib/changelog.js +137 -0
  216. package/dist/lib/changelog.js.map +1 -0
  217. package/dist/lib/clipboard-minus.js +2 -2
  218. package/dist/lib/clipboard-minus.js.map +1 -1
  219. package/dist/lib/dates.d.ts +29 -3
  220. package/dist/lib/dates.js +41 -12
  221. package/dist/lib/dates.js.map +1 -1
  222. package/dist/lib/editable-content.d.ts +33 -0
  223. package/dist/lib/editable-content.js +15 -0
  224. package/dist/lib/editable-content.js.map +1 -0
  225. package/dist/lib/focus-left.d.ts +47 -1
  226. package/dist/lib/focus-left.js +11 -2
  227. package/dist/lib/focus-left.js.map +1 -1
  228. package/dist/lib/server-wake.d.ts +49 -9
  229. package/dist/lib/server-wake.js +25 -14
  230. package/dist/lib/server-wake.js.map +1 -1
  231. package/dist/lib/session-refresh.d.ts +53 -0
  232. package/dist/lib/session-refresh.js +27 -1
  233. package/dist/lib/session-refresh.js.map +1 -1
  234. package/dist/rhf/fields.js +3 -3
  235. package/dist/rhf/fields.js.map +1 -1
  236. package/dist/shell/app-shell.d.ts +25 -1
  237. package/dist/shell/app-shell.js +3 -1
  238. package/dist/shell/app-shell.js.map +1 -1
  239. package/dist/shell/dev-menu.d.ts +3 -0
  240. package/dist/shell/dev-menu.js +4 -2
  241. package/dist/shell/dev-menu.js.map +1 -1
  242. package/dist/shell/held-changes-banner-labels.d.ts +23 -0
  243. package/dist/shell/held-changes-banner-labels.js +14 -0
  244. package/dist/shell/held-changes-banner-labels.js.map +1 -0
  245. package/dist/shell/held-changes-banner.d.ts +75 -0
  246. package/dist/shell/held-changes-banner.js +106 -0
  247. package/dist/shell/held-changes-banner.js.map +1 -0
  248. package/dist/shell/sync-status-indicator.d.ts +27 -12
  249. package/dist/shell/sync-status-indicator.js +13 -4
  250. package/dist/shell/sync-status-indicator.js.map +1 -1
  251. package/dist/shell/top-bar-parts-labels.d.ts +5 -0
  252. package/dist/shell/top-bar-parts-labels.js +3 -1
  253. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  254. package/dist/shell.d.ts +2 -0
  255. package/dist/shell.js +3 -0
  256. package/dist/shell.js.map +1 -1
  257. package/dist/sync/engine.d.ts +11 -2
  258. package/dist/sync/engine.js +13 -5
  259. package/dist/sync/engine.js.map +1 -1
  260. package/dist/sync/hooks.d.ts +3 -2
  261. package/dist/sync/hooks.js +4 -2
  262. package/dist/sync/hooks.js.map +1 -1
  263. package/dist/testing/css-variables.generated.d.ts +2 -2
  264. package/dist/testing/css-variables.generated.js +1 -1
  265. package/dist/testing/css-variables.generated.js.map +1 -1
  266. package/dist/theme/chart-palette.d.ts +18 -0
  267. package/dist/theme/chart-palette.js +17 -1
  268. package/dist/theme/chart-palette.js.map +1 -1
  269. package/dist/theme/contrast-tokens.d.ts +3 -3
  270. package/dist/theme/contrast-tokens.js.map +1 -1
  271. package/dist/theme/contrast.d.ts +4 -4
  272. package/dist/theme/contrast.js.map +1 -1
  273. package/dist/tour/tour-overlay.js +2 -1
  274. package/dist/tour/tour-overlay.js.map +1 -1
  275. package/package.json +9 -12
  276. package/tokens.css +19 -54
  277. package/src/account/account-parts.ts +0 -47
  278. package/src/account/account-settings-labels.ts +0 -240
  279. package/src/account/account-settings.tsx +0 -698
  280. package/src/account/data-export-setting-labels.ts +0 -20
  281. package/src/account/data-export-setting.tsx +0 -200
  282. package/src/account/delete-account-setting-labels.ts +0 -33
  283. package/src/account/delete-account-setting.tsx +0 -342
  284. package/src/account/email-change-setting-labels.ts +0 -31
  285. package/src/account/email-change-setting.tsx +0 -443
  286. package/src/account/passkeys-setting.tsx +0 -445
  287. package/src/account/sessions-setting-labels.ts +0 -24
  288. package/src/account/sessions-setting.tsx +0 -283
  289. package/src/admin/admin-action-confirm-labels.ts +0 -19
  290. package/src/admin/admin-action-confirm.tsx +0 -509
  291. package/src/admin/admin-action-log-labels.ts +0 -83
  292. package/src/admin/admin-action-log.tsx +0 -464
  293. package/src/admin/admin-area-labels.ts +0 -114
  294. package/src/admin/admin-forbidden.tsx +0 -75
  295. package/src/admin/admin-overview.tsx +0 -258
  296. package/src/admin/admin-parts.ts +0 -68
  297. package/src/admin/admin-sections.tsx +0 -208
  298. package/src/admin/admin-tool-links.tsx +0 -90
  299. package/src/admin/invitations-panel-labels.ts +0 -31
  300. package/src/admin/invitations-panel.tsx +0 -516
  301. package/src/admin/mail-test-card-labels.ts +0 -19
  302. package/src/admin/mail-test-card.tsx +0 -189
  303. package/src/admin/plan-change-labels.ts +0 -5
  304. package/src/admin/plan-change.tsx +0 -474
  305. package/src/admin/reviewer-scope-editor-labels.ts +0 -16
  306. package/src/admin/reviewer-scope-editor.tsx +0 -224
  307. package/src/admin/role-select-labels.ts +0 -16
  308. package/src/admin/role-select.tsx +0 -282
  309. package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
  310. package/src/admin/transfer-ownership-dialog.tsx +0 -162
  311. package/src/admin/user-roster-labels.ts +0 -17
  312. package/src/admin/user-roster.tsx +0 -559
  313. package/src/auth/accept-invitation-labels.ts +0 -21
  314. package/src/auth/accept-invitation.tsx +0 -210
  315. package/src/auth/auth-errors-labels.ts +0 -23
  316. package/src/auth/auth-errors.ts +0 -439
  317. package/src/auth/complete-name-dialog-labels.ts +0 -16
  318. package/src/auth/complete-name-dialog.tsx +0 -253
  319. package/src/auth/email-tag.ts +0 -75
  320. package/src/auth/forgot-password-form-labels.ts +0 -20
  321. package/src/auth/forgot-password-form.tsx +0 -221
  322. package/src/auth/form-rules.ts +0 -40
  323. package/src/auth/new-password-fields.tsx +0 -85
  324. package/src/auth/not-found-page-labels.ts +0 -12
  325. package/src/auth/not-found-page.tsx +0 -95
  326. package/src/auth/register-form-labels.ts +0 -31
  327. package/src/auth/register-form.tsx +0 -456
  328. package/src/auth/reset-password-form-labels.ts +0 -27
  329. package/src/auth/reset-password-form.tsx +0 -341
  330. package/src/auth/sign-in-form-labels.ts +0 -44
  331. package/src/auth/sign-in-form.tsx +0 -858
  332. package/src/auth/status-parts.tsx +0 -132
  333. package/src/auth/verify-email-labels.ts +0 -26
  334. package/src/auth/verify-email.tsx +0 -445
  335. package/src/billing/billing-banner.tsx +0 -258
  336. package/src/billing/billing-labels.ts +0 -381
  337. package/src/billing/billing-lock.ts +0 -108
  338. package/src/billing/billing-overview.ts +0 -157
  339. package/src/billing/billing-standing.ts +0 -80
  340. package/src/billing/cancel-request.ts +0 -97
  341. package/src/billing/cancel-subscription.tsx +0 -98
  342. package/src/billing/checkout-processing.ts +0 -394
  343. package/src/billing/checkout-return.ts +0 -90
  344. package/src/billing/pay-page.ts +0 -330
  345. package/src/billing/plan-card.tsx +0 -562
  346. package/src/billing/plan-change-labels.ts +0 -66
  347. package/src/billing/plan-limit-notice.tsx +0 -148
  348. package/src/billing/plan-limit-toast.tsx +0 -90
  349. package/src/billing/plan-limit.ts +0 -52
  350. package/src/billing/plan-price.ts +0 -98
  351. package/src/billing/plan-switch.tsx +0 -216
  352. package/src/billing/provider-page.ts +0 -84
  353. package/src/billing/subscription-actions.tsx +0 -247
  354. package/src/billing/subscription-status-chip.tsx +0 -95
  355. package/src/chart.ts +0 -103
  356. package/src/components/account-chips-labels.ts +0 -16
  357. package/src/components/account-chips.tsx +0 -474
  358. package/src/components/alert-banner.tsx +0 -670
  359. package/src/components/amount-input.tsx +0 -816
  360. package/src/components/appearance-labels.ts +0 -63
  361. package/src/components/authed-image-labels.ts +0 -11
  362. package/src/components/authed-image.tsx +0 -209
  363. package/src/components/autocomplete.tsx +0 -645
  364. package/src/components/breadcrumbs-labels.ts +0 -10
  365. package/src/components/breadcrumbs.tsx +0 -205
  366. package/src/components/bulk-action-bar-labels.ts +0 -11
  367. package/src/components/bulk-action-bar.tsx +0 -443
  368. package/src/components/button-group.tsx +0 -223
  369. package/src/components/button-look.ts +0 -256
  370. package/src/components/button.tsx +0 -339
  371. package/src/components/calculator.tsx +0 -341
  372. package/src/components/calendar-heatmap-labels.ts +0 -15
  373. package/src/components/calendar-heatmap.tsx +0 -600
  374. package/src/components/card.tsx +0 -377
  375. package/src/components/chart-edge.ts +0 -106
  376. package/src/components/chart-figure.tsx +0 -35
  377. package/src/components/chart-height.ts +0 -30
  378. package/src/components/chart-zoom-core.tsx +0 -627
  379. package/src/components/chart-zoom.tsx +0 -22
  380. package/src/components/chart.tsx +0 -554
  381. package/src/components/checkbox-group.tsx +0 -247
  382. package/src/components/checkbox.tsx +0 -346
  383. package/src/components/chip-input.tsx +0 -370
  384. package/src/components/chip-labels.ts +0 -13
  385. package/src/components/chip.tsx +0 -843
  386. package/src/components/choice-card.tsx +0 -544
  387. package/src/components/column-mapper-labels.ts +0 -38
  388. package/src/components/column-mapper.tsx +0 -655
  389. package/src/components/combobox-core.tsx +0 -1321
  390. package/src/components/combobox-suggestions.tsx +0 -131
  391. package/src/components/combobox.tsx +0 -716
  392. package/src/components/confirm-context.ts +0 -7
  393. package/src/components/confirm-dialog-labels.ts +0 -13
  394. package/src/components/confirm-dialog.tsx +0 -341
  395. package/src/components/copy-button-labels.ts +0 -13
  396. package/src/components/copy-button.tsx +0 -192
  397. package/src/components/country-select-labels.ts +0 -11
  398. package/src/components/country-select.tsx +0 -539
  399. package/src/components/currency-option-list.tsx +0 -188
  400. package/src/components/currency-select.tsx +0 -343
  401. package/src/components/danger-confirm-labels.ts +0 -20
  402. package/src/components/danger-confirm.tsx +0 -987
  403. package/src/components/data-table-cells.tsx +0 -118
  404. package/src/components/data-table-column-settings.tsx +0 -198
  405. package/src/components/data-table-filter-popover.tsx +0 -289
  406. package/src/components/data-table-filters.ts +0 -315
  407. package/src/components/data-table-fit.ts +0 -301
  408. package/src/components/data-table-groups.tsx +0 -288
  409. package/src/components/data-table-head.tsx +0 -280
  410. package/src/components/data-table-labels.ts +0 -198
  411. package/src/components/data-table-leading-row.tsx +0 -58
  412. package/src/components/data-table-mobile.tsx +0 -1117
  413. package/src/components/data-table-pagination.tsx +0 -202
  414. package/src/components/data-table-resize.ts +0 -188
  415. package/src/components/data-table-row-actions.tsx +0 -302
  416. package/src/components/data-table-row.tsx +0 -496
  417. package/src/components/data-table-selection.ts +0 -71
  418. package/src/components/data-table-shared.tsx +0 -287
  419. package/src/components/data-table-sort.ts +0 -163
  420. package/src/components/data-table-virtual.ts +0 -576
  421. package/src/components/data-table.tsx +0 -2598
  422. package/src/components/date-field.tsx +0 -684
  423. package/src/components/date-format-setting.tsx +0 -64
  424. package/src/components/date-picker.tsx +0 -404
  425. package/src/components/date-range-picker.tsx +0 -518
  426. package/src/components/description-list-labels.ts +0 -9
  427. package/src/components/description-list.tsx +0 -386
  428. package/src/components/dialog-frame-labels.ts +0 -7
  429. package/src/components/dialog-frame.tsx +0 -284
  430. package/src/components/disclosure.tsx +0 -476
  431. package/src/components/dropdown.tsx +0 -461
  432. package/src/components/empty-state.tsx +0 -136
  433. package/src/components/entity-combobox.tsx +0 -422
  434. package/src/components/error-boundary-labels.ts +0 -20
  435. package/src/components/error-boundary.tsx +0 -675
  436. package/src/components/facing-pair.ts +0 -93
  437. package/src/components/field-base-labels.ts +0 -20
  438. package/src/components/field-base.tsx +0 -656
  439. package/src/components/field-parts.tsx +0 -825
  440. package/src/components/field-strip.tsx +0 -129
  441. package/src/components/field.tsx +0 -206
  442. package/src/components/file-button.tsx +0 -295
  443. package/src/components/file-dropzone.tsx +0 -559
  444. package/src/components/floating-panel-labels.ts +0 -10
  445. package/src/components/floating-panel.tsx +0 -828
  446. package/src/components/focus-ring.ts +0 -16
  447. package/src/components/form-actions-labels.ts +0 -11
  448. package/src/components/form-actions.tsx +0 -578
  449. package/src/components/full-bleed-dialog.tsx +0 -294
  450. package/src/components/grouped-picker.tsx +0 -184
  451. package/src/components/help-popover.tsx +0 -117
  452. package/src/components/hover-menu.tsx +0 -343
  453. package/src/components/iban-input-labels.ts +0 -14
  454. package/src/components/iban-input.tsx +0 -236
  455. package/src/components/icon-button.tsx +0 -959
  456. package/src/components/icon-picker-labels.ts +0 -13
  457. package/src/components/icon-picker.tsx +0 -232
  458. package/src/components/image-grid-labels.ts +0 -11
  459. package/src/components/image-grid.tsx +0 -202
  460. package/src/components/inline-confirm.tsx +0 -116
  461. package/src/components/inline-edit-field-labels.ts +0 -11
  462. package/src/components/inline-edit-field.tsx +0 -552
  463. package/src/components/inline-entity-combobox.tsx +0 -849
  464. package/src/components/input.tsx +0 -394
  465. package/src/components/jump-calendar.tsx +0 -265
  466. package/src/components/language-select.tsx +0 -93
  467. package/src/components/legal-labels.ts +0 -105
  468. package/src/components/legal-page.tsx +0 -711
  469. package/src/components/legal.tsx +0 -668
  470. package/src/components/lightbox-labels.ts +0 -18
  471. package/src/components/lightbox.tsx +0 -399
  472. package/src/components/line-items-labels.ts +0 -14
  473. package/src/components/line-items.tsx +0 -602
  474. package/src/components/list-labels.ts +0 -12
  475. package/src/components/list.tsx +0 -870
  476. package/src/components/loading-state.tsx +0 -78
  477. package/src/components/measured-grid-labels.ts +0 -25
  478. package/src/components/measured-grid.tsx +0 -837
  479. package/src/components/menu-item.tsx +0 -241
  480. package/src/components/mini-calendar-labels.ts +0 -17
  481. package/src/components/mini-calendar.tsx +0 -792
  482. package/src/components/modal.tsx +0 -534
  483. package/src/components/money-draft.ts +0 -83
  484. package/src/components/money-field.tsx +0 -76
  485. package/src/components/month-grid.tsx +0 -229
  486. package/src/components/month-picker-labels.ts +0 -22
  487. package/src/components/month-picker.tsx +0 -1087
  488. package/src/components/multi-entity-combobox.tsx +0 -369
  489. package/src/components/multi-select.tsx +0 -376
  490. package/src/components/nav-pills.tsx +0 -198
  491. package/src/components/number-field.tsx +0 -327
  492. package/src/components/number-input.tsx +0 -559
  493. package/src/components/numeric-field.ts +0 -169
  494. package/src/components/numpad-sheet.tsx +0 -285
  495. package/src/components/one-time-code-input.tsx +0 -270
  496. package/src/components/page-contents-labels.ts +0 -9
  497. package/src/components/page-contents.tsx +0 -282
  498. package/src/components/page-header-context.ts +0 -8
  499. package/src/components/page-header.tsx +0 -298
  500. package/src/components/password-strength-labels.ts +0 -22
  501. package/src/components/password-strength.tsx +0 -241
  502. package/src/components/phone-input-labels.ts +0 -10
  503. package/src/components/phone-input.tsx +0 -282
  504. package/src/components/picker-sheet.tsx +0 -330
  505. package/src/components/pie-chart-labels.ts +0 -40
  506. package/src/components/pie-chart.tsx +0 -480
  507. package/src/components/popover-labels.ts +0 -14
  508. package/src/components/popover.tsx +0 -268
  509. package/src/components/progress-bar-labels.ts +0 -10
  510. package/src/components/progress-bar.tsx +0 -479
  511. package/src/components/qr-code.tsx +0 -76
  512. package/src/components/reauth-dialog-labels.ts +0 -13
  513. package/src/components/reauth-dialog.tsx +0 -265
  514. package/src/components/row-actions-labels.ts +0 -12
  515. package/src/components/row-actions.tsx +0 -795
  516. package/src/components/scroll-area.tsx +0 -123
  517. package/src/components/search-field.tsx +0 -192
  518. package/src/components/select.tsx +0 -294
  519. package/src/components/separator.tsx +0 -33
  520. package/src/components/series-chart-axes.ts +0 -342
  521. package/src/components/series-chart-budget.ts +0 -150
  522. package/src/components/series-chart-labels.ts +0 -33
  523. package/src/components/series-chart-thin.ts +0 -180
  524. package/src/components/series-chart-ticks.ts +0 -194
  525. package/src/components/series-chart.tsx +0 -1906
  526. package/src/components/server-wake-labels.ts +0 -11
  527. package/src/components/server-wake.tsx +0 -129
  528. package/src/components/settings-fields.tsx +0 -237
  529. package/src/components/share-card-labels.ts +0 -30
  530. package/src/components/share-card.tsx +0 -675
  531. package/src/components/signature-pad-labels.ts +0 -24
  532. package/src/components/signature-pad.tsx +0 -747
  533. package/src/components/signed-amount-labels.ts +0 -10
  534. package/src/components/signed-amount.tsx +0 -336
  535. package/src/components/skeleton.tsx +0 -80
  536. package/src/components/slider.tsx +0 -338
  537. package/src/components/sparkline-labels.ts +0 -14
  538. package/src/components/sparkline.tsx +0 -322
  539. package/src/components/spinner.tsx +0 -81
  540. package/src/components/stat-tile-labels.ts +0 -15
  541. package/src/components/stat-tile.tsx +0 -754
  542. package/src/components/status-dot.tsx +0 -170
  543. package/src/components/swatch-picker-labels.ts +0 -10
  544. package/src/components/swatch-picker.tsx +0 -283
  545. package/src/components/swipeable-row.tsx +0 -386
  546. package/src/components/switch.tsx +0 -273
  547. package/src/components/table-skeleton.tsx +0 -136
  548. package/src/components/table.tsx +0 -647
  549. package/src/components/tabs-labels.ts +0 -13
  550. package/src/components/tabs.tsx +0 -693
  551. package/src/components/text-link.tsx +0 -359
  552. package/src/components/text.tsx +0 -150
  553. package/src/components/textarea.tsx +0 -330
  554. package/src/components/tile-radio.tsx +0 -332
  555. package/src/components/time-input.tsx +0 -121
  556. package/src/components/toast-labels.ts +0 -12
  557. package/src/components/toast.tsx +0 -519
  558. package/src/components/toggle-group.tsx +0 -752
  559. package/src/components/toggle-legend.tsx +0 -394
  560. package/src/components/tooltip.tsx +0 -1149
  561. package/src/components/translation-review-editor.tsx +0 -290
  562. package/src/components/translation-review-labels.ts +0 -181
  563. package/src/components/translation-review-parts.tsx +0 -272
  564. package/src/components/translation-review.tsx +0 -1240
  565. package/src/components/tree-view.tsx +0 -589
  566. package/src/components/treemap-labels.ts +0 -47
  567. package/src/components/treemap-layout.ts +0 -210
  568. package/src/components/treemap.tsx +0 -1184
  569. package/src/components/trigger-aria.ts +0 -44
  570. package/src/components/use-anchor-dir.ts +0 -20
  571. package/src/components/use-mobile-reveal.ts +0 -62
  572. package/src/components/use-table-state.ts +0 -753
  573. package/src/components/user-avatar.tsx +0 -116
  574. package/src/components/wizard-stepper.tsx +0 -42
  575. package/src/components/write-lock-labels.ts +0 -10
  576. package/src/components/write-lock.tsx +0 -253
  577. package/src/currencies.ts +0 -90
  578. package/src/data-table.ts +0 -42
  579. package/src/demo/demo-banner.tsx +0 -153
  580. package/src/demo/demo-countdown.ts +0 -136
  581. package/src/demo/demo-ended.tsx +0 -90
  582. package/src/demo/demo-labels.ts +0 -78
  583. package/src/demo/demo-session.ts +0 -32
  584. package/src/demo/demo-start.tsx +0 -156
  585. package/src/eslint/index.ts +0 -161
  586. package/src/eslint/role-utilities.generated.ts +0 -146
  587. package/src/eslint/role-utilities.ts +0 -233
  588. package/src/feedback/feedback-attachment-download.tsx +0 -77
  589. package/src/feedback/feedback-attachment-labels.ts +0 -37
  590. package/src/feedback/feedback-attachment.tsx +0 -1239
  591. package/src/feedback/feedback-capture.ts +0 -287
  592. package/src/feedback/feedback-comment-thread.tsx +0 -353
  593. package/src/feedback/feedback-context-labels.ts +0 -11
  594. package/src/feedback/feedback-context.tsx +0 -245
  595. package/src/feedback/feedback-crash.ts +0 -547
  596. package/src/feedback/feedback-diagnostics.tsx +0 -41
  597. package/src/feedback/feedback-dialog-labels.ts +0 -29
  598. package/src/feedback/feedback-dialog.tsx +0 -452
  599. package/src/feedback/feedback-entry-file.tsx +0 -150
  600. package/src/feedback/feedback-inbox.tsx +0 -552
  601. package/src/feedback/feedback-labels.ts +0 -144
  602. package/src/feedback/feedback-menu-labels.ts +0 -12
  603. package/src/feedback/feedback-menu.tsx +0 -177
  604. package/src/feedback/feedback-mutation.ts +0 -20
  605. package/src/feedback/feedback-page-link.ts +0 -90
  606. package/src/feedback/feedback-public-labels.ts +0 -81
  607. package/src/feedback/feedback-public-list.tsx +0 -524
  608. package/src/feedback/feedback-public-switch.tsx +0 -102
  609. package/src/feedback/feedback-public-types.ts +0 -101
  610. package/src/feedback/feedback-record.ts +0 -428
  611. package/src/feedback/feedback-replies-context.ts +0 -9
  612. package/src/feedback/feedback-replies.tsx +0 -343
  613. package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
  614. package/src/feedback/feedback-row-detail-labels.ts +0 -31
  615. package/src/feedback/feedback-row-detail.tsx +0 -871
  616. package/src/feedback/feedback-status-undo.ts +0 -109
  617. package/src/feedback/feedback-submit-dialog.tsx +0 -131
  618. package/src/feedback/feedback-submit.tsx +0 -270
  619. package/src/feedback/feedback-swipe.tsx +0 -192
  620. package/src/feedback/feedback-table-labels.ts +0 -27
  621. package/src/feedback/feedback-table.tsx +0 -912
  622. package/src/feedback/feedback-thread-labels.ts +0 -22
  623. package/src/feedback/feedback-thread-parts-labels.ts +0 -41
  624. package/src/feedback/feedback-thread.tsx +0 -905
  625. package/src/feedback/feedback-vocabulary.ts +0 -263
  626. package/src/feedback/feedback-vote-button.tsx +0 -124
  627. package/src/feedback.ts +0 -116
  628. package/src/hooks/use-anchored-panel.ts +0 -396
  629. package/src/hooks/use-anchored-rect.ts +0 -77
  630. package/src/hooks/use-announce.ts +0 -85
  631. package/src/hooks/use-authed-src.ts +0 -167
  632. package/src/hooks/use-body-scroll-lock.ts +0 -119
  633. package/src/hooks/use-breakpoint.ts +0 -133
  634. package/src/hooks/use-close-transition.ts +0 -107
  635. package/src/hooks/use-copy-to-clipboard.ts +0 -122
  636. package/src/hooks/use-debounce.ts +0 -114
  637. package/src/hooks/use-dismiss.ts +0 -371
  638. package/src/hooks/use-document-title.ts +0 -33
  639. package/src/hooks/use-file-drop.ts +0 -121
  640. package/src/hooks/use-file-picker-labels.ts +0 -28
  641. package/src/hooks/use-file-picker.tsx +0 -359
  642. package/src/hooks/use-focus-trap.ts +0 -199
  643. package/src/hooks/use-hotkey.ts +0 -212
  644. package/src/hooks/use-large-text.ts +0 -41
  645. package/src/hooks/use-long-press.ts +0 -140
  646. package/src/hooks/use-media-query.ts +0 -39
  647. package/src/hooks/use-mounted.ts +0 -39
  648. package/src/hooks/use-noindex.ts +0 -37
  649. package/src/hooks/use-overlay-history.ts +0 -714
  650. package/src/hooks/use-row-swipe.ts +0 -207
  651. package/src/hooks/use-search-param-state.ts +0 -434
  652. package/src/hooks/use-windowed-rows.ts +0 -225
  653. package/src/i18n/defaults.ts +0 -267
  654. package/src/i18n/german.ts +0 -2059
  655. package/src/i18n/kit-labels.tsx +0 -1003
  656. package/src/i18n/languages.ts +0 -349
  657. package/src/i18n/locales/de-CH.ts +0 -24
  658. package/src/i18n/locales/en.ts +0 -267
  659. package/src/i18n/locales/es.ts +0 -1999
  660. package/src/i18n/locales/fr.ts +0 -2037
  661. package/src/i18n/locales/hu.ts +0 -2004
  662. package/src/i18n/locales/it.ts +0 -2014
  663. package/src/i18n/locales/zh.ts +0 -1912
  664. package/src/i18n/plurals.ts +0 -86
  665. package/src/i18n/review.ts +0 -475
  666. package/src/i18n/shared-labels.ts +0 -138
  667. package/src/i18n/swiss.ts +0 -29
  668. package/src/index.ts +0 -1191
  669. package/src/landing/access.ts +0 -93
  670. package/src/landing/landing-actions.tsx +0 -97
  671. package/src/landing/landing-labels.ts +0 -59
  672. package/src/landing/landing-sections.tsx +0 -341
  673. package/src/landing/page-seo.ts +0 -261
  674. package/src/landing/public-header.tsx +0 -121
  675. package/src/landing/public-page.tsx +0 -106
  676. package/src/landing/routing.tsx +0 -225
  677. package/src/lib/broadcast.ts +0 -52
  678. package/src/lib/calc.ts +0 -323
  679. package/src/lib/chip-edge-snap.ts +0 -212
  680. package/src/lib/clipboard-minus.ts +0 -107
  681. package/src/lib/clipping.ts +0 -34
  682. package/src/lib/cn.ts +0 -28
  683. package/src/lib/column-mapping.ts +0 -234
  684. package/src/lib/countries.ts +0 -91
  685. package/src/lib/data-attributes.ts +0 -14
  686. package/src/lib/date-format.ts +0 -227
  687. package/src/lib/dates.ts +0 -288
  688. package/src/lib/day-format.ts +0 -96
  689. package/src/lib/decimal-marks.ts +0 -108
  690. package/src/lib/describe-failure.ts +0 -18
  691. package/src/lib/device-settings.ts +0 -147
  692. package/src/lib/direction.ts +0 -74
  693. package/src/lib/document-navigation.ts +0 -17
  694. package/src/lib/document-title.ts +0 -41
  695. package/src/lib/download.ts +0 -172
  696. package/src/lib/float-clearance.ts +0 -70
  697. package/src/lib/floating-stack.ts +0 -70
  698. package/src/lib/focus-left.ts +0 -156
  699. package/src/lib/format.ts +0 -585
  700. package/src/lib/http-status.ts +0 -38
  701. package/src/lib/iban.ts +0 -251
  702. package/src/lib/ime.ts +0 -24
  703. package/src/lib/intl-cache.ts +0 -27
  704. package/src/lib/iso-day.ts +0 -13
  705. package/src/lib/lazy-with-fallback.ts +0 -82
  706. package/src/lib/logger.ts +0 -69
  707. package/src/lib/minus.ts +0 -50
  708. package/src/lib/path-pattern.ts +0 -55
  709. package/src/lib/person-name.ts +0 -155
  710. package/src/lib/phone.ts +0 -208
  711. package/src/lib/plain-text.ts +0 -22
  712. package/src/lib/presence.ts +0 -18
  713. package/src/lib/qr-encode.ts +0 -444
  714. package/src/lib/quiet-focus.ts +0 -32
  715. package/src/lib/reduced-motion.ts +0 -22
  716. package/src/lib/refs.ts +0 -22
  717. package/src/lib/round.ts +0 -43
  718. package/src/lib/safe-storage.ts +0 -119
  719. package/src/lib/server-wake.ts +0 -315
  720. package/src/lib/session-refresh.ts +0 -472
  721. package/src/lib/share-format.ts +0 -18
  722. package/src/lib/status-tone.ts +0 -43
  723. package/src/lib/strip-fade.ts +0 -287
  724. package/src/lib/table-text.ts +0 -554
  725. package/src/lib/text-case.ts +0 -19
  726. package/src/lib/text-match.ts +0 -169
  727. package/src/lib/thenable.ts +0 -19
  728. package/src/lib/translation-review.ts +0 -769
  729. package/src/notifications/device-zone-report.ts +0 -67
  730. package/src/notifications/feedback-notification-types-labels.ts +0 -33
  731. package/src/notifications/feedback-notification-types.ts +0 -71
  732. package/src/notifications/notification-bell.tsx +0 -119
  733. package/src/notifications/notification-inbox.tsx +0 -309
  734. package/src/notifications/notification-parts.tsx +0 -81
  735. package/src/notifications/notification-types-setting.tsx +0 -406
  736. package/src/notifications/notification-types.ts +0 -89
  737. package/src/notifications/notifications-labels.ts +0 -287
  738. package/src/notifications/push-device-setting.tsx +0 -230
  739. package/src/notifications/push-devices-setting.tsx +0 -374
  740. package/src/notifications/push-worker.ts +0 -168
  741. package/src/notifications/quiet-hours-setting.tsx +0 -238
  742. package/src/notifications/unsubscribe-panel.tsx +0 -200
  743. package/src/notifications/use-push-device.ts +0 -448
  744. package/src/offline/local-first.ts +0 -100
  745. package/src/offline/pwa-update-prompt-labels.ts +0 -9
  746. package/src/offline/pwa-update-prompt.tsx +0 -78
  747. package/src/offline/query-persistence.ts +0 -391
  748. package/src/offline/service-worker-updates.ts +0 -81
  749. package/src/offline/shared-computer-labels.ts +0 -8
  750. package/src/offline/shared-computer.tsx +0 -87
  751. package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
  752. package/src/offline/sign-out-pending-dialog.tsx +0 -135
  753. package/src/offline.ts +0 -37
  754. package/src/push-worker.ts +0 -25
  755. package/src/rhf/fields.tsx +0 -2187
  756. package/src/rhf/form-core.tsx +0 -324
  757. package/src/rhf/form.tsx +0 -12
  758. package/src/rhf/line-items.tsx +0 -135
  759. package/src/rhf/use-rhf-wizard-step.ts +0 -104
  760. package/src/rhf.ts +0 -21
  761. package/src/search/command-palette-labels.ts +0 -16
  762. package/src/search/command-palette.tsx +0 -624
  763. package/src/search/global-search-labels.ts +0 -13
  764. package/src/search/global-search.tsx +0 -660
  765. package/src/search/search-index.ts +0 -283
  766. package/src/search/use-command-key.ts +0 -23
  767. package/src/search.ts +0 -14
  768. package/src/settings/device-reset-setting-labels.ts +0 -20
  769. package/src/settings/device-reset-setting.tsx +0 -116
  770. package/src/settings/settings-card-classes.ts +0 -11
  771. package/src/settings/settings-catalogue.ts +0 -150
  772. package/src/settings/settings-context.ts +0 -33
  773. package/src/settings/settings-heading.tsx +0 -67
  774. package/src/settings/settings-labels.ts +0 -87
  775. package/src/settings/settings-layout.tsx +0 -471
  776. package/src/settings/settings-section.tsx +0 -108
  777. package/src/settings/use-account-appearance.ts +0 -148
  778. package/src/settings/use-account-language.ts +0 -146
  779. package/src/settings/use-settings-focus.ts +0 -128
  780. package/src/settings/use-settings-route.ts +0 -258
  781. package/src/shell/account-menu-language.tsx +0 -130
  782. package/src/shell/account-menu-text-size.tsx +0 -80
  783. package/src/shell/admin-menu.tsx +0 -183
  784. package/src/shell/app-shell-labels.ts +0 -11
  785. package/src/shell/app-shell.tsx +0 -1560
  786. package/src/shell/auth-layout.tsx +0 -186
  787. package/src/shell/company-switcher-labels.ts +0 -13
  788. package/src/shell/company-switcher.tsx +0 -136
  789. package/src/shell/dev-menu.tsx +0 -257
  790. package/src/shell/fill-height.tsx +0 -45
  791. package/src/shell/language-code.ts +0 -5
  792. package/src/shell/option-switcher-menu.tsx +0 -84
  793. package/src/shell/sync-status-indicator.tsx +0 -303
  794. package/src/shell/top-bar-actions.tsx +0 -87
  795. package/src/shell/top-bar-brand.tsx +0 -78
  796. package/src/shell/top-bar-parts-labels.ts +0 -148
  797. package/src/shell/top-bar.tsx +0 -40
  798. package/src/shell/topbar-action-menu.tsx +0 -567
  799. package/src/shell/topbar-controls.tsx +0 -269
  800. package/src/shell/tour-menu.tsx +0 -187
  801. package/src/shell.ts +0 -108
  802. package/src/sync/db.ts +0 -129
  803. package/src/sync/engine.ts +0 -1011
  804. package/src/sync/hooks.ts +0 -98
  805. package/src/sync/testing.ts +0 -73
  806. package/src/sync/wire.ts +0 -61
  807. package/src/sync.ts +0 -37
  808. package/src/table-text.ts +0 -8
  809. package/src/testing/css-variables.generated.ts +0 -112
  810. package/src/testing/css-variables.ts +0 -189
  811. package/src/testing/index.ts +0 -29
  812. package/src/testing/react.ts +0 -58
  813. package/src/testing/tour.ts +0 -41
  814. package/src/testing/viewport.ts +0 -357
  815. package/src/theme/chart-palette.ts +0 -232
  816. package/src/theme/color.ts +0 -272
  817. package/src/theme/contrast-tokens.ts +0 -268
  818. package/src/theme/contrast.ts +0 -250
  819. package/src/theme/palette-derive.ts +0 -825
  820. package/src/theme/palette-presets.ts +0 -544
  821. package/src/theme/palette-store.ts +0 -108
  822. package/src/theme/text-size.ts +0 -289
  823. package/src/theme/theme-store.ts +0 -157
  824. package/src/tour/tour-catalogue-labels.ts +0 -75
  825. package/src/tour/tour-catalogue.tsx +0 -335
  826. package/src/tour/tour-dock.ts +0 -134
  827. package/src/tour/tour-engine.ts +0 -37
  828. package/src/tour/tour-gates.ts +0 -218
  829. package/src/tour/tour-guide-button.tsx +0 -64
  830. package/src/tour/tour-guide-labels.ts +0 -15
  831. package/src/tour/tour-info.ts +0 -144
  832. package/src/tour/tour-labels.ts +0 -23
  833. package/src/tour/tour-overlay.tsx +0 -1170
  834. package/src/tour/tour.tsx +0 -750
  835. package/src/tour/use-tour-progress.ts +0 -671
  836. package/src/tour/use-tour-url-sync.ts +0 -266
  837. package/src/tour.ts +0 -21
  838. package/src/vite-env.d.ts +0 -23
  839. package/src/wizard/stepper-nav.tsx +0 -360
  840. package/src/wizard/types.ts +0 -275
  841. package/src/wizard/use-wizard.ts +0 -560
  842. package/src/wizard/validation.ts +0 -39
  843. package/src/wizard/wizard-context.tsx +0 -74
  844. package/src/wizard/wizard-labels.ts +0 -29
  845. package/src/wizard/wizard-step.tsx +0 -76
  846. package/src/wizard/wizard-summary.tsx +0 -84
  847. package/src/wizard.ts +0 -15
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/month-picker.tsx"],"sourcesContent":["import { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, KeyboardEvent, ReactNode, Ref, RefObject } from \"react\";\nimport { Calendar, ChevronDown, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { dirOf, horizontalStep, type Direction } from \"../lib/direction\";\nimport { monthKey, pad } from \"../lib/dates\";\nimport { upperFirst } from \"../lib/text-case\";\nimport { Button } from \"./button\";\nimport { FIELD_FLOATING_PAD, FIELD_INVALID, FIELD_TRIGGER } from \"./field-base\";\nimport { IconButton } from \"./icon-button\";\nimport { FOCUS_RING, FOCUS_RING_WIDTH } from \"./focus-ring\";\nimport { Popover } from \"./popover\";\nimport { splitTriggerAria } from \"./trigger-aria\";\nimport type { TriggerAria } from \"./trigger-aria\";\nimport { useKitDateFormatter, useKitLabels, useKitLocale, type LabelOverride } from \"../i18n/kit-labels\";\nimport { hasContent } from \"../lib/presence\";\nimport {\n FieldBox,\n FieldLabelLine,\n LABEL_ABOVE_BOX,\n LockedReason,\n mergeDescribedBy,\n mergeRefs,\n useFieldMessages,\n useLockReason,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport { MonthGrid, clampKey, shiftKey, yearArrowsBlocked } from \"./month-grid\";\nimport { DEFAULT_MONTH_PICKER_LABELS } from \"./month-picker-labels\";\nimport type { CommitScope } from \"./write-lock\";\n\n/**\n * Every string the month picker can speak. The month and year names are NOT here —\n * they come from `Intl` in the `locale` prop, so they are already in the user's\n * language. What is left is the two icon-only year arrows, whose `aria-label` is the\n * only name they have, the panel's name, and a month cell's name.\n */\nexport interface MonthPickerLabels {\n previousYear: string;\n nextYear: string;\n /** The popover's accessible name (`role=\"dialog\"`). */\n panel: string;\n /**\n * Accessible name of one month cell, given that month and its year formatted in\n * `locale` (\"August 2026\"). The cell's visible text is the short month alone, and\n * \"Aug\" read out on its own does not say which year the grid is on.\n */\n month: (monthYear: string) => string;\n /** `variant=\"stepper\"`: the icon-only arrows that step the value by one month. */\n previousMonth: string;\n nextMonth: string;\n /** `variant=\"stepper\"`: the button that jumps to the current month. Visible text. */\n today: string;\n /** `mode=\"year\"`: the popover's accessible name. */\n yearPanel: string;\n /** `mode=\"year\"`: the icon-only arrows that page the year grid back and forward by a\n * page of twelve years. */\n earlierYears: string;\n laterYears: string;\n /** `mode=\"year\"`, `variant=\"stepper\"`: the button that jumps to the current year.\n * Visible text. */\n thisYear: string;\n}\n\n// Defined in ./month-picker-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_MONTH_PICKER_LABELS };\n\n/** Merge caller overrides onto the English defaults. */\nfunction useMonthPickerLabels(partial?: Partial<MonthPickerLabels>): MonthPickerLabels {\n // Prop > <UiKitProvider> > English, like every labelled component in the kit.\n return useKitLabels(\"monthPicker\", DEFAULT_MONTH_PICKER_LABELS, partial);\n}\n\n/**\n * `onChange` is omitted from the `<div>` attributes: the DOM event of that name carries\n * a `FormEvent`, and this one answers with a month key. Everything else reaches the\n * field's root, so a test id or a `data-tour` anchor can find it.\n */\nexport interface MonthPickerProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n /**\n * The selected month as a `\"YYYY-MM\"` key, or `\"\"` for none.\n *\n * A string rather than `{ year, month }` or a `Date`, for the reason every date in\n * this kit is one: it is what `monthKey()` in `lib/dates` produces, it sorts\n * chronologically so the bounds are plain `<` / `>`, and it crosses a URL or an API\n * without a serialiser. A `{ year, month }` caller converts with\n * `` `${year}-${pad(month)}` `` (month 1–12) and back with `key.split(\"-\").map(Number)`.\n */\n value: string;\n /** Called with the picked month as `\"YYYY-MM\"`. */\n onChange: (month: string) => void;\n /** Drives the month and year names (`Intl.DateTimeFormat`). Defaults to the\n * `<UiKitProvider>` locale, then the runtime's. */\n locale?: string;\n /**\n * Inclusive bounds, as `\"YYYY-MM\"`. A full ISO date (`\"YYYY-MM-DD\"`) is accepted and\n * read as its month, so a host holding \"the first day we have data for\" can pass it\n * straight in. Months outside are shown but refuse selection; a year arrow refuses\n * once the whole year beyond it is out of bounds (`aria-disabled`, so the focus stays\n * on the arrow just pressed — 0.52).\n */\n min?: string;\n max?: string;\n /** Embedded top-boundary label, matching the other field-shaped controls. */\n label?: ReactNode;\n /** Trigger text when `value` is `\"\"`. */\n placeholder?: string;\n disabled?: boolean;\n /**\n * Why the month cannot be changed — {@link Select}'s `disabledReason`, for a picker\n * that SAVES on change (a budget page's month, a calendar view kept per user). The\n * trigger stays focusable and `aria-disabled`, the grid does not open, the stepper's\n * Today and ‹ › refuse, and `onChange` never fires; the reason is a line under the\n * field (or the stepper) 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`. 0.52 (refactor audit 2026-10-30, API-3).\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 /**\n * This picker COMMITS — picking saves. Under a locked {@link WriteLockProvider} it is\n * locked the `disabledReason` way, with the lock's reason; a {@link CommitScope}, as on\n * every kit control. A month field inside a form with its own Save stays editable\n * under the lock — leave this off there, and put `commit` on the Save. 0.52 (API-3).\n */\n commit?: CommitScope;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /**\n * What is wrong with the month, in the caller's words — {@link Input}'s `error`\n * (keksdose K4): under the field, attached to the trigger with `aria-describedby`\n * (merged, after any caption), and implies `invalid`. Passing the key keeps the\n * field's box while there is no message, so the trigger is never remounted.\n */\n error?: ReactNode;\n /** Standing advice, as on a {@link Select}: text is a caption under the field; a\n * {@link FieldHint} rides the label line. */\n hint?: ReactNode;\n /**\n * `\"md\"` (default): the form field. `\"sm\"`: the toolbar trigger — 36px tall (an\n * `IconButton`'s height) and as wide as the month it shows, for a picker that sits\n * between two icon buttons in a page header (keksdose K13: the budget header wrote\n * `triggerClassName=\"h-9 w-auto … py-0\"` by hand, live #260 — 34px between two 36px\n * buttons, *\"the kind of 2px that reads as sloppy\"*). Unlabelled pickers only, as on\n * {@link Select}: a floating label needs the tall box to float in. The width follows\n * the month's name; add a `min-w-*` in `triggerClassName` if the buttons beside it\n * must not move as it changes.\n */\n size?: \"sm\" | \"md\";\n /**\n * `\"month\"` (default): the value is a month, `\"YYYY-MM\"`.\n *\n * `\"year\"`: the value is a YEAR, `\"YYYY\"`, and the panel is a grid of years — kastlan's\n * budget fiscal year (a `Select` of 2020 to three years ahead) and keksdose's tax tab\n * (\"this year and four before\", tax-tab.tsx:33). `min` / `max` are read as their year\n * (`\"2022\"`, or any longer key). A bounded span of twelve years or fewer is shown\n * whole, with no paging; otherwise the grid pages twelve years at a time (PageUp /\n * PageDown, or the arrows), starting from `min` when there is one. The stepper\n * variant steps a year, and its Today button reads \"This year\".\n *\n * A string rather than a number, for the reason the month key is one: it is the\n * same type across both modes, it crosses a URL as is, and it sorts. A numeric\n * caller converts with `String(year)` and `Number(key)`.\n */\n mode?: \"month\" | \"year\";\n /** Intl options for the trigger's text (default: long month + numeric year, or the\n * numeric year in `mode=\"year\"`). Win over the provider's `formatDate`. */\n formatOptions?: Intl.DateTimeFormatOptions;\n /**\n * The month to ring as \"now\", as `\"YYYY-MM\"`. Defaults to the local calendar's\n * current month; pass one to pin it (a test, a host with its own notion of today).\n */\n currentMonth?: string;\n /** User-facing strings; see {@link MonthPickerLabels}. */\n labels?: LabelOverride<MonthPickerLabels>;\n /** Extra classes for the field's root. */\n className?: string;\n /** Extra classes for the trigger button — e.g. a compact `h-9 w-auto` trigger\n * sitting between two icon buttons in a toolbar. */\n triggerClassName?: string;\n /**\n * `\"field\"` (default): the field-shaped trigger, for a form.\n *\n * `\"stepper\"`: the header of a month PAGE — the month as a heading that opens the\n * grid, then Today and ‹ › stepping the value by one month. What a calendar page\n * assembled by hand around the field (overriding its trigger to look like a\n * heading). `label`, `placeholder`, `invalid`, `hint`, `error`, `size` and\n * `triggerClassName` are the field's and are not drawn here; with no `value` the\n * arrows step from the current month (or year).\n */\n variant?: \"field\" | \"stepper\";\n /** `variant=\"stepper\"`: the heading level the month is set in (`<h2>` by default), or\n * `false` for no heading element — the page already has one for the month. */\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6 | false;\n /** `variant=\"stepper\"`: draw the Today button. Default `true`. */\n showToday?: boolean;\n /**\n * The TRIGGER — the field's button, or the stepper's heading button — as a React 19\n * ref prop (0.34, API-3, refactor audit 2026-10). The picker took none, so\n * react-hook-form's focus-on-error reached it only through /rhf's id lookup. Not the\n * wrapper, which takes the rest of the props.\n */\n ref?: Ref<HTMLButtonElement>;\n}\n\n// ── Month keys ────────────────────────────────────────────────────────────\n\ninterface YearMonth {\n year: number;\n /** 1–12, as in the key. */\n month: number;\n}\n\nconst KEY = /^(\\d{4})-(\\d{2})/;\n\nfunction parseMonth(key: string | undefined): YearMonth | null {\n const m = key ? KEY.exec(key) : null;\n if (!m) return null;\n const month = Number(m[2]);\n return month >= 1 && month <= 12 ? { year: Number(m[1]), month } : null;\n}\n\nfunction toKey({ year, month }: YearMonth): string {\n return `${year}-${pad(month)}`;\n}\n\n/** Move a month key by whole months, carrying into the year. */\nfunction shiftMonth(ym: YearMonth, delta: number): YearMonth {\n const index = ym.year * 12 + (ym.month - 1) + delta;\n return { year: Math.floor(index / 12), month: (index % 12) + 1 };\n}\n\n/** A bound, normalised to its month key — so `\"2024-03-15\"` compares as `\"2024-03\"`. */\nfunction boundKey(bound: string | undefined): string | null {\n const ym = parseMonth(bound);\n return ym ? toKey(ym) : null;\n}\n\n/** Local-calendar Date for the 1st of a month, for `Intl` to format. */\nfunction firstOf({ year, month }: YearMonth): Date {\n return new Date(year, month - 1, 1);\n}\n\nconst DEFAULT_TRIGGER_FORMAT: Intl.DateTimeFormatOptions = { month: \"long\", year: \"numeric\" };\nconst YEAR_FORMAT: Intl.DateTimeFormatOptions = { year: \"numeric\" };\n\n// ── Year keys ─────────────────────────────────────────────────────────────\n\nconst YEAR = /^(\\d{4})/;\n\n/** The year of `\"YYYY\"`, `\"YYYY-MM\"` or `\"YYYY-MM-DD\"`, or null. */\nfunction parseYear(key: string | undefined): number | null {\n const m = key ? YEAR.exec(key) : null;\n return m ? Number(m[1]) : null;\n}\n\n// ── Trigger ───────────────────────────────────────────────────────────────\n\n/** Hand focus back to the trigger when the panel closes with focus still inside it. */\nfunction useFocusBackOnClose(open: boolean, triggerRef: RefObject<HTMLButtonElement | null>) {\n const wasOpen = useRef(false);\n useEffect(() => {\n const justClosed = wasOpen.current && !open;\n wasOpen.current = open;\n if (!justClosed) return;\n // Only when focus went down with the panel. An outside click has already put it\n // somewhere real, and dragging it back here would be worse than leaving it.\n const active = document.activeElement;\n if (!active || active === document.body) triggerRef.current?.focus();\n }, [open, triggerRef]);\n}\n\n/** The Popover's handle on the trigger and the picker's own `ref` prop (API-3), as one. */\nfunction useTriggerRef(triggerRef: RefObject<HTMLButtonElement | null>, fieldRef: Ref<HTMLButtonElement> | undefined) {\n return useMemo(() => mergeRefs(triggerRef, fieldRef), [triggerRef, fieldRef]);\n}\n\n/**\n * The stepper's trigger: the month as a heading-sized button that opens the grid.\n *\n * Its own text is its name (\"September 2026\"), so it needs no label of its own, and\n * it is `aria-live` for the reason `MiniCalendar`'s caption is: pressing ‹ or › leaves\n * focus on the arrow, and the heading is the only thing that says where the arrow\n * went. A plain button with `aria-haspopup`, not the field's `combobox` — it does not\n * look or behave like a field, and a heading reading \"combobox\" is a surprise.\n */\nfunction MonthHeadingTrigger({\n open,\n toggle,\n triggerRef,\n fieldRef,\n text,\n panelId,\n disabled,\n locked,\n aria,\n}: {\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n fieldRef?: Ref<HTMLButtonElement>;\n text: string;\n panelId: string;\n disabled?: boolean;\n /** The write lock: the focusable kind of disabled. */\n locked: boolean;\n aria: TriggerAria;\n}) {\n useFocusBackOnClose(open, triggerRef);\n const setTrigger = useTriggerRef(triggerRef, fieldRef);\n return (\n <button\n ref={setTrigger}\n type=\"button\"\n disabled={locked ? undefined : disabled}\n aria-disabled={locked || undefined}\n onClick={() => {\n if (!locked) toggle();\n }}\n aria-haspopup=\"dialog\"\n aria-controls={panelId}\n aria-expanded={open}\n id={aria.id}\n aria-label={aria[\"aria-label\"]}\n aria-labelledby={aria[\"aria-labelledby\"]}\n aria-describedby={aria[\"aria-describedby\"]}\n className={cn(\n \"-ms-1.5 inline-flex h-9 min-w-0 items-center gap-1 rounded-md px-1.5 text-lg font-semibold text-primary transition-colors\",\n \"hover:bg-hover\",\n FOCUS_RING,\n (disabled || locked) && \"cursor-not-allowed opacity-50\",\n )}\n >\n <span aria-live=\"polite\" className=\"truncate\">\n {text || \"\\u00a0\"}\n </span>\n <ChevronDown aria-hidden className=\"size-4 shrink-0 text-muted\" />\n </button>\n );\n}\n\n/**\n * The field-shaped button that opens the grid. The same shape as `DatePicker`'s\n * trigger, for the same reasons written down there: `role=\"combobox\"` so it may carry\n * `aria-expanded` / `aria-invalid`, a name made of the label AND the value, and focus\n * handed back here when the panel closes with focus still inside it.\n */\nfunction MonthFieldTrigger({\n open,\n toggle,\n triggerRef,\n fieldRef,\n triggerText,\n hasValue,\n labelledBy,\n valueId,\n panelId,\n padded,\n small,\n disabled,\n locked,\n invalid,\n className,\n aria,\n}: {\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n fieldRef?: Ref<HTMLButtonElement>;\n triggerText: string;\n hasValue: boolean;\n labelledBy: string;\n /** The caller's naming attributes — see `trigger-aria.ts`. */\n aria: TriggerAria;\n valueId: string;\n panelId: string;\n padded: boolean;\n /** `size=\"sm\"`: the 36px, content-wide toolbar trigger. */\n small: boolean;\n disabled?: boolean;\n /** The write lock: the focusable kind of disabled (Button's rule — native `disabled`\n * would take the field out of the tab order, and the reason with it). */\n locked: boolean;\n invalid?: boolean;\n className?: string;\n}) {\n useFocusBackOnClose(open, triggerRef);\n const setTrigger = useTriggerRef(triggerRef, fieldRef);\n\n return (\n <button\n ref={setTrigger}\n type=\"button\"\n disabled={locked ? undefined : disabled}\n aria-disabled={locked || undefined}\n onClick={() => {\n // Locked, the grid does not open. One open when the lock arrives closes: the\n // field moves into the reason's frame, which remounts it, as Select's does.\n if (!locked) toggle();\n }}\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-controls={panelId}\n aria-expanded={open}\n id={aria.id}\n aria-invalid={invalid || aria[\"aria-invalid\"] === true || aria[\"aria-invalid\"] === \"true\" || undefined}\n aria-required={aria[\"aria-required\"]}\n aria-labelledby={aria[\"aria-label\"] && !aria[\"aria-labelledby\"] ? undefined : labelledBy}\n aria-label={aria[\"aria-label\"]}\n aria-describedby={aria[\"aria-describedby\"]}\n className={cn(\n FIELD_TRIGGER,\n \"pe-9\",\n padded && FIELD_FLOATING_PAD,\n // keksdose K13 (live #260): an IconButton's 36px, and only as wide as the\n // month — `py-0` with a fixed height so the box is 36px whatever the type's\n // line height. `pe-9` stays: the calendar glyph still sits at the end.\n small && \"h-9 w-auto py-0\",\n (disabled || locked) && \"cursor-not-allowed opacity-50\",\n invalid && FIELD_INVALID,\n className,\n )}\n >\n {/* The U+00A0 fallback keeps the line box when there is no text — see\n DatePicker. The value arrives capitalised (see `upperFirst`); this span does\n NOT use `capitalize`, which upper-cases every WORD — \"Choose A Month\",\n \"Septiembre De 2026\". It wraps from Large up, as DatePicker's value does\n (`truncate-until-large`, text size §10.17; round 0.34 T1: the sweep read\n \"Choose a mo…\" at 360 px / Extra large). */}\n <span id={valueId} className={cn(\"truncate-until-large\", !hasValue && \"text-placeholder\")}>\n {triggerText || \" \"}\n </span>\n </button>\n );\n}\n\n// ── Grid ──────────────────────────────────────────────────────────────────\n\nconst COLUMNS = 3;\n\n/**\n * A year arrow over a grid (the month grid's ‹ › years, the year grid's pages). At a\n * bound it refuses rather than turning `disabled`, JumpCalendar's rule (refactor audit\n * 2026-10-30, INP-4): the press that reaches the bound is made with the focus ON the\n * arrow, and a focused button that turns `disabled` drops the focus to <body>, mid-panel\n * (keksdose's budget MonthPicker passes `min`). `aria-disabled`, a click that does\n * nothing, and the look through `aria-disabled:`.\n */\nconst PANEL_ARROW =\n \"flex size-7 items-center justify-center rounded text-muted hover:bg-hover aria-disabled:cursor-not-allowed aria-disabled:opacity-30 aria-disabled:hover:bg-transparent\";\n\n/** The stepper's Today and ‹ › while refused: Button's look for its focusable kind of\n * disabled — the kit's `disabled:` classes cannot match an `aria-disabled` button. The\n * arrows, ghost IconButtons, also pin their resting fill under hover, as\n * `disabled:hover:` does for them. */\nconst STEPPER_REFUSED = \"aria-disabled:cursor-not-allowed aria-disabled:opacity-50\";\nconst STEPPER_ARROW_REFUSED = `${STEPPER_REFUSED} aria-disabled:hover:bg-transparent`;\n\n/**\n * The panel: a year caption between ‹ › arrows, over the year's {@link MonthGrid} (4×3,\n * its keys and its bounds — one with JumpCalendar's since 0.52, DUP-11).\n *\n * Mounted only while the popover is open, so every opening starts from the current\n * value's year — a value moved from outside (a prev/next-month button beside the\n * field) is where the grid lands, with no effect needed to snap it back.\n */\nfunction MonthPanel({\n value,\n onPick,\n locale,\n minKey,\n maxKey,\n currentKey,\n labels,\n}: {\n value: YearMonth | null;\n onPick: (key: string) => void;\n locale: string | undefined;\n minKey: string | null;\n maxKey: string | null;\n currentKey: string;\n labels: MonthPickerLabels;\n}) {\n const selectedKey = value ? toKey(value) : null;\n // The month holding the tab order, and with it the year on screen — one piece of\n // state, as in MiniCalendar, so the two cannot disagree. Opens on the value, or on\n // the current month, pulled inside the bounds.\n const [active, setActive] = useState(() => clampKey(selectedKey ?? currentKey, minKey, maxKey));\n const year = Number(active.slice(0, 4));\n const captionId = `${useId()}-year`;\n const yearCaption = new Intl.DateTimeFormat(locale, YEAR_FORMAT).format(new Date(year, 0, 1));\n const blocked = yearArrowsBlocked(active, minKey, maxKey);\n\n return (\n <div className=\"select-none\">\n <div className=\"mb-1 flex items-center justify-between\">\n <button\n type=\"button\"\n aria-label={labels.previousYear}\n aria-disabled={blocked.previous || undefined}\n // Focus stays on the arrow being pressed — only the grid moves.\n onClick={() => {\n if (!blocked.previous) setActive(shiftKey(active, -12));\n }}\n className={PANEL_ARROW}\n >\n {/* Mirrored in RTL, where \"previous\" points the other way. */}\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" />\n </button>\n {/* Names the grid, and `aria-live` so stepping a year says which one you are\n on — the user pressing the arrow is not inside the grid to hear it change. */}\n <div id={captionId} aria-live=\"polite\" className=\"text-sm font-semibold tabular-nums text-primary\">\n {yearCaption}\n </div>\n <button\n type=\"button\"\n aria-label={labels.nextYear}\n aria-disabled={blocked.next || undefined}\n onClick={() => {\n if (!blocked.next) setActive(shiftKey(active, 12));\n }}\n className={PANEL_ARROW}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" />\n </button>\n </div>\n <MonthGrid\n active={active}\n onActiveChange={setActive}\n onPick={onPick}\n from={selectedKey}\n to={selectedKey}\n minKey={minKey}\n maxKey={maxKey}\n currentKey={currentKey}\n locale={locale}\n cellName={labels.month}\n labelledBy={captionId}\n />\n </div>\n );\n}\n\n/** Years on one page of the year grid: the month grid's 4×3. */\nconst YEAR_PAGE = 12;\n\n/**\n * `mode=\"year\"`'s panel: a page of years between ‹ › arrows — {@link MonthGrid}'s shape\n * and keyboard, one level up (kastlan 7, keksdose tax-tab.tsx:33).\n *\n * Which years a page holds:\n * - Bounded on both sides by twelve years or fewer: exactly those years, and no\n * arrows — keksdose's \"this year and four before\" is five cells, not a page of\n * twelve with seven greyed out.\n * - Otherwise twelve at a time, starting from `min` when there is one (so the first\n * page opens on it), else ending at `max`, else on multiples of twelve.\n *\n * The arrow keys walk the years (up/down by a row of three), PageUp / PageDown a page,\n * Home / End the row; walking off a page turns it. Focus stops at the bounds rather\n * than walking into years that refuse selection: a page has no \"next year\" past the\n * last selectable one worth landing on.\n */\nfunction YearGrid({\n value,\n onPick,\n locale,\n minYear,\n maxYear,\n currentYear,\n labels,\n}: {\n value: number | null;\n onPick: (key: string) => void;\n locale: string | undefined;\n minYear: number | null;\n maxYear: number | null;\n currentYear: number;\n labels: MonthPickerLabels;\n}) {\n const clamp = (y: number) => Math.min(maxYear ?? y, Math.max(minYear ?? y, y));\n // A bounded span that fits one page is shown whole.\n const whole =\n minYear !== null && maxYear !== null && maxYear >= minYear && maxYear - minYear < YEAR_PAGE\n ? { first: minYear, count: maxYear - minYear + 1 }\n : null;\n // The year holding the tab order, and with it the page on screen — one piece of\n // state, as in MonthPanel. Opens on the value, or on this year, inside the bounds.\n const [active, setActive] = useState(() => clamp(value ?? currentYear));\n const anchor = minYear ?? (maxYear !== null ? maxYear - (YEAR_PAGE - 1) : 0);\n const first = whole ? whole.first : anchor + Math.floor((active - anchor) / YEAR_PAGE) * YEAR_PAGE;\n const count = whole ? whole.count : YEAR_PAGE;\n const years = Array.from({ length: count }, (_, i) => first + i);\n const outOfBounds = (y: number) => (minYear !== null && y < minYear) || (maxYear !== null && y > maxYear);\n\n const gridRef = useRef<HTMLDivElement>(null);\n const captionId = `${useId()}-years`;\n const yearFormat = useMemo(() => new Intl.DateTimeFormat(locale, YEAR_FORMAT), [locale]);\n // \"2016–2027\" in the locale's own range punctuation and digits.\n const caption = yearFormat.formatRange(new Date(first, 0, 1), new Date(first + count - 1, 0, 1));\n\n const pendingFocus = useRef<number | null>(null);\n useEffect(() => {\n const year = pendingFocus.current;\n if (year === null) return;\n pendingFocus.current = null;\n gridRef.current?.querySelector<HTMLElement>(`[data-year=\"${year}\"]`)?.focus();\n });\n // Opened to pick a year: land on it (before the popover's trap, which keeps it).\n useEffect(() => {\n gridRef.current?.querySelector<HTMLElement>('[tabindex=\"0\"]')?.focus();\n }, []);\n\n const moveTo = (next: number, focus: boolean) => {\n const year = clamp(next);\n if (focus) pendingFocus.current = year;\n setActive(year);\n };\n\n const onCellKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {\n const step = horizontalStep(e.key, e.currentTarget);\n const col = (active - first) % COLUMNS;\n let delta: number;\n switch (step ? \"horizontal\" : e.key) {\n case \"horizontal\":\n delta = step;\n break;\n case \"ArrowUp\":\n delta = -COLUMNS;\n break;\n case \"ArrowDown\":\n delta = COLUMNS;\n break;\n case \"Home\":\n delta = -col;\n break;\n case \"End\":\n delta = Math.min(COLUMNS - 1 - col, first + count - 1 - active);\n break;\n case \"PageUp\":\n delta = -YEAR_PAGE;\n break;\n case \"PageDown\":\n delta = YEAR_PAGE;\n break;\n default:\n return;\n }\n e.preventDefault();\n if (delta !== 0) moveTo(active + delta, true);\n };\n\n const prevBlocked = minYear !== null && first - 1 < minYear;\n const nextBlocked = maxYear !== null && first + count > maxYear;\n\n return (\n <div className=\"select-none\">\n <div className={cn(\"mb-1 flex items-center\", whole ? \"h-7 justify-center\" : \"justify-between\")}>\n {!whole && (\n <button\n type=\"button\"\n aria-label={labels.earlierYears}\n aria-disabled={prevBlocked || undefined}\n onClick={() => {\n if (!prevBlocked) moveTo(active - YEAR_PAGE, false);\n }}\n className={PANEL_ARROW}\n >\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" />\n </button>\n )}\n <div id={captionId} aria-live=\"polite\" className=\"text-sm font-semibold tabular-nums text-primary\">\n {caption}\n </div>\n {!whole && (\n <button\n type=\"button\"\n aria-label={labels.laterYears}\n aria-disabled={nextBlocked || undefined}\n onClick={() => {\n if (!nextBlocked) moveTo(active + YEAR_PAGE, false);\n }}\n className={PANEL_ARROW}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" />\n </button>\n )}\n </div>\n <div ref={gridRef} role=\"grid\" aria-labelledby={captionId} className=\"grid gap-1\">\n {Array.from({ length: Math.ceil(count / COLUMNS) }, (_, row) => (\n <div key={row} role=\"row\" className=\"grid grid-cols-3 gap-1\">\n {years.slice(row * COLUMNS, row * COLUMNS + COLUMNS).map((year) => {\n const disabled = outOfBounds(year);\n const selected = year === value;\n const current = year === currentYear;\n return (\n <button\n key={year}\n type=\"button\"\n role=\"gridcell\"\n data-year={year}\n tabIndex={year === active ? 0 : -1}\n aria-selected={selected}\n aria-disabled={disabled || undefined}\n aria-current={current ? \"date\" : undefined}\n onClick={() => {\n if (disabled) return;\n setActive(year);\n onPick(String(year));\n }}\n onKeyDown={onCellKeyDown}\n className={cn(\n // The month grid's 44px rows (DUP-11): one picker, one row height.\n \"h-11 rounded px-1 text-sm tabular-nums transition-colors\",\n FOCUS_RING_WIDTH,\n \"focus-visible:outline-none focus-visible:ring-inset\",\n selected && !disabled ? \"focus-visible:ring-brand-contrast\" : \"focus-visible:ring-brand\",\n disabled\n ? \"cursor-not-allowed text-placeholder\"\n : selected\n ? \"bg-brand text-brand-contrast hover:bg-brand-hover\"\n : \"text-primary hover:bg-hover\",\n current && !selected && !disabled && \"ring-1 ring-inset ring-strong\",\n )}\n >\n {yearFormat.format(new Date(year, 0, 1))}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n// ── Field ─────────────────────────────────────────────────────────────────\n\n/**\n * Month picker: a field showing the selected month (\"August 2026\"), opening a 12-month\n * grid with year arrows. For a value that IS a month — a budget period, a calendar\n * view, a statement — where a day picker would ask for a day nobody means.\n *\n * One component for the two app-local pickers it replaces: keksdose's budget-header\n * popover (grid + year steppers + a floor) and kastlan's calendar header (a month list\n * and a year list in two popovers). The grid does both jobs — the year arrows and\n * PageUp/PageDown cover what the year list did, without a fixed ±5-year window.\n */\nexport function MonthPicker(props: MonthPickerProps) {\n const {\n value,\n onChange,\n locale: localeProp,\n min,\n max,\n label,\n placeholder,\n disabled,\n disabledReason,\n disabledReasonDisplay,\n commit,\n invalid,\n error,\n hint,\n help,\n size = \"md\",\n mode = \"month\",\n formatOptions,\n currentMonth,\n labels: labelsProp,\n className,\n triggerClassName,\n variant = \"field\",\n headingLevel = 2,\n showToday = true,\n // The trigger's (API-3); named apart from the Popover trigger's own `ref` below.\n ref: fieldRef,\n ...rest\n } = props;\n const labels = useMonthPickerLabels(labelsProp);\n const locale = useKitLocale(localeProp);\n const fromProvider = useKitDateFormatter();\n const years = mode === \"year\";\n const selected = years ? null : parseMonth(value);\n const selectedYear = years ? parseYear(value) : null;\n const minKey = boundKey(min);\n const maxKey = boundKey(max);\n const minYear = parseYear(min);\n const maxYear = parseYear(max);\n const currentKey = boundKey(currentMonth) ?? monthKey(new Date());\n const currentYear = parseYear(currentKey)!;\n // The key the trigger shows, in the mode's own shape: \"YYYY-MM\" or \"YYYY\".\n const shownKey = years ? (selectedYear !== null ? String(selectedYear) : null) : selected ? toKey(selected) : null;\n\n // The kit's order: `formatOptions` (this field's own Intl look), then the provider's\n // `formatDate` (keksdose K12 — the app's one way of writing a month), then the default.\n const triggerText = shownKey\n ? (formatOptions === undefined &&\n fromProvider?.(shownKey, { unit: years ? \"year\" : \"month\", source: \"monthPicker\", locale, weekday: false })) ||\n upperFirst(\n new Intl.DateTimeFormat(locale, formatOptions ?? (years ? YEAR_FORMAT : DEFAULT_TRIGGER_FORMAT)).format(\n years ? new Date(selectedYear!, 0, 1) : firstOf(selected!),\n ),\n locale,\n )\n : (placeholder ?? \"\");\n\n const id = useId();\n // The write lock (API-3): Select's — the trigger stays reachable and says why.\n const lock = useLockReason(commit, disabledReason);\n const locked = lock.locked;\n const reasonId = locked ? lock.reasonId : undefined;\n const change = (key: string) => {\n if (!locked) onChange(key);\n };\n // keksdose K4: the caption and the error, on the trigger after the caller's own\n // description; a lock's reason between the two, as on Select. Painted for `invalid`\n // and a message — not for a bare `aria-invalid`, which this field has never painted for.\n const messages = useFieldMessages({\n hint,\n help,\n name: label ?? rest[\"aria-label\"],\n error,\n describedBy: rest[\"aria-describedby\"],\n reasonId,\n });\n const [ownAria, wrapperRest] = splitTriggerAria(rest);\n // The field's trigger is described by the caption and the error too; the stepper\n // draws neither, so its heading keeps the caller's own description — and a lock's.\n const aria: TriggerAria = { ...ownAria, \"aria-describedby\": messages.describedBy };\n const headingAria: TriggerAria = {\n ...ownAria,\n \"aria-describedby\": mergeDescribedBy(ownAria[\"aria-describedby\"], reasonId),\n };\n // Locked, the field (or the stepper) sits in the reason's frame — the line, or the\n // Tooltip — which becomes the outermost box and takes the caller's classes, as on\n // Select, so a lock does not reflow the page.\n const rootClass = locked ? undefined : className;\n const withReason = (box: ReactNode) =>\n locked ? (\n <LockedReason lock={lock} display={disabledReasonDisplay} className={cn(\"block\", className)}>\n {box}\n </LockedReason>\n ) : (\n box\n );\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const panelId = `${id}-panel`;\n // Only a STRING label can double as a name; a ReactNode may hold its own controls.\n const named = typeof label === \"string\";\n // `sm` is the toolbar trigger, for an unlabelled picker only (see `size`).\n const small = size === \"sm\" && label === undefined;\n // The panel is portalled to <body>, out of the subtree whose `dir` it inherited — an\n // RTL form got an LTR grid. Read when opening (`toggle` is the popover's only way\n // open) and put back on the panel. Also what the arrow keys above read.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n const panelLabel = years ? labels.yearPanel : labels.panel;\n\n const grid = (close: () => void) =>\n years ? (\n <YearGrid\n value={selectedYear}\n locale={locale}\n minYear={minYear}\n maxYear={maxYear}\n currentYear={currentYear}\n labels={labels}\n onPick={(key) => {\n change(key);\n close();\n }}\n />\n ) : (\n <MonthPanel\n value={selected}\n locale={locale}\n minKey={minKey}\n maxKey={maxKey}\n currentKey={currentKey}\n labels={labels}\n onPick={(key) => {\n change(key);\n close();\n }}\n />\n );\n\n if (variant === \"stepper\") {\n // The arrows step from the value, or from \"now\" when there is none; each is dead\n // once the month (or year) it leads to is out of bounds, and Today once \"now\" is.\n let prevKey: string;\n let nextKey: string;\n let nowKey: string;\n let outside: (key: string) => boolean;\n if (years) {\n const base = selectedYear ?? currentYear;\n prevKey = String(base - 1);\n nextKey = String(base + 1);\n nowKey = String(currentYear);\n outside = (key) => {\n const y = Number(key);\n return (minYear !== null && y < minYear) || (maxYear !== null && y > maxYear);\n };\n } else {\n const base = selected ?? parseMonth(currentKey)!;\n prevKey = toKey(shiftMonth(base, -1));\n nextKey = toKey(shiftMonth(base, 1));\n nowKey = currentKey;\n outside = (key) => Boolean((minKey && key < minKey) || (maxKey && key > maxKey));\n }\n const Heading = headingLevel ? (`h${headingLevel}` as const) : null;\n const trigger = (\n <Popover\n width=\"16rem\"\n panelId={panelId}\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={({ open, toggle, ref }) => (\n <MonthHeadingTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n fieldRef={fieldRef}\n text={triggerText}\n panelId={panelId}\n disabled={disabled}\n locked={locked}\n aria={headingAria}\n />\n )}\n >\n {grid}\n </Popover>\n );\n // ‹ › at a bound refuse rather than turn `disabled` (refactor audit 2026-10-30,\n // INP-4): the press that reaches the bound is made with the focus on the arrow, and\n // a focused button that turns `disabled` drops the focus to <body>. A lock refuses\n // them, and Today, the same focusable way — its reason is the frame's, so no name\n // bubble of their own beside it. `disabled` (a lock's reason wins over it) and\n // Today's \"now is out of bounds\", which no press here changes, stay native.\n const off = Boolean(disabled) && !locked;\n const prevRefused = locked || outside(prevKey);\n const nextRefused = locked || outside(nextKey);\n return withReason(\n // `flex-wrap`: where the heading and the buttons do not fit one row, the buttons\n // take the next line, at the end (0.32.1, the 360 px Extra-large sweep;\n // docs/text-size-harmonization.md §4). In one row at 150 % \"Today ‹ ›\" left the\n // heading 30 px on a 360 px phone, and \"October 2026\" was drawn under \"Today\".\n // Where they fit, nothing changes.\n <div {...wrapperRest} ref={rootRef} className={cn(\"flex min-w-0 flex-wrap items-center gap-2\", rootClass)}>\n {Heading ? <Heading className=\"m-0 min-w-0 text-lg font-semibold\">{trigger}</Heading> : trigger}\n <div className=\"ms-auto flex shrink-0 items-center gap-1\">\n {showToday && (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={!locked && (disabled || outside(nowKey))}\n aria-disabled={locked || undefined}\n aria-describedby={reasonId}\n onClick={() => {\n if (nowKey !== shownKey) change(nowKey);\n }}\n className={STEPPER_REFUSED}\n >\n {years ? labels.thisYear : labels.today}\n </Button>\n )}\n <IconButton\n aria-label={years ? labels.previousYear : labels.previousMonth}\n disabled={off}\n aria-disabled={(!off && prevRefused) || undefined}\n aria-describedby={reasonId}\n tooltip={!locked}\n onClick={() => {\n if (!prevRefused) change(prevKey);\n }}\n className={STEPPER_ARROW_REFUSED}\n >\n {/* Mirrored in RTL, where \"previous\" points the other way. */}\n <ChevronLeft className=\"rtl:-scale-x-100\" />\n </IconButton>\n <IconButton\n aria-label={years ? labels.nextYear : labels.nextMonth}\n disabled={off}\n aria-disabled={(!off && nextRefused) || undefined}\n aria-describedby={reasonId}\n tooltip={!locked}\n onClick={() => {\n if (!nextRefused) change(nextKey);\n }}\n className={STEPPER_ARROW_REFUSED}\n >\n <ChevronRight className=\"rtl:-scale-x-100\" />\n </IconButton>\n </div>\n </div>,\n );\n }\n\n const field = (\n // `w-fit` for the toolbar size: the calendar glyph is placed against this box, and a\n // full-width box would park it at the far end of the row, away from its trigger.\n // At Large a labelled box is a grid: the label above, the calendar glyph measured\n // against the trigger's row (LABEL_ABOVE_BOX, 0.33 §10.17).\n <div\n {...wrapperRest}\n ref={rootRef}\n className={cn(\"relative\", small && \"w-fit max-w-full\", label !== undefined && LABEL_ABOVE_BOX, rootClass)}\n >\n {label !== undefined && <FieldLabelLine label={label} hint={messages.labelHint} />}\n {/* The hidden twin the trigger is named by — see DateField. `sr-only-fixed`,\n and inside this `relative` root either way. */}\n {named && (\n <span id={labelId} aria-hidden className=\"sr-only-fixed\">\n {label}\n </span>\n )}\n <Popover\n width=\"16rem\"\n panelId={panelId}\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={({ open, toggle, ref }) => (\n <MonthFieldTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n fieldRef={fieldRef}\n triggerText={triggerText}\n hasValue={shownKey !== null}\n // As DatePicker: a caller's reference first; an `id` without a label of our\n // own references the trigger itself, so an external <label htmlFor> names it.\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 valueId={valueId}\n panelId={panelId}\n padded={label !== undefined}\n small={small}\n disabled={disabled}\n locked={locked}\n invalid={invalid || hasContent(error)}\n className={triggerClassName}\n />\n )}\n >\n {grid}\n </Popover>\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 </div>\n );\n // An unlabelled picker has no label line: a FieldHint goes at its end, outside the\n // box, as on an unlabelled Select.\n const endHint = label === undefined ? messages.labelHint : undefined;\n return (\n <FieldBox below={messages.below} reserve={\"error\" in props || \"hint\" in props}>\n {withReason(\n endHint === undefined ? (\n field\n ) : (\n <div className=\"flex items-center gap-1.5\">\n <div className={cn(\"min-w-0\", !small && \"flex-1\")}>{field}</div>\n <span className=\"flex shrink-0 items-center\">{endHint}</span>\n </div>\n ),\n )}\n </FieldBox>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/month-picker.tsx"],"sourcesContent":["import { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, KeyboardEvent, ReactNode, Ref, RefObject } from \"react\";\nimport { Calendar, ChevronDown, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { dirOf, horizontalStep, type Direction } from \"../lib/direction\";\nimport { currentDate, monthKey, pad } from \"../lib/dates\";\nimport { returnFocus } from \"../lib/focus-left\";\nimport { upperFirst } from \"../lib/text-case\";\nimport { Button } from \"./button\";\nimport { FIELD_FLOATING_PAD, FIELD_INVALID, FIELD_TRIGGER } from \"./field-base\";\nimport { IconButton } from \"./icon-button\";\nimport { FOCUS_RING, FOCUS_RING_WIDTH } from \"./focus-ring\";\nimport { Popover } from \"./popover\";\nimport { splitTriggerAria } from \"./trigger-aria\";\nimport type { TriggerAria } from \"./trigger-aria\";\nimport { useKitDateFormatter, useKitLabels, useKitLocale, type LabelOverride } from \"../i18n/kit-labels\";\nimport { hasContent } from \"../lib/presence\";\nimport {\n FieldBox,\n FieldLabelLine,\n LABEL_ABOVE_BOX,\n LockFrame,\n mergeDescribedBy,\n mergeRefs,\n useFieldMessages,\n useLockReason,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport { MonthGrid, clampKey, shiftKey, yearArrowsBlocked } from \"./month-grid\";\nimport { DEFAULT_MONTH_PICKER_LABELS } from \"./month-picker-labels\";\nimport type { CommitScope } from \"./write-lock\";\n\n/**\n * Every string the month picker can speak. The month and year names are NOT here —\n * they come from `Intl` in the `locale` prop, so they are already in the user's\n * language. What is left is the two icon-only year arrows, whose `aria-label` is the\n * only name they have, the panel's name, and a month cell's name.\n */\nexport interface MonthPickerLabels {\n previousYear: string;\n nextYear: string;\n /** The popover's accessible name (`role=\"dialog\"`). */\n panel: string;\n /**\n * Accessible name of one month cell, given that month and its year formatted in\n * `locale` (\"August 2026\"). The cell's visible text is the short month alone, and\n * \"Aug\" read out on its own does not say which year the grid is on.\n */\n month: (monthYear: string) => string;\n /** `variant=\"stepper\"`: the icon-only arrows that step the value by one month. */\n previousMonth: string;\n nextMonth: string;\n /** `variant=\"stepper\"`: the button that jumps to the current month. Visible text. */\n today: string;\n /** `mode=\"year\"`: the popover's accessible name. */\n yearPanel: string;\n /** `mode=\"year\"`: the icon-only arrows that page the year grid back and forward by a\n * page of twelve years. */\n earlierYears: string;\n laterYears: string;\n /** `mode=\"year\"`, `variant=\"stepper\"`: the button that jumps to the current year.\n * Visible text. */\n thisYear: string;\n}\n\n// Defined in ./month-picker-labels, which holds no component code (0.34, KS U8).\nexport { DEFAULT_MONTH_PICKER_LABELS };\n\n/** Merge caller overrides onto the English defaults. */\nfunction useMonthPickerLabels(partial?: Partial<MonthPickerLabels>): MonthPickerLabels {\n // Prop > <UiKitProvider> > English, like every labelled component in the kit.\n return useKitLabels(\"monthPicker\", DEFAULT_MONTH_PICKER_LABELS, partial);\n}\n\n/**\n * `onChange` is omitted from the `<div>` attributes: the DOM event of that name carries\n * a `FormEvent`, and this one answers with a month key. Everything else reaches the\n * field's root, so a test id or a `data-tour` anchor can find it.\n */\nexport interface MonthPickerProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n /**\n * The selected month as a `\"YYYY-MM\"` key, or `\"\"` for none.\n *\n * A string rather than `{ year, month }` or a `Date`, for the reason every date in\n * this kit is one: it is what `monthKey()` in `lib/dates` produces, it sorts\n * chronologically so the bounds are plain `<` / `>`, and it crosses a URL or an API\n * without a serialiser. A `{ year, month }` caller converts with\n * `` `${year}-${pad(month)}` `` (month 1–12) and back with `key.split(\"-\").map(Number)`.\n */\n value: string;\n /** Called with the picked month as `\"YYYY-MM\"`. */\n onChange: (month: string) => void;\n /** Drives the month and year names (`Intl.DateTimeFormat`). Defaults to the\n * `<UiKitProvider>` locale, then the runtime's. */\n locale?: string;\n /**\n * Inclusive bounds, as `\"YYYY-MM\"`. A full ISO date (`\"YYYY-MM-DD\"`) is accepted and\n * read as its month, so a host holding \"the first day we have data for\" can pass it\n * straight in. Months outside are shown but refuse selection; a year arrow refuses\n * once the whole year beyond it is out of bounds (`aria-disabled`, so the focus stays\n * on the arrow just pressed — 0.52).\n */\n min?: string;\n max?: string;\n /** Embedded top-boundary label, matching the other field-shaped controls. */\n label?: ReactNode;\n /** Trigger text when `value` is `\"\"`. */\n placeholder?: string;\n disabled?: boolean;\n /**\n * Why the month cannot be changed — {@link Select}'s `disabledReason`, for a picker\n * that SAVES on change (a budget page's month, a calendar view kept per user). The\n * trigger stays focusable and `aria-disabled`, the grid does not open, the stepper's\n * Today and ‹ › refuse, and `onChange` never fires; the reason is a line under the\n * field (or the stepper) 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`. 0.52 (refactor audit 2026-10-30, API-3).\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 /**\n * This picker COMMITS — picking saves. Under a locked {@link WriteLockProvider} it is\n * locked the `disabledReason` way, with the lock's reason; a {@link CommitScope}, as on\n * every kit control. A month field inside a form with its own Save stays editable\n * under the lock — leave this off there, and put `commit` on the Save. 0.52 (API-3).\n */\n commit?: CommitScope;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /**\n * What is wrong with the month, in the caller's words — {@link Input}'s `error`\n * (keksdose K4): under the field, attached to the trigger with `aria-describedby`\n * (merged, after any caption), and implies `invalid`. Passing the key keeps the\n * field's box while there is no message, so the trigger is never remounted.\n */\n error?: ReactNode;\n /** Standing advice, as on a {@link Select}: text is a caption under the field; a\n * {@link FieldHint} rides the label line. */\n hint?: ReactNode;\n /**\n * `\"md\"` (default): the form field. `\"sm\"`: the toolbar trigger — 36px tall (an\n * `IconButton`'s height) and as wide as the month it shows, for a picker that sits\n * between two icon buttons in a page header (keksdose K13: the budget header wrote\n * `triggerClassName=\"h-9 w-auto … py-0\"` by hand, live #260 — 34px between two 36px\n * buttons, *\"the kind of 2px that reads as sloppy\"*). Unlabelled pickers only, as on\n * {@link Select}: a floating label needs the tall box to float in. The width follows\n * the month's name; add a `min-w-*` in `triggerClassName` if the buttons beside it\n * must not move as it changes.\n */\n size?: \"sm\" | \"md\";\n /**\n * `\"month\"` (default): the value is a month, `\"YYYY-MM\"`.\n *\n * `\"year\"`: the value is a YEAR, `\"YYYY\"`, and the panel is a grid of years — kastlan's\n * budget fiscal year (a `Select` of 2020 to three years ahead) and keksdose's tax tab\n * (\"this year and four before\", tax-tab.tsx:33). `min` / `max` are read as their year\n * (`\"2022\"`, or any longer key). A bounded span of twelve years or fewer is shown\n * whole, with no paging; otherwise the grid pages twelve years at a time (PageUp /\n * PageDown, or the arrows), starting from `min` when there is one. The stepper\n * variant steps a year, and its Today button reads \"This year\".\n *\n * A string rather than a number, for the reason the month key is one: it is the\n * same type across both modes, it crosses a URL as is, and it sorts. A numeric\n * caller converts with `String(year)` and `Number(key)`.\n */\n mode?: \"month\" | \"year\";\n /** Intl options for the trigger's text (default: long month + numeric year, or the\n * numeric year in `mode=\"year\"`). Win over the provider's `formatDate`. */\n formatOptions?: Intl.DateTimeFormatOptions;\n /**\n * The month to ring as \"now\", as `\"YYYY-MM\"`. Defaults to the local calendar's\n * current month; pass one to pin it (a test, a host with its own notion of today).\n */\n currentMonth?: string;\n /** User-facing strings; see {@link MonthPickerLabels}. */\n labels?: LabelOverride<MonthPickerLabels>;\n /** Extra classes for the field's root. */\n className?: string;\n /** Extra classes for the trigger button — e.g. a compact `h-9 w-auto` trigger\n * sitting between two icon buttons in a toolbar. */\n triggerClassName?: string;\n /**\n * `\"field\"` (default): the field-shaped trigger, for a form.\n *\n * `\"stepper\"`: the header of a month PAGE — the month as a heading that opens the\n * grid, then Today and ‹ › stepping the value by one month. What a calendar page\n * assembled by hand around the field (overriding its trigger to look like a\n * heading). `label`, `placeholder`, `invalid`, `hint`, `error`, `size` and\n * `triggerClassName` are the field's and are not drawn here; with no `value` the\n * arrows step from the current month (or year).\n */\n variant?: \"field\" | \"stepper\";\n /** `variant=\"stepper\"`: the heading level the month is set in (`<h2>` by default), or\n * `false` for no heading element — the page already has one for the month. */\n headingLevel?: 1 | 2 | 3 | 4 | 5 | 6 | false;\n /** `variant=\"stepper\"`: draw the Today button. Default `true`. */\n showToday?: boolean;\n /**\n * The TRIGGER — the field's button, or the stepper's heading button — as a React 19\n * ref prop (0.34, API-3, refactor audit 2026-10). The picker took none, so\n * react-hook-form's focus-on-error reached it only through /rhf's id lookup. Not the\n * wrapper, which takes the rest of the props.\n */\n ref?: Ref<HTMLButtonElement>;\n}\n\n// ── Month keys ────────────────────────────────────────────────────────────\n\ninterface YearMonth {\n year: number;\n /** 1–12, as in the key. */\n month: number;\n}\n\nconst KEY = /^(\\d{4})-(\\d{2})/;\n\nfunction parseMonth(key: string | undefined): YearMonth | null {\n const m = key ? KEY.exec(key) : null;\n if (!m) return null;\n const month = Number(m[2]);\n return month >= 1 && month <= 12 ? { year: Number(m[1]), month } : null;\n}\n\nfunction toKey({ year, month }: YearMonth): string {\n return `${year}-${pad(month)}`;\n}\n\n/** Move a month key by whole months, carrying into the year. */\nfunction shiftMonth(ym: YearMonth, delta: number): YearMonth {\n const index = ym.year * 12 + (ym.month - 1) + delta;\n return { year: Math.floor(index / 12), month: (index % 12) + 1 };\n}\n\n/** A bound, normalised to its month key — so `\"2024-03-15\"` compares as `\"2024-03\"`. */\nfunction boundKey(bound: string | undefined): string | null {\n const ym = parseMonth(bound);\n return ym ? toKey(ym) : null;\n}\n\n/** Local-calendar Date for the 1st of a month, for `Intl` to format. */\nfunction firstOf({ year, month }: YearMonth): Date {\n return new Date(year, month - 1, 1);\n}\n\nconst DEFAULT_TRIGGER_FORMAT: Intl.DateTimeFormatOptions = { month: \"long\", year: \"numeric\" };\nconst YEAR_FORMAT: Intl.DateTimeFormatOptions = { year: \"numeric\" };\n\n// ── Year keys ─────────────────────────────────────────────────────────────\n\nconst YEAR = /^(\\d{4})/;\n\n/** The year of `\"YYYY\"`, `\"YYYY-MM\"` or `\"YYYY-MM-DD\"`, or null. */\nfunction parseYear(key: string | undefined): number | null {\n const m = key ? YEAR.exec(key) : null;\n return m ? Number(m[1]) : null;\n}\n\n// ── Trigger ───────────────────────────────────────────────────────────────\n\n/** Hand focus back to the trigger when the panel closes with focus still inside it. */\nfunction useFocusBackOnClose(open: boolean, triggerRef: RefObject<HTMLButtonElement | null>) {\n const wasOpen = useRef(false);\n useEffect(() => {\n const justClosed = wasOpen.current && !open;\n wasOpen.current = open;\n if (!justClosed) return;\n // Only when focus went down with the panel. An outside click has already put it\n // somewhere real, and dragging it back here would be worse than leaving it. The kit's\n // one rule, lib/focus-left `returnFocus` (0.52 code review 2026-10-31, held to 0.55);\n // the Popover's trap names this trigger too since then, and this stays for a panel\n // closed before its trap engaged (no position measured yet).\n returnFocus({ opener: triggerRef.current });\n }, [open, triggerRef]);\n}\n\n/** The Popover's handle on the trigger and the picker's own `ref` prop (API-3), as one. */\nfunction useTriggerRef(triggerRef: RefObject<HTMLButtonElement | null>, fieldRef: Ref<HTMLButtonElement> | undefined) {\n return useMemo(() => mergeRefs(triggerRef, fieldRef), [triggerRef, fieldRef]);\n}\n\n/**\n * The stepper's trigger: the month as a heading-sized button that opens the grid.\n *\n * Its own text is its name (\"September 2026\"), so it needs no label of its own, and\n * it is `aria-live` for the reason `MiniCalendar`'s caption is: pressing ‹ or › leaves\n * focus on the arrow, and the heading is the only thing that says where the arrow\n * went. A plain button with `aria-haspopup`, not the field's `combobox` — it does not\n * look or behave like a field, and a heading reading \"combobox\" is a surprise.\n */\nfunction MonthHeadingTrigger({\n open,\n toggle,\n triggerRef,\n fieldRef,\n text,\n panelId,\n disabled,\n locked,\n aria,\n}: {\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n fieldRef?: Ref<HTMLButtonElement>;\n text: string;\n panelId: string;\n disabled?: boolean;\n /** The write lock: the focusable kind of disabled. */\n locked: boolean;\n aria: TriggerAria;\n}) {\n useFocusBackOnClose(open, triggerRef);\n const setTrigger = useTriggerRef(triggerRef, fieldRef);\n return (\n <button\n ref={setTrigger}\n type=\"button\"\n disabled={locked ? undefined : disabled}\n aria-disabled={locked || undefined}\n onClick={() => {\n if (!locked) toggle();\n }}\n aria-haspopup=\"dialog\"\n aria-controls={panelId}\n aria-expanded={open}\n id={aria.id}\n aria-label={aria[\"aria-label\"]}\n aria-labelledby={aria[\"aria-labelledby\"]}\n aria-describedby={aria[\"aria-describedby\"]}\n className={cn(\n \"-ms-1.5 inline-flex h-9 min-w-0 items-center gap-1 rounded-md px-1.5 text-lg font-semibold text-primary transition-colors\",\n \"hover:bg-hover\",\n FOCUS_RING,\n (disabled || locked) && \"cursor-not-allowed opacity-50\",\n )}\n >\n <span aria-live=\"polite\" className=\"truncate\">\n {text || \"\\u00a0\"}\n </span>\n <ChevronDown aria-hidden className=\"size-4 shrink-0 text-muted\" />\n </button>\n );\n}\n\n/**\n * The field-shaped button that opens the grid. The same shape as `DatePicker`'s\n * trigger, for the same reasons written down there: `role=\"combobox\"` so it may carry\n * `aria-expanded` / `aria-invalid`, a name made of the label AND the value, and focus\n * handed back here when the panel closes with focus still inside it.\n */\nfunction MonthFieldTrigger({\n open,\n toggle,\n triggerRef,\n fieldRef,\n triggerText,\n hasValue,\n labelledBy,\n valueId,\n panelId,\n padded,\n small,\n disabled,\n locked,\n invalid,\n className,\n aria,\n}: {\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n fieldRef?: Ref<HTMLButtonElement>;\n triggerText: string;\n hasValue: boolean;\n labelledBy: string;\n /** The caller's naming attributes — see `trigger-aria.ts`. */\n aria: TriggerAria;\n valueId: string;\n panelId: string;\n padded: boolean;\n /** `size=\"sm\"`: the 36px, content-wide toolbar trigger. */\n small: boolean;\n disabled?: boolean;\n /** The write lock: the focusable kind of disabled (Button's rule — native `disabled`\n * would take the field out of the tab order, and the reason with it). */\n locked: boolean;\n invalid?: boolean;\n className?: string;\n}) {\n useFocusBackOnClose(open, triggerRef);\n const setTrigger = useTriggerRef(triggerRef, fieldRef);\n\n return (\n <button\n ref={setTrigger}\n type=\"button\"\n disabled={locked ? undefined : disabled}\n aria-disabled={locked || undefined}\n onClick={() => {\n // Locked, the grid does not open. One open when the lock arrives closes: the\n // field moves into the reason's frame, which remounts it, as Select's does.\n if (!locked) toggle();\n }}\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-controls={panelId}\n aria-expanded={open}\n id={aria.id}\n aria-invalid={invalid || aria[\"aria-invalid\"] === true || aria[\"aria-invalid\"] === \"true\" || undefined}\n aria-required={aria[\"aria-required\"]}\n aria-labelledby={aria[\"aria-label\"] && !aria[\"aria-labelledby\"] ? undefined : labelledBy}\n aria-label={aria[\"aria-label\"]}\n aria-describedby={aria[\"aria-describedby\"]}\n className={cn(\n FIELD_TRIGGER,\n \"pe-9\",\n padded && FIELD_FLOATING_PAD,\n // keksdose K13 (live #260): an IconButton's 36px, and only as wide as the\n // month — `py-0` with a fixed height so the box is 36px whatever the type's\n // line height. `pe-9` stays: the calendar glyph still sits at the end.\n small && \"h-9 w-auto py-0\",\n (disabled || locked) && \"cursor-not-allowed opacity-50\",\n invalid && FIELD_INVALID,\n className,\n )}\n >\n {/* The U+00A0 fallback keeps the line box when there is no text — see\n DatePicker. The value arrives capitalised (see `upperFirst`); this span does\n NOT use `capitalize`, which upper-cases every WORD — \"Choose A Month\",\n \"Septiembre De 2026\". It wraps from Large up, as DatePicker's value does\n (`truncate-until-large`, text size §10.17; round 0.34 T1: the sweep read\n \"Choose a mo…\" at 360 px / Extra large). */}\n <span id={valueId} className={cn(\"truncate-until-large\", !hasValue && \"text-placeholder\")}>\n {triggerText || \" \"}\n </span>\n </button>\n );\n}\n\n// ── Grid ──────────────────────────────────────────────────────────────────\n\nconst COLUMNS = 3;\n\n/**\n * A year arrow over a grid (the month grid's ‹ › years, the year grid's pages). At a\n * bound it refuses rather than turning `disabled`, JumpCalendar's rule (refactor audit\n * 2026-10-30, INP-4): the press that reaches the bound is made with the focus ON the\n * arrow, and a focused button that turns `disabled` drops the focus to <body>, mid-panel\n * (keksdose's budget MonthPicker passes `min`). `aria-disabled`, a click that does\n * nothing, and the look through `aria-disabled:`.\n */\nconst PANEL_ARROW =\n \"flex size-7 items-center justify-center rounded text-muted hover:bg-hover aria-disabled:cursor-not-allowed aria-disabled:opacity-30 aria-disabled:hover:bg-transparent\";\n\n/** The stepper's Today and ‹ › while refused: Button's look for its focusable kind of\n * disabled — the kit's `disabled:` classes cannot match an `aria-disabled` button. The\n * arrows, ghost IconButtons, also pin their resting fill under hover, as\n * `disabled:hover:` does for them. */\nconst STEPPER_REFUSED = \"aria-disabled:cursor-not-allowed aria-disabled:opacity-50\";\nconst STEPPER_ARROW_REFUSED = `${STEPPER_REFUSED} aria-disabled:hover:bg-transparent`;\n\n/**\n * The panel: a year caption between ‹ › arrows, over the year's {@link MonthGrid} (4×3,\n * its keys and its bounds — one with JumpCalendar's since 0.52, DUP-11).\n *\n * Mounted only while the popover is open, so every opening starts from the current\n * value's year — a value moved from outside (a prev/next-month button beside the\n * field) is where the grid lands, with no effect needed to snap it back.\n */\nfunction MonthPanel({\n value,\n onPick,\n locale,\n minKey,\n maxKey,\n currentKey,\n labels,\n}: {\n value: YearMonth | null;\n onPick: (key: string) => void;\n locale: string | undefined;\n minKey: string | null;\n maxKey: string | null;\n currentKey: string;\n labels: MonthPickerLabels;\n}) {\n const selectedKey = value ? toKey(value) : null;\n // The month holding the tab order, and with it the year on screen — one piece of\n // state, as in MiniCalendar, so the two cannot disagree. Opens on the value, or on\n // the current month, pulled inside the bounds.\n const [active, setActive] = useState(() => clampKey(selectedKey ?? currentKey, minKey, maxKey));\n const year = Number(active.slice(0, 4));\n const captionId = `${useId()}-year`;\n const yearCaption = new Intl.DateTimeFormat(locale, YEAR_FORMAT).format(new Date(year, 0, 1));\n const blocked = yearArrowsBlocked(active, minKey, maxKey);\n\n return (\n <div className=\"select-none\">\n <div className=\"mb-1 flex items-center justify-between\">\n <button\n type=\"button\"\n aria-label={labels.previousYear}\n aria-disabled={blocked.previous || undefined}\n // Focus stays on the arrow being pressed — only the grid moves.\n onClick={() => {\n if (!blocked.previous) setActive(shiftKey(active, -12));\n }}\n className={PANEL_ARROW}\n >\n {/* Mirrored in RTL, where \"previous\" points the other way. */}\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" />\n </button>\n {/* Names the grid, and `aria-live` so stepping a year says which one you are\n on — the user pressing the arrow is not inside the grid to hear it change. */}\n <div id={captionId} aria-live=\"polite\" className=\"text-sm font-semibold tabular-nums text-primary\">\n {yearCaption}\n </div>\n <button\n type=\"button\"\n aria-label={labels.nextYear}\n aria-disabled={blocked.next || undefined}\n onClick={() => {\n if (!blocked.next) setActive(shiftKey(active, 12));\n }}\n className={PANEL_ARROW}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" />\n </button>\n </div>\n <MonthGrid\n active={active}\n onActiveChange={setActive}\n onPick={onPick}\n from={selectedKey}\n to={selectedKey}\n minKey={minKey}\n maxKey={maxKey}\n currentKey={currentKey}\n locale={locale}\n cellName={labels.month}\n labelledBy={captionId}\n />\n </div>\n );\n}\n\n/** Years on one page of the year grid: the month grid's 4×3. */\nconst YEAR_PAGE = 12;\n\n/**\n * `mode=\"year\"`'s panel: a page of years between ‹ › arrows — {@link MonthGrid}'s shape\n * and keyboard, one level up (kastlan 7, keksdose tax-tab.tsx:33).\n *\n * Which years a page holds:\n * - Bounded on both sides by twelve years or fewer: exactly those years, and no\n * arrows — keksdose's \"this year and four before\" is five cells, not a page of\n * twelve with seven greyed out.\n * - Otherwise twelve at a time, starting from `min` when there is one (so the first\n * page opens on it), else ending at `max`, else on multiples of twelve.\n *\n * The arrow keys walk the years (up/down by a row of three), PageUp / PageDown a page,\n * Home / End the row; walking off a page turns it. Focus stops at the bounds rather\n * than walking into years that refuse selection: a page has no \"next year\" past the\n * last selectable one worth landing on.\n */\nfunction YearGrid({\n value,\n onPick,\n locale,\n minYear,\n maxYear,\n currentYear,\n labels,\n}: {\n value: number | null;\n onPick: (key: string) => void;\n locale: string | undefined;\n minYear: number | null;\n maxYear: number | null;\n currentYear: number;\n labels: MonthPickerLabels;\n}) {\n const clamp = (y: number) => Math.min(maxYear ?? y, Math.max(minYear ?? y, y));\n // A bounded span that fits one page is shown whole.\n const whole =\n minYear !== null && maxYear !== null && maxYear >= minYear && maxYear - minYear < YEAR_PAGE\n ? { first: minYear, count: maxYear - minYear + 1 }\n : null;\n // The year holding the tab order, and with it the page on screen — one piece of\n // state, as in MonthPanel. Opens on the value, or on this year, inside the bounds.\n const [active, setActive] = useState(() => clamp(value ?? currentYear));\n const anchor = minYear ?? (maxYear !== null ? maxYear - (YEAR_PAGE - 1) : 0);\n const first = whole ? whole.first : anchor + Math.floor((active - anchor) / YEAR_PAGE) * YEAR_PAGE;\n const count = whole ? whole.count : YEAR_PAGE;\n const years = Array.from({ length: count }, (_, i) => first + i);\n const outOfBounds = (y: number) => (minYear !== null && y < minYear) || (maxYear !== null && y > maxYear);\n\n const gridRef = useRef<HTMLDivElement>(null);\n const captionId = `${useId()}-years`;\n const yearFormat = useMemo(() => new Intl.DateTimeFormat(locale, YEAR_FORMAT), [locale]);\n // \"2016–2027\" in the locale's own range punctuation and digits.\n const caption = yearFormat.formatRange(new Date(first, 0, 1), new Date(first + count - 1, 0, 1));\n\n const pendingFocus = useRef<number | null>(null);\n useEffect(() => {\n const year = pendingFocus.current;\n if (year === null) return;\n pendingFocus.current = null;\n gridRef.current?.querySelector<HTMLElement>(`[data-year=\"${year}\"]`)?.focus();\n });\n // Opened to pick a year: land on it (before the popover's trap, which keeps it).\n useEffect(() => {\n gridRef.current?.querySelector<HTMLElement>('[tabindex=\"0\"]')?.focus();\n }, []);\n\n const moveTo = (next: number, focus: boolean) => {\n const year = clamp(next);\n if (focus) pendingFocus.current = year;\n setActive(year);\n };\n\n const onCellKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {\n const step = horizontalStep(e.key, e.currentTarget);\n const col = (active - first) % COLUMNS;\n let delta: number;\n switch (step ? \"horizontal\" : e.key) {\n case \"horizontal\":\n delta = step;\n break;\n case \"ArrowUp\":\n delta = -COLUMNS;\n break;\n case \"ArrowDown\":\n delta = COLUMNS;\n break;\n case \"Home\":\n delta = -col;\n break;\n case \"End\":\n delta = Math.min(COLUMNS - 1 - col, first + count - 1 - active);\n break;\n case \"PageUp\":\n delta = -YEAR_PAGE;\n break;\n case \"PageDown\":\n delta = YEAR_PAGE;\n break;\n default:\n return;\n }\n e.preventDefault();\n if (delta !== 0) moveTo(active + delta, true);\n };\n\n const prevBlocked = minYear !== null && first - 1 < minYear;\n const nextBlocked = maxYear !== null && first + count > maxYear;\n\n return (\n <div className=\"select-none\">\n <div className={cn(\"mb-1 flex items-center\", whole ? \"h-7 justify-center\" : \"justify-between\")}>\n {!whole && (\n <button\n type=\"button\"\n aria-label={labels.earlierYears}\n aria-disabled={prevBlocked || undefined}\n onClick={() => {\n if (!prevBlocked) moveTo(active - YEAR_PAGE, false);\n }}\n className={PANEL_ARROW}\n >\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" />\n </button>\n )}\n <div id={captionId} aria-live=\"polite\" className=\"text-sm font-semibold tabular-nums text-primary\">\n {caption}\n </div>\n {!whole && (\n <button\n type=\"button\"\n aria-label={labels.laterYears}\n aria-disabled={nextBlocked || undefined}\n onClick={() => {\n if (!nextBlocked) moveTo(active + YEAR_PAGE, false);\n }}\n className={PANEL_ARROW}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" />\n </button>\n )}\n </div>\n <div ref={gridRef} role=\"grid\" aria-labelledby={captionId} className=\"grid gap-1\">\n {Array.from({ length: Math.ceil(count / COLUMNS) }, (_, row) => (\n <div key={row} role=\"row\" className=\"grid grid-cols-3 gap-1\">\n {years.slice(row * COLUMNS, row * COLUMNS + COLUMNS).map((year) => {\n const disabled = outOfBounds(year);\n const selected = year === value;\n const current = year === currentYear;\n return (\n <button\n key={year}\n type=\"button\"\n role=\"gridcell\"\n data-year={year}\n tabIndex={year === active ? 0 : -1}\n aria-selected={selected}\n aria-disabled={disabled || undefined}\n aria-current={current ? \"date\" : undefined}\n onClick={() => {\n if (disabled) return;\n setActive(year);\n onPick(String(year));\n }}\n onKeyDown={onCellKeyDown}\n className={cn(\n // The month grid's 44px rows (DUP-11): one picker, one row height.\n \"h-11 rounded px-1 text-sm tabular-nums transition-colors\",\n FOCUS_RING_WIDTH,\n \"focus-visible:outline-none focus-visible:ring-inset\",\n selected && !disabled ? \"focus-visible:ring-brand-contrast\" : \"focus-visible:ring-brand\",\n disabled\n ? \"cursor-not-allowed text-placeholder\"\n : selected\n ? \"bg-brand text-brand-contrast hover:bg-brand-hover\"\n : \"text-primary hover:bg-hover\",\n current && !selected && !disabled && \"ring-1 ring-inset ring-strong\",\n )}\n >\n {yearFormat.format(new Date(year, 0, 1))}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n// ── Field ─────────────────────────────────────────────────────────────────\n\n/**\n * Month picker: a field showing the selected month (\"August 2026\"), opening a 12-month\n * grid with year arrows. For a value that IS a month — a budget period, a calendar\n * view, a statement — where a day picker would ask for a day nobody means.\n *\n * One component for the two app-local pickers it replaces: keksdose's budget-header\n * popover (grid + year steppers + a floor) and kastlan's calendar header (a month list\n * and a year list in two popovers). The grid does both jobs — the year arrows and\n * PageUp/PageDown cover what the year list did, without a fixed ±5-year window.\n */\nexport function MonthPicker(props: MonthPickerProps) {\n const {\n value,\n onChange,\n locale: localeProp,\n min,\n max,\n label,\n placeholder,\n disabled,\n disabledReason,\n disabledReasonDisplay,\n commit,\n invalid,\n error,\n hint,\n help,\n size = \"md\",\n mode = \"month\",\n formatOptions,\n currentMonth,\n labels: labelsProp,\n className,\n triggerClassName,\n variant = \"field\",\n headingLevel = 2,\n showToday = true,\n // The trigger's (API-3); named apart from the Popover trigger's own `ref` below.\n ref: fieldRef,\n ...rest\n } = props;\n const labels = useMonthPickerLabels(labelsProp);\n const locale = useKitLocale(localeProp);\n const fromProvider = useKitDateFormatter();\n const years = mode === \"year\";\n const selected = years ? null : parseMonth(value);\n const selectedYear = years ? parseYear(value) : null;\n const minKey = boundKey(min);\n const maxKey = boundKey(max);\n const minYear = parseYear(min);\n const maxYear = parseYear(max);\n const currentKey = boundKey(currentMonth) ?? monthKey(currentDate());\n const currentYear = parseYear(currentKey)!;\n // The key the trigger shows, in the mode's own shape: \"YYYY-MM\" or \"YYYY\".\n const shownKey = years ? (selectedYear !== null ? String(selectedYear) : null) : selected ? toKey(selected) : null;\n\n // The kit's order: `formatOptions` (this field's own Intl look), then the provider's\n // `formatDate` (keksdose K12 — the app's one way of writing a month), then the default.\n const triggerText = shownKey\n ? (formatOptions === undefined &&\n fromProvider?.(shownKey, { unit: years ? \"year\" : \"month\", source: \"monthPicker\", locale, weekday: false })) ||\n upperFirst(\n new Intl.DateTimeFormat(locale, formatOptions ?? (years ? YEAR_FORMAT : DEFAULT_TRIGGER_FORMAT)).format(\n years ? new Date(selectedYear!, 0, 1) : firstOf(selected!),\n ),\n locale,\n )\n : (placeholder ?? \"\");\n\n const id = useId();\n // The write lock (API-3): Select's — the trigger stays reachable and says why.\n const lock = useLockReason(commit, disabledReason);\n const locked = lock.locked;\n const reasonId = locked ? lock.reasonId : undefined;\n const change = (key: string) => {\n if (!locked) onChange(key);\n };\n // keksdose K4: the caption and the error, on the trigger after the caller's own\n // description; a lock's reason between the two, as on Select. Painted for `invalid`\n // and a message — not for a bare `aria-invalid`, which this field has never painted for.\n const messages = useFieldMessages({\n hint,\n help,\n name: label ?? rest[\"aria-label\"],\n error,\n describedBy: rest[\"aria-describedby\"],\n reasonId,\n });\n const [ownAria, wrapperRest] = splitTriggerAria(rest);\n // The field's trigger is described by the caption and the error too; the stepper\n // draws neither, so its heading keeps the caller's own description — and a lock's.\n const aria: TriggerAria = { ...ownAria, \"aria-describedby\": messages.describedBy };\n const headingAria: TriggerAria = {\n ...ownAria,\n \"aria-describedby\": mergeDescribedBy(ownAria[\"aria-describedby\"], reasonId),\n };\n // Locked, the field (or the stepper) sits in the reason's frame — the line, or the\n // Tooltip — which becomes the outermost box and takes the caller's classes, as on\n // Select, so a lock does not reflow the page.\n const rootClass = locked ? undefined : className;\n const withReason = (box: ReactNode) => (\n <LockFrame lock={lock} display={disabledReasonDisplay} className={className}>\n {box}\n </LockFrame>\n );\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const panelId = `${id}-panel`;\n // Only a STRING label can double as a name; a ReactNode may hold its own controls.\n const named = typeof label === \"string\";\n // `sm` is the toolbar trigger, for an unlabelled picker only (see `size`).\n const small = size === \"sm\" && label === undefined;\n // The panel is portalled to <body>, out of the subtree whose `dir` it inherited — an\n // RTL form got an LTR grid. Read when opening (`toggle` is the popover's only way\n // open) and put back on the panel. Also what the arrow keys above read.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n const panelLabel = years ? labels.yearPanel : labels.panel;\n\n const grid = (close: () => void) =>\n years ? (\n <YearGrid\n value={selectedYear}\n locale={locale}\n minYear={minYear}\n maxYear={maxYear}\n currentYear={currentYear}\n labels={labels}\n onPick={(key) => {\n change(key);\n close();\n }}\n />\n ) : (\n <MonthPanel\n value={selected}\n locale={locale}\n minKey={minKey}\n maxKey={maxKey}\n currentKey={currentKey}\n labels={labels}\n onPick={(key) => {\n change(key);\n close();\n }}\n />\n );\n\n if (variant === \"stepper\") {\n // The arrows step from the value, or from \"now\" when there is none; each is dead\n // once the month (or year) it leads to is out of bounds, and Today once \"now\" is.\n let prevKey: string;\n let nextKey: string;\n let nowKey: string;\n let outside: (key: string) => boolean;\n if (years) {\n const base = selectedYear ?? currentYear;\n prevKey = String(base - 1);\n nextKey = String(base + 1);\n nowKey = String(currentYear);\n outside = (key) => {\n const y = Number(key);\n return (minYear !== null && y < minYear) || (maxYear !== null && y > maxYear);\n };\n } else {\n const base = selected ?? parseMonth(currentKey)!;\n prevKey = toKey(shiftMonth(base, -1));\n nextKey = toKey(shiftMonth(base, 1));\n nowKey = currentKey;\n outside = (key) => Boolean((minKey && key < minKey) || (maxKey && key > maxKey));\n }\n const Heading = headingLevel ? (`h${headingLevel}` as const) : null;\n const trigger = (\n <Popover\n width=\"16rem\"\n panelId={panelId}\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={({ open, toggle, ref }) => (\n <MonthHeadingTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n fieldRef={fieldRef}\n text={triggerText}\n panelId={panelId}\n disabled={disabled}\n locked={locked}\n aria={headingAria}\n />\n )}\n >\n {grid}\n </Popover>\n );\n // ‹ › at a bound refuse rather than turn `disabled` (refactor audit 2026-10-30,\n // INP-4): the press that reaches the bound is made with the focus on the arrow, and\n // a focused button that turns `disabled` drops the focus to <body>. A lock refuses\n // them, and Today, the same focusable way — its reason is the frame's, so no name\n // bubble of their own beside it. `disabled` (a lock's reason wins over it) and\n // Today's \"now is out of bounds\", which no press here changes, stay native.\n const off = Boolean(disabled) && !locked;\n const prevRefused = locked || outside(prevKey);\n const nextRefused = locked || outside(nextKey);\n return withReason(\n // `flex-wrap`: where the heading and the buttons do not fit one row, the buttons\n // take the next line, at the end (0.32.1, the 360 px Extra-large sweep;\n // docs/text-size-harmonization.md §4). In one row at 150 % \"Today ‹ ›\" left the\n // heading 30 px on a 360 px phone, and \"October 2026\" was drawn under \"Today\".\n // Where they fit, nothing changes.\n <div {...wrapperRest} ref={rootRef} className={cn(\"flex min-w-0 flex-wrap items-center gap-2\", rootClass)}>\n {Heading ? <Heading className=\"m-0 min-w-0 text-lg font-semibold\">{trigger}</Heading> : trigger}\n <div className=\"ms-auto flex shrink-0 items-center gap-1\">\n {showToday && (\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={!locked && (disabled || outside(nowKey))}\n aria-disabled={locked || undefined}\n aria-describedby={reasonId}\n onClick={() => {\n if (nowKey !== shownKey) change(nowKey);\n }}\n className={STEPPER_REFUSED}\n >\n {years ? labels.thisYear : labels.today}\n </Button>\n )}\n <IconButton\n aria-label={years ? labels.previousYear : labels.previousMonth}\n disabled={off}\n aria-disabled={(!off && prevRefused) || undefined}\n aria-describedby={reasonId}\n tooltip={!locked}\n onClick={() => {\n if (!prevRefused) change(prevKey);\n }}\n className={STEPPER_ARROW_REFUSED}\n >\n {/* Mirrored in RTL, where \"previous\" points the other way. */}\n <ChevronLeft className=\"rtl:-scale-x-100\" />\n </IconButton>\n <IconButton\n aria-label={years ? labels.nextYear : labels.nextMonth}\n disabled={off}\n aria-disabled={(!off && nextRefused) || undefined}\n aria-describedby={reasonId}\n tooltip={!locked}\n onClick={() => {\n if (!nextRefused) change(nextKey);\n }}\n className={STEPPER_ARROW_REFUSED}\n >\n <ChevronRight className=\"rtl:-scale-x-100\" />\n </IconButton>\n </div>\n </div>,\n );\n }\n\n const field = (\n // `w-fit` for the toolbar size: the calendar glyph is placed against this box, and a\n // full-width box would park it at the far end of the row, away from its trigger.\n // At Large a labelled box is a grid: the label above, the calendar glyph measured\n // against the trigger's row (LABEL_ABOVE_BOX, 0.33 §10.17).\n <div\n {...wrapperRest}\n ref={rootRef}\n className={cn(\"relative\", small && \"w-fit max-w-full\", label !== undefined && LABEL_ABOVE_BOX, rootClass)}\n >\n {label !== undefined && <FieldLabelLine label={label} hint={messages.labelHint} />}\n {/* The hidden twin the trigger is named by — see DateField. `sr-only-fixed`,\n and inside this `relative` root either way. */}\n {named && (\n <span id={labelId} aria-hidden className=\"sr-only-fixed\">\n {label}\n </span>\n )}\n <Popover\n width=\"16rem\"\n panelId={panelId}\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={({ open, toggle, ref }) => (\n <MonthFieldTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n fieldRef={fieldRef}\n triggerText={triggerText}\n hasValue={shownKey !== null}\n // As DatePicker: a caller's reference first; an `id` without a label of our\n // own references the trigger itself, so an external <label htmlFor> names it.\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 valueId={valueId}\n panelId={panelId}\n padded={label !== undefined}\n small={small}\n disabled={disabled}\n locked={locked}\n invalid={invalid || hasContent(error)}\n className={triggerClassName}\n />\n )}\n >\n {grid}\n </Popover>\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 </div>\n );\n // An unlabelled picker has no label line: a FieldHint goes at its end, outside the\n // box, as on an unlabelled Select.\n const endHint = label === undefined ? messages.labelHint : undefined;\n return (\n <FieldBox below={messages.below} reserve={\"error\" in props || \"hint\" in props}>\n {withReason(\n endHint === undefined ? (\n field\n ) : (\n <div className=\"flex items-center gap-1.5\">\n <div className={cn(\"min-w-0\", !small && \"flex-1\")}>{field}</div>\n <span className=\"flex shrink-0 items-center\">{endHint}</span>\n </div>\n ),\n )}\n </FieldBox>\n );\n}\n"],"mappings":"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","names":[]}
@@ -12,7 +12,7 @@ import {
12
12
  EndHintRow,
13
13
  FieldCaption,
14
14
  LABEL_IN_ROW,
15
- LockedReason,
15
+ LockFrame,
16
16
  mergeDescribedBy,
17
17
  StaticLabelRow,
18
18
  useFieldError,
@@ -104,7 +104,7 @@ function MultiEntityCombobox(props) {
104
104
  const chosen = value.map(resolve);
105
105
  return chosen.every(Boolean) ? listFormat.format(chosen.map((o) => o.label)) : labels.selectedCount(value.length);
106
106
  })();
107
- const withLock = (trigger) => lock.locked ? /* @__PURE__ */ jsx(LockedReason, { lock, display: disabledReasonDisplay, className: "block", children: trigger }) : trigger;
107
+ const withLock = (trigger) => /* @__PURE__ */ jsx(LockFrame, { lock, display: disabledReasonDisplay, children: trigger });
108
108
  const q = query.trim();
109
109
  const showCreate = offersCreate(
110
110
  onCreate,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/multi-entity-combobox.tsx"],"sourcesContent":["import { useCallback, useId, useMemo, type ComponentPropsWithoutRef, type ReactNode, type Ref } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { FieldChevron, FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID } from \"./field-base\";\nimport { FieldLabel } from \"./field-parts\";\nimport { ComboboxPanel, endHintRowProps, offersCreate, useComboboxCore, type ComboOption } from \"./combobox-core\";\nimport { DEFAULT_COMBOBOX_LABELS, DEFAULT_COMMON_LABELS, useKitLabels, useKitLocale } from \"../i18n/kit-labels\";\nimport {\n assignRef,\n EndHintRow,\n FieldCaption,\n LABEL_IN_ROW,\n LockedReason,\n mergeDescribedBy,\n StaticLabelRow,\n useFieldError,\n useFieldHint,\n useLockReason,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport { useCommitReason, type CommitScope } from \"./write-lock\";\n\n/** `onChange` is the kit's — \"a selection was made\", carrying values — rather\n * than the div's form event, so the DOM's spelling of it is omitted. */\nexport interface MultiEntityComboboxProps<V extends string | number>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n /** Selected ids. */\n value: V[];\n onChange: (value: V[]) => void;\n /** Already-loaded options (client-side filtered); also resolves selected labels. */\n options?: ComboOption<V>[];\n /** Async option source, debounced + race-safe; a rejection shows\n * `loadErrorLabel`. When set, `options` resolves labels only. */\n loadOptions?: (query: string) => Promise<ComboOption<V>[]>;\n loading?: boolean;\n label?: ReactNode;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyLabel?: string;\n /** Trigger summary when 1+ selected (default: the labels as a locale-formatted\n * list, or `combobox.selectedCount` when a label can't be resolved). */\n itemLabel?: (count: number) => string;\n /** Offer a clear \"×\" while anything is chosen; it emits `[]`. A pointer shortcut out\n * of the tab order — the keyboard's way is Delete or Backspace on the CLOSED trigger,\n * or unticking rows in the open list. Not offered while `disabled` or locked. */\n clearable?: boolean;\n clearLabel?: string;\n /** The phone sheet's close button. Its own prop rather than a reuse of\n * `clearLabel`: \"clear the selection\" and \"close this screen\" are different\n * actions, and on a full-screen sheet the close button is the only way out —\n * so it is the one control here that MUST be in the reader's language. */\n closeLabel?: string;\n disabled?: boolean;\n /** Why the selection cannot be changed — {@link EntityCombobox}'s `disabledReason`:\n * focusable and `aria-disabled`, no list, no clear, no `onChange`, the reason in the\n * kit {@link Tooltip}. Wins over `disabled`. */\n disabledReason?: ReactNode;\n /** This picker COMMITS — toggling saves. Locked the `disabledReason` way under a\n * locked {@link WriteLockProvider}. See {@link EntityCombobox}'s `commit`. */\n commit?: CommitScope;\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 (DUP-3). */\n disabledReasonDisplay?: DisabledReasonDisplay;\n /** When set, a \"create\" row appears for a non-empty query with no exact match.\n * The panel stays open (adding the new entity to `value` is the caller's job). */\n onCreate?: (query: string) => void;\n createLabel?: (query: string) => string;\n /** Offer the create row with nothing typed too, worded as this. See\n * {@link EntityCombobox}'s `createEmptyLabel`. */\n createEmptyLabel?: string;\n /** The create row is a write while toggling is not: locked on its own under a locked\n * {@link WriteLockProvider}. See {@link EntityCombobox}'s `createCommit`. */\n createCommit?: CommitScope;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /** What is wrong with the value, as {@link Input}'s `error`: rendered under the\n * field, on the trigger's `aria-describedby`, and implies `invalid`. */\n error?: ReactNode;\n /**\n * Standing advice — the rest of the Combobox family's `hint` since 0.22, which this\n * one was left without (keksdose K4): plain TEXT is a caption under the field on the\n * trigger's `aria-describedby`, before any error; a {@link FieldHint} \"?\" rides the\n * label line, or with no label sits at the trigger's end edge, outside the box.\n * Passed at all — even `undefined` — the field keeps one box, so a hint coming and\n * going never rebuilds the trigger under the cursor (the 0.22 rule).\n */\n hint?: ReactNode;\n /** Narrow `options` client-side by the query. Default `true`; `false` shows them\n * as given (a server-ranked list). */\n filter?: boolean;\n /** Offer nothing, and call no `loadOptions`, below this many characters. Default\n * `0`, i.e. the list loads as the panel opens. */\n minChars?: number;\n /** `loadOptions` debounce. Default 150 ms. */\n debounceMs?: number;\n /** Shown when `loadOptions` rejects. Default: `combobox.loadError`. */\n loadErrorLabel?: string;\n /** The TRIGGER — the focusable `<button>` — as a React 19 ref prop, as on\n * {@link EntityCombobox} and {@link CountrySelect} (0.24, kastlan: react-hook-form's\n * focus-on-error focuses whatever `field.ref` is handed). Not the wrapper. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * Multi-value sibling of {@link EntityCombobox}: pick several entities by search,\n * with the same async `loadOptions`/`onCreate` support. Rows show a checkbox and\n * toggle without closing the panel; the trigger summarises the selection.\n */\nexport function MultiEntityCombobox<V extends string | number>(props: MultiEntityComboboxProps<V>) {\n const {\n value,\n onChange,\n options,\n loadOptions,\n loading,\n label,\n placeholder,\n searchPlaceholder,\n emptyLabel,\n itemLabel,\n clearable,\n clearLabel,\n closeLabel,\n disabled,\n disabledReason,\n disabledReasonDisplay,\n commit,\n onCreate,\n createLabel,\n createEmptyLabel,\n createCommit,\n className,\n invalid,\n error,\n hint,\n help,\n filter,\n minChars,\n debounceMs,\n loadErrorLabel,\n ref,\n \"aria-label\": ariaLabel,\n // The control's own wiring, taken off `rest` so it lands on the TRIGGER rather than\n // the wrapper: `Field`'s render-prop spreads `{ id, aria-describedby, aria-invalid,\n // aria-required }`, and on the role-less wrapper div the label's `htmlFor` named\n // nothing and the hint and required state described nothing — kastlan's\n // communication-page To/Cc/Bcc and lease-tenants-step.tsx read as unnamed pickers.\n id,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n const core = useComboboxCore<V>({\n options,\n loadOptions,\n loading,\n filter,\n minChars,\n debounceMs,\n });\n const hintParts = useFieldHint(hint, ariaDescribedBy, { help, name: label ?? ariaLabel });\n const field = useFieldError(error, invalid, hintParts.describedBy, ariaInvalid);\n // The props are the per-instance overrides, the provider the app-wide ones; a\n // prop left `undefined` falls through to the provider rather than masking it.\n const labels = useKitLabels(\"combobox\", DEFAULT_COMBOBOX_LABELS, {\n search: searchPlaceholder,\n noResults: emptyLabel,\n clear: clearLabel,\n create: createLabel,\n });\n const common = useKitLabels(\"common\", DEFAULT_COMMON_LABELS);\n const { open, results, resolve, setOpen, query, triggerRef } = core;\n // The core's trigger ref and the caller's, fed by one callback — see EntityCombobox.\n const setTrigger = useCallback(\n (el: HTMLButtonElement | null) => {\n triggerRef.current = el;\n assignRef(ref, el);\n },\n [ref, triggerRef],\n );\n // The lock — see EntityCombobox: the trigger stays reachable and says why.\n const lock = useLockReason(commit, disabledReason);\n const inert = lock.locked || Boolean(disabled);\n if (lock.locked && open) setOpen(false);\n const createReason = useCommitReason(createCommit, undefined);\n // One id per instance, generated here rather than in the core: `aria-controls` on\n // the trigger has to name the list while the list is still closed, so the id\n // belongs to whoever renders both ends of it.\n const listboxId = useId();\n\n const valueSet = useMemo(() => new Set(value), [value]);\n // A list of NAMES, not a sentence, so the narrow conjunction: English gets\n // \"Food, Rent, Travel\" (no \"and\" squeezed into a truncating trigger), German and\n // French still get their \"und\"/\"et\" where the language insists on it, and Japanese\n // and Chinese get the ideographic comma \"、\". The `unit` type looked like the\n // natural fit for a summary, but in ja/zh it concatenates the names with nothing\n // between them at all.\n const locale = useKitLocale();\n const listFormat = useMemo(() => new Intl.ListFormat(locale, { type: \"conjunction\", style: \"narrow\" }), [locale]);\n const summary = (() => {\n if (value.length === 0) return placeholder ?? \"\";\n if (itemLabel) return itemLabel(value.length);\n const chosen = value.map(resolve);\n return chosen.every(Boolean) ? listFormat.format(chosen.map((o) => o!.label)) : labels.selectedCount(value.length);\n })();\n\n // While locked the trigger sits in the reason's Tooltip, as on EntityCombobox.\n const withLock = (trigger: ReactNode) =>\n lock.locked ? (\n <LockedReason lock={lock} display={disabledReasonDisplay} className=\"block\">\n {trigger}\n </LockedReason>\n ) : (\n trigger\n );\n\n const q = query.trim();\n const showCreate = offersCreate(\n onCreate,\n q,\n results.map((o) => o.label),\n createEmptyLabel,\n locale,\n );\n const showClear = Boolean(clearable && value.length > 0 && !inert);\n\n const toggle = (o: ComboOption<V>) => {\n if (lock.locked) return;\n core.rememberOption(o);\n if (valueSet.has(o.value)) onChange(value.filter((v) => v !== o.value));\n else onChange([...value, o.value]);\n // Stay open — multi-select keeps adding.\n };\n\n return (\n // `rest` dresses the wrapper, which has no role; the accessible NAME — and the\n // id, description, invalid and required state above — go on the trigger, which\n // has one. Spread FIRST so the trigger's ARIA and the\n // handlers that open the panel cannot be clobbered from outside.\n <div {...rest} className={cn(\"relative\", className)}>\n {label !== undefined && hintParts.labelHint === undefined && <FieldLabel>{label}</FieldLabel>}\n <EndHintRow {...endHintRowProps(\"hint\" in props, label !== undefined, hintParts.labelHint)}>\n {withLock(\n <button\n ref={setTrigger}\n id={id}\n type=\"button\"\n // A combobox, not a button. The distinction is not pedantry: this control\n // carried `aria-invalid`, which `button` does not support, so a required\n // field left empty painted a rose border and told a reader nothing at all —\n // and ESLint flagged it as exactly that (`role-supports-aria-props`). The\n // fix the audit asked for is the role that describes what this IS: a closed\n // choice that expands into the list named below. `combobox` supports\n // `aria-invalid`, so the border and the announcement finally agree.\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-haspopup=\"listbox\"\n // The label is a floating <span>, not a <label for>, so without this the\n // trigger's accessible name is whatever value happens to be selected —\n // \"Checking\" with nothing saying it is the account. The label AND the\n // value, because `aria-label` replaces the content rather than adding to\n // it, and a control that announces only its name has lost the answer.\n // A caller's own name wins over the composition: two fields labelled\n // \"Account\" on a transfer form are the from and the to, and only the\n // caller knows which is which.\n aria-label={ariaLabel ?? (typeof label === \"string\" ? common.fieldValue(label, summary) : undefined)}\n // Focusable while locked — see EntityCombobox.\n disabled={lock.locked ? undefined : disabled}\n aria-disabled={lock.locked || undefined}\n aria-invalid={field.isInvalid || undefined}\n aria-describedby={mergeDescribedBy(field.describedBy, lock.locked && lock.reasonId)}\n aria-required={ariaRequired}\n onClick={() => !inert && setOpen((o) => !o)}\n // Down/Up opens the list from the closed trigger, per the APG. Enter and\n // Space already do it through the button's own click.\n onKeyDown={(e) => {\n if (inert) return;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n setOpen(true);\n } else if (showClear && !open && (e.key === \"Delete\" || e.key === \"Backspace\")) {\n // The keyboard's clear: the \"×\" is a pointer shortcut out of the tab\n // order, so without this a cleared-by-mouse field was set-only from the\n // keyboard. The closed trigger only — CountrySelect's rule (0.23).\n e.preventDefault();\n onChange([]);\n }\n }}\n className={cn(\n FIELD_TRIGGER,\n \"pe-9\",\n label !== undefined && FIELD_FLOATING_PAD,\n inert && \"cursor-not-allowed opacity-50\",\n field.isInvalid && FIELD_INVALID,\n )}\n >\n <span\n className={cn(\n // Wraps from Large up, as MultiSelect's summary does (text size §10.17,\n // round 0.34 T1).\n \"min-w-0 truncate-until-large\",\n // Inherit FIELD_BASE's ink for a value, keep the placeholder tone\n // (`--text-placeholder`) — the same rule every field on a form row\n // follows (Keksdose dev#477).\n !value.length && \"text-placeholder\",\n )}\n >\n {summary}\n </span>\n {showClear ? (\n /* eslint-disable-next-line jsx-a11y/click-events-have-key-events -- tabIndex -1 inside\n the trigger <button>, so it never holds focus and a key handler here could\n never fire. A pointer shortcut; the keyboard paths are Delete on the closed\n trigger and unticking the options in the open list. */\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label={labels.clear}\n onClick={(e) => {\n e.stopPropagation();\n onChange([]);\n }}\n className=\"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-placeholder hover:text-secondary\"\n >\n <X className=\"size-4\" />\n </span>\n ) : (\n <FieldChevron />\n )}\n </button>,\n )}\n </EndHintRow>\n {/* With a \"?\" the label shares the top strip with it — after the trigger, so the\n \"?\" follows the control in the tab order, as on EntityCombobox. */}\n {label !== undefined && hintParts.labelHint !== undefined && (\n <StaticLabelRow hint={hintParts.labelHint}>\n <FieldLabel className={LABEL_IN_ROW}>{label}</FieldLabel>\n </StaticLabelRow>\n )}\n <ComboboxPanel\n core={core}\n listboxId={listboxId}\n multi\n // On a phone the panel becomes a full-screen sheet, which needs the field's\n // own label to say what it is asking for (live #200).\n sheetTitle={label ?? placeholder}\n searchPlaceholder={labels.search}\n emptyLabel={labels.noResults}\n closeLabel={closeLabel}\n loadErrorLabel={loadErrorLabel}\n isSelected={(v) => valueSet.has(v)}\n onChoose={toggle}\n showCreate={showCreate}\n onCreate={() => {\n if (lock.locked) return;\n onCreate?.(q);\n core.setQuery(\"\");\n }}\n createContent={q ? labels.create(q) : createEmptyLabel}\n createDisabledReason={createReason}\n />\n <FieldCaption parts={hintParts} />\n {field.errorEl}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/multi-entity-combobox.tsx"],"sourcesContent":["import { useCallback, useId, useMemo, type ComponentPropsWithoutRef, type ReactNode, type Ref } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { FieldChevron, FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID } from \"./field-base\";\nimport { FieldLabel } from \"./field-parts\";\nimport { ComboboxPanel, endHintRowProps, offersCreate, useComboboxCore, type ComboOption } from \"./combobox-core\";\nimport { DEFAULT_COMBOBOX_LABELS, DEFAULT_COMMON_LABELS, useKitLabels, useKitLocale } from \"../i18n/kit-labels\";\nimport {\n assignRef,\n EndHintRow,\n FieldCaption,\n LABEL_IN_ROW,\n LockFrame,\n mergeDescribedBy,\n StaticLabelRow,\n useFieldError,\n useFieldHint,\n useLockReason,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport { useCommitReason, type CommitScope } from \"./write-lock\";\n\n/** `onChange` is the kit's — \"a selection was made\", carrying values — rather\n * than the div's form event, so the DOM's spelling of it is omitted. */\nexport interface MultiEntityComboboxProps<V extends string | number>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n /** Selected ids. */\n value: V[];\n onChange: (value: V[]) => void;\n /** Already-loaded options (client-side filtered); also resolves selected labels. */\n options?: ComboOption<V>[];\n /** Async option source, debounced + race-safe; a rejection shows\n * `loadErrorLabel`. When set, `options` resolves labels only. */\n loadOptions?: (query: string) => Promise<ComboOption<V>[]>;\n loading?: boolean;\n label?: ReactNode;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyLabel?: string;\n /** Trigger summary when 1+ selected (default: the labels as a locale-formatted\n * list, or `combobox.selectedCount` when a label can't be resolved). */\n itemLabel?: (count: number) => string;\n /** Offer a clear \"×\" while anything is chosen; it emits `[]`. A pointer shortcut out\n * of the tab order — the keyboard's way is Delete or Backspace on the CLOSED trigger,\n * or unticking rows in the open list. Not offered while `disabled` or locked. */\n clearable?: boolean;\n clearLabel?: string;\n /** The phone sheet's close button. Its own prop rather than a reuse of\n * `clearLabel`: \"clear the selection\" and \"close this screen\" are different\n * actions, and on a full-screen sheet the close button is the only way out —\n * so it is the one control here that MUST be in the reader's language. */\n closeLabel?: string;\n disabled?: boolean;\n /** Why the selection cannot be changed — {@link EntityCombobox}'s `disabledReason`:\n * focusable and `aria-disabled`, no list, no clear, no `onChange`, the reason in the\n * kit {@link Tooltip}. Wins over `disabled`. */\n disabledReason?: ReactNode;\n /** This picker COMMITS — toggling saves. Locked the `disabledReason` way under a\n * locked {@link WriteLockProvider}. See {@link EntityCombobox}'s `commit`. */\n commit?: CommitScope;\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 (DUP-3). */\n disabledReasonDisplay?: DisabledReasonDisplay;\n /** When set, a \"create\" row appears for a non-empty query with no exact match.\n * The panel stays open (adding the new entity to `value` is the caller's job). */\n onCreate?: (query: string) => void;\n createLabel?: (query: string) => string;\n /** Offer the create row with nothing typed too, worded as this. See\n * {@link EntityCombobox}'s `createEmptyLabel`. */\n createEmptyLabel?: string;\n /** The create row is a write while toggling is not: locked on its own under a locked\n * {@link WriteLockProvider}. See {@link EntityCombobox}'s `createCommit`. */\n createCommit?: CommitScope;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /** What is wrong with the value, as {@link Input}'s `error`: rendered under the\n * field, on the trigger's `aria-describedby`, and implies `invalid`. */\n error?: ReactNode;\n /**\n * Standing advice — the rest of the Combobox family's `hint` since 0.22, which this\n * one was left without (keksdose K4): plain TEXT is a caption under the field on the\n * trigger's `aria-describedby`, before any error; a {@link FieldHint} \"?\" rides the\n * label line, or with no label sits at the trigger's end edge, outside the box.\n * Passed at all — even `undefined` — the field keeps one box, so a hint coming and\n * going never rebuilds the trigger under the cursor (the 0.22 rule).\n */\n hint?: ReactNode;\n /** Narrow `options` client-side by the query. Default `true`; `false` shows them\n * as given (a server-ranked list). */\n filter?: boolean;\n /** Offer nothing, and call no `loadOptions`, below this many characters. Default\n * `0`, i.e. the list loads as the panel opens. */\n minChars?: number;\n /** `loadOptions` debounce. Default 150 ms. */\n debounceMs?: number;\n /** Shown when `loadOptions` rejects. Default: `combobox.loadError`. */\n loadErrorLabel?: string;\n /** The TRIGGER — the focusable `<button>` — as a React 19 ref prop, as on\n * {@link EntityCombobox} and {@link CountrySelect} (0.24, kastlan: react-hook-form's\n * focus-on-error focuses whatever `field.ref` is handed). Not the wrapper. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * Multi-value sibling of {@link EntityCombobox}: pick several entities by search,\n * with the same async `loadOptions`/`onCreate` support. Rows show a checkbox and\n * toggle without closing the panel; the trigger summarises the selection.\n */\nexport function MultiEntityCombobox<V extends string | number>(props: MultiEntityComboboxProps<V>) {\n const {\n value,\n onChange,\n options,\n loadOptions,\n loading,\n label,\n placeholder,\n searchPlaceholder,\n emptyLabel,\n itemLabel,\n clearable,\n clearLabel,\n closeLabel,\n disabled,\n disabledReason,\n disabledReasonDisplay,\n commit,\n onCreate,\n createLabel,\n createEmptyLabel,\n createCommit,\n className,\n invalid,\n error,\n hint,\n help,\n filter,\n minChars,\n debounceMs,\n loadErrorLabel,\n ref,\n \"aria-label\": ariaLabel,\n // The control's own wiring, taken off `rest` so it lands on the TRIGGER rather than\n // the wrapper: `Field`'s render-prop spreads `{ id, aria-describedby, aria-invalid,\n // aria-required }`, and on the role-less wrapper div the label's `htmlFor` named\n // nothing and the hint and required state described nothing — kastlan's\n // communication-page To/Cc/Bcc and lease-tenants-step.tsx read as unnamed pickers.\n id,\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n const core = useComboboxCore<V>({\n options,\n loadOptions,\n loading,\n filter,\n minChars,\n debounceMs,\n });\n const hintParts = useFieldHint(hint, ariaDescribedBy, { help, name: label ?? ariaLabel });\n const field = useFieldError(error, invalid, hintParts.describedBy, ariaInvalid);\n // The props are the per-instance overrides, the provider the app-wide ones; a\n // prop left `undefined` falls through to the provider rather than masking it.\n const labels = useKitLabels(\"combobox\", DEFAULT_COMBOBOX_LABELS, {\n search: searchPlaceholder,\n noResults: emptyLabel,\n clear: clearLabel,\n create: createLabel,\n });\n const common = useKitLabels(\"common\", DEFAULT_COMMON_LABELS);\n const { open, results, resolve, setOpen, query, triggerRef } = core;\n // The core's trigger ref and the caller's, fed by one callback — see EntityCombobox.\n const setTrigger = useCallback(\n (el: HTMLButtonElement | null) => {\n triggerRef.current = el;\n assignRef(ref, el);\n },\n [ref, triggerRef],\n );\n // The lock — see EntityCombobox: the trigger stays reachable and says why.\n const lock = useLockReason(commit, disabledReason);\n const inert = lock.locked || Boolean(disabled);\n if (lock.locked && open) setOpen(false);\n const createReason = useCommitReason(createCommit, undefined);\n // One id per instance, generated here rather than in the core: `aria-controls` on\n // the trigger has to name the list while the list is still closed, so the id\n // belongs to whoever renders both ends of it.\n const listboxId = useId();\n\n const valueSet = useMemo(() => new Set(value), [value]);\n // A list of NAMES, not a sentence, so the narrow conjunction: English gets\n // \"Food, Rent, Travel\" (no \"and\" squeezed into a truncating trigger), German and\n // French still get their \"und\"/\"et\" where the language insists on it, and Japanese\n // and Chinese get the ideographic comma \"、\". The `unit` type looked like the\n // natural fit for a summary, but in ja/zh it concatenates the names with nothing\n // between them at all.\n const locale = useKitLocale();\n const listFormat = useMemo(() => new Intl.ListFormat(locale, { type: \"conjunction\", style: \"narrow\" }), [locale]);\n const summary = (() => {\n if (value.length === 0) return placeholder ?? \"\";\n if (itemLabel) return itemLabel(value.length);\n const chosen = value.map(resolve);\n return chosen.every(Boolean) ? listFormat.format(chosen.map((o) => o!.label)) : labels.selectedCount(value.length);\n })();\n\n // While locked the trigger sits in the reason's Tooltip, as on EntityCombobox.\n const withLock = (trigger: ReactNode) => (\n <LockFrame lock={lock} display={disabledReasonDisplay}>\n {trigger}\n </LockFrame>\n );\n\n const q = query.trim();\n const showCreate = offersCreate(\n onCreate,\n q,\n results.map((o) => o.label),\n createEmptyLabel,\n locale,\n );\n const showClear = Boolean(clearable && value.length > 0 && !inert);\n\n const toggle = (o: ComboOption<V>) => {\n if (lock.locked) return;\n core.rememberOption(o);\n if (valueSet.has(o.value)) onChange(value.filter((v) => v !== o.value));\n else onChange([...value, o.value]);\n // Stay open — multi-select keeps adding.\n };\n\n return (\n // `rest` dresses the wrapper, which has no role; the accessible NAME — and the\n // id, description, invalid and required state above — go on the trigger, which\n // has one. Spread FIRST so the trigger's ARIA and the\n // handlers that open the panel cannot be clobbered from outside.\n <div {...rest} className={cn(\"relative\", className)}>\n {label !== undefined && hintParts.labelHint === undefined && <FieldLabel>{label}</FieldLabel>}\n <EndHintRow {...endHintRowProps(\"hint\" in props, label !== undefined, hintParts.labelHint)}>\n {withLock(\n <button\n ref={setTrigger}\n id={id}\n type=\"button\"\n // A combobox, not a button. The distinction is not pedantry: this control\n // carried `aria-invalid`, which `button` does not support, so a required\n // field left empty painted a rose border and told a reader nothing at all —\n // and ESLint flagged it as exactly that (`role-supports-aria-props`). The\n // fix the audit asked for is the role that describes what this IS: a closed\n // choice that expands into the list named below. `combobox` supports\n // `aria-invalid`, so the border and the announcement finally agree.\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-haspopup=\"listbox\"\n // The label is a floating <span>, not a <label for>, so without this the\n // trigger's accessible name is whatever value happens to be selected —\n // \"Checking\" with nothing saying it is the account. The label AND the\n // value, because `aria-label` replaces the content rather than adding to\n // it, and a control that announces only its name has lost the answer.\n // A caller's own name wins over the composition: two fields labelled\n // \"Account\" on a transfer form are the from and the to, and only the\n // caller knows which is which.\n aria-label={ariaLabel ?? (typeof label === \"string\" ? common.fieldValue(label, summary) : undefined)}\n // Focusable while locked — see EntityCombobox.\n disabled={lock.locked ? undefined : disabled}\n aria-disabled={lock.locked || undefined}\n aria-invalid={field.isInvalid || undefined}\n aria-describedby={mergeDescribedBy(field.describedBy, lock.locked && lock.reasonId)}\n aria-required={ariaRequired}\n onClick={() => !inert && setOpen((o) => !o)}\n // Down/Up opens the list from the closed trigger, per the APG. Enter and\n // Space already do it through the button's own click.\n onKeyDown={(e) => {\n if (inert) return;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n setOpen(true);\n } else if (showClear && !open && (e.key === \"Delete\" || e.key === \"Backspace\")) {\n // The keyboard's clear: the \"×\" is a pointer shortcut out of the tab\n // order, so without this a cleared-by-mouse field was set-only from the\n // keyboard. The closed trigger only — CountrySelect's rule (0.23).\n e.preventDefault();\n onChange([]);\n }\n }}\n className={cn(\n FIELD_TRIGGER,\n \"pe-9\",\n label !== undefined && FIELD_FLOATING_PAD,\n inert && \"cursor-not-allowed opacity-50\",\n field.isInvalid && FIELD_INVALID,\n )}\n >\n <span\n className={cn(\n // Wraps from Large up, as MultiSelect's summary does (text size §10.17,\n // round 0.34 T1).\n \"min-w-0 truncate-until-large\",\n // Inherit FIELD_BASE's ink for a value, keep the placeholder tone\n // (`--text-placeholder`) — the same rule every field on a form row\n // follows (Keksdose dev#477).\n !value.length && \"text-placeholder\",\n )}\n >\n {summary}\n </span>\n {showClear ? (\n /* eslint-disable-next-line jsx-a11y/click-events-have-key-events -- tabIndex -1 inside\n the trigger <button>, so it never holds focus and a key handler here could\n never fire. A pointer shortcut; the keyboard paths are Delete on the closed\n trigger and unticking the options in the open list. */\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label={labels.clear}\n onClick={(e) => {\n e.stopPropagation();\n onChange([]);\n }}\n className=\"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-placeholder hover:text-secondary\"\n >\n <X className=\"size-4\" />\n </span>\n ) : (\n <FieldChevron />\n )}\n </button>,\n )}\n </EndHintRow>\n {/* With a \"?\" the label shares the top strip with it — after the trigger, so the\n \"?\" follows the control in the tab order, as on EntityCombobox. */}\n {label !== undefined && hintParts.labelHint !== undefined && (\n <StaticLabelRow hint={hintParts.labelHint}>\n <FieldLabel className={LABEL_IN_ROW}>{label}</FieldLabel>\n </StaticLabelRow>\n )}\n <ComboboxPanel\n core={core}\n listboxId={listboxId}\n multi\n // On a phone the panel becomes a full-screen sheet, which needs the field's\n // own label to say what it is asking for (live #200).\n sheetTitle={label ?? placeholder}\n searchPlaceholder={labels.search}\n emptyLabel={labels.noResults}\n closeLabel={closeLabel}\n loadErrorLabel={loadErrorLabel}\n isSelected={(v) => valueSet.has(v)}\n onChoose={toggle}\n showCreate={showCreate}\n onCreate={() => {\n if (lock.locked) return;\n onCreate?.(q);\n core.setQuery(\"\");\n }}\n createContent={q ? labels.create(q) : createEmptyLabel}\n createDisabledReason={createReason}\n />\n <FieldCaption parts={hintParts} />\n {field.errorEl}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -9,7 +9,7 @@ import {
9
9
  assignRef,
10
10
  EndHintRow,
11
11
  LABEL_IN_ROW,
12
- LockedReason,
12
+ LockFrame,
13
13
  StaticLabelRow,
14
14
  useFieldMessages,
15
15
  useLockReason
@@ -148,11 +148,7 @@ function MultiSelect(props) {
148
148
  // handlers that open the list, cannot be replaced by accident from outside.
149
149
  /* @__PURE__ */ jsxs("div", { ...rest, className: cn("relative", className), children: [
150
150
  label !== void 0 && field.labelHint === void 0 && /* @__PURE__ */ jsx(FieldLabel, { className: inert ? "opacity-50" : void 0, children: label }),
151
- /* @__PURE__ */ jsx(EndHintRow, { hint: label === void 0 ? field.labelHint : void 0, children: lock.locked ? (
152
- // Button's frame: the line under the trigger, or the Tooltip with a hidden copy
153
- // for `aria-describedby`.
154
- /* @__PURE__ */ jsx(LockedReason, { lock, display: disabledReasonDisplay, className: "block", children: trigger })
155
- ) : trigger }),
151
+ /* @__PURE__ */ jsx(EndHintRow, { hint: label === void 0 ? field.labelHint : void 0, children: /* @__PURE__ */ jsx(LockFrame, { lock, display: disabledReasonDisplay, children: trigger }) }),
156
152
  label !== void 0 && field.labelHint !== void 0 && /* @__PURE__ */ jsx(StaticLabelRow, { hint: field.labelHint, children: /* @__PURE__ */ jsx(FieldLabel, { className: cn(LABEL_IN_ROW, inert && "opacity-50"), children: label }) }),
157
153
  field.below,
158
154
  /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/multi-select.tsx"],"sourcesContent":["import { useCallback, useId, useMemo } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode, Ref } from \"react\";\nimport { FieldChevron, FIELD_TRIGGER, FIELD_INVALID, FIELD_FLOATING_PAD } from \"./field-base\";\nimport { FieldLabel } from \"./field-parts\";\nimport { cn } from \"../lib/cn\";\nimport { ComboboxPanel, useComboboxCore, type ComboOption } from \"./combobox-core\";\nimport {\n assignRef,\n EndHintRow,\n LABEL_IN_ROW,\n LockedReason,\n StaticLabelRow,\n useFieldMessages,\n useLockReason,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport type { CommitScope } from \"./write-lock\";\nimport {\n DEFAULT_COMMON_LABELS,\n DEFAULT_MULTI_SELECT_LABELS,\n useKitLabels,\n type MultiSelectLabels,\n type LabelOverride,\n} from \"../i18n/kit-labels\";\n\n/**\n * One row of a {@link MultiSelect}. Generic over its value since 0.34 (API-4), so a list of\n * number ids hands `onChange` numbers — `MultiSelectOption` alone is the old\n * `string | number` row.\n */\nexport interface MultiSelectOption<V extends string | number = string | number> {\n value: V;\n label: string;\n /** A second line under the label — a postcode, a count (keksdose #147). Searchable. */\n hint?: string;\n /**\n * Shown but not takeable — {@link ComboOption.disabled}: dimmed, `aria-disabled`, passed\n * over by the arrow keys, and a press does nothing. A chosen disabled row stays chosen:\n * \"Select all\" and \"Clear\" leave it as it is. Say why in `hint`.\n */\n disabled?: boolean;\n}\n\n/** `onChange` is the kit's — \"the selection changed\", carrying the new values —\n * rather than the div's form event. Generic over the option value (API-4): `V` is\n * inferred from `options` and `value`, so `onChange` hands back `number[]` for number\n * ids and `string[]` for string ones, and no call site casts. */\nexport interface MultiSelectProps<V extends string | number = string | number>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n options: readonly MultiSelectOption<V>[];\n /** The chosen values — the select family's `value` (API-4). */\n value?: readonly V[];\n onChange: (next: V[]) => void;\n /** Singular/plural countable label, used when 1+ items are selected. Default:\n * `labels.selectedCount`, else `multiSelect.selectedCount` from the provider — the\n * bare count in English. */\n itemLabel?: (count: number) => string;\n /** Shown when nothing is picked, unless `labels.all` says otherwise — it has always\n * doubled as the \"nothing picked\" text. */\n placeholder?: string;\n /** Embedded top-boundary label, matching the native Input/Select fields. */\n label?: ReactNode;\n /**\n * Per-instance overrides of the provider's `multiSelect` namespace, over English — the\n * kit's `labels` shape, where this took four string props until 0.35 (API-4: `allLabel`,\n * `searchLabel`, `selectAllLabel`, `clearLabel`). `all` is the trigger when nothing is\n * picked: `labels.all`, then `placeholder`, then the provider's.\n */\n labels?: LabelOverride<MultiSelectLabels>;\n /**\n * Extra classes for the open PANEL. The panel is portalled now (dev#548) and sizes\n * itself: as wide as the trigger and at least 16rem, growing with its rows up to 32rem\n * (or the trigger) — what this prop was asked for (keksdose feedback #147: the market\n * picker's postcode, distance and receipt-count hints were cut at 16rem). A `w-*` class\n * here no longer sets the width.\n */\n panelClassName?: string;\n /** Required and unanswered — {@link FIELD_INVALID}, the same rose border the\n * native `Select` has worn since feedback #235. Every field-styled control in\n * this package carries it now, so a form can mark any of its fields rather than\n * only the one that happened to have it first. */\n invalid?: boolean;\n /** What is wrong with the selection, in the caller's words. Rendered under the\n * field, pointed at by the trigger's `aria-describedby` (merged with any the\n * caller passed) and implies `invalid` — {@link Input}'s `error`, on the same\n * rules. */\n error?: ReactNode;\n /**\n * Standing advice for the field — {@link Combobox}'s `hint` (keksdose K4): plain TEXT\n * is a caption under the field on the trigger's `aria-describedby`, before any error;\n * a {@link FieldHint} \"?\" rides the label line, or with no label sits at the trigger's\n * end edge, outside the box. Not an option's `hint`, which stays on its row.\n */\n hint?: ReactNode;\n /** The trigger cannot be opened: FIELD_BASE's settled grey, no chevron (as a\n * disabled {@link Select} drops its own), and a dimmed label. */\n disabled?: boolean;\n /**\n * Why the selection cannot be changed — {@link EntityCombobox}'s `disabledReason`: the\n * trigger stays focusable and `aria-disabled`, the list does not open and `onChange`\n * never fires; the reason is a line under the field at Large and on a touch screen,\n * else in the kit {@link Tooltip} (`disabledReasonDisplay`), and on the trigger's\n * `aria-describedby`. Wins over `disabled`.\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 /** This picker COMMITS — toggling saves (a filter persisted per user, a settings row).\n * Locked the `disabledReason` way under a locked {@link WriteLockProvider}, with the\n * lock's reason. Leave it off inside a form that has its own Save. */\n commit?: CommitScope;\n /** The TRIGGER — the focusable `<button>` — as a React 19 ref prop, as on\n * {@link MultiEntityCombobox} (react-hook-form's focus-on-error). Not the wrapper. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/** \"Select all\" and \"Clear\": dimmed, with no hover, while there is nothing for them to do. */\nconst TOOLBAR_BUTTON =\n \"rounded px-2 py-0.5 text-secondary hover:bg-hover aria-disabled:cursor-not-allowed aria-disabled:opacity-40 aria-disabled:hover:bg-transparent\";\n\n/**\n * Pick several of a fixed list — a filter's accounts, a comparison's data sets — with a\n * search box, \"Select all\" and \"Clear\"; nothing picked reads as \"all\".\n *\n * Built on the picker family's foundation since round 0.34 (Wave 2, DUP-4):\n * `useComboboxCore` + `ComboboxPanel`, as {@link MultiEntityCombobox} is. It was an\n * absolute `DropdownPanel`, which an `overflow` ancestor clipped (dev#548) and which on a\n * phone was a 16rem panel between the field and the keyboard; it now has the full-screen\n * sheet (live #200), the portal, the live result count, disabled rows and the search\n * that folds accents (\"zurich\" finds Zürich, DUP-5).\n */\nexport function MultiSelect<V extends string | number = string | number>(props: MultiSelectProps<V>) {\n const {\n options,\n value: valueProp,\n onChange,\n itemLabel,\n placeholder,\n label,\n labels: labelsProp,\n className,\n panelClassName,\n invalid,\n error,\n hint,\n help,\n disabled,\n disabledReason,\n disabledReasonDisplay,\n commit,\n ref,\n id,\n \"aria-label\": ariaLabel,\n // Off `rest` and onto the trigger, which is the combobox a reader meets: `Field`'s\n // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and on\n // the wrapper div the label named nothing and the required state described nothing\n // (DUP-8, refactor audit 2026-10 — Combobox's rule).\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n const value = useMemo<readonly V[]>(() => valueProp ?? [], [valueProp]);\n // The rows as the family's options — the hint is the sublabel, searched like the label.\n const comboOptions = useMemo(\n () =>\n options.map((o): ComboOption<V> => ({ value: o.value, label: o.label, sublabel: o.hint, disabled: o.disabled })),\n [options],\n );\n // Back closes the open list, as it did on `useDropdown` (keksdose live #309).\n const core = useComboboxCore<V>({ options: comboOptions, backCloses: true });\n const { open, setOpen, triggerRef } = core;\n // The core anchors the panel to the trigger and hands focus back to it; the caller's\n // `ref` wants the same element. One callback feeds both.\n const setTrigger = useCallback(\n (el: HTMLButtonElement | null) => {\n triggerRef.current = el;\n assignRef(ref, el);\n },\n [ref, triggerRef],\n );\n // `labels` > the provider's `multiSelect` > English. `all` is resolved apart below:\n // between the instance's and the provider's sits `placeholder`, which has always\n // doubled as the \"nothing picked\" text, and a caller who set it must keep seeing it.\n const text = useKitLabels(\"multiSelect\", DEFAULT_MULTI_SELECT_LABELS, {\n search: labelsProp?.search,\n selectAll: labelsProp?.selectAll,\n clear: labelsProp?.clear,\n selectedCount: labelsProp?.selectedCount ?? itemLabel,\n });\n const common = useKitLabels(\"common\", DEFAULT_COMMON_LABELS);\n\n // The lock — EntityCombobox's: the trigger stays reachable and says why.\n const lock = useLockReason(commit, disabledReason);\n const inert = lock.locked || Boolean(disabled);\n // A lock (or `disabled`) arriving while the list is up closes it — adjusted while\n // rendering, as CountrySelect does, so no frame shows a list that can no longer be used.\n if (inert && open) setOpen(false);\n\n // The caller's ids, the caption, a lock's reason, the error — Select's order.\n const field = useFieldMessages({\n hint,\n help,\n name: label ?? ariaLabel,\n error,\n invalid,\n describedBy: ariaDescribedBy,\n ariaInvalid,\n reasonId: lock.locked && lock.reasonId,\n });\n // One id per instance: `aria-controls` on the trigger names the list while the\n // list is still closed.\n const listboxId = `${useId()}-listbox`;\n\n const valueSet = useMemo(() => new Set<V>(value), [value]);\n const allChecked = options.length > 0 && options.every((o) => valueSet.has(o.value));\n const selectedSummary =\n value.length === 0 || allChecked ? (labelsProp?.all ?? placeholder ?? text.all) : text.selectedCount(value.length);\n\n const toggle = (o: ComboOption<V>) => {\n if (lock.locked) return;\n if (valueSet.has(o.value)) onChange(value.filter((v) => v !== o.value));\n else onChange([...value, o.value]);\n // Stays open, as a pressed row always has: this control answers \"which of these\",\n // which is rarely one of them.\n };\n // A chosen disabled row stays chosen, an unchosen one stays out — the row refuses to be\n // toggled, by a press or by the two actions.\n const selectAll = () => onChange(options.filter((o) => !o.disabled || valueSet.has(o.value)).map((o) => o.value));\n const clearAll = () => onChange(options.filter((o) => o.disabled && valueSet.has(o.value)).map((o) => o.value));\n const canSelectAll = options.some((o) => !o.disabled && !valueSet.has(o.value));\n const canClear = options.some((o) => !o.disabled && valueSet.has(o.value));\n\n const trigger = (\n <button\n ref={setTrigger}\n id={id}\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={lock.locked ? undefined : disabled}\n aria-disabled={lock.locked || undefined}\n // A combobox, not a bare button: this trigger carried `aria-invalid`, which\n // `button` does not support, so a required-and-empty filter painted its rose\n // border and announced nothing (ESLint's `role-supports-aria-props`, the\n // audit's §a11y). The role that describes it is the one that also supports\n // the attribute.\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-haspopup=\"listbox\"\n // The label is a floating <span>, not a <label for>, so without this the\n // trigger announces only its summary — \"3 selected\", with nothing saying\n // three of WHAT. The label AND the summary, because `aria-label` replaces\n // the content rather than adding to it; the same composition its two\n // siblings use, so a form row of pickers reads the same way throughout.\n // A caller's own name wins: two filters labelled \"Accounts\" on one screen have\n // to be told apart, and only the caller knows by what.\n aria-label={ariaLabel ?? (typeof label === \"string\" ? common.fieldValue(label, selectedSummary) : undefined)}\n onClick={() => {\n if (!inert) setOpen((v) => !v);\n }}\n // Down/Up opens the list from the closed trigger, per the APG; Enter and\n // Space already do through the button's own click.\n onKeyDown={(e) => {\n if (inert) return;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n setOpen(true);\n }\n }}\n aria-invalid={field.isInvalid || undefined}\n aria-required={ariaRequired}\n // On the TRIGGER, where focus lands — the `aria-describedby` a caller put on\n // the component is taken off the wrapper for the same reason.\n aria-describedby={field.describedBy}\n className={cn(\n FIELD_TRIGGER,\n \"pe-9\",\n label !== undefined && FIELD_FLOATING_PAD,\n // FIELD_TRIGGER's hover would still light a trigger nothing can open.\n \"disabled:hover:bg-surface-2\",\n // Dimmed like EntityCombobox's and MultiEntityCombobox's disabled triggers;\n // only the label was, so a disabled MultiSelect read as live (showcase audit).\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n lock.locked && \"cursor-not-allowed opacity-50\",\n field.isInvalid && FIELD_INVALID,\n )}\n >\n {/* The summary IS the field's value, so it inherits FIELD_BASE's ink rather\n than restating a lighter one (Keksdose dev#477). An ellipsis at Normal; from\n Large up it wraps and the trigger grows, as DatePicker's value does (text\n size §10.17, round 0.34 T1). */}\n <span className=\"truncate-until-large\">{selectedSummary}</span>\n {!inert && <FieldChevron />}\n </button>\n );\n\n return (\n // `rest` dresses the wrapper; the name belongs to the trigger below it, which is\n // the thing with a role. Spread FIRST so the ARIA the trigger composes, and the\n // handlers that open the list, cannot be replaced by accident from outside.\n <div {...rest} className={cn(\"relative\", className)}>\n {label !== undefined && field.labelHint === undefined && (\n <FieldLabel className={inert ? \"opacity-50\" : undefined}>{label}</FieldLabel>\n )}\n <EndHintRow hint={label === undefined ? field.labelHint : undefined}>\n {lock.locked ? (\n // Button's frame: the line under the trigger, or the Tooltip with a hidden copy\n // for `aria-describedby`.\n <LockedReason lock={lock} display={disabledReasonDisplay} className=\"block\">\n {trigger}\n </LockedReason>\n ) : (\n trigger\n )}\n </EndHintRow>\n {/* With a \"?\" the label shares the top strip with it — after the trigger, so the\n \"?\" follows the control in the tab order, as on Input and Select. */}\n {label !== undefined && field.labelHint !== undefined && (\n <StaticLabelRow hint={field.labelHint}>\n <FieldLabel className={cn(LABEL_IN_ROW, inert && \"opacity-50\")}>{label}</FieldLabel>\n </StaticLabelRow>\n )}\n {field.below}\n <ComboboxPanel\n core={core}\n listboxId={listboxId}\n multi\n // On a phone the list is a full-screen sheet, which needs the field's own name\n // to say what it asks for (live #200).\n sheetTitle={label ?? placeholder}\n searchPlaceholder={text.search}\n isSelected={(v) => valueSet.has(v)}\n onChoose={toggle}\n showCreate={false}\n onCreate={() => {}}\n createContent={null}\n // At least the trigger's width and 16rem, growing with the rows to 32rem: the\n // hints are why it was widened at all (keksdose #147).\n width={{ minRem: 16, fit: true, maxRem: 32 }}\n className={panelClassName}\n // Reached by Tab from the search box (the panel's rows are not tab stops), and\n // `aria-disabled` with a guarded click rather than `disabled`: a pressed \"Clear\"\n // with nothing left to clear disabled itself under the focus, which dropped it to\n // <body> — JumpCalendar's arrows are the rule (refactor audit 2026-10-30, INP-9).\n toolbar={\n <div className=\"flex items-center justify-between gap-2 px-2 py-1.5 text-xs\">\n <button\n type=\"button\"\n onClick={() => {\n if (canSelectAll) selectAll();\n }}\n aria-disabled={!canSelectAll || undefined}\n className={TOOLBAR_BUTTON}\n >\n {text.selectAll}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n if (canClear) clearAll();\n }}\n aria-disabled={!canClear || undefined}\n className={TOOLBAR_BUTTON}\n >\n {text.clear}\n </button>\n </div>\n }\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/multi-select.tsx"],"sourcesContent":["import { useCallback, useId, useMemo } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode, Ref } from \"react\";\nimport { FieldChevron, FIELD_TRIGGER, FIELD_INVALID, FIELD_FLOATING_PAD } from \"./field-base\";\nimport { FieldLabel } from \"./field-parts\";\nimport { cn } from \"../lib/cn\";\nimport { ComboboxPanel, useComboboxCore, type ComboOption } from \"./combobox-core\";\nimport {\n assignRef,\n EndHintRow,\n LABEL_IN_ROW,\n LockFrame,\n StaticLabelRow,\n useFieldMessages,\n useLockReason,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n} from \"./field-parts\";\nimport type { CommitScope } from \"./write-lock\";\nimport {\n DEFAULT_COMMON_LABELS,\n DEFAULT_MULTI_SELECT_LABELS,\n useKitLabels,\n type MultiSelectLabels,\n type LabelOverride,\n} from \"../i18n/kit-labels\";\n\n/**\n * One row of a {@link MultiSelect}. Generic over its value since 0.34 (API-4), so a list of\n * number ids hands `onChange` numbers — `MultiSelectOption` alone is the old\n * `string | number` row.\n */\nexport interface MultiSelectOption<V extends string | number = string | number> {\n value: V;\n label: string;\n /** A second line under the label — a postcode, a count (keksdose #147). Searchable. */\n hint?: string;\n /**\n * Shown but not takeable — {@link ComboOption.disabled}: dimmed, `aria-disabled`, passed\n * over by the arrow keys, and a press does nothing. A chosen disabled row stays chosen:\n * \"Select all\" and \"Clear\" leave it as it is. Say why in `hint`.\n */\n disabled?: boolean;\n}\n\n/** `onChange` is the kit's — \"the selection changed\", carrying the new values —\n * rather than the div's form event. Generic over the option value (API-4): `V` is\n * inferred from `options` and `value`, so `onChange` hands back `number[]` for number\n * ids and `string[]` for string ones, and no call site casts. */\nexport interface MultiSelectProps<V extends string | number = string | number>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\">, FieldHelpProps {\n options: readonly MultiSelectOption<V>[];\n /** The chosen values — the select family's `value` (API-4). */\n value?: readonly V[];\n onChange: (next: V[]) => void;\n /** Singular/plural countable label, used when 1+ items are selected. Default:\n * `labels.selectedCount`, else `multiSelect.selectedCount` from the provider — the\n * bare count in English. */\n itemLabel?: (count: number) => string;\n /** Shown when nothing is picked, unless `labels.all` says otherwise — it has always\n * doubled as the \"nothing picked\" text. */\n placeholder?: string;\n /** Embedded top-boundary label, matching the native Input/Select fields. */\n label?: ReactNode;\n /**\n * Per-instance overrides of the provider's `multiSelect` namespace, over English — the\n * kit's `labels` shape, where this took four string props until 0.35 (API-4: `allLabel`,\n * `searchLabel`, `selectAllLabel`, `clearLabel`). `all` is the trigger when nothing is\n * picked: `labels.all`, then `placeholder`, then the provider's.\n */\n labels?: LabelOverride<MultiSelectLabels>;\n /**\n * Extra classes for the open PANEL. The panel is portalled now (dev#548) and sizes\n * itself: as wide as the trigger and at least 16rem, growing with its rows up to 32rem\n * (or the trigger) — what this prop was asked for (keksdose feedback #147: the market\n * picker's postcode, distance and receipt-count hints were cut at 16rem). A `w-*` class\n * here no longer sets the width.\n */\n panelClassName?: string;\n /** Required and unanswered — {@link FIELD_INVALID}, the same rose border the\n * native `Select` has worn since feedback #235. Every field-styled control in\n * this package carries it now, so a form can mark any of its fields rather than\n * only the one that happened to have it first. */\n invalid?: boolean;\n /** What is wrong with the selection, in the caller's words. Rendered under the\n * field, pointed at by the trigger's `aria-describedby` (merged with any the\n * caller passed) and implies `invalid` — {@link Input}'s `error`, on the same\n * rules. */\n error?: ReactNode;\n /**\n * Standing advice for the field — {@link Combobox}'s `hint` (keksdose K4): plain TEXT\n * is a caption under the field on the trigger's `aria-describedby`, before any error;\n * a {@link FieldHint} \"?\" rides the label line, or with no label sits at the trigger's\n * end edge, outside the box. Not an option's `hint`, which stays on its row.\n */\n hint?: ReactNode;\n /** The trigger cannot be opened: FIELD_BASE's settled grey, no chevron (as a\n * disabled {@link Select} drops its own), and a dimmed label. */\n disabled?: boolean;\n /**\n * Why the selection cannot be changed — {@link EntityCombobox}'s `disabledReason`: the\n * trigger stays focusable and `aria-disabled`, the list does not open and `onChange`\n * never fires; the reason is a line under the field at Large and on a touch screen,\n * else in the kit {@link Tooltip} (`disabledReasonDisplay`), and on the trigger's\n * `aria-describedby`. Wins over `disabled`.\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 /** This picker COMMITS — toggling saves (a filter persisted per user, a settings row).\n * Locked the `disabledReason` way under a locked {@link WriteLockProvider}, with the\n * lock's reason. Leave it off inside a form that has its own Save. */\n commit?: CommitScope;\n /** The TRIGGER — the focusable `<button>` — as a React 19 ref prop, as on\n * {@link MultiEntityCombobox} (react-hook-form's focus-on-error). Not the wrapper. */\n ref?: Ref<HTMLButtonElement>;\n}\n\n/** \"Select all\" and \"Clear\": dimmed, with no hover, while there is nothing for them to do. */\nconst TOOLBAR_BUTTON =\n \"rounded px-2 py-0.5 text-secondary hover:bg-hover aria-disabled:cursor-not-allowed aria-disabled:opacity-40 aria-disabled:hover:bg-transparent\";\n\n/**\n * Pick several of a fixed list — a filter's accounts, a comparison's data sets — with a\n * search box, \"Select all\" and \"Clear\"; nothing picked reads as \"all\".\n *\n * Built on the picker family's foundation since round 0.34 (Wave 2, DUP-4):\n * `useComboboxCore` + `ComboboxPanel`, as {@link MultiEntityCombobox} is. It was an\n * absolute `DropdownPanel`, which an `overflow` ancestor clipped (dev#548) and which on a\n * phone was a 16rem panel between the field and the keyboard; it now has the full-screen\n * sheet (live #200), the portal, the live result count, disabled rows and the search\n * that folds accents (\"zurich\" finds Zürich, DUP-5).\n */\nexport function MultiSelect<V extends string | number = string | number>(props: MultiSelectProps<V>) {\n const {\n options,\n value: valueProp,\n onChange,\n itemLabel,\n placeholder,\n label,\n labels: labelsProp,\n className,\n panelClassName,\n invalid,\n error,\n hint,\n help,\n disabled,\n disabledReason,\n disabledReasonDisplay,\n commit,\n ref,\n id,\n \"aria-label\": ariaLabel,\n // Off `rest` and onto the trigger, which is the combobox a reader meets: `Field`'s\n // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and on\n // the wrapper div the label named nothing and the required state described nothing\n // (DUP-8, refactor audit 2026-10 — Combobox's rule).\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n const value = useMemo<readonly V[]>(() => valueProp ?? [], [valueProp]);\n // The rows as the family's options — the hint is the sublabel, searched like the label.\n const comboOptions = useMemo(\n () =>\n options.map((o): ComboOption<V> => ({ value: o.value, label: o.label, sublabel: o.hint, disabled: o.disabled })),\n [options],\n );\n // Back closes the open list, as it did on `useDropdown` (keksdose live #309).\n const core = useComboboxCore<V>({ options: comboOptions, backCloses: true });\n const { open, setOpen, triggerRef } = core;\n // The core anchors the panel to the trigger and hands focus back to it; the caller's\n // `ref` wants the same element. One callback feeds both.\n const setTrigger = useCallback(\n (el: HTMLButtonElement | null) => {\n triggerRef.current = el;\n assignRef(ref, el);\n },\n [ref, triggerRef],\n );\n // `labels` > the provider's `multiSelect` > English. `all` is resolved apart below:\n // between the instance's and the provider's sits `placeholder`, which has always\n // doubled as the \"nothing picked\" text, and a caller who set it must keep seeing it.\n const text = useKitLabels(\"multiSelect\", DEFAULT_MULTI_SELECT_LABELS, {\n search: labelsProp?.search,\n selectAll: labelsProp?.selectAll,\n clear: labelsProp?.clear,\n selectedCount: labelsProp?.selectedCount ?? itemLabel,\n });\n const common = useKitLabels(\"common\", DEFAULT_COMMON_LABELS);\n\n // The lock — EntityCombobox's: the trigger stays reachable and says why.\n const lock = useLockReason(commit, disabledReason);\n const inert = lock.locked || Boolean(disabled);\n // A lock (or `disabled`) arriving while the list is up closes it — adjusted while\n // rendering, as CountrySelect does, so no frame shows a list that can no longer be used.\n if (inert && open) setOpen(false);\n\n // The caller's ids, the caption, a lock's reason, the error — Select's order.\n const field = useFieldMessages({\n hint,\n help,\n name: label ?? ariaLabel,\n error,\n invalid,\n describedBy: ariaDescribedBy,\n ariaInvalid,\n reasonId: lock.locked && lock.reasonId,\n });\n // One id per instance: `aria-controls` on the trigger names the list while the\n // list is still closed.\n const listboxId = `${useId()}-listbox`;\n\n const valueSet = useMemo(() => new Set<V>(value), [value]);\n const allChecked = options.length > 0 && options.every((o) => valueSet.has(o.value));\n const selectedSummary =\n value.length === 0 || allChecked ? (labelsProp?.all ?? placeholder ?? text.all) : text.selectedCount(value.length);\n\n const toggle = (o: ComboOption<V>) => {\n if (lock.locked) return;\n if (valueSet.has(o.value)) onChange(value.filter((v) => v !== o.value));\n else onChange([...value, o.value]);\n // Stays open, as a pressed row always has: this control answers \"which of these\",\n // which is rarely one of them.\n };\n // A chosen disabled row stays chosen, an unchosen one stays out — the row refuses to be\n // toggled, by a press or by the two actions.\n const selectAll = () => onChange(options.filter((o) => !o.disabled || valueSet.has(o.value)).map((o) => o.value));\n const clearAll = () => onChange(options.filter((o) => o.disabled && valueSet.has(o.value)).map((o) => o.value));\n const canSelectAll = options.some((o) => !o.disabled && !valueSet.has(o.value));\n const canClear = options.some((o) => !o.disabled && valueSet.has(o.value));\n\n const trigger = (\n <button\n ref={setTrigger}\n id={id}\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={lock.locked ? undefined : disabled}\n aria-disabled={lock.locked || undefined}\n // A combobox, not a bare button: this trigger carried `aria-invalid`, which\n // `button` does not support, so a required-and-empty filter painted its rose\n // border and announced nothing (ESLint's `role-supports-aria-props`, the\n // audit's §a11y). The role that describes it is the one that also supports\n // the attribute.\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-haspopup=\"listbox\"\n // The label is a floating <span>, not a <label for>, so without this the\n // trigger announces only its summary — \"3 selected\", with nothing saying\n // three of WHAT. The label AND the summary, because `aria-label` replaces\n // the content rather than adding to it; the same composition its two\n // siblings use, so a form row of pickers reads the same way throughout.\n // A caller's own name wins: two filters labelled \"Accounts\" on one screen have\n // to be told apart, and only the caller knows by what.\n aria-label={ariaLabel ?? (typeof label === \"string\" ? common.fieldValue(label, selectedSummary) : undefined)}\n onClick={() => {\n if (!inert) setOpen((v) => !v);\n }}\n // Down/Up opens the list from the closed trigger, per the APG; Enter and\n // Space already do through the button's own click.\n onKeyDown={(e) => {\n if (inert) return;\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n setOpen(true);\n }\n }}\n aria-invalid={field.isInvalid || undefined}\n aria-required={ariaRequired}\n // On the TRIGGER, where focus lands — the `aria-describedby` a caller put on\n // the component is taken off the wrapper for the same reason.\n aria-describedby={field.describedBy}\n className={cn(\n FIELD_TRIGGER,\n \"pe-9\",\n label !== undefined && FIELD_FLOATING_PAD,\n // FIELD_TRIGGER's hover would still light a trigger nothing can open.\n \"disabled:hover:bg-surface-2\",\n // Dimmed like EntityCombobox's and MultiEntityCombobox's disabled triggers;\n // only the label was, so a disabled MultiSelect read as live (showcase audit).\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n lock.locked && \"cursor-not-allowed opacity-50\",\n field.isInvalid && FIELD_INVALID,\n )}\n >\n {/* The summary IS the field's value, so it inherits FIELD_BASE's ink rather\n than restating a lighter one (Keksdose dev#477). An ellipsis at Normal; from\n Large up it wraps and the trigger grows, as DatePicker's value does (text\n size §10.17, round 0.34 T1). */}\n <span className=\"truncate-until-large\">{selectedSummary}</span>\n {!inert && <FieldChevron />}\n </button>\n );\n\n return (\n // `rest` dresses the wrapper; the name belongs to the trigger below it, which is\n // the thing with a role. Spread FIRST so the ARIA the trigger composes, and the\n // handlers that open the list, cannot be replaced by accident from outside.\n <div {...rest} className={cn(\"relative\", className)}>\n {label !== undefined && field.labelHint === undefined && (\n <FieldLabel className={inert ? \"opacity-50\" : undefined}>{label}</FieldLabel>\n )}\n <EndHintRow hint={label === undefined ? field.labelHint : undefined}>\n {/* Button's frame: the line under the trigger, or the Tooltip with a hidden copy\n for `aria-describedby`. */}\n <LockFrame lock={lock} display={disabledReasonDisplay}>\n {trigger}\n </LockFrame>\n </EndHintRow>\n {/* With a \"?\" the label shares the top strip with it — after the trigger, so the\n \"?\" follows the control in the tab order, as on Input and Select. */}\n {label !== undefined && field.labelHint !== undefined && (\n <StaticLabelRow hint={field.labelHint}>\n <FieldLabel className={cn(LABEL_IN_ROW, inert && \"opacity-50\")}>{label}</FieldLabel>\n </StaticLabelRow>\n )}\n {field.below}\n <ComboboxPanel\n core={core}\n listboxId={listboxId}\n multi\n // On a phone the list is a full-screen sheet, which needs the field's own name\n // to say what it asks for (live #200).\n sheetTitle={label ?? placeholder}\n searchPlaceholder={text.search}\n isSelected={(v) => valueSet.has(v)}\n onChoose={toggle}\n showCreate={false}\n onCreate={() => {}}\n createContent={null}\n // At least the trigger's width and 16rem, growing with the rows to 32rem: the\n // hints are why it was widened at all (keksdose #147).\n width={{ minRem: 16, fit: true, maxRem: 32 }}\n className={panelClassName}\n // Reached by Tab from the search box (the panel's rows are not tab stops), and\n // `aria-disabled` with a guarded click rather than `disabled`: a pressed \"Clear\"\n // with nothing left to clear disabled itself under the focus, which dropped it to\n // <body> — JumpCalendar's arrows are the rule (refactor audit 2026-10-30, INP-9).\n toolbar={\n <div className=\"flex items-center justify-between gap-2 px-2 py-1.5 text-xs\">\n <button\n type=\"button\"\n onClick={() => {\n if (canSelectAll) selectAll();\n }}\n aria-disabled={!canSelectAll || undefined}\n className={TOOLBAR_BUTTON}\n >\n {text.selectAll}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n if (canClear) clearAll();\n }}\n aria-disabled={!canClear || undefined}\n className={TOOLBAR_BUTTON}\n >\n {text.clear}\n </button>\n </div>\n }\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -5,7 +5,7 @@ import { CalculatorButton } from "./calculator.js";
5
5
  import { NumberPadSheet } from "./numpad-sheet.js";
6
6
  import { FIELD_BASE, FIELD_DISPLAY, FIELD_INVALID, FLOATING_INPUT_CLASS, FloatingField } from "./field-base.js";
7
7
  import { CURRENCIES } from "../currencies.js";
8
- import { FieldCaption, LockedReason, mergeRefs } from "./field-parts.js";
8
+ import { FieldCaption, LockFrame, mergeRefs } from "./field-parts.js";
9
9
  import { useEndWidth, useNumericField } from "./numeric-field.js";
10
10
  import { cn } from "../lib/cn.js";
11
11
  import { commitExpression, formatResult } from "../lib/calc.js";
@@ -201,7 +201,7 @@ function NumberInput(props) {
201
201
  ]
202
202
  }
203
203
  );
204
- const box = locked ? /* @__PURE__ */ jsx(LockedReason, { lock, display: disabledReasonDisplay, className: cn("block w-full", className), children: field }) : field;
204
+ const box = /* @__PURE__ */ jsx(LockFrame, { lock, display: disabledReasonDisplay, className: cn("w-full", className), children: field });
205
205
  const data = dataAttributes(props);
206
206
  if (numeric.hint.captionText === void 0 && numeric.errorEl === null && !reserve) {
207
207
  return Object.keys(data).length > 0 ? /* @__PURE__ */ jsx("div", { ...data, children: box }) : box;