@eifi1/ui-kit 0.53.0 → 0.55.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 (902) hide show
  1. package/README.md +96 -82
  2. package/dist/account/delete-account-setting.d.ts +3 -2
  3. package/dist/account/delete-account-setting.js.map +1 -1
  4. package/dist/admin/plan-change.js +2 -2
  5. package/dist/admin/plan-change.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +6 -0
  7. package/dist/admin/user-roster.js +4 -0
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/auth-errors.d.ts +36 -0
  10. package/dist/auth/auth-errors.js +20 -0
  11. package/dist/auth/auth-errors.js.map +1 -1
  12. package/dist/billing/billing-labels.d.ts +26 -0
  13. package/dist/billing/billing-labels.js +9 -0
  14. package/dist/billing/billing-labels.js.map +1 -1
  15. package/dist/billing/subscription-standing.d.ts +61 -0
  16. package/dist/billing/subscription-standing.js +51 -0
  17. package/dist/billing/subscription-standing.js.map +1 -0
  18. package/dist/billing/usage-meter.d.ts +52 -0
  19. package/dist/billing/usage-meter.js +52 -0
  20. package/dist/billing/usage-meter.js.map +1 -0
  21. package/dist/chart.d.ts +1 -0
  22. package/dist/chart.js +4 -0
  23. package/dist/chart.js.map +1 -1
  24. package/dist/components/alert-banner.js +2 -3
  25. package/dist/components/alert-banner.js.map +1 -1
  26. package/dist/components/amount-input.d.ts +2 -1
  27. package/dist/components/amount-input.js +3 -15
  28. package/dist/components/amount-input.js.map +1 -1
  29. package/dist/components/autocomplete.js +2 -2
  30. package/dist/components/autocomplete.js.map +1 -1
  31. package/dist/components/bulk-action-bar.js +2 -1
  32. package/dist/components/bulk-action-bar.js.map +1 -1
  33. package/dist/components/button.d.ts +35 -1
  34. package/dist/components/button.js +5 -9
  35. package/dist/components/button.js.map +1 -1
  36. package/dist/components/calendar-heatmap.js +6 -4
  37. package/dist/components/calendar-heatmap.js.map +1 -1
  38. package/dist/components/camera-button.d.ts +89 -0
  39. package/dist/components/camera-button.js +122 -0
  40. package/dist/components/camera-button.js.map +1 -0
  41. package/dist/components/camera-capture.d.ts +66 -0
  42. package/dist/components/camera-capture.js +190 -0
  43. package/dist/components/camera-capture.js.map +1 -0
  44. package/dist/components/camera-labels.d.ts +20 -0
  45. package/dist/components/camera-labels.js +15 -0
  46. package/dist/components/camera-labels.js.map +1 -0
  47. package/dist/components/chart-edge.d.ts +9 -0
  48. package/dist/components/chart-edge.js +14 -3
  49. package/dist/components/chart-edge.js.map +1 -1
  50. package/dist/components/chart-zoom-core.js +34 -29
  51. package/dist/components/chart-zoom-core.js.map +1 -1
  52. package/dist/components/checkbox-group.js +1 -1
  53. package/dist/components/checkbox-group.js.map +1 -1
  54. package/dist/components/combobox-core.js +3 -2
  55. package/dist/components/combobox-core.js.map +1 -1
  56. package/dist/components/combobox.js +2 -2
  57. package/dist/components/combobox.js.map +1 -1
  58. package/dist/components/consent-again-notice-labels.d.ts +25 -0
  59. package/dist/components/consent-again-notice-labels.js +15 -0
  60. package/dist/components/consent-again-notice-labels.js.map +1 -0
  61. package/dist/components/consent-again-notice.d.ts +85 -0
  62. package/dist/components/consent-again-notice.js +94 -0
  63. package/dist/components/consent-again-notice.js.map +1 -0
  64. package/dist/components/country-select.js +2 -7
  65. package/dist/components/country-select.js.map +1 -1
  66. package/dist/components/currency-option-list.d.ts +2 -1
  67. package/dist/components/currency-option-list.js +10 -3
  68. package/dist/components/currency-option-list.js.map +1 -1
  69. package/dist/components/currency-select.d.ts +3 -1
  70. package/dist/components/currency-select.js +2 -2
  71. package/dist/components/currency-select.js.map +1 -1
  72. package/dist/components/danger-confirm.d.ts +22 -1
  73. package/dist/components/danger-confirm.js +52 -30
  74. package/dist/components/danger-confirm.js.map +1 -1
  75. package/dist/components/data-table-column-settings.js +2 -2
  76. package/dist/components/data-table-column-settings.js.map +1 -1
  77. package/dist/components/data-table-filter-popover.d.ts +2 -1
  78. package/dist/components/data-table-filter-popover.js +3 -2
  79. package/dist/components/data-table-filter-popover.js.map +1 -1
  80. package/dist/components/data-table-filters.d.ts +33 -3
  81. package/dist/components/data-table-filters.js +6 -4
  82. package/dist/components/data-table-filters.js.map +1 -1
  83. package/dist/components/data-table-fit.js.map +1 -1
  84. package/dist/components/data-table-virtual.js +13 -3
  85. package/dist/components/data-table-virtual.js.map +1 -1
  86. package/dist/components/date-field.js +2 -2
  87. package/dist/components/date-field.js.map +1 -1
  88. package/dist/components/date-picker.js +2 -2
  89. package/dist/components/date-picker.js.map +1 -1
  90. package/dist/components/date-range-picker.js +2 -6
  91. package/dist/components/date-range-picker.js.map +1 -1
  92. package/dist/components/dialog-frame.d.ts +5 -1
  93. package/dist/components/dialog-frame.js +6 -2
  94. package/dist/components/dialog-frame.js.map +1 -1
  95. package/dist/components/dropdown.js +7 -3
  96. package/dist/components/dropdown.js.map +1 -1
  97. package/dist/components/entity-combobox.js +2 -2
  98. package/dist/components/entity-combobox.js.map +1 -1
  99. package/dist/components/field-parts.d.ts +108 -6
  100. package/dist/components/field-parts.js +59 -6
  101. package/dist/components/field-parts.js.map +1 -1
  102. package/dist/components/field-suggestion-labels.d.ts +2 -0
  103. package/dist/components/field-suggestion-labels.js +10 -0
  104. package/dist/components/field-suggestion-labels.js.map +1 -0
  105. package/dist/components/field.js +1 -1
  106. package/dist/components/field.js.map +1 -1
  107. package/dist/components/file-button.d.ts +1 -1
  108. package/dist/components/file-button.js.map +1 -1
  109. package/dist/components/floating-panel.d.ts +7 -6
  110. package/dist/components/floating-panel.js +3 -17
  111. package/dist/components/floating-panel.js.map +1 -1
  112. package/dist/components/form-actions.d.ts +3 -2
  113. package/dist/components/form-actions.js.map +1 -1
  114. package/dist/components/hover-menu.js +3 -2
  115. package/dist/components/hover-menu.js.map +1 -1
  116. package/dist/components/icon-button.d.ts +23 -5
  117. package/dist/components/icon-button.js +6 -8
  118. package/dist/components/icon-button.js.map +1 -1
  119. package/dist/components/inline-confirm.js +2 -1
  120. package/dist/components/inline-confirm.js.map +1 -1
  121. package/dist/components/inline-entity-combobox.js +2 -2
  122. package/dist/components/inline-entity-combobox.js.map +1 -1
  123. package/dist/components/input.js +2 -2
  124. package/dist/components/input.js.map +1 -1
  125. package/dist/components/jump-calendar.js +3 -3
  126. package/dist/components/jump-calendar.js.map +1 -1
  127. package/dist/components/legal.d.ts +13 -1
  128. package/dist/components/legal.js +12 -0
  129. package/dist/components/legal.js.map +1 -1
  130. package/dist/components/legended-series-chart.d.ts +80 -0
  131. package/dist/components/legended-series-chart.js +70 -0
  132. package/dist/components/legended-series-chart.js.map +1 -0
  133. package/dist/components/lightbox.js +2 -1
  134. package/dist/components/lightbox.js.map +1 -1
  135. package/dist/components/mini-calendar.js +16 -7
  136. package/dist/components/mini-calendar.js.map +1 -1
  137. package/dist/components/modal.d.ts +48 -0
  138. package/dist/components/modal.js +26 -6
  139. package/dist/components/modal.js.map +1 -1
  140. package/dist/components/month-picker.d.ts +1 -1
  141. package/dist/components/month-picker.js +6 -6
  142. package/dist/components/month-picker.js.map +1 -1
  143. package/dist/components/multi-entity-combobox.js +2 -2
  144. package/dist/components/multi-entity-combobox.js.map +1 -1
  145. package/dist/components/multi-select.js +2 -6
  146. package/dist/components/multi-select.js.map +1 -1
  147. package/dist/components/number-input.js +2 -2
  148. package/dist/components/number-input.js.map +1 -1
  149. package/dist/components/page-header-layout.d.ts +18 -0
  150. package/dist/components/page-header-layout.js +23 -0
  151. package/dist/components/page-header-layout.js.map +1 -0
  152. package/dist/components/page-header-skeleton.d.ts +41 -0
  153. package/dist/components/page-header-skeleton.js +52 -0
  154. package/dist/components/page-header-skeleton.js.map +1 -0
  155. package/dist/components/page-header.js +4 -17
  156. package/dist/components/page-header.js.map +1 -1
  157. package/dist/components/popover.js +7 -2
  158. package/dist/components/popover.js.map +1 -1
  159. package/dist/components/release-notes-labels.d.ts +16 -0
  160. package/dist/components/release-notes-labels.js +18 -0
  161. package/dist/components/release-notes-labels.js.map +1 -0
  162. package/dist/components/release-notes.d.ts +65 -0
  163. package/dist/components/release-notes.js +65 -0
  164. package/dist/components/release-notes.js.map +1 -0
  165. package/dist/components/select.js +2 -2
  166. package/dist/components/select.js.map +1 -1
  167. package/dist/components/series-chart-labels.d.ts +9 -0
  168. package/dist/components/series-chart-labels.js +3 -1
  169. package/dist/components/series-chart-labels.js.map +1 -1
  170. package/dist/components/series-chart.d.ts +5 -0
  171. package/dist/components/series-chart.js +89 -36
  172. package/dist/components/series-chart.js.map +1 -1
  173. package/dist/components/stat-tile.d.ts +21 -2
  174. package/dist/components/stat-tile.js +4 -2
  175. package/dist/components/stat-tile.js.map +1 -1
  176. package/dist/components/toggle-group.js +3 -2
  177. package/dist/components/toggle-group.js.map +1 -1
  178. package/dist/components/treemap-layout.js.map +1 -1
  179. package/dist/components/treemap.d.ts +17 -15
  180. package/dist/components/treemap.js +167 -85
  181. package/dist/components/treemap.js.map +1 -1
  182. package/dist/currencies.d.ts +16 -2
  183. package/dist/currencies.js +2 -1
  184. package/dist/currencies.js.map +1 -1
  185. package/dist/eslint/index.js +1 -1
  186. package/dist/eslint/role-utilities.d.ts +2 -2
  187. package/dist/eslint/role-utilities.js.map +1 -1
  188. package/dist/feedback/feedback-context-labels.js +2 -1
  189. package/dist/feedback/feedback-context-labels.js.map +1 -1
  190. package/dist/feedback/feedback-context.d.ts +19 -3
  191. package/dist/feedback/feedback-context.js +12 -1
  192. package/dist/feedback/feedback-context.js.map +1 -1
  193. package/dist/feedback/feedback-diagnostics.d.ts +17 -0
  194. package/dist/feedback/feedback-diagnostics.js +27 -0
  195. package/dist/feedback/feedback-diagnostics.js.map +1 -0
  196. package/dist/feedback/feedback-dialog.d.ts +10 -0
  197. package/dist/feedback/feedback-dialog.js +6 -4
  198. package/dist/feedback/feedback-dialog.js.map +1 -1
  199. package/dist/feedback/feedback-inbox-page.d.ts +260 -0
  200. package/dist/feedback/feedback-inbox-page.js +207 -0
  201. package/dist/feedback/feedback-inbox-page.js.map +1 -0
  202. package/dist/feedback/feedback-record.d.ts +5 -0
  203. package/dist/feedback/feedback-record.js.map +1 -1
  204. package/dist/feedback/feedback-row-detail-labels.js +1 -0
  205. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  206. package/dist/feedback/feedback-row-detail.d.ts +3 -0
  207. package/dist/feedback/feedback-row-detail.js +3 -0
  208. package/dist/feedback/feedback-row-detail.js.map +1 -1
  209. package/dist/feedback/feedback-submit-dialog.d.ts +5 -1
  210. package/dist/feedback/feedback-submit-dialog.js +15 -3
  211. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  212. package/dist/feedback/feedback-submit.d.ts +30 -2
  213. package/dist/feedback/feedback-submit.js +5 -2
  214. package/dist/feedback/feedback-submit.js.map +1 -1
  215. package/dist/feedback/feedback-vocabulary.js +2 -2
  216. package/dist/feedback/feedback-vocabulary.js.map +1 -1
  217. package/dist/feedback.d.ts +1 -0
  218. package/dist/feedback.js +6 -0
  219. package/dist/feedback.js.map +1 -1
  220. package/dist/hooks/use-debounce.d.ts +32 -5
  221. package/dist/hooks/use-debounce.js +6 -5
  222. package/dist/hooks/use-debounce.js.map +1 -1
  223. package/dist/hooks/use-file-picker.d.ts +3 -1
  224. package/dist/hooks/use-file-picker.js.map +1 -1
  225. package/dist/hooks/use-focus-trap.d.ts +19 -3
  226. package/dist/hooks/use-focus-trap.js +15 -5
  227. package/dist/hooks/use-focus-trap.js.map +1 -1
  228. package/dist/hooks/use-has-camera.d.ts +36 -0
  229. package/dist/hooks/use-has-camera.js +53 -0
  230. package/dist/hooks/use-has-camera.js.map +1 -0
  231. package/dist/hooks/use-hotkey.js +2 -1
  232. package/dist/hooks/use-hotkey.js.map +1 -1
  233. package/dist/hooks/use-overlay-history.d.ts +6 -1
  234. package/dist/hooks/use-overlay-history.js +10 -2
  235. package/dist/hooks/use-overlay-history.js.map +1 -1
  236. package/dist/i18n/defaults.js +11 -1
  237. package/dist/i18n/defaults.js.map +1 -1
  238. package/dist/i18n/german.js +78 -4
  239. package/dist/i18n/german.js.map +1 -1
  240. package/dist/i18n/kit-labels.d.ts +20 -0
  241. package/dist/i18n/kit-labels.js.map +1 -1
  242. package/dist/i18n/locales/en.js +95 -5
  243. package/dist/i18n/locales/en.js.map +3 -3
  244. package/dist/i18n/locales/es.js +74 -4
  245. package/dist/i18n/locales/es.js.map +1 -1
  246. package/dist/i18n/locales/fr.js +75 -3
  247. package/dist/i18n/locales/fr.js.map +1 -1
  248. package/dist/i18n/locales/hu.js +77 -4
  249. package/dist/i18n/locales/hu.js.map +1 -1
  250. package/dist/i18n/locales/it.js +77 -4
  251. package/dist/i18n/locales/it.js.map +1 -1
  252. package/dist/i18n/locales/zh.js +74 -4
  253. package/dist/i18n/locales/zh.js.map +1 -1
  254. package/dist/i18n/review.js +22 -1
  255. package/dist/i18n/review.js.map +1 -1
  256. package/dist/index.d.ts +27 -8
  257. package/dist/index.js +41 -2
  258. package/dist/index.js.map +1 -1
  259. package/dist/landing/landing-labels.d.ts +8 -0
  260. package/dist/landing/landing-labels.js +2 -1
  261. package/dist/landing/landing-labels.js.map +1 -1
  262. package/dist/lib/changelog.d.ts +130 -0
  263. package/dist/lib/changelog.js +137 -0
  264. package/dist/lib/changelog.js.map +1 -0
  265. package/dist/lib/clipboard-minus.js +2 -2
  266. package/dist/lib/clipboard-minus.js.map +1 -1
  267. package/dist/lib/dates.d.ts +36 -4
  268. package/dist/lib/dates.js +49 -13
  269. package/dist/lib/dates.js.map +1 -1
  270. package/dist/lib/editable-content.d.ts +33 -0
  271. package/dist/lib/editable-content.js +15 -0
  272. package/dist/lib/editable-content.js.map +1 -0
  273. package/dist/lib/focus-left.d.ts +47 -1
  274. package/dist/lib/focus-left.js +11 -2
  275. package/dist/lib/focus-left.js.map +1 -1
  276. package/dist/lib/format.d.ts +37 -2
  277. package/dist/lib/format.js +25 -1
  278. package/dist/lib/format.js.map +1 -1
  279. package/dist/lib/intl-cache.d.ts +4 -2
  280. package/dist/lib/intl-cache.js.map +1 -1
  281. package/dist/lib/server-wake.d.ts +49 -9
  282. package/dist/lib/server-wake.js +25 -14
  283. package/dist/lib/server-wake.js.map +1 -1
  284. package/dist/lib/session-refresh.d.ts +53 -0
  285. package/dist/lib/session-refresh.js +27 -1
  286. package/dist/lib/session-refresh.js.map +1 -1
  287. package/dist/lib/translation-review.d.ts +26 -0
  288. package/dist/lib/translation-review.js +14 -0
  289. package/dist/lib/translation-review.js.map +1 -1
  290. package/dist/rhf/fields.js +3 -3
  291. package/dist/rhf/fields.js.map +1 -1
  292. package/dist/shell/app-shell.d.ts +25 -1
  293. package/dist/shell/app-shell.js +3 -1
  294. package/dist/shell/app-shell.js.map +1 -1
  295. package/dist/shell/dev-menu.d.ts +3 -0
  296. package/dist/shell/dev-menu.js +4 -2
  297. package/dist/shell/dev-menu.js.map +1 -1
  298. package/dist/shell/held-changes-banner-labels.d.ts +23 -0
  299. package/dist/shell/held-changes-banner-labels.js +14 -0
  300. package/dist/shell/held-changes-banner-labels.js.map +1 -0
  301. package/dist/shell/held-changes-banner.d.ts +75 -0
  302. package/dist/shell/held-changes-banner.js +106 -0
  303. package/dist/shell/held-changes-banner.js.map +1 -0
  304. package/dist/shell/sync-status-indicator.d.ts +27 -12
  305. package/dist/shell/sync-status-indicator.js +13 -4
  306. package/dist/shell/sync-status-indicator.js.map +1 -1
  307. package/dist/shell/top-bar-parts-labels.d.ts +5 -0
  308. package/dist/shell/top-bar-parts-labels.js +3 -1
  309. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  310. package/dist/shell.d.ts +2 -0
  311. package/dist/shell.js +3 -0
  312. package/dist/shell.js.map +1 -1
  313. package/dist/sync/engine.d.ts +11 -2
  314. package/dist/sync/engine.js +13 -5
  315. package/dist/sync/engine.js.map +1 -1
  316. package/dist/sync/hooks.d.ts +3 -2
  317. package/dist/sync/hooks.js +4 -2
  318. package/dist/sync/hooks.js.map +1 -1
  319. package/dist/testing/css-variables.generated.d.ts +2 -2
  320. package/dist/testing/css-variables.generated.js +1 -1
  321. package/dist/testing/css-variables.generated.js.map +1 -1
  322. package/dist/theme/chart-palette.d.ts +18 -0
  323. package/dist/theme/chart-palette.js +17 -1
  324. package/dist/theme/chart-palette.js.map +1 -1
  325. package/dist/theme/contrast-tokens.d.ts +3 -3
  326. package/dist/theme/contrast-tokens.js.map +1 -1
  327. package/dist/theme/contrast.d.ts +4 -4
  328. package/dist/theme/contrast.js.map +1 -1
  329. package/dist/tour/tour-overlay.js +2 -1
  330. package/dist/tour/tour-overlay.js.map +1 -1
  331. package/package.json +5 -9
  332. package/tokens.css +19 -54
  333. package/src/account/account-parts.ts +0 -47
  334. package/src/account/account-settings-labels.ts +0 -240
  335. package/src/account/account-settings.tsx +0 -698
  336. package/src/account/data-export-setting-labels.ts +0 -20
  337. package/src/account/data-export-setting.tsx +0 -200
  338. package/src/account/delete-account-setting-labels.ts +0 -33
  339. package/src/account/delete-account-setting.tsx +0 -341
  340. package/src/account/email-change-setting-labels.ts +0 -31
  341. package/src/account/email-change-setting.tsx +0 -443
  342. package/src/account/passkeys-setting.tsx +0 -445
  343. package/src/account/sessions-setting-labels.ts +0 -24
  344. package/src/account/sessions-setting.tsx +0 -283
  345. package/src/admin/admin-action-confirm-labels.ts +0 -19
  346. package/src/admin/admin-action-confirm.tsx +0 -509
  347. package/src/admin/admin-action-log-labels.ts +0 -83
  348. package/src/admin/admin-action-log.tsx +0 -464
  349. package/src/admin/admin-area-labels.ts +0 -114
  350. package/src/admin/admin-forbidden.tsx +0 -75
  351. package/src/admin/admin-overview.tsx +0 -258
  352. package/src/admin/admin-parts.ts +0 -68
  353. package/src/admin/admin-sections.tsx +0 -208
  354. package/src/admin/admin-tool-links.tsx +0 -90
  355. package/src/admin/invitations-panel-labels.ts +0 -31
  356. package/src/admin/invitations-panel.tsx +0 -516
  357. package/src/admin/mail-test-card-labels.ts +0 -19
  358. package/src/admin/mail-test-card.tsx +0 -189
  359. package/src/admin/plan-change-labels.ts +0 -5
  360. package/src/admin/plan-change.tsx +0 -474
  361. package/src/admin/reviewer-scope-editor-labels.ts +0 -16
  362. package/src/admin/reviewer-scope-editor.tsx +0 -224
  363. package/src/admin/role-select-labels.ts +0 -16
  364. package/src/admin/role-select.tsx +0 -282
  365. package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
  366. package/src/admin/transfer-ownership-dialog.tsx +0 -162
  367. package/src/admin/user-roster-labels.ts +0 -17
  368. package/src/admin/user-roster.tsx +0 -559
  369. package/src/auth/accept-invitation-labels.ts +0 -21
  370. package/src/auth/accept-invitation.tsx +0 -210
  371. package/src/auth/auth-errors-labels.ts +0 -23
  372. package/src/auth/auth-errors.ts +0 -370
  373. package/src/auth/complete-name-dialog-labels.ts +0 -16
  374. package/src/auth/complete-name-dialog.tsx +0 -253
  375. package/src/auth/email-tag.ts +0 -75
  376. package/src/auth/forgot-password-form-labels.ts +0 -20
  377. package/src/auth/forgot-password-form.tsx +0 -221
  378. package/src/auth/form-rules.ts +0 -40
  379. package/src/auth/new-password-fields.tsx +0 -85
  380. package/src/auth/not-found-page-labels.ts +0 -12
  381. package/src/auth/not-found-page.tsx +0 -95
  382. package/src/auth/register-form-labels.ts +0 -31
  383. package/src/auth/register-form.tsx +0 -456
  384. package/src/auth/reset-password-form-labels.ts +0 -27
  385. package/src/auth/reset-password-form.tsx +0 -341
  386. package/src/auth/sign-in-form-labels.ts +0 -44
  387. package/src/auth/sign-in-form.tsx +0 -858
  388. package/src/auth/status-parts.tsx +0 -132
  389. package/src/auth/verify-email-labels.ts +0 -26
  390. package/src/auth/verify-email.tsx +0 -445
  391. package/src/billing/billing-banner.tsx +0 -258
  392. package/src/billing/billing-labels.ts +0 -381
  393. package/src/billing/billing-lock.ts +0 -108
  394. package/src/billing/billing-overview.ts +0 -157
  395. package/src/billing/billing-standing.ts +0 -80
  396. package/src/billing/cancel-request.ts +0 -97
  397. package/src/billing/cancel-subscription.tsx +0 -98
  398. package/src/billing/checkout-processing.ts +0 -394
  399. package/src/billing/checkout-return.ts +0 -90
  400. package/src/billing/pay-page.ts +0 -330
  401. package/src/billing/plan-card.tsx +0 -562
  402. package/src/billing/plan-change-labels.ts +0 -66
  403. package/src/billing/plan-limit-notice.tsx +0 -148
  404. package/src/billing/plan-limit-toast.tsx +0 -90
  405. package/src/billing/plan-limit.ts +0 -52
  406. package/src/billing/plan-price.ts +0 -98
  407. package/src/billing/plan-switch.tsx +0 -216
  408. package/src/billing/provider-page.ts +0 -84
  409. package/src/billing/subscription-actions.tsx +0 -247
  410. package/src/billing/subscription-status-chip.tsx +0 -95
  411. package/src/chart.ts +0 -103
  412. package/src/components/account-chips-labels.ts +0 -16
  413. package/src/components/account-chips.tsx +0 -474
  414. package/src/components/alert-banner.tsx +0 -670
  415. package/src/components/amount-input.tsx +0 -815
  416. package/src/components/appearance-labels.ts +0 -63
  417. package/src/components/authed-image-labels.ts +0 -11
  418. package/src/components/authed-image.tsx +0 -209
  419. package/src/components/autocomplete.tsx +0 -645
  420. package/src/components/breadcrumbs-labels.ts +0 -10
  421. package/src/components/breadcrumbs.tsx +0 -205
  422. package/src/components/bulk-action-bar-labels.ts +0 -11
  423. package/src/components/bulk-action-bar.tsx +0 -443
  424. package/src/components/button-group.tsx +0 -223
  425. package/src/components/button-look.ts +0 -256
  426. package/src/components/button.tsx +0 -339
  427. package/src/components/calculator.tsx +0 -341
  428. package/src/components/calendar-heatmap-labels.ts +0 -15
  429. package/src/components/calendar-heatmap.tsx +0 -594
  430. package/src/components/card.tsx +0 -377
  431. package/src/components/chart-edge.ts +0 -106
  432. package/src/components/chart-figure.tsx +0 -35
  433. package/src/components/chart-height.ts +0 -30
  434. package/src/components/chart-zoom-core.tsx +0 -627
  435. package/src/components/chart-zoom.tsx +0 -22
  436. package/src/components/chart.tsx +0 -554
  437. package/src/components/checkbox-group.tsx +0 -247
  438. package/src/components/checkbox.tsx +0 -346
  439. package/src/components/chip-input.tsx +0 -370
  440. package/src/components/chip-labels.ts +0 -13
  441. package/src/components/chip.tsx +0 -843
  442. package/src/components/choice-card.tsx +0 -544
  443. package/src/components/column-mapper-labels.ts +0 -38
  444. package/src/components/column-mapper.tsx +0 -655
  445. package/src/components/combobox-core.tsx +0 -1321
  446. package/src/components/combobox-suggestions.tsx +0 -131
  447. package/src/components/combobox.tsx +0 -716
  448. package/src/components/confirm-context.ts +0 -7
  449. package/src/components/confirm-dialog-labels.ts +0 -13
  450. package/src/components/confirm-dialog.tsx +0 -341
  451. package/src/components/copy-button-labels.ts +0 -13
  452. package/src/components/copy-button.tsx +0 -192
  453. package/src/components/country-select-labels.ts +0 -11
  454. package/src/components/country-select.tsx +0 -539
  455. package/src/components/currency-option-list.tsx +0 -180
  456. package/src/components/currency-select.tsx +0 -341
  457. package/src/components/danger-confirm-labels.ts +0 -20
  458. package/src/components/danger-confirm.tsx +0 -934
  459. package/src/components/data-table-cells.tsx +0 -118
  460. package/src/components/data-table-column-settings.tsx +0 -198
  461. package/src/components/data-table-filter-popover.tsx +0 -281
  462. package/src/components/data-table-filters.ts +0 -287
  463. package/src/components/data-table-fit.ts +0 -301
  464. package/src/components/data-table-groups.tsx +0 -288
  465. package/src/components/data-table-head.tsx +0 -280
  466. package/src/components/data-table-labels.ts +0 -198
  467. package/src/components/data-table-leading-row.tsx +0 -58
  468. package/src/components/data-table-mobile.tsx +0 -1117
  469. package/src/components/data-table-pagination.tsx +0 -202
  470. package/src/components/data-table-resize.ts +0 -188
  471. package/src/components/data-table-row-actions.tsx +0 -302
  472. package/src/components/data-table-row.tsx +0 -496
  473. package/src/components/data-table-selection.ts +0 -71
  474. package/src/components/data-table-shared.tsx +0 -287
  475. package/src/components/data-table-sort.ts +0 -163
  476. package/src/components/data-table-virtual.ts +0 -576
  477. package/src/components/data-table.tsx +0 -2598
  478. package/src/components/date-field.tsx +0 -684
  479. package/src/components/date-format-setting.tsx +0 -64
  480. package/src/components/date-picker.tsx +0 -404
  481. package/src/components/date-range-picker.tsx +0 -518
  482. package/src/components/description-list-labels.ts +0 -9
  483. package/src/components/description-list.tsx +0 -386
  484. package/src/components/dialog-frame-labels.ts +0 -7
  485. package/src/components/dialog-frame.tsx +0 -276
  486. package/src/components/disclosure.tsx +0 -476
  487. package/src/components/dropdown.tsx +0 -461
  488. package/src/components/empty-state.tsx +0 -136
  489. package/src/components/entity-combobox.tsx +0 -422
  490. package/src/components/error-boundary-labels.ts +0 -20
  491. package/src/components/error-boundary.tsx +0 -675
  492. package/src/components/facing-pair.ts +0 -93
  493. package/src/components/field-base-labels.ts +0 -20
  494. package/src/components/field-base.tsx +0 -656
  495. package/src/components/field-parts.tsx +0 -825
  496. package/src/components/field-strip.tsx +0 -129
  497. package/src/components/field.tsx +0 -206
  498. package/src/components/file-button.tsx +0 -295
  499. package/src/components/file-dropzone.tsx +0 -559
  500. package/src/components/floating-panel-labels.ts +0 -10
  501. package/src/components/floating-panel.tsx +0 -828
  502. package/src/components/focus-ring.ts +0 -16
  503. package/src/components/form-actions-labels.ts +0 -11
  504. package/src/components/form-actions.tsx +0 -578
  505. package/src/components/full-bleed-dialog.tsx +0 -294
  506. package/src/components/grouped-picker.tsx +0 -184
  507. package/src/components/help-popover.tsx +0 -117
  508. package/src/components/hover-menu.tsx +0 -343
  509. package/src/components/iban-input-labels.ts +0 -14
  510. package/src/components/iban-input.tsx +0 -236
  511. package/src/components/icon-button.tsx +0 -959
  512. package/src/components/icon-picker-labels.ts +0 -13
  513. package/src/components/icon-picker.tsx +0 -232
  514. package/src/components/image-grid-labels.ts +0 -11
  515. package/src/components/image-grid.tsx +0 -202
  516. package/src/components/inline-confirm.tsx +0 -116
  517. package/src/components/inline-edit-field-labels.ts +0 -11
  518. package/src/components/inline-edit-field.tsx +0 -552
  519. package/src/components/inline-entity-combobox.tsx +0 -849
  520. package/src/components/input.tsx +0 -394
  521. package/src/components/jump-calendar.tsx +0 -265
  522. package/src/components/language-select.tsx +0 -93
  523. package/src/components/legal-labels.ts +0 -105
  524. package/src/components/legal-page.tsx +0 -711
  525. package/src/components/legal.tsx +0 -668
  526. package/src/components/lightbox-labels.ts +0 -18
  527. package/src/components/lightbox.tsx +0 -399
  528. package/src/components/line-items-labels.ts +0 -14
  529. package/src/components/line-items.tsx +0 -602
  530. package/src/components/list-labels.ts +0 -12
  531. package/src/components/list.tsx +0 -870
  532. package/src/components/loading-state.tsx +0 -78
  533. package/src/components/measured-grid-labels.ts +0 -25
  534. package/src/components/measured-grid.tsx +0 -837
  535. package/src/components/menu-item.tsx +0 -241
  536. package/src/components/mini-calendar-labels.ts +0 -17
  537. package/src/components/mini-calendar.tsx +0 -789
  538. package/src/components/modal.tsx +0 -505
  539. package/src/components/money-draft.ts +0 -83
  540. package/src/components/money-field.tsx +0 -76
  541. package/src/components/month-grid.tsx +0 -229
  542. package/src/components/month-picker-labels.ts +0 -22
  543. package/src/components/month-picker.tsx +0 -1087
  544. package/src/components/multi-entity-combobox.tsx +0 -369
  545. package/src/components/multi-select.tsx +0 -376
  546. package/src/components/nav-pills.tsx +0 -198
  547. package/src/components/number-field.tsx +0 -327
  548. package/src/components/number-input.tsx +0 -558
  549. package/src/components/numeric-field.ts +0 -169
  550. package/src/components/numpad-sheet.tsx +0 -285
  551. package/src/components/one-time-code-input.tsx +0 -270
  552. package/src/components/page-contents-labels.ts +0 -9
  553. package/src/components/page-contents.tsx +0 -282
  554. package/src/components/page-header-context.ts +0 -8
  555. package/src/components/page-header.tsx +0 -298
  556. package/src/components/password-strength-labels.ts +0 -22
  557. package/src/components/password-strength.tsx +0 -241
  558. package/src/components/phone-input-labels.ts +0 -10
  559. package/src/components/phone-input.tsx +0 -282
  560. package/src/components/picker-sheet.tsx +0 -330
  561. package/src/components/pie-chart-labels.ts +0 -40
  562. package/src/components/pie-chart.tsx +0 -480
  563. package/src/components/popover-labels.ts +0 -14
  564. package/src/components/popover.tsx +0 -268
  565. package/src/components/progress-bar-labels.ts +0 -10
  566. package/src/components/progress-bar.tsx +0 -479
  567. package/src/components/qr-code.tsx +0 -76
  568. package/src/components/reauth-dialog-labels.ts +0 -13
  569. package/src/components/reauth-dialog.tsx +0 -265
  570. package/src/components/row-actions-labels.ts +0 -12
  571. package/src/components/row-actions.tsx +0 -795
  572. package/src/components/scroll-area.tsx +0 -123
  573. package/src/components/search-field.tsx +0 -192
  574. package/src/components/select.tsx +0 -294
  575. package/src/components/separator.tsx +0 -33
  576. package/src/components/series-chart-axes.ts +0 -342
  577. package/src/components/series-chart-budget.ts +0 -150
  578. package/src/components/series-chart-labels.ts +0 -33
  579. package/src/components/series-chart-thin.ts +0 -180
  580. package/src/components/series-chart-ticks.ts +0 -194
  581. package/src/components/series-chart.tsx +0 -1906
  582. package/src/components/server-wake-labels.ts +0 -11
  583. package/src/components/server-wake.tsx +0 -129
  584. package/src/components/settings-fields.tsx +0 -237
  585. package/src/components/share-card-labels.ts +0 -30
  586. package/src/components/share-card.tsx +0 -675
  587. package/src/components/signature-pad-labels.ts +0 -24
  588. package/src/components/signature-pad.tsx +0 -747
  589. package/src/components/signed-amount-labels.ts +0 -10
  590. package/src/components/signed-amount.tsx +0 -336
  591. package/src/components/skeleton.tsx +0 -80
  592. package/src/components/slider.tsx +0 -338
  593. package/src/components/sparkline-labels.ts +0 -14
  594. package/src/components/sparkline.tsx +0 -322
  595. package/src/components/spinner.tsx +0 -81
  596. package/src/components/stat-tile-labels.ts +0 -15
  597. package/src/components/stat-tile.tsx +0 -725
  598. package/src/components/status-dot.tsx +0 -170
  599. package/src/components/swatch-picker-labels.ts +0 -10
  600. package/src/components/swatch-picker.tsx +0 -283
  601. package/src/components/swipeable-row.tsx +0 -386
  602. package/src/components/switch.tsx +0 -273
  603. package/src/components/table-skeleton.tsx +0 -136
  604. package/src/components/table.tsx +0 -647
  605. package/src/components/tabs-labels.ts +0 -13
  606. package/src/components/tabs.tsx +0 -693
  607. package/src/components/text-link.tsx +0 -359
  608. package/src/components/text.tsx +0 -150
  609. package/src/components/textarea.tsx +0 -330
  610. package/src/components/tile-radio.tsx +0 -332
  611. package/src/components/time-input.tsx +0 -121
  612. package/src/components/toast-labels.ts +0 -12
  613. package/src/components/toast.tsx +0 -519
  614. package/src/components/toggle-group.tsx +0 -752
  615. package/src/components/toggle-legend.tsx +0 -394
  616. package/src/components/tooltip.tsx +0 -1149
  617. package/src/components/translation-review-editor.tsx +0 -290
  618. package/src/components/translation-review-labels.ts +0 -181
  619. package/src/components/translation-review-parts.tsx +0 -272
  620. package/src/components/translation-review.tsx +0 -1240
  621. package/src/components/tree-view.tsx +0 -589
  622. package/src/components/treemap-labels.ts +0 -47
  623. package/src/components/treemap-layout.ts +0 -210
  624. package/src/components/treemap.tsx +0 -1184
  625. package/src/components/trigger-aria.ts +0 -44
  626. package/src/components/use-anchor-dir.ts +0 -20
  627. package/src/components/use-mobile-reveal.ts +0 -62
  628. package/src/components/use-table-state.ts +0 -753
  629. package/src/components/user-avatar.tsx +0 -116
  630. package/src/components/wizard-stepper.tsx +0 -42
  631. package/src/components/write-lock-labels.ts +0 -10
  632. package/src/components/write-lock.tsx +0 -253
  633. package/src/currencies.ts +0 -70
  634. package/src/data-table.ts +0 -42
  635. package/src/demo/demo-banner.tsx +0 -153
  636. package/src/demo/demo-countdown.ts +0 -136
  637. package/src/demo/demo-ended.tsx +0 -90
  638. package/src/demo/demo-labels.ts +0 -78
  639. package/src/demo/demo-session.ts +0 -32
  640. package/src/demo/demo-start.tsx +0 -156
  641. package/src/eslint/index.ts +0 -161
  642. package/src/eslint/role-utilities.generated.ts +0 -146
  643. package/src/eslint/role-utilities.ts +0 -233
  644. package/src/feedback/feedback-attachment-download.tsx +0 -77
  645. package/src/feedback/feedback-attachment-labels.ts +0 -37
  646. package/src/feedback/feedback-attachment.tsx +0 -1239
  647. package/src/feedback/feedback-capture.ts +0 -287
  648. package/src/feedback/feedback-comment-thread.tsx +0 -353
  649. package/src/feedback/feedback-context-labels.ts +0 -10
  650. package/src/feedback/feedback-context.tsx +0 -213
  651. package/src/feedback/feedback-crash.ts +0 -547
  652. package/src/feedback/feedback-dialog-labels.ts +0 -29
  653. package/src/feedback/feedback-dialog.tsx +0 -440
  654. package/src/feedback/feedback-entry-file.tsx +0 -150
  655. package/src/feedback/feedback-inbox.tsx +0 -552
  656. package/src/feedback/feedback-labels.ts +0 -144
  657. package/src/feedback/feedback-menu-labels.ts +0 -12
  658. package/src/feedback/feedback-menu.tsx +0 -177
  659. package/src/feedback/feedback-mutation.ts +0 -20
  660. package/src/feedback/feedback-page-link.ts +0 -90
  661. package/src/feedback/feedback-public-labels.ts +0 -81
  662. package/src/feedback/feedback-public-list.tsx +0 -524
  663. package/src/feedback/feedback-public-switch.tsx +0 -102
  664. package/src/feedback/feedback-public-types.ts +0 -101
  665. package/src/feedback/feedback-record.ts +0 -423
  666. package/src/feedback/feedback-replies-context.ts +0 -9
  667. package/src/feedback/feedback-replies.tsx +0 -343
  668. package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
  669. package/src/feedback/feedback-row-detail-labels.ts +0 -30
  670. package/src/feedback/feedback-row-detail.tsx +0 -858
  671. package/src/feedback/feedback-status-undo.ts +0 -109
  672. package/src/feedback/feedback-submit-dialog.tsx +0 -118
  673. package/src/feedback/feedback-submit.tsx +0 -237
  674. package/src/feedback/feedback-swipe.tsx +0 -192
  675. package/src/feedback/feedback-table-labels.ts +0 -27
  676. package/src/feedback/feedback-table.tsx +0 -912
  677. package/src/feedback/feedback-thread-labels.ts +0 -22
  678. package/src/feedback/feedback-thread-parts-labels.ts +0 -41
  679. package/src/feedback/feedback-thread.tsx +0 -905
  680. package/src/feedback/feedback-vocabulary.ts +0 -263
  681. package/src/feedback/feedback-vote-button.tsx +0 -124
  682. package/src/feedback.ts +0 -116
  683. package/src/hooks/use-anchored-panel.ts +0 -396
  684. package/src/hooks/use-anchored-rect.ts +0 -77
  685. package/src/hooks/use-announce.ts +0 -85
  686. package/src/hooks/use-authed-src.ts +0 -167
  687. package/src/hooks/use-body-scroll-lock.ts +0 -119
  688. package/src/hooks/use-breakpoint.ts +0 -133
  689. package/src/hooks/use-close-transition.ts +0 -107
  690. package/src/hooks/use-copy-to-clipboard.ts +0 -122
  691. package/src/hooks/use-debounce.ts +0 -114
  692. package/src/hooks/use-dismiss.ts +0 -371
  693. package/src/hooks/use-document-title.ts +0 -33
  694. package/src/hooks/use-file-drop.ts +0 -121
  695. package/src/hooks/use-file-picker-labels.ts +0 -28
  696. package/src/hooks/use-file-picker.tsx +0 -359
  697. package/src/hooks/use-focus-trap.ts +0 -199
  698. package/src/hooks/use-hotkey.ts +0 -189
  699. package/src/hooks/use-large-text.ts +0 -41
  700. package/src/hooks/use-long-press.ts +0 -140
  701. package/src/hooks/use-media-query.ts +0 -39
  702. package/src/hooks/use-mounted.ts +0 -39
  703. package/src/hooks/use-noindex.ts +0 -37
  704. package/src/hooks/use-overlay-history.ts +0 -714
  705. package/src/hooks/use-row-swipe.ts +0 -207
  706. package/src/hooks/use-search-param-state.ts +0 -434
  707. package/src/hooks/use-windowed-rows.ts +0 -225
  708. package/src/i18n/defaults.ts +0 -267
  709. package/src/i18n/german.ts +0 -2057
  710. package/src/i18n/kit-labels.tsx +0 -1003
  711. package/src/i18n/languages.ts +0 -349
  712. package/src/i18n/locales/de-CH.ts +0 -24
  713. package/src/i18n/locales/en.ts +0 -267
  714. package/src/i18n/locales/es.ts +0 -1997
  715. package/src/i18n/locales/fr.ts +0 -2035
  716. package/src/i18n/locales/hu.ts +0 -2002
  717. package/src/i18n/locales/it.ts +0 -2012
  718. package/src/i18n/locales/zh.ts +0 -1910
  719. package/src/i18n/plurals.ts +0 -86
  720. package/src/i18n/review.ts +0 -475
  721. package/src/i18n/shared-labels.ts +0 -138
  722. package/src/i18n/swiss.ts +0 -29
  723. package/src/index.ts +0 -1179
  724. package/src/landing/access.ts +0 -93
  725. package/src/landing/landing-actions.tsx +0 -97
  726. package/src/landing/landing-labels.ts +0 -59
  727. package/src/landing/landing-sections.tsx +0 -341
  728. package/src/landing/page-seo.ts +0 -261
  729. package/src/landing/public-header.tsx +0 -121
  730. package/src/landing/public-page.tsx +0 -106
  731. package/src/landing/routing.tsx +0 -225
  732. package/src/lib/broadcast.ts +0 -52
  733. package/src/lib/calc.ts +0 -323
  734. package/src/lib/chip-edge-snap.ts +0 -212
  735. package/src/lib/clipboard-minus.ts +0 -107
  736. package/src/lib/clipping.ts +0 -34
  737. package/src/lib/cn.ts +0 -28
  738. package/src/lib/column-mapping.ts +0 -234
  739. package/src/lib/countries.ts +0 -91
  740. package/src/lib/data-attributes.ts +0 -14
  741. package/src/lib/date-format.ts +0 -227
  742. package/src/lib/dates.ts +0 -266
  743. package/src/lib/day-format.ts +0 -96
  744. package/src/lib/decimal-marks.ts +0 -108
  745. package/src/lib/describe-failure.ts +0 -18
  746. package/src/lib/device-settings.ts +0 -147
  747. package/src/lib/direction.ts +0 -74
  748. package/src/lib/document-navigation.ts +0 -17
  749. package/src/lib/document-title.ts +0 -41
  750. package/src/lib/download.ts +0 -172
  751. package/src/lib/float-clearance.ts +0 -70
  752. package/src/lib/floating-stack.ts +0 -70
  753. package/src/lib/focus-left.ts +0 -156
  754. package/src/lib/format.ts +0 -522
  755. package/src/lib/http-status.ts +0 -38
  756. package/src/lib/iban.ts +0 -251
  757. package/src/lib/ime.ts +0 -24
  758. package/src/lib/intl-cache.ts +0 -25
  759. package/src/lib/iso-day.ts +0 -13
  760. package/src/lib/lazy-with-fallback.ts +0 -82
  761. package/src/lib/logger.ts +0 -69
  762. package/src/lib/minus.ts +0 -50
  763. package/src/lib/path-pattern.ts +0 -55
  764. package/src/lib/person-name.ts +0 -155
  765. package/src/lib/phone.ts +0 -208
  766. package/src/lib/plain-text.ts +0 -22
  767. package/src/lib/presence.ts +0 -18
  768. package/src/lib/qr-encode.ts +0 -444
  769. package/src/lib/quiet-focus.ts +0 -32
  770. package/src/lib/reduced-motion.ts +0 -22
  771. package/src/lib/refs.ts +0 -22
  772. package/src/lib/round.ts +0 -43
  773. package/src/lib/safe-storage.ts +0 -119
  774. package/src/lib/server-wake.ts +0 -315
  775. package/src/lib/session-refresh.ts +0 -472
  776. package/src/lib/share-format.ts +0 -18
  777. package/src/lib/status-tone.ts +0 -43
  778. package/src/lib/strip-fade.ts +0 -287
  779. package/src/lib/table-text.ts +0 -554
  780. package/src/lib/text-case.ts +0 -19
  781. package/src/lib/text-match.ts +0 -169
  782. package/src/lib/thenable.ts +0 -19
  783. package/src/lib/translation-review.ts +0 -717
  784. package/src/notifications/device-zone-report.ts +0 -67
  785. package/src/notifications/feedback-notification-types-labels.ts +0 -33
  786. package/src/notifications/feedback-notification-types.ts +0 -71
  787. package/src/notifications/notification-bell.tsx +0 -119
  788. package/src/notifications/notification-inbox.tsx +0 -309
  789. package/src/notifications/notification-parts.tsx +0 -81
  790. package/src/notifications/notification-types-setting.tsx +0 -406
  791. package/src/notifications/notification-types.ts +0 -89
  792. package/src/notifications/notifications-labels.ts +0 -287
  793. package/src/notifications/push-device-setting.tsx +0 -230
  794. package/src/notifications/push-devices-setting.tsx +0 -374
  795. package/src/notifications/push-worker.ts +0 -168
  796. package/src/notifications/quiet-hours-setting.tsx +0 -238
  797. package/src/notifications/unsubscribe-panel.tsx +0 -200
  798. package/src/notifications/use-push-device.ts +0 -448
  799. package/src/offline/local-first.ts +0 -100
  800. package/src/offline/pwa-update-prompt-labels.ts +0 -9
  801. package/src/offline/pwa-update-prompt.tsx +0 -78
  802. package/src/offline/query-persistence.ts +0 -391
  803. package/src/offline/service-worker-updates.ts +0 -81
  804. package/src/offline/shared-computer-labels.ts +0 -8
  805. package/src/offline/shared-computer.tsx +0 -87
  806. package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
  807. package/src/offline/sign-out-pending-dialog.tsx +0 -135
  808. package/src/offline.ts +0 -37
  809. package/src/push-worker.ts +0 -25
  810. package/src/rhf/fields.tsx +0 -2187
  811. package/src/rhf/form-core.tsx +0 -324
  812. package/src/rhf/form.tsx +0 -12
  813. package/src/rhf/line-items.tsx +0 -135
  814. package/src/rhf/use-rhf-wizard-step.ts +0 -104
  815. package/src/rhf.ts +0 -21
  816. package/src/search/command-palette-labels.ts +0 -16
  817. package/src/search/command-palette.tsx +0 -624
  818. package/src/search/global-search-labels.ts +0 -13
  819. package/src/search/global-search.tsx +0 -660
  820. package/src/search/search-index.ts +0 -283
  821. package/src/search/use-command-key.ts +0 -23
  822. package/src/search.ts +0 -14
  823. package/src/settings/device-reset-setting-labels.ts +0 -20
  824. package/src/settings/device-reset-setting.tsx +0 -116
  825. package/src/settings/settings-card-classes.ts +0 -11
  826. package/src/settings/settings-catalogue.ts +0 -150
  827. package/src/settings/settings-context.ts +0 -33
  828. package/src/settings/settings-heading.tsx +0 -67
  829. package/src/settings/settings-labels.ts +0 -87
  830. package/src/settings/settings-layout.tsx +0 -471
  831. package/src/settings/settings-section.tsx +0 -108
  832. package/src/settings/use-account-appearance.ts +0 -148
  833. package/src/settings/use-account-language.ts +0 -146
  834. package/src/settings/use-settings-focus.ts +0 -128
  835. package/src/settings/use-settings-route.ts +0 -258
  836. package/src/shell/account-menu-language.tsx +0 -130
  837. package/src/shell/account-menu-text-size.tsx +0 -80
  838. package/src/shell/admin-menu.tsx +0 -183
  839. package/src/shell/app-shell-labels.ts +0 -11
  840. package/src/shell/app-shell.tsx +0 -1560
  841. package/src/shell/auth-layout.tsx +0 -186
  842. package/src/shell/company-switcher-labels.ts +0 -13
  843. package/src/shell/company-switcher.tsx +0 -136
  844. package/src/shell/dev-menu.tsx +0 -257
  845. package/src/shell/fill-height.tsx +0 -45
  846. package/src/shell/language-code.ts +0 -5
  847. package/src/shell/option-switcher-menu.tsx +0 -84
  848. package/src/shell/sync-status-indicator.tsx +0 -303
  849. package/src/shell/top-bar-actions.tsx +0 -87
  850. package/src/shell/top-bar-brand.tsx +0 -78
  851. package/src/shell/top-bar-parts-labels.ts +0 -148
  852. package/src/shell/top-bar.tsx +0 -40
  853. package/src/shell/topbar-action-menu.tsx +0 -567
  854. package/src/shell/topbar-controls.tsx +0 -269
  855. package/src/shell/tour-menu.tsx +0 -187
  856. package/src/shell.ts +0 -108
  857. package/src/sync/db.ts +0 -129
  858. package/src/sync/engine.ts +0 -1011
  859. package/src/sync/hooks.ts +0 -98
  860. package/src/sync/testing.ts +0 -73
  861. package/src/sync/wire.ts +0 -61
  862. package/src/sync.ts +0 -37
  863. package/src/table-text.ts +0 -8
  864. package/src/testing/css-variables.generated.ts +0 -112
  865. package/src/testing/css-variables.ts +0 -189
  866. package/src/testing/index.ts +0 -29
  867. package/src/testing/react.ts +0 -58
  868. package/src/testing/tour.ts +0 -41
  869. package/src/testing/viewport.ts +0 -357
  870. package/src/theme/chart-palette.ts +0 -232
  871. package/src/theme/color.ts +0 -272
  872. package/src/theme/contrast-tokens.ts +0 -268
  873. package/src/theme/contrast.ts +0 -250
  874. package/src/theme/palette-derive.ts +0 -825
  875. package/src/theme/palette-presets.ts +0 -544
  876. package/src/theme/palette-store.ts +0 -108
  877. package/src/theme/text-size.ts +0 -289
  878. package/src/theme/theme-store.ts +0 -157
  879. package/src/tour/tour-catalogue-labels.ts +0 -75
  880. package/src/tour/tour-catalogue.tsx +0 -335
  881. package/src/tour/tour-dock.ts +0 -134
  882. package/src/tour/tour-engine.ts +0 -37
  883. package/src/tour/tour-gates.ts +0 -218
  884. package/src/tour/tour-guide-button.tsx +0 -64
  885. package/src/tour/tour-guide-labels.ts +0 -15
  886. package/src/tour/tour-info.ts +0 -144
  887. package/src/tour/tour-labels.ts +0 -23
  888. package/src/tour/tour-overlay.tsx +0 -1170
  889. package/src/tour/tour.tsx +0 -750
  890. package/src/tour/use-tour-progress.ts +0 -671
  891. package/src/tour/use-tour-url-sync.ts +0 -266
  892. package/src/tour.ts +0 -21
  893. package/src/vite-env.d.ts +0 -23
  894. package/src/wizard/stepper-nav.tsx +0 -360
  895. package/src/wizard/types.ts +0 -275
  896. package/src/wizard/use-wizard.ts +0 -560
  897. package/src/wizard/validation.ts +0 -39
  898. package/src/wizard/wizard-context.tsx +0 -74
  899. package/src/wizard/wizard-labels.ts +0 -29
  900. package/src/wizard/wizard-step.tsx +0 -76
  901. package/src/wizard/wizard-summary.tsx +0 -84
  902. 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;