@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/inline-entity-combobox.tsx"],"sourcesContent":["// The id-keyed sibling of Combobox. Moved out of combobox.tsx as it was (MOD-9,\n// refactor audit 2026-10); combobox still exports `InlineEntityCombobox` and its props.\nimport { useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, KeyboardEvent, ReactNode, Ref } from \"react\";\nimport { ChevronDown, Plus, X } from \"lucide-react\";\nimport { FIELD_BASE, FIELD_FLOATING_PAD, FIELD_INVALID } from \"./field-base\";\nimport { cn } from \"../lib/cn\";\nimport { useDropdown } from \"./dropdown\";\nimport { claimEscape, takeEscape, useDismissLayer } from \"../hooks/use-dismiss\";\nimport { isImeEnter } from \"../lib/ime\";\nimport { isQuietFocus } from \"../lib/quiet-focus\";\nimport { usePhoneLayout } from \"../hooks/use-breakpoint\";\nimport { PickerSheet, SHEET_ROW_CLASS } from \"./picker-sheet\";\nimport {\n anchoredHeadingClass,\n ComboboxFieldLabel,\n DISABLED_ROW_CLASS,\n endHintRowProps,\n GroupedOptions,\n groupRuns,\n isOptionEnabled,\n offersCreate,\n SHEET_HEADING_CLASS,\n stepEnabled,\n useTypedListStatus,\n type ComboClearValue,\n type ComboOption,\n} from \"./combobox-core\";\nimport { DEFAULT_COMBOBOX_LABELS, useKitLabels, useKitLocale } from \"../i18n/kit-labels\";\nimport { sameName, searchRanked } from \"../lib/text-match\";\nimport {\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 { hasContent } from \"../lib/presence\";\nimport { useCommitReason, type CommitScope } from \"./write-lock\";\nimport { lockedKeyDown, rowClass, SuggestionList, usePrimaryPressOnly } from \"./combobox-suggestions\";\n\n/** {@link InlineEntityCombobox}'s create row over the shared row look: in the brand\n * colour {@link Combobox}'s own create row wears, with a leading \"+\" as the panel\n * pickers' has — an action at the foot of the list, not one more record. */\nconst CREATE_ROW_CLASS = \"flex items-center gap-2 font-medium text-brand\";\n\n/** `id` is the INPUT's and `onChange` is the field's — see {@link ComboboxProps}\n * for why the div's spellings of them are omitted. */\nexport interface InlineEntityComboboxProps<V extends string | number, C extends ComboClearValue = null>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"id\" | \"onChange\">, FieldHelpProps {\n /** Selected option id; `null` or `clearValue` when nothing is selected. */\n value: V | C | null;\n /** Required and unanswered — see {@link Combobox}'s `invalid`. */\n invalid?: boolean;\n /** What is wrong with the value — see {@link Combobox}'s `error`. */\n error?: ReactNode;\n /** Standing advice — see {@link Combobox}'s `hint` (keksdose K4). */\n hint?: ReactNode;\n /** A picked/typed option emits its id; emptying the field, or the \"×\", emits\n * `clearValue`. */\n onChange: (v: V | C) => void;\n /** What a clear emits — `null` by default, `\"\"` for a schema that wants an empty\n * string. See {@link EntityCombobox}'s `clearValue`. */\n clearValue?: C;\n options: readonly ComboOption<V>[];\n /** The visible label — see {@link Combobox}'s `label` (a ReactNode since 0.34, API-9). */\n label?: ReactNode;\n id?: string;\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n /** Focus on mount, the way `<input autoFocus>` does — and, through `onFocus`\n * below, open the list with it. A click-the-value editor needs it: the cell\n * the user clicked names the field the editor should land the caret in, and\n * without a passthrough this component's fixed prop list puts the `<input>`\n * out of a caller's reach entirely (Keksdose feedback live #218). */\n autoFocus?: boolean;\n /** Phone sheet only — the anchored list has the field itself to type in. Falls\n * back to `placeholder`, then to `combobox.search` from the {@link UiKitProvider}. */\n searchPlaceholder?: string;\n /** What a query that finds nothing says: on the phone sheet, where a full screen\n * showing nothing has to say why, and in the field's live status (0.52) — the anchored\n * list itself simply does not open. Default: `combobox.noResults` from the provider. */\n emptyLabel?: string;\n closeLabel?: string;\n /** The phone sheet's heading (and, as a string, its accessible name) for a field\n * whose label is drawn by someone else — a bound field's label;\n * `RhfInlineEntityCombobox` hands its label over here (0.24). Default: `label`.\n * See {@link Combobox}'s `sheetTitle`. */\n sheetTitle?: ReactNode;\n /** Offer a clear \"×\" in place of the chevron whenever something is selected.\n *\n * Emptying the text already clears (see {@link reconcile}), and on a desktop that\n * is the fast way. It is not a way at all on a PHONE: touching the field opens the\n * full-screen sheet, which covers the very input the text would have been deleted\n * from — so a field that had been answered could not be UNanswered by any gesture\n * the screen offered (Keksdose live #236, filed from a phone). The \"×\" is the one\n * affordance both shells share, and it clears without opening anything. */\n clearable?: boolean;\n clearLabel?: string;\n /**\n * Why the choice cannot be changed — {@link Button}'s `disabledReason`, for a picker\n * that SAVES on change. keksdose G2: the statement review's account picker IS the\n * write (picking re-runs the duplicate check on the server), and on the read-only demo\n * the app wrapped it in a hand-made Tooltip over a native `disabled` — out of the tab\n * order, so the reason never reached a keyboard.\n *\n * With a reason the input stays focusable and `aria-disabled`, is `readOnly` (the\n * settled grey), opens no list or sheet, offers no clear \"×\", and no pick, typed\n * label or clear reaches `onChange`; the reason is in the kit {@link Tooltip} and on\n * the input's `aria-describedby`. Wins over `disabled`. Use it CONTROLLED, as the\n * component always is: the shown label is `value`'s.\n */\n disabledReason?: ReactNode;\n /**\n * This picker COMMITS — choosing saves. Under a locked {@link WriteLockProvider} it is\n * locked the `disabledReason` way with the lock's reason (which wins over its own).\n * A picker inside a form with its own Save stays editable under the lock — leave this\n * off there and put `commit` on the Save. No provider, or an unlocked one: no effect.\n */\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 /**\n * A last row that makes a new record from what was typed — keksdose G9, whose \"Create\n * cash account\" had to become a separate button because this picker had no such row.\n *\n * Shown while the typed query is non-empty and names no option exactly\n * (case-insensitive — an exact hit is a record the list already shows), worded\n * `createLabel(query)`, default `combobox.create`: \"Create “{query}”\". The arrow keys\n * reach it like an option; Enter or a click calls `onCreate(query)` and closes the\n * list. Making the record, and then passing its id as `value`, is the caller's: the\n * field shows the old selection until it does.\n *\n * The same rule as {@link EntityCombobox}'s `onCreate`, so the family agrees. It is a\n * row of the LIST, not an option: it is never `aria-selected`, never matched by a\n * typed label on blur, and never sorted among the records the way a sentinel option\n * would be (the reason keksdose gave for the separate button).\n */\n onCreate?: (query: string) => void;\n /** The create row's words for a query. Default: `combobox.create` from the\n * {@link UiKitProvider}, else English `Create “{query}”`. */\n createLabel?: (query: string) => string;\n /**\n * Offer the create row with NOTHING typed too, worded as this — keksdose's \"Create cash\n * account\", which mints an account under a default name and so needs no query.\n * `onCreate` then receives `\"\"`, and the caller supplies the name. Left out, the row\n * needs a query.\n *\n * One string rather than a switch: a row with no query has nothing to quote, so the\n * kit cannot word it — \"Create “”\" says nothing — and naming it is what turns it on.\n * With both, one handler serves both rows:\n *\n * ```tsx\n * onCreate={(name) => createCash(name || t(\"cash_account_default_name\"))}\n * createEmptyLabel={t(\"create_cash_account\")}\n * ```\n */\n createEmptyLabel?: string;\n /**\n * The create row WRITES while picking does not — keksdose dev#496: on the read-only\n * demo choosing the cash account is draft state and stays live, minting one is a\n * write. Under a locked {@link WriteLockProvider} the row stays in the list, dimmed and\n * passed over by the arrows, with the lock's reason as its second line (a hover is the\n * one explanation a phone cannot show), and `onCreate` is not called. Not needed with\n * `commit`: a locked field opens no list at all.\n */\n createCommit?: CommitScope;\n /**\n * The `<input>` — the focusable control, the one a reader types in — as a React 19\n * ref prop (0.24, kastlan). Its account picker is this component inside an\n * `RhfField` render, and react-hook-form's focus-on-error calls `focus()` on whatever\n * `field.ref` is handed: with no ref there was nothing to hand it, so a line-item form\n * whose account was missing failed its submit and left the caret where it was. Pass\n * `field.ref` here, or use `RhfInlineEntityCombobox`, which does. The wrapper is not\n * reachable this way — focus and selection are the input's. See {@link Combobox}'s `ref`.\n */\n ref?: Ref<HTMLInputElement>;\n}\n\n/**\n * The id-keyed sibling of {@link Combobox}: identical anatomy (a real text input\n * with the shared field styling, chevron and inline as-you-type filtering) but\n * the value is an option id, not free text. Built so an entity picker can sit\n * next to a free-text combobox without any visible difference — a button-trigger\n * panel picker ({@link EntityCombobox}) never matches an input field exactly.\n *\n * Text-vs-value reconciliation: the input's text is transient. Picking a row or\n * typing an exact (unique) label commits that option; emptying the text commits\n * a clear; anything else reverts to the selected option's label on blur/Escape.\n *\n * **On a phone the list opens as a full-screen sheet**, exactly like {@link Combobox}\n * beside it (Keksdose live #200, extended by dev#477 — the account picker moved onto\n * this component and must not lose its sheet). The sheet carries its OWN query state\n * rather than reusing `text`: `text` is what {@link reconcile} judges on blur, so a\n * sheet that emptied it to show the full list would read as \"the user cleared the\n * field\" the moment it closed.\n */\nexport function InlineEntityCombobox<V extends string | number, C extends ComboClearValue = null>(\n props: InlineEntityComboboxProps<V, C>,\n) {\n const {\n value: rawValue,\n onChange,\n clearValue = null as C,\n options,\n label,\n id,\n placeholder,\n className,\n disabled,\n autoFocus,\n searchPlaceholder,\n emptyLabel,\n closeLabel,\n sheetTitle,\n clearable,\n clearLabel,\n disabledReason,\n disabledReasonDisplay,\n commit,\n onCreate,\n createLabel,\n createEmptyLabel,\n createCommit,\n invalid,\n error,\n hint,\n help,\n ref,\n \"aria-label\": ariaLabel,\n // Off `rest` and onto the <input>, which is the combobox a reader meets: `Field`'s\n // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and\n // on the wrapper div the hint and required state described nothing.\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n // The clear value reads as \"nothing selected\", whichever one the caller picked —\n // so from here down `value` is the id or `null`, as it always was.\n const value: V | null = rawValue == null || rawValue === clearValue ? null : (rawValue as V);\n const hintParts = useFieldHint(hint, ariaDescribedBy, { help, name: label ?? ariaLabel });\n const field = useFieldError(error, invalid, hintParts.describedBy, ariaInvalid);\n const generated = useId();\n const fieldId = id ?? generated;\n // See the twin above on why these hang off the generated id.\n const listboxId = `${generated}-listbox`;\n const optionId = (index: number) => `${generated}-option-${index}`;\n const isPhone = usePhoneLayout();\n // See the twin above: the phone sheet owns its own Back entry (live #309), and the\n // Escape layer is this field's own.\n const { open, setOpen, wrapperRef, panelRef } = useDropdown({ backCloses: !isPhone, escapeLayer: false });\n const primaryOnly = usePrimaryPressOnly();\n // The field took the tour's quiet focus (§11.8) and has not opened since.\n const quietRef = useRef(false);\n const [active, setActive] = useState(-1);\n // null = not editing → the input shows the selected option's label.\n const [text, setText] = useState<string | null>(null);\n const sheetInputRef = useRef<HTMLInputElement | null>(null);\n // The box the portalled list hangs off — see {@link SuggestionList}.\n const fieldRef = useRef<HTMLDivElement>(null);\n // The sheet's search box. Starts empty on every open, so a field that already\n // holds a value still offers the whole list — the shape a native <select> has\n // on a phone, and what the anchored panel gets from `query` below.\n const [sheetQuery, setSheetQuery] = useState(\"\");\n // Props first, provider second — see `EntityCombobox`. `emptyLabel` used to render\n // nothing when omitted, which left a phone user staring at a blank full-screen\n // sheet; now an omitted one says \"No results\" in the provider's language.\n const labels = useKitLabels(\"combobox\", DEFAULT_COMBOBOX_LABELS, {\n search: searchPlaceholder,\n noResults: emptyLabel,\n clear: clearLabel,\n create: createLabel,\n });\n // The lock (see `lockedKeyDown` above). A lock arriving mid-edit closes the list and\n // drops the loose text, adjusted while rendering: nothing typed before it may be\n // judged by `reconcile` after it.\n const lock = useLockReason(commit, disabledReason);\n const locked = lock.locked;\n const inert = locked || Boolean(disabled);\n if (locked && (open || text !== null)) {\n setOpen(false);\n setText(null);\n }\n // The create row's own lock (`createCommit`): the list stays live, the row does not.\n const createReason = useCommitReason(createCommit, undefined);\n const createLocked = hasContent(createReason);\n\n const selected = useMemo(\n () => (value == null ? null : (options.find((o) => o.value === value) ?? null)),\n [options, value],\n );\n const shown = text ?? selected?.label ?? \"\";\n // Nothing selected has nothing to clear, and the chevron comes back — the field\n // keeps exactly one trailing control, so the \"×\" never crowds the value it sits on.\n const showClear = Boolean(clearable && value != null && !inert);\n\n // Focusing select-alls the current label; filtering only kicks in once the\n // text actually differs from it, so an already-filled field still opens on\n // the FULL list instead of a single-row \"filter\" of its own value.\n const typedQuery = text !== null && text !== (selected?.label ?? \"\") ? text.trim() : \"\";\n const query = isPhone ? sheetQuery.trim() : typedQuery;\n // Folded as every kit list folds (DUP-5): accents off and the locale's case, so\n // \"zurich\" finds Zürich and, in Turkish, \"ı\" finds \"IRMAK\".\n const locale = useKitLocale();\n const matches = useMemo(() => {\n let ranked = options;\n if (query) {\n // Every word of the query, in the label or the sublabel (0.51.4). The label's whole\n // name or its start first (rank 0 and 1), then a match anywhere in the label or the\n // sublabel; a stable sort keeps the options' order within each. Not the group\n // (0.40, kk run 79): with first-letter groups one typed letter matched its whole\n // group. Each option folded once (`searchRanked`).\n ranked = searchRanked(options, query, (o) => [o.label, o.sublabel], { locale })\n .sort((a, b) => Number(a.rank > 1) - Number(b.rank > 1))\n .map((x) => x.row);\n }\n if (!ranked.some((o) => o.group)) return ranked;\n // Keep each group contiguous so the headings below appear once instead of\n // re-appearing every time the ranking interleaves two groups — while preserving\n // \"best match first\": a Map keeps insertion order, so groups come out ordered by\n // their best-ranked member and members keep their rank order inside the group.\n const blocks = new Map<string, ComboOption<V>[]>();\n for (const o of ranked) {\n const key = o.group ?? \"\";\n if (!blocks.has(key)) blocks.set(key, []);\n blocks.get(key)!.push(o);\n }\n return [...blocks.values()].flat();\n }, [options, query, locale]);\n\n // The create row (`onCreate`), on the query as typed — not lowercased: it becomes a\n // name. Compared against EVERY option, not `matches`: an exact hit anywhere is a\n // record that exists, disabled or not. Restated from `text` rather than derived from\n // `typedQuery`: this value is handed to the caller's `onCreate`, and the React\n // Compiler cannot then keep `matches` memoised on anything it was derived from.\n const createQuery = isPhone\n ? sheetQuery.trim()\n : text !== null && text !== (selected?.label ?? \"\")\n ? text.trim()\n : \"\";\n const showCreate = offersCreate(\n onCreate,\n createQuery,\n options.map((o) => o.label),\n createEmptyLabel,\n locale,\n );\n const createText = createQuery ? labels.create(createQuery) : createEmptyLabel;\n // Every row the keyboard can land on: the options, then the create row — passed over\n // like a disabled option while a write lock holds it.\n const rows: readonly { disabled?: boolean }[] = showCreate ? [...matches, { disabled: createLocked }] : matches;\n const createIndex = matches.length;\n\n const close = () => {\n setOpen(false);\n setActive(-1);\n setSheetQuery(\"\");\n };\n // Whether a list shows, the row the keyboard is on — in the anchored list or the phone\n // sheet, whose search box takes the same keys (INP-6) — and the live status, as\n // Combobox has them. A disabled row is never the keyboard's, even if the list changed\n // under it.\n const { showing, expanded, activeOption, statusRegion } = useTypedListStatus({\n isPhone,\n open,\n inert,\n rowCount: matches.length,\n extraRow: showCreate,\n query,\n active: isOptionEnabled(rows[active]) ? active : -1,\n optionId,\n labels,\n });\n // A layer while the list SHOWS, and Escape puts the label back.\n useDismissLayer(showing, () => {\n setText(null);\n close();\n });\n const take = (o: ComboOption<V>) => {\n // A `disabled` option is listed, never taken — by any path; nor is anything while\n // the field is locked.\n if (o.disabled || locked) return;\n if (o.value !== value) onChange(o.value);\n setText(null);\n close();\n };\n /** Turn loose text into a decision: empty clears, an exact label match that names\n * ONE entity commits, anything else reverts to the selected label.\n *\n * Unique by VALUE, not by row. An option may deliberately appear twice — Keksdose\n * repeats recently-used categories in a \"Recent\" group at the top (live #203) — and\n * counting rows made every such option uncommittable: two hits, so nothing fired and\n * the field reverted to whatever was selected before. Typing a category you had just\n * used, then tabbing away, silently discarded it, and the more categories you used\n * the more of them stopped working. Two rows naming the same id are not an ambiguity;\n * two ids sharing a label are. */\n const create = () => {\n if (!onCreate || locked || createLocked) return;\n onCreate(createQuery);\n setText(null);\n close();\n };\n // The ONE option a name typed out in full names, else null. A disabled option's label\n // is not a hit — typing it is not a way round `disabled` — and a label is typed out in\n // full as the list matches it, so \"cafe\" names Café (DUP-5).\n const exactHit = (q: string): ComboOption<V> | null => {\n const hits = options.filter((o) => !o.disabled && sameName(o.label, q, locale));\n return new Set(hits.map((h) => h.value)).size === 1 ? hits[0] : null;\n };\n const reconcile = () => {\n // Locked, loose text is dropped rather than judged: nothing reaches `onChange`.\n if (text !== null && !locked) {\n const q = text.trim();\n if (!q) {\n if (value != null) onChange(clearValue);\n } else {\n // Anything but a hit reverts, like any other non-answer.\n const hit = exactHit(q);\n if (hit && hit.value !== value) onChange(hit.value);\n }\n }\n setText(null);\n close();\n };\n // The phone sheet's way out — the X, Escape, Back — keeps the value and judges nothing.\n // Not `reconcile`: on a phone `text` is never typed in (the sheet's own box is), so it\n // only holds what the field showed at focus, and that is \"\" for a value whose option\n // has not loaded yet — judged, it read as \"the user emptied the field\" and the X\n // cleared the answer (0.52 code review, inline-entity-combobox.tsx).\n const dismissSheet = () => {\n setText(null);\n close();\n };\n\n /** The list's keys — in the field and, on a phone, in the sheet's search box (INP-6).\n * Disabled rows are passed over; Up from \"nothing highlighted\" lands on the first\n * takeable row, as it always landed on row 0. The create row is the last row the\n * arrows reach. */\n const listKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setOpen(true);\n setActive((i) => stepEnabled(rows, i, 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActive((i) => (i < 0 ? stepEnabled(rows, -1, 1) : stepEnabled(rows, i, -1)));\n } else if (e.key === \"Enter\") {\n // An IME's Enter takes nothing, and judges no loose text (BUG-9).\n if (isImeEnter(e)) return;\n if (open && isOptionEnabled(rows[active])) {\n e.preventDefault();\n if (active === createIndex && showCreate) create();\n else take(matches[active]);\n } else if (isPhone) {\n // The sheet's search box: its QUERY is what was typed, and a name typed out in\n // full is taken, as the desktop field takes it. Anything else leaves the sheet\n // and its search as they are — the panel pickers' sheet does the same. This was\n // `reconcile`, which judged the field's text from before the sheet opened and\n // closed: the search was thrown away and \"Groceries\" typed out was not taken\n // (0.52 code review, inline-entity-combobox.tsx).\n const hit = exactHit(sheetQuery.trim());\n if (hit) {\n e.preventDefault();\n take(hit);\n }\n } else {\n reconcile();\n }\n }\n };\n\n // The create row's inside, one for both shells: a \"+\" (it is an action, not a record),\n // the words, and — while a write lock holds it — the lock's reason as a second line.\n // Both lines wrap from Large up (text size §10.17, round 0.34 T1): the words hold what\n // the reader typed, which an ellipsis would cut.\n const createRowContent = (\n <>\n <Plus aria-hidden className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate-until-large\">{createText}</span>\n {createLocked && (\n <span className=\"block truncate-until-large text-xs font-normal text-placeholder\">{createReason}</span>\n )}\n </span>\n </>\n );\n // While locked the field sits in the reason's Tooltip — see Combobox.\n const withLock = (box: ReactNode) =>\n locked ? (\n <LockedReason lock={lock} display={disabledReasonDisplay} className=\"block\">\n {box}\n </LockedReason>\n ) : (\n box\n );\n\n /** The rows in their runs, each group a `role=\"group\"` its heading names (INP-8), for\n * the sheet or the anchored list — a group heading at each boundary rather than the\n * group on every row (feedback #136). `matches` is group-contiguous, so comparing with\n * the row above is enough. An option may deliberately appear in two groups (see\n * `reconcile`): a value is a row's key within its group. */\n const groupedRows = (sheet: boolean, row: (o: ComboOption<V>, i: number) => ReactNode) => (\n <GroupedOptions\n runs={groupRuns(matches, (o) => o.group)}\n listboxId={listboxId}\n rowKey={(o) => String(o.value)}\n row={row}\n headingClassName={(run) => (sheet ? SHEET_HEADING_CLASS : anchoredHeadingClass(run))}\n />\n );\n\n return (\n // `rest` dresses the outer box — a `data-tour` anchor, a test id. Not the NAME,\n // description, invalid or required state: those belong on the <input> below,\n // which is the combobox a reader meets. Spread FIRST, so the field's ARIA and the\n // handlers carrying live #309 and dev#549 cannot be replaced from outside.\n <div {...rest} ref={wrapperRef} className={cn(\"relative\", className)}>\n {/* A real <label for> — see {@link Combobox}; with a \"?\", in a row after the field. */}\n {label !== undefined && hintParts.labelHint === undefined && (\n <ComboboxFieldLabel htmlFor={fieldId} className={inert ? \"opacity-50\" : undefined}>\n {label}\n </ComboboxFieldLabel>\n )}\n <EndHintRow {...endHintRowProps(\"hint\" in props, label !== undefined, hintParts.labelHint)}>\n {withLock(\n /* Inner wrapper for chevron centering — same reasoning as Combobox above.\n It is also what the portalled list anchors to.\n Dimmed as a whole when disabled (or locked), the way EntityCombobox dims its\n trigger. FIELD_BASE's grey alone left a disabled picker looking like a\n filled-in one beside the pickers that do dim (the label is not the input's\n `peer`, so it is dimmed by hand above). */\n <div ref={fieldRef} className={cn(\"relative\", inert && \"cursor-not-allowed opacity-50\")}>\n <input\n ref={ref}\n id={fieldId}\n value={shown}\n placeholder={placeholder}\n // The caller's name, over the <label for> above. Just the label, never\n // \"label: value\" the way a trigger button has to compose it: an input\n // already exposes its value separately.\n aria-label={ariaLabel}\n role=\"combobox\"\n aria-expanded={expanded}\n // Required by the role, and the half that was missing: the field said it\n // was expanded and never said what it had expanded (ESLint's\n // `role-has-required-aria-props`, the audit's §a11y).\n aria-controls={listboxId}\n // Pointer-device only: on a phone the sheet's own search box holds focus\n // and names the row; the field behind it must not claim one.\n aria-activedescendant={isPhone ? undefined : activeOption}\n aria-autocomplete=\"list\"\n aria-invalid={field.isInvalid || undefined}\n aria-describedby={mergeDescribedBy(field.describedBy, locked && lock.reasonId)}\n aria-required={ariaRequired}\n autoComplete=\"off\"\n // Locked: focusable, `readOnly`, `aria-disabled` — see `lockedKeyDown`.\n disabled={locked ? undefined : disabled}\n readOnly={locked || undefined}\n aria-disabled={locked || undefined}\n // eslint-disable-next-line jsx-a11y/no-autofocus -- a documented prop the caller opts into (off by default); the field never takes focus on its own.\n autoFocus={autoFocus}\n // `inputMode=\"none\"` rather than readOnly, for the same reason Combobox\n // above gives: the sheet carries the keyboard, and a readOnly field would\n // take FIELD_BASE's settled look on a field that is perfectly editable.\n inputMode={isPhone ? \"none\" : undefined}\n onMouseDown={primaryOnly.onMouseDown}\n onFocus={(e) => {\n // See {@link usePrimaryPressOnly}: a back/forward button lands here on\n // its way to navigating, and neither the list nor the select-all is\n // anything it asked for (live #309 rework). A locked field takes focus to\n // say why, and opens nothing.\n if (primaryOnly.fromAuxButton() || locked) return;\n setText(shown);\n // The tour's focus on arrival (§11.8) opens nothing: the list would cover\n // its card (0.50.1). A click, ArrowDown or typing opens it.\n if (isQuietFocus(e.currentTarget)) {\n quietRef.current = true;\n return;\n }\n e.currentTarget.select();\n setOpen(true);\n // The sheet has its own input; the field behind it must not also pull up\n // the keyboard and scroll the page under the dialog.\n if (isPhone) sheetInputRef.current?.focus();\n }}\n // After a quiet focus, the person's click on the already-focused field\n // fires no `focus`: the click is their \"open\" instead.\n onClick={() => {\n if (!quietRef.current || locked) return;\n quietRef.current = false;\n setOpen(true);\n }}\n onBlur={() => {\n quietRef.current = false;\n // On a phone the blur is the SHEET taking focus, not the user leaving the\n // field — reconciling there would close the sheet the instant it opened.\n if (!isPhone) reconcile();\n }}\n onChange={(e) => {\n if (locked) return;\n setText(e.target.value);\n setOpen(true);\n setActive(-1);\n }}\n onKeyDown={(e) => {\n if (locked) return lockedKeyDown(e);\n listKeyDown(e);\n if (e.key === \"Escape\") {\n // A showing list takes the key and stops it — see the twin in\n // {@link Combobox} (round 0.34 BUG-1).\n if (showing && takeEscape(e)) return;\n // With no list, putting loose text back is this field's own undo step,\n // and the step is all the key does: claimed, so the dialog around the\n // field stays — it closed with the form in it (refactor audit\n // 2026-10-30, INP-5; ChipInput's draft is the rule). With nothing to\n // undo the key goes on, as before.\n if (text !== null && text !== (selected?.label ?? \"\")) claimEscape(e);\n setText(null);\n close();\n } else if (e.key === \"Tab\") {\n // Focus is in THIS input and stays there, so the browser's own Tab is\n // left alone; all that is needed is that the list stop covering what\n // the user is tabbing to. `reconcile` rather than `close`, because\n // leaving the field is exactly when loose text has to be judged.\n reconcile();\n }\n // Home/End stay with the caret — see the note in {@link Combobox}: this\n // field is editable, and its text is what `reconcile` judges.\n }}\n className={cn(\n FIELD_BASE,\n label !== undefined && FIELD_FLOATING_PAD,\n \"pe-9\",\n field.isInvalid && FIELD_INVALID,\n )}\n />\n {showClear ? (\n <button\n type=\"button\"\n // Out of the tab order, like the clear on `EntityCombobox`: the keyboard\n // already clears this field by selecting its text and deleting, and a\n // second stop between every picker and the next field is a worse trade\n // than the one gesture it saves.\n tabIndex={-1}\n aria-label={labels.clear}\n // preventDefault, exactly as the chevron does: without it the press\n // focuses the input, which on a phone opens the sheet over the field the\n // press was clearing.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setText(null);\n close();\n if (!locked) onChange(clearValue);\n }}\n className={cn(\n \"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-placeholder\",\n \"hover:text-secondary\",\n )}\n >\n <X aria-hidden className=\"size-4\" />\n </button>\n ) : (\n <ChevronDown\n aria-hidden\n onMouseDown={(e) => {\n // Toggle on the chevron without stealing focus from the input — and\n // not at all on a disabled or locked field, which the input's own\n // `disabled`/`readOnly` does not stop from here.\n e.preventDefault();\n if (!inert) setOpen((o) => !o);\n }}\n className={cn(\n \"absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-placeholder\",\n // The box above does the dimming; dimming here too would halve it again.\n !inert && \"cursor-pointer\",\n )}\n />\n )}\n </div>,\n )}\n </EndHintRow>\n {label !== undefined && hintParts.labelHint !== undefined && (\n <StaticLabelRow hint={hintParts.labelHint}>\n <ComboboxFieldLabel htmlFor={fieldId} className={cn(LABEL_IN_ROW, inert && \"opacity-50\")}>\n {label}\n </ComboboxFieldLabel>\n </StaticLabelRow>\n )}\n <FieldCaption parts={hintParts} />\n {field.errorEl}\n {/* The live status — see `useTypedListStatus` for where it goes and why. */}\n {!isPhone && statusRegion}\n {isPhone && (\n <PickerSheet\n open={open && !inert}\n // Closing without choosing keeps the value — see `dismissSheet`.\n onClose={dismissSheet}\n title={sheetTitle ?? label}\n query={sheetQuery}\n onQueryChange={(v) => {\n setSheetQuery(v);\n setActive(-1);\n }}\n searchPlaceholder={searchPlaceholder ?? placeholder ?? labels.search}\n inputRef={sheetInputRef}\n closeLabel={closeLabel}\n // The field's own keys, in the box focus is in while the sheet is up: arrows\n // and Enter there did nothing (INP-6, as BUG-5 gave the panel pickers).\n onSearchKeyDown={listKeyDown}\n listboxId={listboxId}\n activeId={activeOption}\n >\n <ul id={listboxId} role=\"listbox\">\n {groupedRows(true, (o, i) => (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(i)}\n role=\"option\"\n aria-selected={o.value === value}\n aria-disabled={o.disabled || undefined}\n onClick={() => take(o)}\n className={cn(\n SHEET_ROW_CLASS,\n // The row the sheet's arrows are on.\n i === active && !o.disabled && \"bg-active\",\n o.value === value && \"font-medium\",\n o.disabled && DISABLED_ROW_CLASS,\n )}\n >\n {o.label}\n </button>\n </li>\n ))}\n {showCreate && (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(createIndex)}\n role=\"option\"\n // An action, never chosen — see `onCreate`.\n aria-selected={false}\n aria-disabled={createLocked || undefined}\n onClick={create}\n className={cn(\n SHEET_ROW_CLASS,\n CREATE_ROW_CLASS,\n active === createIndex && !createLocked && \"bg-active\",\n createLocked && DISABLED_ROW_CLASS,\n )}\n >\n {createRowContent}\n </button>\n </li>\n )}\n {/* \"Nothing matched\" only when there is nothing to do either: with a create\n row the list is not empty, it is offering to fill itself. A picture: the\n status below says it to a reader. */}\n {matches.length === 0 && !showCreate && (\n <li aria-hidden className=\"px-4 py-3 text-sm text-muted\">\n {labels.noResults}\n </li>\n )}\n </ul>\n {statusRegion}\n </PickerSheet>\n )}\n {showing && (\n <SuggestionList id={listboxId} anchorRef={fieldRef} panelRef={panelRef}>\n {groupedRows(false, (o, i) => (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(i)}\n // See {@link Combobox}: the role rides the button, `aria-selected` is the\n // CHOSEN row, and the keyboard's row is the field's\n // `aria-activedescendant`.\n role=\"option\"\n aria-selected={o.value === value}\n aria-disabled={o.disabled || undefined}\n tabIndex={-1}\n onMouseDown={(e) => {\n // mousedown (not click) so the input's blur can't close the list\n // before the selection registers. Prevented on a disabled row too, so\n // pressing one leaves the field focused and open.\n e.preventDefault();\n take(o);\n }}\n onMouseEnter={() => {\n if (!o.disabled) setActive(i);\n }}\n className={cn(\n rowClass(i === active && !o.disabled),\n o.disabled && DISABLED_ROW_CLASS,\n // Indented under its heading, so the hierarchy is readable at a glance\n // instead of inferred from grey trailing text.\n o.group && \"ps-6\",\n o.value === value && \"font-medium\",\n )}\n >\n {o.label}\n {o.sublabel && <span className=\"ms-2 text-xs text-placeholder\">{o.sublabel}</span>}\n </button>\n </li>\n ))}\n {showCreate && (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(createIndex)}\n role=\"option\"\n // An action, never chosen; whether the keyboard is ON it is the field's\n // `aria-activedescendant`'s to say.\n aria-selected={false}\n aria-disabled={createLocked || undefined}\n tabIndex={-1}\n onMouseDown={(e) => {\n // mousedown, like the rows above: the input's blur would otherwise\n // reconcile and close the list before the click landed.\n e.preventDefault();\n create();\n }}\n onMouseEnter={() => {\n if (!createLocked) setActive(createIndex);\n }}\n className={cn(\n rowClass(active === createIndex && !createLocked),\n CREATE_ROW_CLASS,\n createLocked && DISABLED_ROW_CLASS,\n )}\n >\n {createRowContent}\n </button>\n </li>\n )}\n </SuggestionList>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/inline-entity-combobox.tsx"],"sourcesContent":["// The id-keyed sibling of Combobox. Moved out of combobox.tsx as it was (MOD-9,\n// refactor audit 2026-10); combobox still exports `InlineEntityCombobox` and its props.\nimport { useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, KeyboardEvent, ReactNode, Ref } from \"react\";\nimport { ChevronDown, Plus, X } from \"lucide-react\";\nimport { FIELD_BASE, FIELD_FLOATING_PAD, FIELD_INVALID } from \"./field-base\";\nimport { cn } from \"../lib/cn\";\nimport { useDropdown } from \"./dropdown\";\nimport { claimEscape, takeEscape, useDismissLayer } from \"../hooks/use-dismiss\";\nimport { isImeEnter } from \"../lib/ime\";\nimport { isQuietFocus } from \"../lib/quiet-focus\";\nimport { usePhoneLayout } from \"../hooks/use-breakpoint\";\nimport { PickerSheet, SHEET_ROW_CLASS } from \"./picker-sheet\";\nimport {\n anchoredHeadingClass,\n ComboboxFieldLabel,\n DISABLED_ROW_CLASS,\n endHintRowProps,\n GroupedOptions,\n groupRuns,\n isOptionEnabled,\n offersCreate,\n SHEET_HEADING_CLASS,\n stepEnabled,\n useTypedListStatus,\n type ComboClearValue,\n type ComboOption,\n} from \"./combobox-core\";\nimport { DEFAULT_COMBOBOX_LABELS, useKitLabels, useKitLocale } from \"../i18n/kit-labels\";\nimport { sameName, searchRanked } from \"../lib/text-match\";\nimport {\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 { hasContent } from \"../lib/presence\";\nimport { useCommitReason, type CommitScope } from \"./write-lock\";\nimport { lockedKeyDown, rowClass, SuggestionList, usePrimaryPressOnly } from \"./combobox-suggestions\";\n\n/** {@link InlineEntityCombobox}'s create row over the shared row look: in the brand\n * colour {@link Combobox}'s own create row wears, with a leading \"+\" as the panel\n * pickers' has — an action at the foot of the list, not one more record. */\nconst CREATE_ROW_CLASS = \"flex items-center gap-2 font-medium text-brand\";\n\n/** `id` is the INPUT's and `onChange` is the field's — see {@link ComboboxProps}\n * for why the div's spellings of them are omitted. */\nexport interface InlineEntityComboboxProps<V extends string | number, C extends ComboClearValue = null>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"id\" | \"onChange\">, FieldHelpProps {\n /** Selected option id; `null` or `clearValue` when nothing is selected. */\n value: V | C | null;\n /** Required and unanswered — see {@link Combobox}'s `invalid`. */\n invalid?: boolean;\n /** What is wrong with the value — see {@link Combobox}'s `error`. */\n error?: ReactNode;\n /** Standing advice — see {@link Combobox}'s `hint` (keksdose K4). */\n hint?: ReactNode;\n /** A picked/typed option emits its id; emptying the field, or the \"×\", emits\n * `clearValue`. */\n onChange: (v: V | C) => void;\n /** What a clear emits — `null` by default, `\"\"` for a schema that wants an empty\n * string. See {@link EntityCombobox}'s `clearValue`. */\n clearValue?: C;\n options: readonly ComboOption<V>[];\n /** The visible label — see {@link Combobox}'s `label` (a ReactNode since 0.34, API-9). */\n label?: ReactNode;\n id?: string;\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n /** Focus on mount, the way `<input autoFocus>` does — and, through `onFocus`\n * below, open the list with it. A click-the-value editor needs it: the cell\n * the user clicked names the field the editor should land the caret in, and\n * without a passthrough this component's fixed prop list puts the `<input>`\n * out of a caller's reach entirely (Keksdose feedback live #218). */\n autoFocus?: boolean;\n /** Phone sheet only — the anchored list has the field itself to type in. Falls\n * back to `placeholder`, then to `combobox.search` from the {@link UiKitProvider}. */\n searchPlaceholder?: string;\n /** What a query that finds nothing says: on the phone sheet, where a full screen\n * showing nothing has to say why, and in the field's live status (0.52) — the anchored\n * list itself simply does not open. Default: `combobox.noResults` from the provider. */\n emptyLabel?: string;\n closeLabel?: string;\n /** The phone sheet's heading (and, as a string, its accessible name) for a field\n * whose label is drawn by someone else — a bound field's label;\n * `RhfInlineEntityCombobox` hands its label over here (0.24). Default: `label`.\n * See {@link Combobox}'s `sheetTitle`. */\n sheetTitle?: ReactNode;\n /** Offer a clear \"×\" in place of the chevron whenever something is selected.\n *\n * Emptying the text already clears (see {@link reconcile}), and on a desktop that\n * is the fast way. It is not a way at all on a PHONE: touching the field opens the\n * full-screen sheet, which covers the very input the text would have been deleted\n * from — so a field that had been answered could not be UNanswered by any gesture\n * the screen offered (Keksdose live #236, filed from a phone). The \"×\" is the one\n * affordance both shells share, and it clears without opening anything. */\n clearable?: boolean;\n clearLabel?: string;\n /**\n * Why the choice cannot be changed — {@link Button}'s `disabledReason`, for a picker\n * that SAVES on change. keksdose G2: the statement review's account picker IS the\n * write (picking re-runs the duplicate check on the server), and on the read-only demo\n * the app wrapped it in a hand-made Tooltip over a native `disabled` — out of the tab\n * order, so the reason never reached a keyboard.\n *\n * With a reason the input stays focusable and `aria-disabled`, is `readOnly` (the\n * settled grey), opens no list or sheet, offers no clear \"×\", and no pick, typed\n * label or clear reaches `onChange`; the reason is in the kit {@link Tooltip} and on\n * the input's `aria-describedby`. Wins over `disabled`. Use it CONTROLLED, as the\n * component always is: the shown label is `value`'s.\n */\n disabledReason?: ReactNode;\n /**\n * This picker COMMITS — choosing saves. Under a locked {@link WriteLockProvider} it is\n * locked the `disabledReason` way with the lock's reason (which wins over its own).\n * A picker inside a form with its own Save stays editable under the lock — leave this\n * off there and put `commit` on the Save. No provider, or an unlocked one: no effect.\n */\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 /**\n * A last row that makes a new record from what was typed — keksdose G9, whose \"Create\n * cash account\" had to become a separate button because this picker had no such row.\n *\n * Shown while the typed query is non-empty and names no option exactly\n * (case-insensitive — an exact hit is a record the list already shows), worded\n * `createLabel(query)`, default `combobox.create`: \"Create “{query}”\". The arrow keys\n * reach it like an option; Enter or a click calls `onCreate(query)` and closes the\n * list. Making the record, and then passing its id as `value`, is the caller's: the\n * field shows the old selection until it does.\n *\n * The same rule as {@link EntityCombobox}'s `onCreate`, so the family agrees. It is a\n * row of the LIST, not an option: it is never `aria-selected`, never matched by a\n * typed label on blur, and never sorted among the records the way a sentinel option\n * would be (the reason keksdose gave for the separate button).\n */\n onCreate?: (query: string) => void;\n /** The create row's words for a query. Default: `combobox.create` from the\n * {@link UiKitProvider}, else English `Create “{query}”`. */\n createLabel?: (query: string) => string;\n /**\n * Offer the create row with NOTHING typed too, worded as this — keksdose's \"Create cash\n * account\", which mints an account under a default name and so needs no query.\n * `onCreate` then receives `\"\"`, and the caller supplies the name. Left out, the row\n * needs a query.\n *\n * One string rather than a switch: a row with no query has nothing to quote, so the\n * kit cannot word it — \"Create “”\" says nothing — and naming it is what turns it on.\n * With both, one handler serves both rows:\n *\n * ```tsx\n * onCreate={(name) => createCash(name || t(\"cash_account_default_name\"))}\n * createEmptyLabel={t(\"create_cash_account\")}\n * ```\n */\n createEmptyLabel?: string;\n /**\n * The create row WRITES while picking does not — keksdose dev#496: on the read-only\n * demo choosing the cash account is draft state and stays live, minting one is a\n * write. Under a locked {@link WriteLockProvider} the row stays in the list, dimmed and\n * passed over by the arrows, with the lock's reason as its second line (a hover is the\n * one explanation a phone cannot show), and `onCreate` is not called. Not needed with\n * `commit`: a locked field opens no list at all.\n */\n createCommit?: CommitScope;\n /**\n * The `<input>` — the focusable control, the one a reader types in — as a React 19\n * ref prop (0.24, kastlan). Its account picker is this component inside an\n * `RhfField` render, and react-hook-form's focus-on-error calls `focus()` on whatever\n * `field.ref` is handed: with no ref there was nothing to hand it, so a line-item form\n * whose account was missing failed its submit and left the caret where it was. Pass\n * `field.ref` here, or use `RhfInlineEntityCombobox`, which does. The wrapper is not\n * reachable this way — focus and selection are the input's. See {@link Combobox}'s `ref`.\n */\n ref?: Ref<HTMLInputElement>;\n}\n\n/**\n * The id-keyed sibling of {@link Combobox}: identical anatomy (a real text input\n * with the shared field styling, chevron and inline as-you-type filtering) but\n * the value is an option id, not free text. Built so an entity picker can sit\n * next to a free-text combobox without any visible difference — a button-trigger\n * panel picker ({@link EntityCombobox}) never matches an input field exactly.\n *\n * Text-vs-value reconciliation: the input's text is transient. Picking a row or\n * typing an exact (unique) label commits that option; emptying the text commits\n * a clear; anything else reverts to the selected option's label on blur/Escape.\n *\n * **On a phone the list opens as a full-screen sheet**, exactly like {@link Combobox}\n * beside it (Keksdose live #200, extended by dev#477 — the account picker moved onto\n * this component and must not lose its sheet). The sheet carries its OWN query state\n * rather than reusing `text`: `text` is what {@link reconcile} judges on blur, so a\n * sheet that emptied it to show the full list would read as \"the user cleared the\n * field\" the moment it closed.\n */\nexport function InlineEntityCombobox<V extends string | number, C extends ComboClearValue = null>(\n props: InlineEntityComboboxProps<V, C>,\n) {\n const {\n value: rawValue,\n onChange,\n clearValue = null as C,\n options,\n label,\n id,\n placeholder,\n className,\n disabled,\n autoFocus,\n searchPlaceholder,\n emptyLabel,\n closeLabel,\n sheetTitle,\n clearable,\n clearLabel,\n disabledReason,\n disabledReasonDisplay,\n commit,\n onCreate,\n createLabel,\n createEmptyLabel,\n createCommit,\n invalid,\n error,\n hint,\n help,\n ref,\n \"aria-label\": ariaLabel,\n // Off `rest` and onto the <input>, which is the combobox a reader meets: `Field`'s\n // render-prop spreads `{ id, aria-describedby, aria-invalid, aria-required }`, and\n // on the wrapper div the hint and required state described nothing.\n \"aria-describedby\": ariaDescribedBy,\n \"aria-invalid\": ariaInvalid,\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n // The clear value reads as \"nothing selected\", whichever one the caller picked —\n // so from here down `value` is the id or `null`, as it always was.\n const value: V | null = rawValue == null || rawValue === clearValue ? null : (rawValue as V);\n const hintParts = useFieldHint(hint, ariaDescribedBy, { help, name: label ?? ariaLabel });\n const field = useFieldError(error, invalid, hintParts.describedBy, ariaInvalid);\n const generated = useId();\n const fieldId = id ?? generated;\n // See the twin above on why these hang off the generated id.\n const listboxId = `${generated}-listbox`;\n const optionId = (index: number) => `${generated}-option-${index}`;\n const isPhone = usePhoneLayout();\n // See the twin above: the phone sheet owns its own Back entry (live #309), and the\n // Escape layer is this field's own.\n const { open, setOpen, wrapperRef, panelRef } = useDropdown({ backCloses: !isPhone, escapeLayer: false });\n const primaryOnly = usePrimaryPressOnly();\n // The field took the tour's quiet focus (§11.8) and has not opened since.\n const quietRef = useRef(false);\n const [active, setActive] = useState(-1);\n // null = not editing → the input shows the selected option's label.\n const [text, setText] = useState<string | null>(null);\n const sheetInputRef = useRef<HTMLInputElement | null>(null);\n // The box the portalled list hangs off — see {@link SuggestionList}.\n const fieldRef = useRef<HTMLDivElement>(null);\n // The sheet's search box. Starts empty on every open, so a field that already\n // holds a value still offers the whole list — the shape a native <select> has\n // on a phone, and what the anchored panel gets from `query` below.\n const [sheetQuery, setSheetQuery] = useState(\"\");\n // Props first, provider second — see `EntityCombobox`. `emptyLabel` used to render\n // nothing when omitted, which left a phone user staring at a blank full-screen\n // sheet; now an omitted one says \"No results\" in the provider's language.\n const labels = useKitLabels(\"combobox\", DEFAULT_COMBOBOX_LABELS, {\n search: searchPlaceholder,\n noResults: emptyLabel,\n clear: clearLabel,\n create: createLabel,\n });\n // The lock (see `lockedKeyDown` above). A lock arriving mid-edit closes the list and\n // drops the loose text, adjusted while rendering: nothing typed before it may be\n // judged by `reconcile` after it.\n const lock = useLockReason(commit, disabledReason);\n const locked = lock.locked;\n const inert = locked || Boolean(disabled);\n if (locked && (open || text !== null)) {\n setOpen(false);\n setText(null);\n }\n // The create row's own lock (`createCommit`): the list stays live, the row does not.\n const createReason = useCommitReason(createCommit, undefined);\n const createLocked = hasContent(createReason);\n\n const selected = useMemo(\n () => (value == null ? null : (options.find((o) => o.value === value) ?? null)),\n [options, value],\n );\n const shown = text ?? selected?.label ?? \"\";\n // Nothing selected has nothing to clear, and the chevron comes back — the field\n // keeps exactly one trailing control, so the \"×\" never crowds the value it sits on.\n const showClear = Boolean(clearable && value != null && !inert);\n\n // Focusing select-alls the current label; filtering only kicks in once the\n // text actually differs from it, so an already-filled field still opens on\n // the FULL list instead of a single-row \"filter\" of its own value.\n const typedQuery = text !== null && text !== (selected?.label ?? \"\") ? text.trim() : \"\";\n const query = isPhone ? sheetQuery.trim() : typedQuery;\n // Folded as every kit list folds (DUP-5): accents off and the locale's case, so\n // \"zurich\" finds Zürich and, in Turkish, \"ı\" finds \"IRMAK\".\n const locale = useKitLocale();\n const matches = useMemo(() => {\n let ranked = options;\n if (query) {\n // Every word of the query, in the label or the sublabel (0.51.4). The label's whole\n // name or its start first (rank 0 and 1), then a match anywhere in the label or the\n // sublabel; a stable sort keeps the options' order within each. Not the group\n // (0.40, kk run 79): with first-letter groups one typed letter matched its whole\n // group. Each option folded once (`searchRanked`).\n ranked = searchRanked(options, query, (o) => [o.label, o.sublabel], { locale })\n .sort((a, b) => Number(a.rank > 1) - Number(b.rank > 1))\n .map((x) => x.row);\n }\n if (!ranked.some((o) => o.group)) return ranked;\n // Keep each group contiguous so the headings below appear once instead of\n // re-appearing every time the ranking interleaves two groups — while preserving\n // \"best match first\": a Map keeps insertion order, so groups come out ordered by\n // their best-ranked member and members keep their rank order inside the group.\n const blocks = new Map<string, ComboOption<V>[]>();\n for (const o of ranked) {\n const key = o.group ?? \"\";\n if (!blocks.has(key)) blocks.set(key, []);\n blocks.get(key)!.push(o);\n }\n return [...blocks.values()].flat();\n }, [options, query, locale]);\n\n // The create row (`onCreate`), on the query as typed — not lowercased: it becomes a\n // name. Compared against EVERY option, not `matches`: an exact hit anywhere is a\n // record that exists, disabled or not. Restated from `text` rather than derived from\n // `typedQuery`: this value is handed to the caller's `onCreate`, and the React\n // Compiler cannot then keep `matches` memoised on anything it was derived from.\n const createQuery = isPhone\n ? sheetQuery.trim()\n : text !== null && text !== (selected?.label ?? \"\")\n ? text.trim()\n : \"\";\n const showCreate = offersCreate(\n onCreate,\n createQuery,\n options.map((o) => o.label),\n createEmptyLabel,\n locale,\n );\n const createText = createQuery ? labels.create(createQuery) : createEmptyLabel;\n // Every row the keyboard can land on: the options, then the create row — passed over\n // like a disabled option while a write lock holds it.\n const rows: readonly { disabled?: boolean }[] = showCreate ? [...matches, { disabled: createLocked }] : matches;\n const createIndex = matches.length;\n\n const close = () => {\n setOpen(false);\n setActive(-1);\n setSheetQuery(\"\");\n };\n // Whether a list shows, the row the keyboard is on — in the anchored list or the phone\n // sheet, whose search box takes the same keys (INP-6) — and the live status, as\n // Combobox has them. A disabled row is never the keyboard's, even if the list changed\n // under it.\n const { showing, expanded, activeOption, statusRegion } = useTypedListStatus({\n isPhone,\n open,\n inert,\n rowCount: matches.length,\n extraRow: showCreate,\n query,\n active: isOptionEnabled(rows[active]) ? active : -1,\n optionId,\n labels,\n });\n // A layer while the list SHOWS, and Escape puts the label back.\n useDismissLayer(showing, () => {\n setText(null);\n close();\n });\n const take = (o: ComboOption<V>) => {\n // A `disabled` option is listed, never taken — by any path; nor is anything while\n // the field is locked.\n if (o.disabled || locked) return;\n if (o.value !== value) onChange(o.value);\n setText(null);\n close();\n };\n /** Turn loose text into a decision: empty clears, an exact label match that names\n * ONE entity commits, anything else reverts to the selected label.\n *\n * Unique by VALUE, not by row. An option may deliberately appear twice — Keksdose\n * repeats recently-used categories in a \"Recent\" group at the top (live #203) — and\n * counting rows made every such option uncommittable: two hits, so nothing fired and\n * the field reverted to whatever was selected before. Typing a category you had just\n * used, then tabbing away, silently discarded it, and the more categories you used\n * the more of them stopped working. Two rows naming the same id are not an ambiguity;\n * two ids sharing a label are. */\n const create = () => {\n if (!onCreate || locked || createLocked) return;\n onCreate(createQuery);\n setText(null);\n close();\n };\n // The ONE option a name typed out in full names, else null. A disabled option's label\n // is not a hit — typing it is not a way round `disabled` — and a label is typed out in\n // full as the list matches it, so \"cafe\" names Café (DUP-5).\n const exactHit = (q: string): ComboOption<V> | null => {\n const hits = options.filter((o) => !o.disabled && sameName(o.label, q, locale));\n return new Set(hits.map((h) => h.value)).size === 1 ? hits[0] : null;\n };\n const reconcile = () => {\n // Locked, loose text is dropped rather than judged: nothing reaches `onChange`.\n if (text !== null && !locked) {\n const q = text.trim();\n if (!q) {\n if (value != null) onChange(clearValue);\n } else {\n // Anything but a hit reverts, like any other non-answer.\n const hit = exactHit(q);\n if (hit && hit.value !== value) onChange(hit.value);\n }\n }\n setText(null);\n close();\n };\n // The phone sheet's way out — the X, Escape, Back — keeps the value and judges nothing.\n // Not `reconcile`: on a phone `text` is never typed in (the sheet's own box is), so it\n // only holds what the field showed at focus, and that is \"\" for a value whose option\n // has not loaded yet — judged, it read as \"the user emptied the field\" and the X\n // cleared the answer (0.52 code review, inline-entity-combobox.tsx).\n const dismissSheet = () => {\n setText(null);\n close();\n };\n\n /** The list's keys — in the field and, on a phone, in the sheet's search box (INP-6).\n * Disabled rows are passed over; Up from \"nothing highlighted\" lands on the first\n * takeable row, as it always landed on row 0. The create row is the last row the\n * arrows reach. */\n const listKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setOpen(true);\n setActive((i) => stepEnabled(rows, i, 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActive((i) => (i < 0 ? stepEnabled(rows, -1, 1) : stepEnabled(rows, i, -1)));\n } else if (e.key === \"Enter\") {\n // An IME's Enter takes nothing, and judges no loose text (BUG-9).\n if (isImeEnter(e)) return;\n if (open && isOptionEnabled(rows[active])) {\n e.preventDefault();\n if (active === createIndex && showCreate) create();\n else take(matches[active]);\n } else if (isPhone) {\n // The sheet's search box: its QUERY is what was typed, and a name typed out in\n // full is taken, as the desktop field takes it. Anything else leaves the sheet\n // and its search as they are — the panel pickers' sheet does the same. This was\n // `reconcile`, which judged the field's text from before the sheet opened and\n // closed: the search was thrown away and \"Groceries\" typed out was not taken\n // (0.52 code review, inline-entity-combobox.tsx).\n const hit = exactHit(sheetQuery.trim());\n if (hit) {\n e.preventDefault();\n take(hit);\n }\n } else {\n reconcile();\n }\n }\n };\n\n // The create row's inside, one for both shells: a \"+\" (it is an action, not a record),\n // the words, and — while a write lock holds it — the lock's reason as a second line.\n // Both lines wrap from Large up (text size §10.17, round 0.34 T1): the words hold what\n // the reader typed, which an ellipsis would cut.\n const createRowContent = (\n <>\n <Plus aria-hidden className=\"size-4 shrink-0\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate-until-large\">{createText}</span>\n {createLocked && (\n <span className=\"block truncate-until-large text-xs font-normal text-placeholder\">{createReason}</span>\n )}\n </span>\n </>\n );\n // While locked the field sits in the reason's Tooltip — see Combobox.\n const withLock = (box: ReactNode) => (\n <LockFrame lock={lock} display={disabledReasonDisplay}>\n {box}\n </LockFrame>\n );\n\n /** The rows in their runs, each group a `role=\"group\"` its heading names (INP-8), for\n * the sheet or the anchored list — a group heading at each boundary rather than the\n * group on every row (feedback #136). `matches` is group-contiguous, so comparing with\n * the row above is enough. An option may deliberately appear in two groups (see\n * `reconcile`): a value is a row's key within its group. */\n const groupedRows = (sheet: boolean, row: (o: ComboOption<V>, i: number) => ReactNode) => (\n <GroupedOptions\n runs={groupRuns(matches, (o) => o.group)}\n listboxId={listboxId}\n rowKey={(o) => String(o.value)}\n row={row}\n headingClassName={(run) => (sheet ? SHEET_HEADING_CLASS : anchoredHeadingClass(run))}\n />\n );\n\n return (\n // `rest` dresses the outer box — a `data-tour` anchor, a test id. Not the NAME,\n // description, invalid or required state: those belong on the <input> below,\n // which is the combobox a reader meets. Spread FIRST, so the field's ARIA and the\n // handlers carrying live #309 and dev#549 cannot be replaced from outside.\n <div {...rest} ref={wrapperRef} className={cn(\"relative\", className)}>\n {/* A real <label for> — see {@link Combobox}; with a \"?\", in a row after the field. */}\n {label !== undefined && hintParts.labelHint === undefined && (\n <ComboboxFieldLabel htmlFor={fieldId} className={inert ? \"opacity-50\" : undefined}>\n {label}\n </ComboboxFieldLabel>\n )}\n <EndHintRow {...endHintRowProps(\"hint\" in props, label !== undefined, hintParts.labelHint)}>\n {withLock(\n /* Inner wrapper for chevron centering — same reasoning as Combobox above.\n It is also what the portalled list anchors to.\n Dimmed as a whole when disabled (or locked), the way EntityCombobox dims its\n trigger. FIELD_BASE's grey alone left a disabled picker looking like a\n filled-in one beside the pickers that do dim (the label is not the input's\n `peer`, so it is dimmed by hand above). */\n <div ref={fieldRef} className={cn(\"relative\", inert && \"cursor-not-allowed opacity-50\")}>\n <input\n ref={ref}\n id={fieldId}\n value={shown}\n placeholder={placeholder}\n // The caller's name, over the <label for> above. Just the label, never\n // \"label: value\" the way a trigger button has to compose it: an input\n // already exposes its value separately.\n aria-label={ariaLabel}\n role=\"combobox\"\n aria-expanded={expanded}\n // Required by the role, and the half that was missing: the field said it\n // was expanded and never said what it had expanded (ESLint's\n // `role-has-required-aria-props`, the audit's §a11y).\n aria-controls={listboxId}\n // Pointer-device only: on a phone the sheet's own search box holds focus\n // and names the row; the field behind it must not claim one.\n aria-activedescendant={isPhone ? undefined : activeOption}\n aria-autocomplete=\"list\"\n aria-invalid={field.isInvalid || undefined}\n aria-describedby={mergeDescribedBy(field.describedBy, locked && lock.reasonId)}\n aria-required={ariaRequired}\n autoComplete=\"off\"\n // Locked: focusable, `readOnly`, `aria-disabled` — see `lockedKeyDown`.\n disabled={locked ? undefined : disabled}\n readOnly={locked || undefined}\n aria-disabled={locked || undefined}\n // eslint-disable-next-line jsx-a11y/no-autofocus -- a documented prop the caller opts into (off by default); the field never takes focus on its own.\n autoFocus={autoFocus}\n // `inputMode=\"none\"` rather than readOnly, for the same reason Combobox\n // above gives: the sheet carries the keyboard, and a readOnly field would\n // take FIELD_BASE's settled look on a field that is perfectly editable.\n inputMode={isPhone ? \"none\" : undefined}\n onMouseDown={primaryOnly.onMouseDown}\n onFocus={(e) => {\n // See {@link usePrimaryPressOnly}: a back/forward button lands here on\n // its way to navigating, and neither the list nor the select-all is\n // anything it asked for (live #309 rework). A locked field takes focus to\n // say why, and opens nothing.\n if (primaryOnly.fromAuxButton() || locked) return;\n setText(shown);\n // The tour's focus on arrival (§11.8) opens nothing: the list would cover\n // its card (0.50.1). A click, ArrowDown or typing opens it.\n if (isQuietFocus(e.currentTarget)) {\n quietRef.current = true;\n return;\n }\n e.currentTarget.select();\n setOpen(true);\n // The sheet has its own input; the field behind it must not also pull up\n // the keyboard and scroll the page under the dialog.\n if (isPhone) sheetInputRef.current?.focus();\n }}\n // After a quiet focus, the person's click on the already-focused field\n // fires no `focus`: the click is their \"open\" instead.\n onClick={() => {\n if (!quietRef.current || locked) return;\n quietRef.current = false;\n setOpen(true);\n }}\n onBlur={() => {\n quietRef.current = false;\n // On a phone the blur is the SHEET taking focus, not the user leaving the\n // field — reconciling there would close the sheet the instant it opened.\n if (!isPhone) reconcile();\n }}\n onChange={(e) => {\n if (locked) return;\n setText(e.target.value);\n setOpen(true);\n setActive(-1);\n }}\n onKeyDown={(e) => {\n if (locked) return lockedKeyDown(e);\n listKeyDown(e);\n if (e.key === \"Escape\") {\n // A showing list takes the key and stops it — see the twin in\n // {@link Combobox} (round 0.34 BUG-1).\n if (showing && takeEscape(e)) return;\n // With no list, putting loose text back is this field's own undo step,\n // and the step is all the key does: claimed, so the dialog around the\n // field stays — it closed with the form in it (refactor audit\n // 2026-10-30, INP-5; ChipInput's draft is the rule). With nothing to\n // undo the key goes on, as before.\n if (text !== null && text !== (selected?.label ?? \"\")) claimEscape(e);\n setText(null);\n close();\n } else if (e.key === \"Tab\") {\n // Focus is in THIS input and stays there, so the browser's own Tab is\n // left alone; all that is needed is that the list stop covering what\n // the user is tabbing to. `reconcile` rather than `close`, because\n // leaving the field is exactly when loose text has to be judged.\n reconcile();\n }\n // Home/End stay with the caret — see the note in {@link Combobox}: this\n // field is editable, and its text is what `reconcile` judges.\n }}\n className={cn(\n FIELD_BASE,\n label !== undefined && FIELD_FLOATING_PAD,\n \"pe-9\",\n field.isInvalid && FIELD_INVALID,\n )}\n />\n {showClear ? (\n <button\n type=\"button\"\n // Out of the tab order, like the clear on `EntityCombobox`: the keyboard\n // already clears this field by selecting its text and deleting, and a\n // second stop between every picker and the next field is a worse trade\n // than the one gesture it saves.\n tabIndex={-1}\n aria-label={labels.clear}\n // preventDefault, exactly as the chevron does: without it the press\n // focuses the input, which on a phone opens the sheet over the field the\n // press was clearing.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => {\n setText(null);\n close();\n if (!locked) onChange(clearValue);\n }}\n className={cn(\n \"absolute end-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-placeholder\",\n \"hover:text-secondary\",\n )}\n >\n <X aria-hidden className=\"size-4\" />\n </button>\n ) : (\n <ChevronDown\n aria-hidden\n onMouseDown={(e) => {\n // Toggle on the chevron without stealing focus from the input — and\n // not at all on a disabled or locked field, which the input's own\n // `disabled`/`readOnly` does not stop from here.\n e.preventDefault();\n if (!inert) setOpen((o) => !o);\n }}\n className={cn(\n \"absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-placeholder\",\n // The box above does the dimming; dimming here too would halve it again.\n !inert && \"cursor-pointer\",\n )}\n />\n )}\n </div>,\n )}\n </EndHintRow>\n {label !== undefined && hintParts.labelHint !== undefined && (\n <StaticLabelRow hint={hintParts.labelHint}>\n <ComboboxFieldLabel htmlFor={fieldId} className={cn(LABEL_IN_ROW, inert && \"opacity-50\")}>\n {label}\n </ComboboxFieldLabel>\n </StaticLabelRow>\n )}\n <FieldCaption parts={hintParts} />\n {field.errorEl}\n {/* The live status — see `useTypedListStatus` for where it goes and why. */}\n {!isPhone && statusRegion}\n {isPhone && (\n <PickerSheet\n open={open && !inert}\n // Closing without choosing keeps the value — see `dismissSheet`.\n onClose={dismissSheet}\n title={sheetTitle ?? label}\n query={sheetQuery}\n onQueryChange={(v) => {\n setSheetQuery(v);\n setActive(-1);\n }}\n searchPlaceholder={searchPlaceholder ?? placeholder ?? labels.search}\n inputRef={sheetInputRef}\n closeLabel={closeLabel}\n // The field's own keys, in the box focus is in while the sheet is up: arrows\n // and Enter there did nothing (INP-6, as BUG-5 gave the panel pickers).\n onSearchKeyDown={listKeyDown}\n listboxId={listboxId}\n activeId={activeOption}\n >\n <ul id={listboxId} role=\"listbox\">\n {groupedRows(true, (o, i) => (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(i)}\n role=\"option\"\n aria-selected={o.value === value}\n aria-disabled={o.disabled || undefined}\n onClick={() => take(o)}\n className={cn(\n SHEET_ROW_CLASS,\n // The row the sheet's arrows are on.\n i === active && !o.disabled && \"bg-active\",\n o.value === value && \"font-medium\",\n o.disabled && DISABLED_ROW_CLASS,\n )}\n >\n {o.label}\n </button>\n </li>\n ))}\n {showCreate && (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(createIndex)}\n role=\"option\"\n // An action, never chosen — see `onCreate`.\n aria-selected={false}\n aria-disabled={createLocked || undefined}\n onClick={create}\n className={cn(\n SHEET_ROW_CLASS,\n CREATE_ROW_CLASS,\n active === createIndex && !createLocked && \"bg-active\",\n createLocked && DISABLED_ROW_CLASS,\n )}\n >\n {createRowContent}\n </button>\n </li>\n )}\n {/* \"Nothing matched\" only when there is nothing to do either: with a create\n row the list is not empty, it is offering to fill itself. A picture: the\n status below says it to a reader. */}\n {matches.length === 0 && !showCreate && (\n <li aria-hidden className=\"px-4 py-3 text-sm text-muted\">\n {labels.noResults}\n </li>\n )}\n </ul>\n {statusRegion}\n </PickerSheet>\n )}\n {showing && (\n <SuggestionList id={listboxId} anchorRef={fieldRef} panelRef={panelRef}>\n {groupedRows(false, (o, i) => (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(i)}\n // See {@link Combobox}: the role rides the button, `aria-selected` is the\n // CHOSEN row, and the keyboard's row is the field's\n // `aria-activedescendant`.\n role=\"option\"\n aria-selected={o.value === value}\n aria-disabled={o.disabled || undefined}\n tabIndex={-1}\n onMouseDown={(e) => {\n // mousedown (not click) so the input's blur can't close the list\n // before the selection registers. Prevented on a disabled row too, so\n // pressing one leaves the field focused and open.\n e.preventDefault();\n take(o);\n }}\n onMouseEnter={() => {\n if (!o.disabled) setActive(i);\n }}\n className={cn(\n rowClass(i === active && !o.disabled),\n o.disabled && DISABLED_ROW_CLASS,\n // Indented under its heading, so the hierarchy is readable at a glance\n // instead of inferred from grey trailing text.\n o.group && \"ps-6\",\n o.value === value && \"font-medium\",\n )}\n >\n {o.label}\n {o.sublabel && <span className=\"ms-2 text-xs text-placeholder\">{o.sublabel}</span>}\n </button>\n </li>\n ))}\n {showCreate && (\n <li role=\"presentation\">\n <button\n type=\"button\"\n id={optionId(createIndex)}\n role=\"option\"\n // An action, never chosen; whether the keyboard is ON it is the field's\n // `aria-activedescendant`'s to say.\n aria-selected={false}\n aria-disabled={createLocked || undefined}\n tabIndex={-1}\n onMouseDown={(e) => {\n // mousedown, like the rows above: the input's blur would otherwise\n // reconcile and close the list before the click landed.\n e.preventDefault();\n create();\n }}\n onMouseEnter={() => {\n if (!createLocked) setActive(createIndex);\n }}\n className={cn(\n rowClass(active === createIndex && !createLocked),\n CREATE_ROW_CLASS,\n createLocked && DISABLED_ROW_CLASS,\n )}\n >\n {createRowContent}\n </button>\n </li>\n )}\n </SuggestionList>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -9,7 +9,7 @@ import { useKitLabels } from "../i18n/kit-labels.js";
9
9
  import {
10
10
  EndHintRow,
11
11
  FieldCaption,
12
- LockedReason,
12
+ LockFrame,
13
13
  mergeDescribedBy,
14
14
  useFieldError,
15
15
  useFieldHint,
@@ -103,7 +103,7 @@ function Input(props) {
103
103
  onKeyDown: swallowEnter(rest.onKeyDown)
104
104
  } : void 0;
105
105
  const boxClass = locked ? void 0 : className;
106
- const withReason = (box) => locked ? /* @__PURE__ */ jsx(LockedReason, { lock, display: disabledReasonDisplay, className: cn("block", className), children: box }) : box;
106
+ const withReason = (box) => /* @__PURE__ */ jsx(LockFrame, { lock, display: disabledReasonDisplay, className, children: box });
107
107
  const asDisplay = usePhoneLayout() && variant === "display";
108
108
  const isPassword = type === "password";
109
109
  const [revealed, setRevealed] = useState(false);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/input.tsx"],"sourcesContent":["import { useId, useState } from \"react\";\nimport { Eye, EyeOff } from \"lucide-react\";\nimport type { ChangeEvent, InputHTMLAttributes, KeyboardEvent, MouseEvent, ReactNode, Ref } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { FOCUS_RING } from \"./focus-ring\";\nimport { usePhoneLayout } from \"../hooks/use-breakpoint\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport type { CommitScope } from \"./write-lock\";\nimport {\n EndHintRow,\n FieldCaption,\n LockedReason,\n mergeDescribedBy,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n useFieldError,\n useFieldHint,\n useLockReason,\n} from \"./field-parts\";\nimport { DEFAULT_PASSWORD_REVEAL_LABELS } from \"./field-base-labels\";\nimport {\n CharacterCount,\n FIELD_BASE,\n FIELD_DISPLAY,\n FIELD_INVALID,\n FieldGroup,\n FloatingField,\n FLOATING_INPUT_CLASS,\n FLOATING_PLACEHOLDER_ON_FOCUS,\n fieldBelow,\n hasPlaceholderText,\n useCharacterCountLabels,\n useTypedLength,\n type CharacterCountLabels,\n type PasswordRevealLabels,\n} from \"./field-base\";\n\n// Native date/time inputs only reveal the calendar via the tiny trailing icon;\n// open the picker on a click anywhere in the field instead (feedback #224).\nconst PICKER_TYPES = new Set([\"date\", \"datetime-local\", \"month\", \"time\", \"week\"]);\n\nexport interface InputProps extends InputHTMLAttributes<HTMLInputElement>, FieldHelpProps {\n label?: ReactNode;\n /** Classes for the `<input>` itself, as distinct from `className`, which\n * styles the field WRAPPER once a `label` turns this into a FloatingField.\n * Without it a labelled Input had no way to reach its own element — so\n * `tabular-nums` on a numeric text field, which NumberInput has supported\n * all along through the identically named prop, was simply unavailable. */\n inputClassName?: string;\n /** {@link FIELD_DISPLAY} — on a phone, drop the chrome and set the value as a\n * heading. For the one field a form is about (a feedback subject, an account\n * name), never for a stack of them. Labelled fields only: the label is what\n * the placeholder falls back to once it goes `sr-only`. */\n variant?: \"field\" | \"display\";\n /** The field is required and unanswered, or holds something that cannot be\n * saved — {@link FIELD_INVALID}, the same rose border/ring `Select` has worn\n * since feedback #235.\n *\n * It exists here because writing `aria-invalid` by hand did NOT do this. The\n * attribute spreads onto the element and nothing styles it — there is no\n * `[aria-invalid]` rule in this package or in either consumer's stylesheet —\n * so three Keksdose dialogs flagged a mismatched passphrase to a screen reader\n * and painted the field exactly as if it were fine. Setting the prop sets the\n * attribute too, so the two can no longer be spelled separately. */\n invalid?: boolean;\n /** What is wrong with the value, in the caller's own words (\"That IBAN has 21\n * digits\"). Rendered under the field, pointed at by the control's\n * `aria-describedby` — MERGED with any the caller already passed — and implies\n * `invalid`, so the field paints as well as announces. `invalid` alone still\n * covers the case where the message lives elsewhere. See {@link useFieldError}. */\n error?: ReactNode;\n /**\n * Standing advice for the field (keksdose K4: Input had `error` but no `hint`, so the\n * app built the caption by hand under each field, and half of those never reached a\n * screen reader). The {@link Select} rule, so every field reads alike:\n * - plain TEXT (a string or a number) is a caption UNDER the field, attached with\n * `aria-describedby` — after the caller's own ids, before the error's;\n * - anything else (a {@link FieldHint} \"?\") rides the label line beside the\n * animated label, as NumberInput's does — or, on a field with no label, sits at the\n * field's end edge outside the box, and `className` then styles that row.\n */\n hint?: ReactNode;\n /**\n * A live \"12/80\" under the field, with `maxLength` (keksdose K11 — the broadcast,\n * support and handoff-note fields counted by hand). Read with the field as \"12 of 80\n * characters\", and announced politely only on entering the last stretch and on\n * reaching the limit — never on every keystroke. See {@link CharacterCount}. Ignored\n * without a `maxLength`: a count with no limit is not what this answers. Counts\n * UTF-16 code units, as `maxLength` itself does. Controlled, it counts `value`;\n * uncontrolled, `defaultValue` and then every change event — a value written from\n * outside without one (a form library's `reset`) shows from the next keystroke.\n */\n showCount?: boolean;\n /** The counter's words. See {@link CharacterCountLabels}. */\n countLabels?: Partial<CharacterCountLabels>;\n /** Names for the password reveal toggle, English by default — it is the one\n * string this component renders on its own behalf, and a German form was\n * reading it out in English. See {@link PasswordRevealLabels}. */\n passwordLabels?: Partial<PasswordRevealLabels>;\n /**\n * Why the value cannot be changed — {@link Button}'s `disabledReason`, for a text field\n * that SAVES itself: a name committed on blur (0.34, KS U10 — Kurvenschmiede's segment\n * and profile names, behind a local `CommitInput`). `disabled` takes the field out of\n * the tab order, so the reason never reached a keyboard.\n *\n * With a reason the field stays in the tab order and shows its value, `readOnly` (the\n * settled grey FIELD_BASE gives a `[readonly]` field) and `aria-disabled` — NumberInput's\n * lock (0.33.1), Autocomplete's. Nothing a save listens to is reported: not `onChange`,\n * not `onBlur` (on a field that saves itself, blur IS the save), and not Enter, which is\n * swallowed so the field does not submit the form around it; the caller's `onKeyDown`\n * still hears every other key. The reason is a line under the field at Large and on a\n * touch screen, else in the kit {@link Tooltip} (`disabledReasonDisplay`), and on the\n * input's `aria-describedby`, between the caption and the error. Wins over `disabled`.\n */\n disabledReason?: ReactNode;\n /**\n * Where the reason shows — {@link Button}'s `disabledReasonDisplay`: by default a line\n * under the field at Large and on a touch screen, the tooltip otherwise (§4 \"No fact\n * only in a tooltip\"; DUP-3, refactor audit 2026-10).\n */\n disabledReasonDisplay?: DisabledReasonDisplay;\n /**\n * This field COMMITS — {@link Button}'s `commit`: under a locked\n * {@link WriteLockProvider} it is locked the `disabledReason` way with the lock's reason\n * (which wins over its own). A field whose value only reaches the server through its\n * form's Save leaves this off and stays editable under the lock — the Save takes\n * `commit`. No provider, or an unlocked one: no effect.\n */\n commit?: CommitScope;\n /** The `<input>`. A React 19 ref prop since 0.34 (MOD-4, refactor audit 2026-10): as\n * `forwardRef`, with a `displayName` line beside it, this module could not drop an\n * unused component, and `{ Spinner }` brought Input, Select and Textarea along. */\n ref?: Ref<HTMLInputElement>;\n}\n\n/** What a locked {@link Input} swallows of a key: Enter, which would submit the form. */\nfunction swallowEnter(onKeyDown: ((e: KeyboardEvent<HTMLInputElement>) => void) | undefined) {\n return (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n return;\n }\n onKeyDown?.(e);\n };\n}\n\nexport function Input(props: InputProps) {\n const {\n ref,\n className,\n inputClassName,\n label,\n id,\n placeholder,\n type,\n variant = \"field\",\n invalid,\n error,\n hint,\n help,\n showCount,\n countLabels,\n passwordLabels,\n disabledReason,\n disabledReasonDisplay,\n commit,\n ...rest\n } = props;\n // Passed at all — even as `undefined` — the field keeps its box; see FieldGroup. The\n // same for `hint` and `showCount` (keksdose, 0.22): a caption coming and going under\n // the control must not rebuild it, or focus and the caret are lost mid-edit.\n const reserve = \"error\" in props || \"hint\" in props || \"showCount\" in props;\n const generated = useId();\n const fieldId = id ?? generated;\n const countId = useId();\n const hintParts = useFieldHint(hint, rest[\"aria-describedby\"], { help, name: label ?? rest[\"aria-label\"] });\n const maxLength = rest.maxLength;\n const counting = Boolean(showCount) && typeof maxLength === \"number\" && maxLength > 0;\n const length = useTypedLength(rest.value, rest.defaultValue);\n const countText = useCharacterCountLabels(countLabels);\n // The lock (0.34, KS U10): focusable, `readOnly`, nothing a save listens to is\n // reported — see `disabledReason`.\n const lock = useLockReason(commit, disabledReason);\n const locked = lock.locked;\n const { isInvalid, describedBy, errorEl } = useFieldError(\n error,\n invalid,\n // Caller's, caption, count, a lock's reason — then the error, appended by the hook.\n mergeDescribedBy(hintParts.describedBy, counting && countId, locked && lock.reasonId),\n rest[\"aria-invalid\"],\n );\n const counter = counting ? (\n <CharacterCount\n id={countId}\n used={length.used}\n max={maxLength}\n labels={countText}\n caption={<FieldCaption parts={hintParts} className=\"mt-0\" />}\n />\n ) : null;\n const below = fieldBelow(hintParts, counter, errorEl);\n const onChange = locked\n ? undefined\n : counting\n ? (e: ChangeEvent<HTMLInputElement>) => {\n length.track(e.target.value);\n rest.onChange?.(e);\n }\n : rest.onChange;\n // Locked, these win over what the caller's spread carried: focusable rather than\n // `disabled`, `readOnly`, no blur reported (blur is a self-saving field's save), and\n // Enter swallowed. Spread AFTER `rest` on every branch's input.\n const lockProps = locked\n ? {\n disabled: undefined,\n readOnly: true,\n \"aria-disabled\": true as const,\n onBlur: undefined,\n onKeyDown: swallowEnter(rest.onKeyDown),\n }\n : undefined;\n // While locked the field sits in the reason's frame (the line, or the Tooltip), which\n // becomes its outermost box — so `className` (a width, a flex or grid cell) moves onto\n // that, as on Select, and the form keeps its layout. `block` so a full-width field\n // stays full width in it.\n const boxClass = 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 asDisplay = usePhoneLayout() && variant === \"display\";\n // Password fields get a reveal toggle so users can check what they typed.\n const isPassword = type === \"password\";\n const [revealed, setRevealed] = useState(false);\n const effectiveType = isPassword && revealed ? \"text\" : type;\n const handleClick = PICKER_TYPES.has(type ?? \"\")\n ? (e: MouseEvent<HTMLInputElement>) => {\n rest.onClick?.(e);\n try {\n // showPicker throws if unsupported or not user-activated — a click is\n // a valid activation, so this is safe; guard for older browsers.\n (e.currentTarget as HTMLInputElement & { showPicker?: () => void }).showPicker?.();\n } catch {\n /* ignore */\n }\n }\n : rest.onClick;\n const passwordText = useKitLabels(\"passwordReveal\", DEFAULT_PASSWORD_REVEAL_LABELS, passwordLabels);\n const revealToggle = isPassword ? (\n <button\n type=\"button\"\n // `tabIndex={-1}` sat here, which made this a painted, clickable control that\n // Tab stepped straight over — on the one field whose value cannot be checked by\n // looking at it. There is no mouse-only case for a reveal toggle; the people who\n // cannot see what they typed are exactly who it is for. It is a tab stop now,\n // and it needs a focus ring of its own, because FIELD_BASE's ring belongs to the\n // input underneath and stays put while focus moves onto the button on top of it.\n onClick={() => setRevealed((v) => !v)}\n // …and since it is a tab stop, a field the user may not edit must not hand the\n // keyboard a control that shows what is in it — a locked one included.\n disabled={rest.disabled || locked}\n aria-label={revealed ? passwordText.hide : passwordText.show}\n aria-pressed={revealed}\n className={cn(\n \"absolute inset-y-0 end-0 flex items-center rounded-e-md px-2.5 text-placeholder transition-colors hover:text-secondary focus:outline-none disabled:cursor-default disabled:opacity-50\",\n FOCUS_RING,\n )}\n >\n {revealed ? <EyeOff className=\"size-4\" /> : <Eye className=\"size-4\" />}\n </button>\n ) : null;\n if (label === undefined) {\n // A \"?\" with no label line to ride goes at the end edge, outside the box, and\n // `className` moves to that row (see `hint`); without one nothing moves.\n const endHint = hintParts.labelHint;\n const ownClass = endHint === undefined ? boxClass : undefined;\n if (!isPassword) {\n return (\n <FieldGroup errorEl={below} reserve={reserve}>\n {withReason(\n <EndHintRow hint={endHint} className={boxClass}>\n <input\n ref={ref}\n id={id}\n type={type}\n placeholder={placeholder}\n {...rest}\n {...lockProps}\n // AFTER the spread, so the prop wins — but OR-ed with whatever the spread\n // carried, or setting `invalid` would have quietly deleted a caller's own\n // `aria-invalid`. The prop is the one that also paints; a bare attribute\n // still announces, which is all it ever did.\n aria-invalid={isInvalid || rest[\"aria-invalid\"] || undefined}\n // Likewise merged rather than replaced — see {@link useFieldError}.\n aria-describedby={describedBy}\n onClick={handleClick}\n onChange={onChange}\n className={cn(\n FIELD_BASE,\n ownClass,\n inputClassName,\n isInvalid && FIELD_INVALID,\n locked && \"cursor-not-allowed\",\n )}\n />\n </EndHintRow>,\n )}\n </FieldGroup>\n );\n }\n return (\n <FieldGroup errorEl={below} reserve={reserve}>\n {withReason(\n <EndHintRow hint={endHint} className={boxClass}>\n <div className={cn(\"relative\", ownClass)}>\n <input\n ref={ref}\n id={id}\n type={effectiveType}\n placeholder={placeholder}\n {...rest}\n {...lockProps}\n aria-invalid={isInvalid || rest[\"aria-invalid\"] || undefined}\n aria-describedby={describedBy}\n onChange={onChange}\n className={cn(\n FIELD_BASE,\n \"pe-9\",\n inputClassName,\n isInvalid && FIELD_INVALID,\n locked && \"cursor-not-allowed\",\n )}\n />\n {revealToggle}\n </div>\n </EndHintRow>,\n )}\n </FieldGroup>\n );\n }\n const ownPlaceholder = hasPlaceholderText(placeholder);\n return (\n <FieldGroup errorEl={below} reserve={reserve}>\n {withReason(\n <FloatingField\n className={boxClass}\n htmlFor={fieldId}\n label={label}\n srOnlyLabel={asDisplay}\n hint={hintParts.labelHint}\n >\n <input\n ref={ref}\n id={fieldId}\n type={effectiveType}\n // A labelled field with no placeholder of its own gets a single space, which\n // feeds the floating label's peer-placeholder-shown trick. A caller's own is\n // kept and shown on focus, once the label has floated out of its way (kastlan\n // 8) — see FLOATING_PLACEHOLDER_ON_FOCUS. With the label sr-only there is no\n // float left to drive, and an empty borderless line would say nothing at all\n // — so there the label text is the placeholder when the caller gave none.\n placeholder={\n asDisplay\n ? (placeholder ?? (typeof label === \"string\" ? label : \" \"))\n : ownPlaceholder\n ? placeholder\n : \" \"\n }\n {...rest}\n {...lockProps}\n aria-invalid={isInvalid || rest[\"aria-invalid\"] || undefined}\n aria-describedby={describedBy}\n onClick={handleClick}\n onChange={onChange}\n className={cn(\n asDisplay\n ? cn(FIELD_DISPLAY, \"text-xl font-semibold leading-snug\")\n : cn(FLOATING_INPUT_CLASS, ownPlaceholder && FLOATING_PLACEHOLDER_ON_FOCUS),\n isPassword && \"pe-9\",\n inputClassName,\n isInvalid && FIELD_INVALID,\n locked && \"cursor-not-allowed\",\n )}\n />\n {revealToggle}\n </FloatingField>,\n )}\n </FieldGroup>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/input.tsx"],"sourcesContent":["import { useId, useState } from \"react\";\nimport { Eye, EyeOff } from \"lucide-react\";\nimport type { ChangeEvent, InputHTMLAttributes, KeyboardEvent, MouseEvent, ReactNode, Ref } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { FOCUS_RING } from \"./focus-ring\";\nimport { usePhoneLayout } from \"../hooks/use-breakpoint\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport type { CommitScope } from \"./write-lock\";\nimport {\n EndHintRow,\n FieldCaption,\n LockFrame,\n mergeDescribedBy,\n type DisabledReasonDisplay,\n type FieldHelpProps,\n useFieldError,\n useFieldHint,\n useLockReason,\n} from \"./field-parts\";\nimport { DEFAULT_PASSWORD_REVEAL_LABELS } from \"./field-base-labels\";\nimport {\n CharacterCount,\n FIELD_BASE,\n FIELD_DISPLAY,\n FIELD_INVALID,\n FieldGroup,\n FloatingField,\n FLOATING_INPUT_CLASS,\n FLOATING_PLACEHOLDER_ON_FOCUS,\n fieldBelow,\n hasPlaceholderText,\n useCharacterCountLabels,\n useTypedLength,\n type CharacterCountLabels,\n type PasswordRevealLabels,\n} from \"./field-base\";\n\n// Native date/time inputs only reveal the calendar via the tiny trailing icon;\n// open the picker on a click anywhere in the field instead (feedback #224).\nconst PICKER_TYPES = new Set([\"date\", \"datetime-local\", \"month\", \"time\", \"week\"]);\n\nexport interface InputProps extends InputHTMLAttributes<HTMLInputElement>, FieldHelpProps {\n label?: ReactNode;\n /** Classes for the `<input>` itself, as distinct from `className`, which\n * styles the field WRAPPER once a `label` turns this into a FloatingField.\n * Without it a labelled Input had no way to reach its own element — so\n * `tabular-nums` on a numeric text field, which NumberInput has supported\n * all along through the identically named prop, was simply unavailable. */\n inputClassName?: string;\n /** {@link FIELD_DISPLAY} — on a phone, drop the chrome and set the value as a\n * heading. For the one field a form is about (a feedback subject, an account\n * name), never for a stack of them. Labelled fields only: the label is what\n * the placeholder falls back to once it goes `sr-only`. */\n variant?: \"field\" | \"display\";\n /** The field is required and unanswered, or holds something that cannot be\n * saved — {@link FIELD_INVALID}, the same rose border/ring `Select` has worn\n * since feedback #235.\n *\n * It exists here because writing `aria-invalid` by hand did NOT do this. The\n * attribute spreads onto the element and nothing styles it — there is no\n * `[aria-invalid]` rule in this package or in either consumer's stylesheet —\n * so three Keksdose dialogs flagged a mismatched passphrase to a screen reader\n * and painted the field exactly as if it were fine. Setting the prop sets the\n * attribute too, so the two can no longer be spelled separately. */\n invalid?: boolean;\n /** What is wrong with the value, in the caller's own words (\"That IBAN has 21\n * digits\"). Rendered under the field, pointed at by the control's\n * `aria-describedby` — MERGED with any the caller already passed — and implies\n * `invalid`, so the field paints as well as announces. `invalid` alone still\n * covers the case where the message lives elsewhere. See {@link useFieldError}. */\n error?: ReactNode;\n /**\n * Standing advice for the field (keksdose K4: Input had `error` but no `hint`, so the\n * app built the caption by hand under each field, and half of those never reached a\n * screen reader). The {@link Select} rule, so every field reads alike:\n * - plain TEXT (a string or a number) is a caption UNDER the field, attached with\n * `aria-describedby` — after the caller's own ids, before the error's;\n * - anything else (a {@link FieldHint} \"?\") rides the label line beside the\n * animated label, as NumberInput's does — or, on a field with no label, sits at the\n * field's end edge outside the box, and `className` then styles that row.\n */\n hint?: ReactNode;\n /**\n * A live \"12/80\" under the field, with `maxLength` (keksdose K11 — the broadcast,\n * support and handoff-note fields counted by hand). Read with the field as \"12 of 80\n * characters\", and announced politely only on entering the last stretch and on\n * reaching the limit — never on every keystroke. See {@link CharacterCount}. Ignored\n * without a `maxLength`: a count with no limit is not what this answers. Counts\n * UTF-16 code units, as `maxLength` itself does. Controlled, it counts `value`;\n * uncontrolled, `defaultValue` and then every change event — a value written from\n * outside without one (a form library's `reset`) shows from the next keystroke.\n */\n showCount?: boolean;\n /** The counter's words. See {@link CharacterCountLabels}. */\n countLabels?: Partial<CharacterCountLabels>;\n /** Names for the password reveal toggle, English by default — it is the one\n * string this component renders on its own behalf, and a German form was\n * reading it out in English. See {@link PasswordRevealLabels}. */\n passwordLabels?: Partial<PasswordRevealLabels>;\n /**\n * Why the value cannot be changed — {@link Button}'s `disabledReason`, for a text field\n * that SAVES itself: a name committed on blur (0.34, KS U10 — Kurvenschmiede's segment\n * and profile names, behind a local `CommitInput`). `disabled` takes the field out of\n * the tab order, so the reason never reached a keyboard.\n *\n * With a reason the field stays in the tab order and shows its value, `readOnly` (the\n * settled grey FIELD_BASE gives a `[readonly]` field) and `aria-disabled` — NumberInput's\n * lock (0.33.1), Autocomplete's. Nothing a save listens to is reported: not `onChange`,\n * not `onBlur` (on a field that saves itself, blur IS the save), and not Enter, which is\n * swallowed so the field does not submit the form around it; the caller's `onKeyDown`\n * still hears every other key. The reason is a line under the field at Large and on a\n * touch screen, else in the kit {@link Tooltip} (`disabledReasonDisplay`), and on the\n * input's `aria-describedby`, between the caption and the error. Wins over `disabled`.\n */\n disabledReason?: ReactNode;\n /**\n * Where the reason shows — {@link Button}'s `disabledReasonDisplay`: by default a line\n * under the field at Large and on a touch screen, the tooltip otherwise (§4 \"No fact\n * only in a tooltip\"; DUP-3, refactor audit 2026-10).\n */\n disabledReasonDisplay?: DisabledReasonDisplay;\n /**\n * This field COMMITS — {@link Button}'s `commit`: under a locked\n * {@link WriteLockProvider} it is locked the `disabledReason` way with the lock's reason\n * (which wins over its own). A field whose value only reaches the server through its\n * form's Save leaves this off and stays editable under the lock — the Save takes\n * `commit`. No provider, or an unlocked one: no effect.\n */\n commit?: CommitScope;\n /** The `<input>`. A React 19 ref prop since 0.34 (MOD-4, refactor audit 2026-10): as\n * `forwardRef`, with a `displayName` line beside it, this module could not drop an\n * unused component, and `{ Spinner }` brought Input, Select and Textarea along. */\n ref?: Ref<HTMLInputElement>;\n}\n\n/** What a locked {@link Input} swallows of a key: Enter, which would submit the form. */\nfunction swallowEnter(onKeyDown: ((e: KeyboardEvent<HTMLInputElement>) => void) | undefined) {\n return (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n return;\n }\n onKeyDown?.(e);\n };\n}\n\nexport function Input(props: InputProps) {\n const {\n ref,\n className,\n inputClassName,\n label,\n id,\n placeholder,\n type,\n variant = \"field\",\n invalid,\n error,\n hint,\n help,\n showCount,\n countLabels,\n passwordLabels,\n disabledReason,\n disabledReasonDisplay,\n commit,\n ...rest\n } = props;\n // Passed at all — even as `undefined` — the field keeps its box; see FieldGroup. The\n // same for `hint` and `showCount` (keksdose, 0.22): a caption coming and going under\n // the control must not rebuild it, or focus and the caret are lost mid-edit.\n const reserve = \"error\" in props || \"hint\" in props || \"showCount\" in props;\n const generated = useId();\n const fieldId = id ?? generated;\n const countId = useId();\n const hintParts = useFieldHint(hint, rest[\"aria-describedby\"], { help, name: label ?? rest[\"aria-label\"] });\n const maxLength = rest.maxLength;\n const counting = Boolean(showCount) && typeof maxLength === \"number\" && maxLength > 0;\n const length = useTypedLength(rest.value, rest.defaultValue);\n const countText = useCharacterCountLabels(countLabels);\n // The lock (0.34, KS U10): focusable, `readOnly`, nothing a save listens to is\n // reported — see `disabledReason`.\n const lock = useLockReason(commit, disabledReason);\n const locked = lock.locked;\n const { isInvalid, describedBy, errorEl } = useFieldError(\n error,\n invalid,\n // Caller's, caption, count, a lock's reason — then the error, appended by the hook.\n mergeDescribedBy(hintParts.describedBy, counting && countId, locked && lock.reasonId),\n rest[\"aria-invalid\"],\n );\n const counter = counting ? (\n <CharacterCount\n id={countId}\n used={length.used}\n max={maxLength}\n labels={countText}\n caption={<FieldCaption parts={hintParts} className=\"mt-0\" />}\n />\n ) : null;\n const below = fieldBelow(hintParts, counter, errorEl);\n const onChange = locked\n ? undefined\n : counting\n ? (e: ChangeEvent<HTMLInputElement>) => {\n length.track(e.target.value);\n rest.onChange?.(e);\n }\n : rest.onChange;\n // Locked, these win over what the caller's spread carried: focusable rather than\n // `disabled`, `readOnly`, no blur reported (blur is a self-saving field's save), and\n // Enter swallowed. Spread AFTER `rest` on every branch's input.\n const lockProps = locked\n ? {\n disabled: undefined,\n readOnly: true,\n \"aria-disabled\": true as const,\n onBlur: undefined,\n onKeyDown: swallowEnter(rest.onKeyDown),\n }\n : undefined;\n // While locked the field sits in the reason's frame (the line, or the Tooltip), which\n // becomes its outermost box — so `className` (a width, a flex or grid cell) moves onto\n // that, as on Select, and the form keeps its layout. `block` so a full-width field\n // stays full width in it.\n const boxClass = locked ? undefined : className;\n const withReason = (box: ReactNode) => (\n <LockFrame lock={lock} display={disabledReasonDisplay} className={className}>\n {box}\n </LockFrame>\n );\n const asDisplay = usePhoneLayout() && variant === \"display\";\n // Password fields get a reveal toggle so users can check what they typed.\n const isPassword = type === \"password\";\n const [revealed, setRevealed] = useState(false);\n const effectiveType = isPassword && revealed ? \"text\" : type;\n const handleClick = PICKER_TYPES.has(type ?? \"\")\n ? (e: MouseEvent<HTMLInputElement>) => {\n rest.onClick?.(e);\n try {\n // showPicker throws if unsupported or not user-activated — a click is\n // a valid activation, so this is safe; guard for older browsers.\n (e.currentTarget as HTMLInputElement & { showPicker?: () => void }).showPicker?.();\n } catch {\n /* ignore */\n }\n }\n : rest.onClick;\n const passwordText = useKitLabels(\"passwordReveal\", DEFAULT_PASSWORD_REVEAL_LABELS, passwordLabels);\n const revealToggle = isPassword ? (\n <button\n type=\"button\"\n // `tabIndex={-1}` sat here, which made this a painted, clickable control that\n // Tab stepped straight over — on the one field whose value cannot be checked by\n // looking at it. There is no mouse-only case for a reveal toggle; the people who\n // cannot see what they typed are exactly who it is for. It is a tab stop now,\n // and it needs a focus ring of its own, because FIELD_BASE's ring belongs to the\n // input underneath and stays put while focus moves onto the button on top of it.\n onClick={() => setRevealed((v) => !v)}\n // …and since it is a tab stop, a field the user may not edit must not hand the\n // keyboard a control that shows what is in it — a locked one included.\n disabled={rest.disabled || locked}\n aria-label={revealed ? passwordText.hide : passwordText.show}\n aria-pressed={revealed}\n className={cn(\n \"absolute inset-y-0 end-0 flex items-center rounded-e-md px-2.5 text-placeholder transition-colors hover:text-secondary focus:outline-none disabled:cursor-default disabled:opacity-50\",\n FOCUS_RING,\n )}\n >\n {revealed ? <EyeOff className=\"size-4\" /> : <Eye className=\"size-4\" />}\n </button>\n ) : null;\n if (label === undefined) {\n // A \"?\" with no label line to ride goes at the end edge, outside the box, and\n // `className` moves to that row (see `hint`); without one nothing moves.\n const endHint = hintParts.labelHint;\n const ownClass = endHint === undefined ? boxClass : undefined;\n if (!isPassword) {\n return (\n <FieldGroup errorEl={below} reserve={reserve}>\n {withReason(\n <EndHintRow hint={endHint} className={boxClass}>\n <input\n ref={ref}\n id={id}\n type={type}\n placeholder={placeholder}\n {...rest}\n {...lockProps}\n // AFTER the spread, so the prop wins — but OR-ed with whatever the spread\n // carried, or setting `invalid` would have quietly deleted a caller's own\n // `aria-invalid`. The prop is the one that also paints; a bare attribute\n // still announces, which is all it ever did.\n aria-invalid={isInvalid || rest[\"aria-invalid\"] || undefined}\n // Likewise merged rather than replaced — see {@link useFieldError}.\n aria-describedby={describedBy}\n onClick={handleClick}\n onChange={onChange}\n className={cn(\n FIELD_BASE,\n ownClass,\n inputClassName,\n isInvalid && FIELD_INVALID,\n locked && \"cursor-not-allowed\",\n )}\n />\n </EndHintRow>,\n )}\n </FieldGroup>\n );\n }\n return (\n <FieldGroup errorEl={below} reserve={reserve}>\n {withReason(\n <EndHintRow hint={endHint} className={boxClass}>\n <div className={cn(\"relative\", ownClass)}>\n <input\n ref={ref}\n id={id}\n type={effectiveType}\n placeholder={placeholder}\n {...rest}\n {...lockProps}\n aria-invalid={isInvalid || rest[\"aria-invalid\"] || undefined}\n aria-describedby={describedBy}\n onChange={onChange}\n className={cn(\n FIELD_BASE,\n \"pe-9\",\n inputClassName,\n isInvalid && FIELD_INVALID,\n locked && \"cursor-not-allowed\",\n )}\n />\n {revealToggle}\n </div>\n </EndHintRow>,\n )}\n </FieldGroup>\n );\n }\n const ownPlaceholder = hasPlaceholderText(placeholder);\n return (\n <FieldGroup errorEl={below} reserve={reserve}>\n {withReason(\n <FloatingField\n className={boxClass}\n htmlFor={fieldId}\n label={label}\n srOnlyLabel={asDisplay}\n hint={hintParts.labelHint}\n >\n <input\n ref={ref}\n id={fieldId}\n type={effectiveType}\n // A labelled field with no placeholder of its own gets a single space, which\n // feeds the floating label's peer-placeholder-shown trick. A caller's own is\n // kept and shown on focus, once the label has floated out of its way (kastlan\n // 8) — see FLOATING_PLACEHOLDER_ON_FOCUS. With the label sr-only there is no\n // float left to drive, and an empty borderless line would say nothing at all\n // — so there the label text is the placeholder when the caller gave none.\n placeholder={\n asDisplay\n ? (placeholder ?? (typeof label === \"string\" ? label : \" \"))\n : ownPlaceholder\n ? placeholder\n : \" \"\n }\n {...rest}\n {...lockProps}\n aria-invalid={isInvalid || rest[\"aria-invalid\"] || undefined}\n aria-describedby={describedBy}\n onClick={handleClick}\n onChange={onChange}\n className={cn(\n asDisplay\n ? cn(FIELD_DISPLAY, \"text-xl font-semibold leading-snug\")\n : cn(FLOATING_INPUT_CLASS, ownPlaceholder && FLOATING_PLACEHOLDER_ON_FOCUS),\n isPassword && \"pe-9\",\n inputClassName,\n isInvalid && FIELD_INVALID,\n locked && \"cursor-not-allowed\",\n )}\n />\n {revealToggle}\n </FloatingField>,\n )}\n </FieldGroup>\n );\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useId, useMemo, useRef, useState } from "react";
4
4
  import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
5
5
  import { cn } from "../lib/cn.js";
6
- import { monthKey, parseIsoDate } from "../lib/dates.js";
6
+ import { currentDate, monthKey, parseIsoDate } from "../lib/dates.js";
7
7
  import { upperFirst } from "../lib/text-case.js";
8
8
  import { useKitLabels } from "../i18n/kit-labels.js";
9
9
  import { DEFAULT_MINI_CALENDAR_LABELS, MiniCalendar } from "./mini-calendar.js";
@@ -11,7 +11,7 @@ import { DEFAULT_MONTH_PICKER_LABELS } from "./month-picker-labels.js";
11
11
  import { MonthGrid, clampKey, firstOfKey, shiftKey, yearArrowsBlocked } from "./month-grid.js";
12
12
  import { claimEscape } from "../hooks/use-dismiss.js";
13
13
  function openingMonth(from, to, lo, hi) {
14
- const start = parseIsoDate(from) ?? parseIsoDate(to) ?? /* @__PURE__ */ new Date();
14
+ const start = parseIsoDate(from) ?? parseIsoDate(to) ?? currentDate();
15
15
  if (lo && start < lo) return monthKey(lo);
16
16
  if (hi && start > hi) return monthKey(hi);
17
17
  return monthKey(start);
@@ -169,7 +169,7 @@ function JumpCalendar({
169
169
  to: toKey,
170
170
  minKey,
171
171
  maxKey,
172
- currentKey: monthKey(/* @__PURE__ */ new Date()),
172
+ currentKey: monthKey(currentDate()),
173
173
  locale,
174
174
  cellName: monthText.month,
175
175
  labelledBy: captionId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/jump-calendar.tsx"],"sourcesContent":["// The date pickers' calendar with a month jump (`monthJump`, kastlan): MiniCalendar\n// under a ‹ caption › whose caption opens a month grid. Moved out of date-picker.tsx as\n// it was (MOD-9, refactor audit 2026-10); internal, both pickers reach it through\n// `PanelCalendar`.\nimport { useId, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent } from \"react\";\nimport { ChevronDown, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { monthKey, parseIsoDate } from \"../lib/dates\";\nimport { upperFirst } from \"../lib/text-case\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport { DEFAULT_MINI_CALENDAR_LABELS, MiniCalendar, type MiniCalendarProps } from \"./mini-calendar\";\nimport { DEFAULT_MONTH_PICKER_LABELS } from \"./month-picker-labels\";\nimport { MonthGrid, clampKey, firstOfKey, shiftKey, yearArrowsBlocked } from \"./month-grid\";\nimport { claimEscape } from \"../hooks/use-dismiss\";\n\n// ── Month jump (`monthJump`, kastlan) ─────────────────────────────────────\n\n/**\n * The month `MiniCalendar` opens on — the selection, or today, pulled inside the\n * bounds — worked out the way it does, so the caption drawn over a calendar that was\n * given no `month` names the month that calendar is showing.\n */\nfunction openingMonth(from: string, to: string, lo: Date | null, hi: Date | null): string {\n const start = parseIsoDate(from) ?? parseIsoDate(to) ?? new Date();\n if (lo && start < lo) return monthKey(lo);\n if (hi && start > hi) return monthKey(hi);\n return monthKey(start);\n}\n\nexport type PanelCalendarProps = Pick<\n MiniCalendarProps,\n \"mode\" | \"from\" | \"to\" | \"locale\" | \"min\" | \"max\" | \"labels\" | \"onSelect\" | \"focusOnOpen\"\n> & {\n /** See {@link DatePickerBaseProps.monthJump}. */\n monthJump?: boolean;\n};\n\n/** The panel's calendar: the plain {@link MiniCalendar}, exactly as before — or, with\n * `monthJump`, the same calendar under a caption that opens a month grid. */\nexport function PanelCalendar({ monthJump, ...calendar }: PanelCalendarProps) {\n return monthJump ? <JumpCalendar {...calendar} /> : <MiniCalendar {...calendar} />;\n}\n\n/** How the day grid is mounted: `key` remounts it, `month` is the month it follows\n * (left out until the panel first moves, so it opens exactly where it always has), and\n * `focus` is its `focusOnOpen`. */\ninterface DayGridMount {\n key: number;\n month?: string;\n focus: boolean;\n}\n\n/**\n * {@link MiniCalendar} with its navigation drawn here — ‹ caption › — so the caption can\n * be a button that swaps the days for a month grid ({@link DatePickerBaseProps.monthJump}).\n *\n * Built on the calendar's own `hideNavigation` / `month` / `onMonthChange`, not a fork\n * of it: the days, their keys, the range band and every announcement stay the\n * calendar's. What this keeps is `shown`, the month on screen, for the caption — fed by\n * the arrows here, by the calendar's `onMonthChange` (PageUp, arrowing off the edge),\n * and by the value, whose changes the calendar follows WITHOUT reporting them (a preset\n * under `apply` moves the grid to its start). That last one is mirrored below with\n * the calendar's own rule, and when it lands in another month the day grid is remounted\n * there rather than steered by `month`: a `month` that changes in the same render as the\n * value would win over the value's day, and the grid's tab stop would land on the\n * month's same-numbered day instead of the preset's start.\n *\n * The month grid is `MonthPicker`'s own {@link MonthGrid} (month-grid.tsx, one since\n * 0.52 — refactor audit 2026-10-30, DUP-11), with its words (`monthPicker` labels).\n */\nfunction JumpCalendar({\n from = \"\",\n to = \"\",\n mode = \"range\",\n locale,\n min,\n max,\n labels,\n onSelect,\n focusOnOpen,\n}: Omit<PanelCalendarProps, \"monthJump\">) {\n // The month arrows say what they said when the calendar drew them; the month grid\n // says what `MonthPicker`'s says.\n const calendarText = useKitLabels(\"miniCalendar\", DEFAULT_MINI_CALENDAR_LABELS, labels);\n const monthText = useKitLabels(\"monthPicker\", DEFAULT_MONTH_PICKER_LABELS);\n const id = useId();\n const captionId = `${id}-caption`;\n const hintId = `${id}-hint`;\n const captionRef = useRef<HTMLButtonElement>(null);\n\n const lo = min ? parseIsoDate(min) : null;\n const hi = max ? parseIsoDate(max) : null;\n const minKey = lo ? monthKey(lo) : null;\n const maxKey = hi ? monthKey(hi) : null;\n\n const [view, setView] = useState<\"days\" | \"months\">(\"days\");\n const [shown, setShown] = useState(() => openingMonth(from, to, lo, hi));\n const [dayGrid, setDayGrid] = useState<DayGridMount>(() => ({ key: 0, focus: Boolean(focusOnOpen) }));\n // The month holding the month grid's tab stop — and with it the year on show.\n const [active, setActive] = useState(shown);\n\n // The value moved from outside the day grid (a preset, a reset): follow the end that\n // changed, by the calendar's own rule, and remount the days there — see above. Also\n // what takes an open month grid back to the days when a preset is picked beside it.\n const [lastValue, setLastValue] = useState({ from, to });\n if (lastValue.from !== from || lastValue.to !== to) {\n const changed = lastValue.from !== from ? from : to;\n setLastValue({ from, to });\n const next = parseIsoDate(changed) ?? parseIsoDate(from) ?? parseIsoDate(to);\n const key = next ? monthKey(next) : null;\n if (key && (key !== shown || view === \"months\")) {\n setShown(key);\n setView(\"days\");\n setDayGrid((d) => ({ key: d.key + 1, month: key, focus: false }));\n }\n }\n\n const showMonth = (key: string) => {\n setShown(key);\n // Follows by `month`: the calendar keeps the day of the month (clamped), and the\n // focus stays on the arrow being pressed.\n setDayGrid((d) => ({ ...d, month: key }));\n };\n const openMonths = () => {\n // The grid takes the focus onto this month as it mounts.\n setActive(clampKey(shown, minKey, maxKey));\n setView(\"months\");\n };\n const closeMonths = (focusCaption: boolean) => {\n setView(\"days\");\n setDayGrid((d) => ({ ...d, month: shown, focus: false }));\n if (focusCaption) captionRef.current?.focus();\n };\n const pick = (key: string) => {\n setShown(key);\n setView(\"days\");\n // Picked to go there: the focus goes with it, onto the days.\n setDayGrid((d) => ({ key: d.key + 1, month: key, focus: true }));\n };\n\n const inDays = view === \"days\";\n // Escape in the month grid is \"back\", not \"close\": the month grid is a step INSIDE the\n // panel, and losing the half-made range to a key meant to undo that step would be the\n // worse surprise. Claimed on the kit's Escape stack (Wave 2, DUP-1) and stopped here,\n // so neither the popover nor the phone sheet nor a dialog around them closes.\n const escapeToDays = (e: KeyboardEvent<HTMLElement>) => {\n if (e.key !== \"Escape\" || inDays) return;\n claimEscape(e);\n e.stopPropagation();\n closeMonths(true);\n };\n\n const year = Number(active.slice(0, 4));\n const yearBlocked = yearArrowsBlocked(active, minKey, maxKey);\n const prevBlocked = !inDays && yearBlocked.previous;\n const nextBlocked = !inDays && yearBlocked.next;\n\n const longName = useMemo(() => new Intl.DateTimeFormat(locale, { month: \"long\", year: \"numeric\" }), [locale]);\n const caption = inDays\n ? upperFirst(longName.format(firstOfKey(shown)), locale)\n : new Intl.DateTimeFormat(locale, { year: \"numeric\" }).format(new Date(year, 0, 1));\n\n // The selection, as months: both ends, and the months between (a range's band).\n const fromDate = parseIsoDate(from);\n const toDate = parseIsoDate(to);\n const fromKey = fromDate ? monthKey(fromDate) : null;\n const toKey = toDate ? monthKey(toDate) : null;\n\n // Aria-disabled rather than disabled, like the day cells: a focused button that\n // turns `disabled` drops the focus to <body>, mid-panel.\n const arrow =\n \"rounded p-1 text-muted hover:bg-hover aria-disabled:cursor-not-allowed aria-disabled:opacity-30 aria-disabled:hover:bg-transparent\";\n\n return (\n <div className=\"select-none\">\n {/* The calendar's own row, redrawn: the same arrows in the same places. Each\n button stays mounted across the two views and changes what it steps, so a\n press never pulls the element out from under the pointer or the focus. */}\n <div className=\"flex items-center justify-between gap-1 pb-1\">\n <button\n type=\"button\"\n aria-label={inDays ? calendarText.previousMonth : monthText.previousYear}\n aria-disabled={prevBlocked || undefined}\n onClick={() => {\n // Focus stays on the arrow being pressed — only the grid moves.\n if (inDays) showMonth(shiftKey(shown, -1));\n else if (!prevBlocked) setActive(shiftKey(active, -12));\n }}\n onKeyDown={escapeToDays}\n className={arrow}\n >\n {/* Mirrored in RTL: \"previous\" points to the reading START, which is right. */}\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" aria-hidden />\n </button>\n <button\n ref={captionRef}\n type=\"button\"\n aria-expanded={!inDays}\n aria-describedby={inDays ? hintId : undefined}\n onClick={inDays ? openMonths : () => closeMonths(false)}\n onKeyDown={escapeToDays}\n className=\"inline-flex min-w-0 items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium text-secondary hover:bg-hover\"\n >\n {/* The name is the text, so it says what is on screen; `aria-live` because\n pressing an arrow leaves focus ON the arrow, and this is the only thing\n that says where it went. It also names the month grid. */}\n <span id={captionId} aria-live=\"polite\" className=\"truncate\">\n {caption}\n </span>\n <ChevronDown aria-hidden className={cn(\"size-3.5 shrink-0 text-muted\", !inDays && \"rotate-180\")} />\n </button>\n <button\n type=\"button\"\n aria-label={inDays ? calendarText.nextMonth : monthText.nextYear}\n aria-disabled={nextBlocked || undefined}\n onClick={() => {\n if (inDays) showMonth(shiftKey(shown, 1));\n else if (!nextBlocked) setActive(shiftKey(active, 12));\n }}\n onKeyDown={escapeToDays}\n className={arrow}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" aria-hidden />\n </button>\n </div>\n {/* The caption's description — read through the reference, never on its own. */}\n <span id={hintId} hidden>\n {monthText.panel}\n </span>\n {inDays ? (\n <MiniCalendar\n key={dayGrid.key}\n mode={mode}\n from={from}\n to={to}\n locale={locale}\n min={min}\n max={max}\n labels={labels}\n onSelect={onSelect}\n focusOnOpen={dayGrid.focus}\n hideNavigation\n month={dayGrid.month}\n onMonthChange={showMonth}\n />\n ) : (\n <MonthGrid\n active={active}\n onActiveChange={setActive}\n onPick={pick}\n from={fromKey}\n to={toKey}\n minKey={minKey}\n maxKey={maxKey}\n currentKey={monthKey(new Date())}\n locale={locale}\n cellName={monthText.month}\n labelledBy={captionId}\n onEscape={escapeToDays}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/jump-calendar.tsx"],"sourcesContent":["// The date pickers' calendar with a month jump (`monthJump`, kastlan): MiniCalendar\n// under a ‹ caption › whose caption opens a month grid. Moved out of date-picker.tsx as\n// it was (MOD-9, refactor audit 2026-10); internal, both pickers reach it through\n// `PanelCalendar`.\nimport { useId, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent } from \"react\";\nimport { ChevronDown, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { currentDate, monthKey, parseIsoDate } from \"../lib/dates\";\nimport { upperFirst } from \"../lib/text-case\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport { DEFAULT_MINI_CALENDAR_LABELS, MiniCalendar, type MiniCalendarProps } from \"./mini-calendar\";\nimport { DEFAULT_MONTH_PICKER_LABELS } from \"./month-picker-labels\";\nimport { MonthGrid, clampKey, firstOfKey, shiftKey, yearArrowsBlocked } from \"./month-grid\";\nimport { claimEscape } from \"../hooks/use-dismiss\";\n\n// ── Month jump (`monthJump`, kastlan) ─────────────────────────────────────\n\n/**\n * The month `MiniCalendar` opens on — the selection, or today, pulled inside the\n * bounds — worked out the way it does, so the caption drawn over a calendar that was\n * given no `month` names the month that calendar is showing.\n */\nfunction openingMonth(from: string, to: string, lo: Date | null, hi: Date | null): string {\n const start = parseIsoDate(from) ?? parseIsoDate(to) ?? currentDate();\n if (lo && start < lo) return monthKey(lo);\n if (hi && start > hi) return monthKey(hi);\n return monthKey(start);\n}\n\nexport type PanelCalendarProps = Pick<\n MiniCalendarProps,\n \"mode\" | \"from\" | \"to\" | \"locale\" | \"min\" | \"max\" | \"labels\" | \"onSelect\" | \"focusOnOpen\"\n> & {\n /** See {@link DatePickerBaseProps.monthJump}. */\n monthJump?: boolean;\n};\n\n/** The panel's calendar: the plain {@link MiniCalendar}, exactly as before — or, with\n * `monthJump`, the same calendar under a caption that opens a month grid. */\nexport function PanelCalendar({ monthJump, ...calendar }: PanelCalendarProps) {\n return monthJump ? <JumpCalendar {...calendar} /> : <MiniCalendar {...calendar} />;\n}\n\n/** How the day grid is mounted: `key` remounts it, `month` is the month it follows\n * (left out until the panel first moves, so it opens exactly where it always has), and\n * `focus` is its `focusOnOpen`. */\ninterface DayGridMount {\n key: number;\n month?: string;\n focus: boolean;\n}\n\n/**\n * {@link MiniCalendar} with its navigation drawn here — ‹ caption › — so the caption can\n * be a button that swaps the days for a month grid ({@link DatePickerBaseProps.monthJump}).\n *\n * Built on the calendar's own `hideNavigation` / `month` / `onMonthChange`, not a fork\n * of it: the days, their keys, the range band and every announcement stay the\n * calendar's. What this keeps is `shown`, the month on screen, for the caption — fed by\n * the arrows here, by the calendar's `onMonthChange` (PageUp, arrowing off the edge),\n * and by the value, whose changes the calendar follows WITHOUT reporting them (a preset\n * under `apply` moves the grid to its start). That last one is mirrored below with\n * the calendar's own rule, and when it lands in another month the day grid is remounted\n * there rather than steered by `month`: a `month` that changes in the same render as the\n * value would win over the value's day, and the grid's tab stop would land on the\n * month's same-numbered day instead of the preset's start.\n *\n * The month grid is `MonthPicker`'s own {@link MonthGrid} (month-grid.tsx, one since\n * 0.52 — refactor audit 2026-10-30, DUP-11), with its words (`monthPicker` labels).\n */\nfunction JumpCalendar({\n from = \"\",\n to = \"\",\n mode = \"range\",\n locale,\n min,\n max,\n labels,\n onSelect,\n focusOnOpen,\n}: Omit<PanelCalendarProps, \"monthJump\">) {\n // The month arrows say what they said when the calendar drew them; the month grid\n // says what `MonthPicker`'s says.\n const calendarText = useKitLabels(\"miniCalendar\", DEFAULT_MINI_CALENDAR_LABELS, labels);\n const monthText = useKitLabels(\"monthPicker\", DEFAULT_MONTH_PICKER_LABELS);\n const id = useId();\n const captionId = `${id}-caption`;\n const hintId = `${id}-hint`;\n const captionRef = useRef<HTMLButtonElement>(null);\n\n const lo = min ? parseIsoDate(min) : null;\n const hi = max ? parseIsoDate(max) : null;\n const minKey = lo ? monthKey(lo) : null;\n const maxKey = hi ? monthKey(hi) : null;\n\n const [view, setView] = useState<\"days\" | \"months\">(\"days\");\n const [shown, setShown] = useState(() => openingMonth(from, to, lo, hi));\n const [dayGrid, setDayGrid] = useState<DayGridMount>(() => ({ key: 0, focus: Boolean(focusOnOpen) }));\n // The month holding the month grid's tab stop — and with it the year on show.\n const [active, setActive] = useState(shown);\n\n // The value moved from outside the day grid (a preset, a reset): follow the end that\n // changed, by the calendar's own rule, and remount the days there — see above. Also\n // what takes an open month grid back to the days when a preset is picked beside it.\n const [lastValue, setLastValue] = useState({ from, to });\n if (lastValue.from !== from || lastValue.to !== to) {\n const changed = lastValue.from !== from ? from : to;\n setLastValue({ from, to });\n const next = parseIsoDate(changed) ?? parseIsoDate(from) ?? parseIsoDate(to);\n const key = next ? monthKey(next) : null;\n if (key && (key !== shown || view === \"months\")) {\n setShown(key);\n setView(\"days\");\n setDayGrid((d) => ({ key: d.key + 1, month: key, focus: false }));\n }\n }\n\n const showMonth = (key: string) => {\n setShown(key);\n // Follows by `month`: the calendar keeps the day of the month (clamped), and the\n // focus stays on the arrow being pressed.\n setDayGrid((d) => ({ ...d, month: key }));\n };\n const openMonths = () => {\n // The grid takes the focus onto this month as it mounts.\n setActive(clampKey(shown, minKey, maxKey));\n setView(\"months\");\n };\n const closeMonths = (focusCaption: boolean) => {\n setView(\"days\");\n setDayGrid((d) => ({ ...d, month: shown, focus: false }));\n if (focusCaption) captionRef.current?.focus();\n };\n const pick = (key: string) => {\n setShown(key);\n setView(\"days\");\n // Picked to go there: the focus goes with it, onto the days.\n setDayGrid((d) => ({ key: d.key + 1, month: key, focus: true }));\n };\n\n const inDays = view === \"days\";\n // Escape in the month grid is \"back\", not \"close\": the month grid is a step INSIDE the\n // panel, and losing the half-made range to a key meant to undo that step would be the\n // worse surprise. Claimed on the kit's Escape stack (Wave 2, DUP-1) and stopped here,\n // so neither the popover nor the phone sheet nor a dialog around them closes.\n const escapeToDays = (e: KeyboardEvent<HTMLElement>) => {\n if (e.key !== \"Escape\" || inDays) return;\n claimEscape(e);\n e.stopPropagation();\n closeMonths(true);\n };\n\n const year = Number(active.slice(0, 4));\n const yearBlocked = yearArrowsBlocked(active, minKey, maxKey);\n const prevBlocked = !inDays && yearBlocked.previous;\n const nextBlocked = !inDays && yearBlocked.next;\n\n const longName = useMemo(() => new Intl.DateTimeFormat(locale, { month: \"long\", year: \"numeric\" }), [locale]);\n const caption = inDays\n ? upperFirst(longName.format(firstOfKey(shown)), locale)\n : new Intl.DateTimeFormat(locale, { year: \"numeric\" }).format(new Date(year, 0, 1));\n\n // The selection, as months: both ends, and the months between (a range's band).\n const fromDate = parseIsoDate(from);\n const toDate = parseIsoDate(to);\n const fromKey = fromDate ? monthKey(fromDate) : null;\n const toKey = toDate ? monthKey(toDate) : null;\n\n // Aria-disabled rather than disabled, like the day cells: a focused button that\n // turns `disabled` drops the focus to <body>, mid-panel.\n const arrow =\n \"rounded p-1 text-muted hover:bg-hover aria-disabled:cursor-not-allowed aria-disabled:opacity-30 aria-disabled:hover:bg-transparent\";\n\n return (\n <div className=\"select-none\">\n {/* The calendar's own row, redrawn: the same arrows in the same places. Each\n button stays mounted across the two views and changes what it steps, so a\n press never pulls the element out from under the pointer or the focus. */}\n <div className=\"flex items-center justify-between gap-1 pb-1\">\n <button\n type=\"button\"\n aria-label={inDays ? calendarText.previousMonth : monthText.previousYear}\n aria-disabled={prevBlocked || undefined}\n onClick={() => {\n // Focus stays on the arrow being pressed — only the grid moves.\n if (inDays) showMonth(shiftKey(shown, -1));\n else if (!prevBlocked) setActive(shiftKey(active, -12));\n }}\n onKeyDown={escapeToDays}\n className={arrow}\n >\n {/* Mirrored in RTL: \"previous\" points to the reading START, which is right. */}\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" aria-hidden />\n </button>\n <button\n ref={captionRef}\n type=\"button\"\n aria-expanded={!inDays}\n aria-describedby={inDays ? hintId : undefined}\n onClick={inDays ? openMonths : () => closeMonths(false)}\n onKeyDown={escapeToDays}\n className=\"inline-flex min-w-0 items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium text-secondary hover:bg-hover\"\n >\n {/* The name is the text, so it says what is on screen; `aria-live` because\n pressing an arrow leaves focus ON the arrow, and this is the only thing\n that says where it went. It also names the month grid. */}\n <span id={captionId} aria-live=\"polite\" className=\"truncate\">\n {caption}\n </span>\n <ChevronDown aria-hidden className={cn(\"size-3.5 shrink-0 text-muted\", !inDays && \"rotate-180\")} />\n </button>\n <button\n type=\"button\"\n aria-label={inDays ? calendarText.nextMonth : monthText.nextYear}\n aria-disabled={nextBlocked || undefined}\n onClick={() => {\n if (inDays) showMonth(shiftKey(shown, 1));\n else if (!nextBlocked) setActive(shiftKey(active, 12));\n }}\n onKeyDown={escapeToDays}\n className={arrow}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" aria-hidden />\n </button>\n </div>\n {/* The caption's description — read through the reference, never on its own. */}\n <span id={hintId} hidden>\n {monthText.panel}\n </span>\n {inDays ? (\n <MiniCalendar\n key={dayGrid.key}\n mode={mode}\n from={from}\n to={to}\n locale={locale}\n min={min}\n max={max}\n labels={labels}\n onSelect={onSelect}\n focusOnOpen={dayGrid.focus}\n hideNavigation\n month={dayGrid.month}\n onMonthChange={showMonth}\n />\n ) : (\n <MonthGrid\n active={active}\n onActiveChange={setActive}\n onPick={pick}\n from={fromKey}\n to={toKey}\n minKey={minKey}\n maxKey={maxKey}\n currentKey={monthKey(currentDate())}\n locale={locale}\n cellName={monthText.month}\n labelledBy={captionId}\n onEscape={escapeToDays}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -310,6 +310,16 @@ export interface LegalFooterProps {
310
310
  /** Marks that link `aria-current="page"`. {@link LegalPage}'s default footer passes
311
311
  * its own page. */
312
312
  currentHref?: string;
313
+ /**
314
+ * 0.55: the app's other public pages — the changelog, a status page, the docs — as a row
315
+ * of their own above the legal links. The legal row's look (quiet links, `·` between
316
+ * them, the current one marked), in a `<nav>` of its own named `landing.siteNavLabel`
317
+ * ("More pages"): a screen reader hears the footer's links announced as the legal ones,
318
+ * so a link that is not one goes in a row that says so (docs/landing-demo-harmonization.md
319
+ * §4.4). The name is a `landing` word, not a `legal` one — it is no legal wording — so
320
+ * the provider sets it, not {@link labels}. Left out or empty, no row.
321
+ */
322
+ siteLinks?: readonly LegalLink[];
313
323
  /**
314
324
  * Default true: the footer is a `<footer>`. `false` draws a `<div>` — for a host that is
315
325
  * a footer already: `AuthLayout`'s `footer` slot is one, and HTML forbids a footer inside
@@ -335,8 +345,10 @@ export interface LegalFooterProps {
335
345
  * 0.51.4: an app with a security page or an accessibility statement passes their routes
336
346
  * (`securityHref`, `accessibilityHref`) and the row goes on with "Security · Accessibility"
337
347
  * in the kit's words, so the footer reads the same in every app that has them.
348
+ *
349
+ * 0.55: `siteLinks`, the app's other public pages, in a row above them that is named apart.
338
350
  */
339
- export declare function LegalFooter({ children, links, securityHref, accessibilityHref, currentHref, landmark, className, labels, }: LegalFooterProps): import("react").JSX.Element;
351
+ export declare function LegalFooter({ children, links, securityHref, accessibilityHref, currentHref, siteLinks, landmark, className, labels, }: LegalFooterProps): import("react").JSX.Element;
340
352
  export interface LegalSectionProps {
341
353
  heading: ReactNode;
342
354
  /** Running text. Line breaks in it are kept (`white-space: pre-line`), so a
@@ -7,6 +7,7 @@ import { countryName } from "../lib/countries.js";
7
7
  import { AlertBanner } from "./alert-banner.js";
8
8
  import { TextLink } from "./text-link.js";
9
9
  import { DEFAULT_LEGAL_LABELS } from "./legal-labels.js";
10
+ import { DEFAULT_LANDING_LABELS, useLandingLabels } from "../landing/landing-labels.js";
10
11
  const LEGAL_HREFS = Object.freeze({
11
12
  impressum: "/impressum",
12
13
  privacy: "/privacy",
@@ -124,12 +125,23 @@ function LegalFooter({
124
125
  securityHref,
125
126
  accessibilityHref,
126
127
  currentHref,
128
+ siteLinks,
127
129
  landmark = true,
128
130
  className,
129
131
  labels
130
132
  }) {
131
133
  const Root = landmark ? "footer" : "div";
134
+ const siteNavLabel = useLandingLabels().siteNavLabel ?? DEFAULT_LANDING_LABELS.siteNavLabel ?? "";
132
135
  return /* @__PURE__ */ jsx(Root, { className: cn("border-t border-subtle bg-surface", className), children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-5xl px-4 py-8 text-sm text-muted", children: [
136
+ !!siteLinks?.length && /* @__PURE__ */ jsx(
137
+ LegalLinks,
138
+ {
139
+ links: siteLinks,
140
+ currentHref,
141
+ labels: { navLabel: siteNavLabel },
142
+ className: "mb-3 justify-center gap-y-2 text-sm"
143
+ }
144
+ ),
133
145
  /* @__PURE__ */ jsx(
134
146
  LegalLinks,
135
147
  {