@eifi1/ui-kit 0.51.7 → 0.53.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 (775) hide show
  1. package/README.md +178 -67
  2. package/dist/account/account-parts.d.ts +10 -8
  3. package/dist/account/account-parts.js +1 -18
  4. package/dist/account/account-parts.js.map +1 -1
  5. package/dist/account/data-export-setting-labels.js +1 -1
  6. package/dist/account/data-export-setting-labels.js.map +1 -1
  7. package/dist/account/delete-account-setting-labels.js +1 -1
  8. package/dist/account/delete-account-setting-labels.js.map +1 -1
  9. package/dist/account/email-change-setting-labels.js +1 -1
  10. package/dist/account/email-change-setting-labels.js.map +1 -1
  11. package/dist/account/passkeys-setting.d.ts +9 -2
  12. package/dist/account/passkeys-setting.js +41 -29
  13. package/dist/account/passkeys-setting.js.map +1 -1
  14. package/dist/account/sessions-setting.d.ts +3 -2
  15. package/dist/account/sessions-setting.js +10 -8
  16. package/dist/account/sessions-setting.js.map +1 -1
  17. package/dist/admin/admin-action-confirm.js +1 -3
  18. package/dist/admin/admin-action-confirm.js.map +1 -1
  19. package/dist/admin/admin-parts.d.ts +6 -3
  20. package/dist/admin/admin-parts.js +6 -21
  21. package/dist/admin/admin-parts.js.map +1 -1
  22. package/dist/admin/mail-test-card-labels.js +1 -1
  23. package/dist/admin/mail-test-card-labels.js.map +1 -1
  24. package/dist/admin/plan-change-labels.d.ts +2 -2
  25. package/dist/admin/plan-change-labels.js +1 -17
  26. package/dist/admin/plan-change-labels.js.map +1 -1
  27. package/dist/admin/plan-change.d.ts +3 -42
  28. package/dist/admin/plan-change.js +1 -1
  29. package/dist/admin/plan-change.js.map +1 -1
  30. package/dist/admin/reviewer-scope-editor.js +3 -3
  31. package/dist/admin/reviewer-scope-editor.js.map +1 -1
  32. package/dist/auth/auth-errors-labels.d.ts +11 -0
  33. package/dist/auth/auth-errors-labels.js +13 -0
  34. package/dist/auth/auth-errors-labels.js.map +1 -0
  35. package/dist/auth/auth-errors.d.ts +7 -23
  36. package/dist/auth/auth-errors.js +2 -15
  37. package/dist/auth/auth-errors.js.map +1 -1
  38. package/dist/auth/complete-name-dialog.js +2 -1
  39. package/dist/auth/complete-name-dialog.js.map +1 -1
  40. package/dist/auth/forgot-password-form-labels.js +1 -1
  41. package/dist/auth/forgot-password-form-labels.js.map +1 -1
  42. package/dist/auth/forgot-password-form.js +7 -2
  43. package/dist/auth/forgot-password-form.js.map +1 -1
  44. package/dist/auth/register-form-labels.js +1 -1
  45. package/dist/auth/register-form-labels.js.map +1 -1
  46. package/dist/auth/reset-password-form-labels.js +1 -1
  47. package/dist/auth/reset-password-form-labels.js.map +1 -1
  48. package/dist/auth/reset-password-form.js +5 -1
  49. package/dist/auth/reset-password-form.js.map +1 -1
  50. package/dist/auth/sign-in-form-labels.js +1 -1
  51. package/dist/auth/sign-in-form-labels.js.map +1 -1
  52. package/dist/auth/status-parts.d.ts +0 -3
  53. package/dist/auth/status-parts.js +0 -4
  54. package/dist/auth/status-parts.js.map +1 -1
  55. package/dist/auth/verify-email-labels.js +1 -1
  56. package/dist/auth/verify-email-labels.js.map +1 -1
  57. package/dist/auth/verify-email.js +7 -2
  58. package/dist/auth/verify-email.js.map +1 -1
  59. package/dist/billing/cancel-subscription.d.ts +10 -7
  60. package/dist/billing/cancel-subscription.js +6 -6
  61. package/dist/billing/cancel-subscription.js.map +1 -1
  62. package/dist/billing/plan-card.d.ts +5 -3
  63. package/dist/billing/plan-card.js +7 -8
  64. package/dist/billing/plan-card.js.map +1 -1
  65. package/dist/billing/plan-change-labels.d.ts +42 -0
  66. package/dist/billing/plan-change-labels.js +22 -0
  67. package/dist/billing/plan-change-labels.js.map +1 -0
  68. package/dist/billing/plan-switch.d.ts +9 -6
  69. package/dist/billing/plan-switch.js +7 -5
  70. package/dist/billing/plan-switch.js.map +1 -1
  71. package/dist/chart.d.ts +4 -3
  72. package/dist/chart.js +72 -4
  73. package/dist/chart.js.map +1 -1
  74. package/dist/components/account-chips.d.ts +5 -5
  75. package/dist/components/account-chips.js +11 -13
  76. package/dist/components/account-chips.js.map +1 -1
  77. package/dist/components/alert-banner.d.ts +16 -5
  78. package/dist/components/alert-banner.js +46 -1
  79. package/dist/components/alert-banner.js.map +1 -1
  80. package/dist/components/amount-input.d.ts +13 -0
  81. package/dist/components/amount-input.js +1 -0
  82. package/dist/components/amount-input.js.map +1 -1
  83. package/dist/components/autocomplete.js +57 -65
  84. package/dist/components/autocomplete.js.map +1 -1
  85. package/dist/components/bulk-action-bar.d.ts +12 -2
  86. package/dist/components/bulk-action-bar.js +65 -21
  87. package/dist/components/bulk-action-bar.js.map +1 -1
  88. package/dist/components/button-look.d.ts +5 -5
  89. package/dist/components/button-look.js.map +1 -1
  90. package/dist/components/button.d.ts +2 -2
  91. package/dist/components/button.js.map +1 -1
  92. package/dist/components/chart-edge.d.ts +16 -0
  93. package/dist/components/chart-edge.js +14 -1
  94. package/dist/components/chart-edge.js.map +1 -1
  95. package/dist/components/chart-figure.d.ts +13 -0
  96. package/dist/components/chart-figure.js +15 -0
  97. package/dist/components/chart-figure.js.map +1 -0
  98. package/dist/components/chart-zoom-core.js +1 -4
  99. package/dist/components/chart-zoom-core.js.map +1 -1
  100. package/dist/components/chart.d.ts +8 -1
  101. package/dist/components/chart.js +19 -10
  102. package/dist/components/chart.js.map +1 -1
  103. package/dist/components/combobox-core.d.ts +111 -2
  104. package/dist/components/combobox-core.js +148 -42
  105. package/dist/components/combobox-core.js.map +1 -1
  106. package/dist/components/combobox.d.ts +1 -1
  107. package/dist/components/combobox.js +141 -130
  108. package/dist/components/combobox.js.map +1 -1
  109. package/dist/components/copy-button.d.ts +6 -5
  110. package/dist/components/copy-button.js +5 -3
  111. package/dist/components/copy-button.js.map +1 -1
  112. package/dist/components/country-select.js +2 -2
  113. package/dist/components/country-select.js.map +1 -1
  114. package/dist/components/data-table-column-settings.d.ts +1 -1
  115. package/dist/components/data-table-column-settings.js +33 -7
  116. package/dist/components/data-table-column-settings.js.map +1 -1
  117. package/dist/components/data-table-filters.d.ts +4 -2
  118. package/dist/components/data-table-filters.js +2 -2
  119. package/dist/components/data-table-filters.js.map +1 -1
  120. package/dist/components/data-table-fit.d.ts +6 -4
  121. package/dist/components/data-table-fit.js +4 -1
  122. package/dist/components/data-table-fit.js.map +1 -1
  123. package/dist/components/data-table-groups.d.ts +1 -1
  124. package/dist/components/data-table-groups.js.map +1 -1
  125. package/dist/components/data-table-head.d.ts +1 -1
  126. package/dist/components/data-table-head.js +15 -12
  127. package/dist/components/data-table-head.js.map +1 -1
  128. package/dist/components/data-table-mobile.d.ts +12 -7
  129. package/dist/components/data-table-mobile.js +43 -30
  130. package/dist/components/data-table-mobile.js.map +1 -1
  131. package/dist/components/data-table-pagination.js +11 -6
  132. package/dist/components/data-table-pagination.js.map +1 -1
  133. package/dist/components/data-table-resize.d.ts +1 -1
  134. package/dist/components/data-table-resize.js.map +1 -1
  135. package/dist/components/data-table-row-actions.d.ts +2 -2
  136. package/dist/components/data-table-row-actions.js.map +1 -1
  137. package/dist/components/data-table-row.d.ts +6 -2
  138. package/dist/components/data-table-row.js +17 -10
  139. package/dist/components/data-table-row.js.map +1 -1
  140. package/dist/components/data-table-selection.d.ts +1 -1
  141. package/dist/components/data-table-selection.js.map +1 -1
  142. package/dist/components/data-table-shared.d.ts +10 -2
  143. package/dist/components/data-table-shared.js +18 -0
  144. package/dist/components/data-table-shared.js.map +1 -1
  145. package/dist/components/data-table-sort.d.ts +21 -2
  146. package/dist/components/data-table-sort.js +24 -0
  147. package/dist/components/data-table-sort.js.map +1 -1
  148. package/dist/components/data-table-virtual.d.ts +1 -1
  149. package/dist/components/data-table-virtual.js +18 -6
  150. package/dist/components/data-table-virtual.js.map +1 -1
  151. package/dist/components/data-table.d.ts +13 -4
  152. package/dist/components/data-table.js +139 -36
  153. package/dist/components/data-table.js.map +1 -1
  154. package/dist/components/date-field.d.ts +25 -3
  155. package/dist/components/date-field.js +33 -8
  156. package/dist/components/date-field.js.map +1 -1
  157. package/dist/components/date-picker.d.ts +16 -3
  158. package/dist/components/date-picker.js +55 -26
  159. package/dist/components/date-picker.js.map +1 -1
  160. package/dist/components/date-range-picker.d.ts +38 -24
  161. package/dist/components/date-range-picker.js +23 -7
  162. package/dist/components/date-range-picker.js.map +1 -1
  163. package/dist/components/error-boundary.js +37 -30
  164. package/dist/components/error-boundary.js.map +1 -1
  165. package/dist/components/{ui-labels.d.ts → field-base-labels.d.ts} +0 -2
  166. package/dist/components/{ui-labels.js → field-base-labels.js} +2 -9
  167. package/dist/components/field-base-labels.js.map +1 -0
  168. package/dist/components/field-base.d.ts +4 -3
  169. package/dist/components/field-base.js +3 -3
  170. package/dist/components/field-base.js.map +1 -1
  171. package/dist/components/form-actions.js +11 -5
  172. package/dist/components/form-actions.js.map +1 -1
  173. package/dist/components/hover-menu.d.ts +3 -0
  174. package/dist/components/hover-menu.js +5 -3
  175. package/dist/components/hover-menu.js.map +1 -1
  176. package/dist/components/icon-button.js +14 -9
  177. package/dist/components/icon-button.js.map +1 -1
  178. package/dist/components/inline-confirm.d.ts +43 -0
  179. package/dist/components/inline-confirm.js +67 -0
  180. package/dist/components/inline-confirm.js.map +1 -0
  181. package/dist/components/inline-entity-combobox.d.ts +4 -3
  182. package/dist/components/inline-entity-combobox.js +148 -140
  183. package/dist/components/inline-entity-combobox.js.map +1 -1
  184. package/dist/components/input.js +1 -1
  185. package/dist/components/input.js.map +1 -1
  186. package/dist/components/jump-calendar.js +28 -112
  187. package/dist/components/jump-calendar.js.map +1 -1
  188. package/dist/components/legal-page.d.ts +3 -1
  189. package/dist/components/legal-page.js +3 -2
  190. package/dist/components/legal-page.js.map +1 -1
  191. package/dist/components/legal.d.ts +4 -2
  192. package/dist/components/legal.js.map +1 -1
  193. package/dist/components/lightbox.d.ts +2 -2
  194. package/dist/components/lightbox.js +17 -6
  195. package/dist/components/lightbox.js.map +1 -1
  196. package/dist/components/list.d.ts +6 -2
  197. package/dist/components/list.js +3 -2
  198. package/dist/components/list.js.map +1 -1
  199. package/dist/components/measured-grid.d.ts +1 -1
  200. package/dist/components/measured-grid.js +2 -1
  201. package/dist/components/measured-grid.js.map +1 -1
  202. package/dist/components/mini-calendar.js +3 -5
  203. package/dist/components/mini-calendar.js.map +1 -1
  204. package/dist/components/modal.js +3 -1
  205. package/dist/components/modal.js.map +1 -1
  206. package/dist/components/month-grid.d.ts +55 -0
  207. package/dist/components/month-grid.js +148 -0
  208. package/dist/components/month-grid.js.map +1 -0
  209. package/dist/components/month-picker.d.ts +36 -4
  210. package/dist/components/month-picker.js +123 -146
  211. package/dist/components/month-picker.js.map +1 -1
  212. package/dist/components/multi-select.d.ts +2 -2
  213. package/dist/components/multi-select.js +11 -6
  214. package/dist/components/multi-select.js.map +1 -1
  215. package/dist/components/nav-pills.d.ts +1 -1
  216. package/dist/components/nav-pills.js.map +1 -1
  217. package/dist/components/picker-sheet.js +2 -1
  218. package/dist/components/picker-sheet.js.map +1 -1
  219. package/dist/components/pie-chart-labels.d.ts +7 -0
  220. package/dist/components/pie-chart-labels.js +1 -0
  221. package/dist/components/pie-chart-labels.js.map +1 -1
  222. package/dist/components/pie-chart.d.ts +5 -3
  223. package/dist/components/pie-chart.js +7 -22
  224. package/dist/components/pie-chart.js.map +1 -1
  225. package/dist/components/popover.d.ts +3 -0
  226. package/dist/components/popover.js +7 -1
  227. package/dist/components/popover.js.map +1 -1
  228. package/dist/components/row-actions.js +2 -1
  229. package/dist/components/row-actions.js.map +1 -1
  230. package/dist/components/series-chart-axes.d.ts +4 -4
  231. package/dist/components/series-chart-axes.js.map +1 -1
  232. package/dist/components/series-chart-labels.d.ts +7 -4
  233. package/dist/components/series-chart-labels.js.map +1 -1
  234. package/dist/components/series-chart.d.ts +92 -23
  235. package/dist/components/series-chart.js +74 -30
  236. package/dist/components/series-chart.js.map +1 -1
  237. package/dist/components/share-card.js +3 -3
  238. package/dist/components/share-card.js.map +1 -1
  239. package/dist/components/tabs-labels.d.ts +2 -0
  240. package/dist/components/tabs-labels.js +11 -0
  241. package/dist/components/tabs-labels.js.map +1 -0
  242. package/dist/components/tabs.d.ts +1 -1
  243. package/dist/components/tabs.js +1 -1
  244. package/dist/components/tabs.js.map +1 -1
  245. package/dist/components/toggle-legend.d.ts +2 -2
  246. package/dist/components/toggle-legend.js +6 -6
  247. package/dist/components/toggle-legend.js.map +1 -1
  248. package/dist/components/translation-review-editor.d.ts +4 -2
  249. package/dist/components/translation-review-editor.js +2 -3
  250. package/dist/components/translation-review-editor.js.map +1 -1
  251. package/dist/components/translation-review.d.ts +2 -1
  252. package/dist/components/translation-review.js +2 -2
  253. package/dist/components/translation-review.js.map +1 -1
  254. package/dist/components/treemap-labels.d.ts +31 -0
  255. package/dist/components/treemap-labels.js +10 -0
  256. package/dist/components/treemap-labels.js.map +1 -0
  257. package/dist/components/treemap-layout.d.ts +62 -0
  258. package/dist/components/treemap-layout.js +126 -0
  259. package/dist/components/treemap-layout.js.map +1 -0
  260. package/dist/components/treemap.d.ts +167 -19
  261. package/dist/components/treemap.js +340 -100
  262. package/dist/components/treemap.js.map +1 -1
  263. package/dist/components/use-table-state.d.ts +5 -5
  264. package/dist/components/use-table-state.js +4 -1
  265. package/dist/components/use-table-state.js.map +1 -1
  266. package/dist/data-table.d.ts +1 -2
  267. package/dist/data-table.js +1 -4
  268. package/dist/data-table.js.map +1 -1
  269. package/dist/demo/demo-countdown.d.ts +0 -5
  270. package/dist/demo/demo-countdown.js +0 -4
  271. package/dist/demo/demo-countdown.js.map +1 -1
  272. package/dist/eslint/index.js +1 -1
  273. package/dist/eslint/role-utilities.d.ts +36 -7
  274. package/dist/eslint/role-utilities.js +42 -9
  275. package/dist/eslint/role-utilities.js.map +1 -1
  276. package/dist/feedback/feedback-dialog.d.ts +25 -16
  277. package/dist/feedback/feedback-dialog.js +6 -2
  278. package/dist/feedback/feedback-dialog.js.map +1 -1
  279. package/dist/feedback/feedback-mutation.d.ts +18 -0
  280. package/dist/feedback/feedback-mutation.js +8 -0
  281. package/dist/feedback/feedback-mutation.js.map +1 -0
  282. package/dist/feedback/feedback-page-link.d.ts +53 -0
  283. package/dist/feedback/feedback-page-link.js +37 -0
  284. package/dist/feedback/feedback-page-link.js.map +1 -0
  285. package/dist/feedback/feedback-public-list.d.ts +8 -4
  286. package/dist/feedback/feedback-public-list.js +2 -2
  287. package/dist/feedback/feedback-public-list.js.map +1 -1
  288. package/dist/feedback/feedback-replies.d.ts +3 -1
  289. package/dist/feedback/feedback-replies.js +5 -4
  290. package/dist/feedback/feedback-replies.js.map +1 -1
  291. package/dist/feedback/feedback-row-detail-labels.js +0 -3
  292. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  293. package/dist/feedback/feedback-row-detail.d.ts +33 -44
  294. package/dist/feedback/feedback-row-detail.js +28 -34
  295. package/dist/feedback/feedback-row-detail.js.map +1 -1
  296. package/dist/feedback/feedback-status-undo.d.ts +17 -14
  297. package/dist/feedback/feedback-status-undo.js +10 -15
  298. package/dist/feedback/feedback-status-undo.js.map +1 -1
  299. package/dist/feedback/feedback-submit-dialog.d.ts +1 -1
  300. package/dist/feedback/feedback-submit-dialog.js +1 -1
  301. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  302. package/dist/feedback/feedback-submit.js +9 -3
  303. package/dist/feedback/feedback-submit.js.map +1 -1
  304. package/dist/feedback/feedback-swipe.d.ts +1 -1
  305. package/dist/feedback/feedback-swipe.js.map +1 -1
  306. package/dist/feedback/feedback-table.d.ts +21 -9
  307. package/dist/feedback/feedback-table.js +45 -25
  308. package/dist/feedback/feedback-table.js.map +1 -1
  309. package/dist/feedback/feedback-thread-labels.d.ts +2 -2
  310. package/dist/feedback/feedback-thread-labels.js.map +1 -1
  311. package/dist/feedback/feedback-thread.d.ts +15 -23
  312. package/dist/feedback/feedback-thread.js +2 -1
  313. package/dist/feedback/feedback-thread.js.map +1 -1
  314. package/dist/feedback.d.ts +6 -6
  315. package/dist/feedback.js +90 -6
  316. package/dist/feedback.js.map +1 -1
  317. package/dist/hooks/use-focus-trap.d.ts +35 -0
  318. package/dist/hooks/use-focus-trap.js +6 -4
  319. package/dist/hooks/use-focus-trap.js.map +1 -1
  320. package/dist/hooks/use-hotkey.d.ts +5 -2
  321. package/dist/hooks/use-hotkey.js +5 -4
  322. package/dist/hooks/use-hotkey.js.map +1 -1
  323. package/dist/hooks/use-long-press.d.ts +7 -4
  324. package/dist/hooks/use-long-press.js +3 -0
  325. package/dist/hooks/use-long-press.js.map +1 -1
  326. package/dist/hooks/use-media-query.js +12 -4
  327. package/dist/hooks/use-media-query.js.map +1 -1
  328. package/dist/hooks/use-mounted.d.ts +22 -0
  329. package/dist/hooks/use-mounted.js +24 -0
  330. package/dist/hooks/use-mounted.js.map +1 -0
  331. package/dist/hooks/use-overlay-history.js +19 -3
  332. package/dist/hooks/use-overlay-history.js.map +1 -1
  333. package/dist/i18n/defaults.js +4 -9
  334. package/dist/i18n/defaults.js.map +1 -1
  335. package/dist/i18n/german.js +7 -18
  336. package/dist/i18n/german.js.map +1 -1
  337. package/dist/i18n/kit-labels.d.ts +9 -11
  338. package/dist/i18n/kit-labels.js.map +1 -1
  339. package/dist/i18n/locales/en.js +15 -64
  340. package/dist/i18n/locales/en.js.map +3 -3
  341. package/dist/i18n/locales/es.js +7 -18
  342. package/dist/i18n/locales/es.js.map +1 -1
  343. package/dist/i18n/locales/fr.js +20 -27
  344. package/dist/i18n/locales/fr.js.map +1 -1
  345. package/dist/i18n/locales/hu.js +9 -18
  346. package/dist/i18n/locales/hu.js.map +1 -1
  347. package/dist/i18n/locales/it.js +7 -19
  348. package/dist/i18n/locales/it.js.map +1 -1
  349. package/dist/i18n/locales/zh.js +8 -18
  350. package/dist/i18n/locales/zh.js.map +1 -1
  351. package/dist/i18n/review.js +12 -2
  352. package/dist/i18n/review.js.map +1 -1
  353. package/dist/i18n/shared-labels.js +0 -1
  354. package/dist/i18n/shared-labels.js.map +1 -1
  355. package/dist/index.d.ts +21 -23
  356. package/dist/index.js +50 -65
  357. package/dist/index.js.map +1 -1
  358. package/dist/landing/routing.d.ts +3 -1
  359. package/dist/landing/routing.js +4 -7
  360. package/dist/landing/routing.js.map +1 -1
  361. package/dist/lib/broadcast.d.ts +17 -0
  362. package/dist/lib/broadcast.js +30 -0
  363. package/dist/lib/broadcast.js.map +1 -0
  364. package/dist/lib/date-format.d.ts +37 -2
  365. package/dist/lib/date-format.js +34 -8
  366. package/dist/lib/date-format.js.map +1 -1
  367. package/dist/lib/day-format.d.ts +46 -0
  368. package/dist/lib/day-format.js +46 -0
  369. package/dist/lib/day-format.js.map +1 -0
  370. package/dist/lib/describe-failure.d.ts +14 -0
  371. package/dist/lib/describe-failure.js +9 -0
  372. package/dist/lib/describe-failure.js.map +1 -0
  373. package/dist/lib/direction.d.ts +16 -0
  374. package/dist/lib/direction.js +9 -1
  375. package/dist/lib/direction.js.map +1 -1
  376. package/dist/lib/focus-left.d.ts +63 -0
  377. package/dist/lib/focus-left.js +67 -0
  378. package/dist/lib/focus-left.js.map +1 -0
  379. package/dist/lib/format.js +5 -14
  380. package/dist/lib/format.js.map +1 -1
  381. package/dist/lib/http-status.d.ts +24 -0
  382. package/dist/lib/http-status.js +14 -0
  383. package/dist/lib/http-status.js.map +1 -0
  384. package/dist/lib/intl-cache.d.ts +14 -0
  385. package/dist/lib/intl-cache.js +15 -0
  386. package/dist/lib/intl-cache.js.map +1 -0
  387. package/dist/lib/iso-day.d.ts +11 -0
  388. package/dist/lib/iso-day.js +8 -0
  389. package/dist/lib/iso-day.js.map +1 -0
  390. package/dist/lib/lazy-with-fallback.d.ts +31 -0
  391. package/dist/lib/lazy-with-fallback.js +40 -0
  392. package/dist/lib/lazy-with-fallback.js.map +1 -0
  393. package/dist/lib/path-pattern.d.ts +34 -0
  394. package/dist/lib/path-pattern.js +24 -0
  395. package/dist/lib/path-pattern.js.map +1 -0
  396. package/dist/lib/reduced-motion.d.ts +2 -1
  397. package/dist/lib/reduced-motion.js.map +1 -1
  398. package/dist/lib/round.d.ts +2 -1
  399. package/dist/lib/round.js +2 -1
  400. package/dist/lib/round.js.map +1 -1
  401. package/dist/lib/safe-storage.d.ts +24 -5
  402. package/dist/lib/safe-storage.js +27 -2
  403. package/dist/lib/safe-storage.js.map +1 -1
  404. package/dist/lib/session-refresh.js +1 -1
  405. package/dist/lib/session-refresh.js.map +1 -1
  406. package/dist/lib/share-format.d.ts +13 -0
  407. package/dist/lib/share-format.js +10 -0
  408. package/dist/lib/share-format.js.map +1 -0
  409. package/dist/lib/text-case.d.ts +17 -0
  410. package/dist/lib/text-case.js +8 -0
  411. package/dist/lib/text-case.js.map +1 -0
  412. package/dist/lib/text-match.d.ts +29 -0
  413. package/dist/lib/text-match.js +20 -2
  414. package/dist/lib/text-match.js.map +1 -1
  415. package/dist/lib/thenable.d.ts +13 -0
  416. package/dist/lib/thenable.js +8 -0
  417. package/dist/lib/thenable.js.map +1 -0
  418. package/dist/lib/translation-review.d.ts +7 -2
  419. package/dist/lib/translation-review.js +4 -4
  420. package/dist/lib/translation-review.js.map +1 -1
  421. package/dist/notifications/device-zone-report.js +1 -1
  422. package/dist/notifications/device-zone-report.js.map +1 -1
  423. package/dist/notifications/notification-bell.d.ts +2 -1
  424. package/dist/notifications/notification-bell.js +3 -3
  425. package/dist/notifications/notification-bell.js.map +1 -1
  426. package/dist/notifications/notification-inbox.d.ts +10 -6
  427. package/dist/notifications/notification-inbox.js +26 -48
  428. package/dist/notifications/notification-inbox.js.map +1 -1
  429. package/dist/notifications/notification-parts.d.ts +3 -2
  430. package/dist/notifications/notification-parts.js +1 -3
  431. package/dist/notifications/notification-parts.js.map +1 -1
  432. package/dist/notifications/notification-types-setting.js +1 -1
  433. package/dist/notifications/notification-types-setting.js.map +1 -1
  434. package/dist/notifications/notifications-labels.d.ts +6 -0
  435. package/dist/notifications/notifications-labels.js +1 -0
  436. package/dist/notifications/notifications-labels.js.map +1 -1
  437. package/dist/notifications/push-device-setting.js +1 -1
  438. package/dist/notifications/push-device-setting.js.map +1 -1
  439. package/dist/notifications/push-devices-setting.d.ts +2 -1
  440. package/dist/notifications/push-devices-setting.js +27 -49
  441. package/dist/notifications/push-devices-setting.js.map +1 -1
  442. package/dist/notifications/quiet-hours-setting.d.ts +10 -3
  443. package/dist/notifications/quiet-hours-setting.js +65 -26
  444. package/dist/notifications/quiet-hours-setting.js.map +1 -1
  445. package/dist/notifications/unsubscribe-panel.js +4 -7
  446. package/dist/notifications/unsubscribe-panel.js.map +1 -1
  447. package/dist/notifications/use-push-device.d.ts +39 -1
  448. package/dist/notifications/use-push-device.js +28 -7
  449. package/dist/notifications/use-push-device.js.map +1 -1
  450. package/dist/offline/query-persistence.d.ts +3 -1
  451. package/dist/offline/query-persistence.js +19 -2
  452. package/dist/offline/query-persistence.js.map +1 -1
  453. package/dist/rhf/fields.d.ts +11 -4
  454. package/dist/rhf/fields.js.map +1 -1
  455. package/dist/search/command-palette.js +1 -1
  456. package/dist/search/command-palette.js.map +1 -1
  457. package/dist/search/global-search.js +19 -9
  458. package/dist/search/global-search.js.map +1 -1
  459. package/dist/search/search-index.d.ts +0 -6
  460. package/dist/search/search-index.js +0 -4
  461. package/dist/search/search-index.js.map +1 -1
  462. package/dist/search/use-command-key.d.ts +12 -1
  463. package/dist/search/use-command-key.js +3 -12
  464. package/dist/search/use-command-key.js.map +1 -1
  465. package/dist/search.d.ts +1 -1
  466. package/dist/search.js +6 -1
  467. package/dist/search.js.map +1 -1
  468. package/dist/settings/use-settings-focus.js +1 -0
  469. package/dist/settings/use-settings-focus.js.map +1 -1
  470. package/dist/shell/account-menu-language.d.ts +5 -3
  471. package/dist/shell/account-menu-language.js +2 -2
  472. package/dist/shell/account-menu-language.js.map +1 -1
  473. package/dist/shell/account-menu-text-size.d.ts +9 -6
  474. package/dist/shell/account-menu-text-size.js +2 -2
  475. package/dist/shell/account-menu-text-size.js.map +1 -1
  476. package/dist/shell/app-shell.d.ts +0 -10
  477. package/dist/shell/app-shell.js +1 -7
  478. package/dist/shell/app-shell.js.map +1 -1
  479. package/dist/shell/company-switcher.d.ts +1 -1
  480. package/dist/shell/company-switcher.js.map +1 -1
  481. package/dist/shell/dev-menu.d.ts +1 -1
  482. package/dist/shell/dev-menu.js +5 -18
  483. package/dist/shell/dev-menu.js.map +1 -1
  484. package/dist/shell/top-bar-actions.d.ts +1 -1
  485. package/dist/shell/top-bar-actions.js.map +1 -1
  486. package/dist/shell/top-bar-parts-labels.d.ts +1 -3
  487. package/dist/shell/top-bar-parts-labels.js +0 -1
  488. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  489. package/dist/shell.d.ts +7 -10
  490. package/dist/shell.js +14 -23
  491. package/dist/shell.js.map +1 -1
  492. package/dist/sync/engine.d.ts +30 -4
  493. package/dist/sync/engine.js +172 -52
  494. package/dist/sync/engine.js.map +1 -1
  495. package/dist/sync.d.ts +2 -2
  496. package/dist/sync.js +2 -10
  497. package/dist/sync.js.map +1 -1
  498. package/dist/testing/css-variables.js +3 -8
  499. package/dist/testing/css-variables.js.map +1 -1
  500. package/dist/theme/chart-palette.d.ts +0 -5
  501. package/dist/theme/chart-palette.js +0 -2
  502. package/dist/theme/chart-palette.js.map +1 -1
  503. package/dist/theme/contrast.d.ts +2 -1
  504. package/dist/theme/contrast.js +3 -8
  505. package/dist/theme/contrast.js.map +1 -1
  506. package/dist/theme/palette-store.js +6 -6
  507. package/dist/theme/palette-store.js.map +1 -1
  508. package/dist/theme/text-size.js +3 -8
  509. package/dist/theme/text-size.js.map +1 -1
  510. package/dist/theme/theme-store.js +3 -6
  511. package/dist/theme/theme-store.js.map +1 -1
  512. package/dist/tour/tour-catalogue.d.ts +7 -4
  513. package/dist/tour/tour-catalogue.js +12 -10
  514. package/dist/tour/tour-catalogue.js.map +1 -1
  515. package/dist/tour/tour-dock.d.ts +0 -2
  516. package/dist/tour/tour-dock.js +0 -2
  517. package/dist/tour/tour-dock.js.map +1 -1
  518. package/dist/tour/tour-gates.d.ts +4 -0
  519. package/dist/tour/tour-gates.js +14 -0
  520. package/dist/tour/tour-gates.js.map +1 -1
  521. package/dist/tour/tour-overlay.d.ts +27 -0
  522. package/dist/tour/tour-overlay.js +741 -0
  523. package/dist/tour/tour-overlay.js.map +1 -0
  524. package/dist/tour/tour.d.ts +13 -3
  525. package/dist/tour/tour.js +59 -781
  526. package/dist/tour/tour.js.map +1 -1
  527. package/dist/tour/use-tour-progress.js +1 -1
  528. package/dist/tour/use-tour-progress.js.map +1 -1
  529. package/dist/wizard/stepper-nav.d.ts +5 -1
  530. package/dist/wizard/stepper-nav.js.map +1 -1
  531. package/dist/wizard/types.d.ts +6 -2
  532. package/dist/wizard/types.js.map +1 -1
  533. package/dist/wizard/use-wizard.d.ts +3 -1
  534. package/dist/wizard/use-wizard.js.map +1 -1
  535. package/dist/wizard/validation.d.ts +3 -1
  536. package/dist/wizard/validation.js.map +1 -1
  537. package/package.json +10 -6
  538. package/src/account/account-parts.ts +5 -25
  539. package/src/account/data-export-setting-labels.ts +1 -1
  540. package/src/account/delete-account-setting-labels.ts +1 -1
  541. package/src/account/email-change-setting-labels.ts +1 -1
  542. package/src/account/passkeys-setting.tsx +60 -38
  543. package/src/account/sessions-setting.tsx +18 -12
  544. package/src/admin/admin-action-confirm.tsx +1 -5
  545. package/src/admin/admin-parts.ts +11 -25
  546. package/src/admin/mail-test-card-labels.ts +1 -1
  547. package/src/admin/plan-change-labels.ts +5 -23
  548. package/src/admin/plan-change.tsx +7 -44
  549. package/src/admin/reviewer-scope-editor.tsx +3 -3
  550. package/src/auth/auth-errors-labels.ts +23 -0
  551. package/src/auth/auth-errors.ts +11 -38
  552. package/src/auth/complete-name-dialog.tsx +4 -1
  553. package/src/auth/forgot-password-form-labels.ts +1 -1
  554. package/src/auth/forgot-password-form.tsx +11 -6
  555. package/src/auth/register-form-labels.ts +1 -1
  556. package/src/auth/reset-password-form-labels.ts +1 -1
  557. package/src/auth/reset-password-form.tsx +8 -4
  558. package/src/auth/sign-in-form-labels.ts +1 -1
  559. package/src/auth/status-parts.tsx +0 -6
  560. package/src/auth/verify-email-labels.ts +1 -1
  561. package/src/auth/verify-email.tsx +11 -6
  562. package/src/billing/cancel-subscription.tsx +15 -12
  563. package/src/billing/plan-card.tsx +18 -10
  564. package/src/billing/plan-change-labels.ts +66 -0
  565. package/src/billing/plan-switch.tsx +15 -12
  566. package/src/chart.ts +73 -6
  567. package/src/components/account-chips.tsx +20 -23
  568. package/src/components/alert-banner.tsx +82 -5
  569. package/src/components/amount-input.tsx +18 -7
  570. package/src/components/autocomplete.tsx +64 -70
  571. package/src/components/bulk-action-bar.tsx +70 -2
  572. package/src/components/button-look.ts +5 -5
  573. package/src/components/button.tsx +2 -2
  574. package/src/components/chart-edge.ts +34 -1
  575. package/src/components/chart-figure.tsx +35 -0
  576. package/src/components/chart-zoom-core.tsx +6 -5
  577. package/src/components/chart.tsx +26 -5
  578. package/src/components/combobox-core.tsx +293 -62
  579. package/src/components/combobox.tsx +183 -153
  580. package/src/components/copy-button.tsx +34 -23
  581. package/src/components/country-select.tsx +16 -11
  582. package/src/components/data-table-column-settings.tsx +52 -10
  583. package/src/components/data-table-filters.ts +6 -4
  584. package/src/components/data-table-fit.ts +19 -5
  585. package/src/components/data-table-groups.tsx +1 -1
  586. package/src/components/data-table-head.tsx +32 -22
  587. package/src/components/data-table-mobile.tsx +61 -26
  588. package/src/components/data-table-pagination.tsx +21 -6
  589. package/src/components/data-table-resize.ts +1 -1
  590. package/src/components/data-table-row-actions.tsx +3 -3
  591. package/src/components/data-table-row.tsx +25 -14
  592. package/src/components/data-table-selection.ts +1 -1
  593. package/src/components/data-table-shared.tsx +33 -1
  594. package/src/components/data-table-sort.ts +57 -2
  595. package/src/components/data-table-virtual.ts +32 -8
  596. package/src/components/data-table.tsx +239 -68
  597. package/src/components/date-field.tsx +66 -9
  598. package/src/components/date-picker.tsx +125 -49
  599. package/src/components/date-range-picker.tsx +71 -36
  600. package/src/components/error-boundary.tsx +53 -39
  601. package/src/components/field-base-labels.ts +20 -0
  602. package/src/components/field-base.tsx +13 -7
  603. package/src/components/form-actions.tsx +20 -5
  604. package/src/components/hover-menu.tsx +18 -8
  605. package/src/components/icon-button.tsx +14 -9
  606. package/src/components/inline-confirm.tsx +116 -0
  607. package/src/components/inline-entity-combobox.tsx +197 -153
  608. package/src/components/input.tsx +1 -1
  609. package/src/components/jump-calendar.tsx +30 -146
  610. package/src/components/legal-page.tsx +10 -7
  611. package/src/components/legal.tsx +4 -2
  612. package/src/components/lightbox.tsx +21 -8
  613. package/src/components/list.tsx +10 -4
  614. package/src/components/measured-grid.tsx +8 -4
  615. package/src/components/mini-calendar.tsx +6 -5
  616. package/src/components/modal.tsx +6 -1
  617. package/src/components/month-grid.tsx +229 -0
  618. package/src/components/month-picker.tsx +192 -200
  619. package/src/components/multi-select.tsx +21 -9
  620. package/src/components/nav-pills.tsx +1 -1
  621. package/src/components/picker-sheet.tsx +12 -9
  622. package/src/components/pie-chart-labels.ts +8 -0
  623. package/src/components/pie-chart.tsx +22 -28
  624. package/src/components/popover.tsx +18 -1
  625. package/src/components/row-actions.tsx +2 -1
  626. package/src/components/series-chart-axes.ts +9 -4
  627. package/src/components/series-chart-labels.ts +7 -4
  628. package/src/components/series-chart.tsx +253 -87
  629. package/src/components/share-card.tsx +6 -6
  630. package/src/components/tabs-labels.ts +13 -0
  631. package/src/components/tabs.tsx +2 -2
  632. package/src/components/toggle-legend.tsx +16 -11
  633. package/src/components/translation-review-editor.tsx +6 -5
  634. package/src/components/translation-review.tsx +4 -3
  635. package/src/components/treemap-labels.ts +47 -0
  636. package/src/components/treemap-layout.ts +210 -0
  637. package/src/components/treemap.tsx +780 -138
  638. package/src/components/use-table-state.ts +12 -9
  639. package/src/data-table.ts +1 -4
  640. package/src/demo/demo-countdown.ts +3 -8
  641. package/src/eslint/role-utilities.ts +97 -19
  642. package/src/feedback/feedback-dialog.tsx +30 -19
  643. package/src/feedback/feedback-mutation.ts +20 -0
  644. package/src/feedback/feedback-page-link.ts +90 -0
  645. package/src/feedback/feedback-public-list.tsx +13 -7
  646. package/src/feedback/feedback-replies.tsx +13 -6
  647. package/src/feedback/feedback-row-detail-labels.ts +0 -3
  648. package/src/feedback/feedback-row-detail.tsx +70 -84
  649. package/src/feedback/feedback-status-undo.ts +28 -28
  650. package/src/feedback/feedback-submit-dialog.tsx +2 -2
  651. package/src/feedback/feedback-submit.tsx +21 -7
  652. package/src/feedback/feedback-swipe.tsx +1 -1
  653. package/src/feedback/feedback-table.tsx +86 -38
  654. package/src/feedback/feedback-thread-labels.ts +2 -2
  655. package/src/feedback/feedback-thread.tsx +18 -26
  656. package/src/feedback.ts +89 -6
  657. package/src/hooks/use-focus-trap.ts +8 -24
  658. package/src/hooks/use-hotkey.ts +24 -7
  659. package/src/hooks/use-long-press.ts +16 -2
  660. package/src/hooks/use-media-query.ts +20 -4
  661. package/src/hooks/use-mounted.ts +39 -0
  662. package/src/hooks/use-overlay-history.ts +32 -3
  663. package/src/i18n/defaults.ts +4 -9
  664. package/src/i18n/german.ts +8 -19
  665. package/src/i18n/kit-labels.tsx +9 -11
  666. package/src/i18n/locales/es.ts +8 -19
  667. package/src/i18n/locales/fr.ts +21 -30
  668. package/src/i18n/locales/hu.ts +10 -19
  669. package/src/i18n/locales/it.ts +8 -20
  670. package/src/i18n/locales/zh.ts +9 -19
  671. package/src/i18n/review.ts +12 -2
  672. package/src/i18n/shared-labels.ts +0 -1
  673. package/src/index.ts +57 -49
  674. package/src/landing/routing.tsx +14 -10
  675. package/src/lib/broadcast.ts +52 -0
  676. package/src/lib/date-format.ts +89 -13
  677. package/src/lib/day-format.ts +96 -0
  678. package/src/lib/describe-failure.ts +18 -0
  679. package/src/lib/direction.ts +39 -0
  680. package/src/lib/focus-left.ts +156 -0
  681. package/src/lib/format.ts +5 -17
  682. package/src/lib/http-status.ts +38 -0
  683. package/src/lib/intl-cache.ts +25 -0
  684. package/src/lib/iso-day.ts +13 -0
  685. package/src/lib/lazy-with-fallback.ts +82 -0
  686. package/src/lib/path-pattern.ts +55 -0
  687. package/src/lib/reduced-motion.ts +2 -1
  688. package/src/lib/round.ts +7 -2
  689. package/src/lib/safe-storage.ts +48 -6
  690. package/src/lib/session-refresh.ts +1 -1
  691. package/src/lib/share-format.ts +18 -0
  692. package/src/lib/text-case.ts +19 -0
  693. package/src/lib/text-match.ts +57 -2
  694. package/src/lib/thenable.ts +19 -0
  695. package/src/lib/translation-review.ts +13 -5
  696. package/src/notifications/device-zone-report.ts +1 -1
  697. package/src/notifications/notification-bell.tsx +7 -4
  698. package/src/notifications/notification-inbox.tsx +40 -58
  699. package/src/notifications/notification-parts.tsx +3 -4
  700. package/src/notifications/notification-types-setting.tsx +1 -1
  701. package/src/notifications/notifications-labels.ts +7 -0
  702. package/src/notifications/push-device-setting.tsx +1 -1
  703. package/src/notifications/push-devices-setting.tsx +30 -55
  704. package/src/notifications/quiet-hours-setting.tsx +93 -31
  705. package/src/notifications/unsubscribe-panel.tsx +4 -7
  706. package/src/notifications/use-push-device.ts +76 -10
  707. package/src/offline/query-persistence.ts +35 -3
  708. package/src/rhf/fields.tsx +11 -4
  709. package/src/search/command-palette.tsx +2 -4
  710. package/src/search/global-search.tsx +36 -15
  711. package/src/search/search-index.ts +0 -15
  712. package/src/search/use-command-key.ts +13 -12
  713. package/src/search.ts +10 -1
  714. package/src/settings/use-settings-focus.ts +3 -0
  715. package/src/shell/account-menu-language.tsx +6 -4
  716. package/src/shell/account-menu-text-size.tsx +10 -7
  717. package/src/shell/app-shell.tsx +0 -13
  718. package/src/shell/company-switcher.tsx +1 -1
  719. package/src/shell/dev-menu.tsx +8 -22
  720. package/src/shell/top-bar-actions.tsx +1 -1
  721. package/src/shell/top-bar-parts-labels.ts +1 -4
  722. package/src/shell.ts +21 -17
  723. package/src/sync/engine.ts +339 -61
  724. package/src/sync.ts +2 -8
  725. package/src/testing/css-variables.ts +8 -9
  726. package/src/theme/chart-palette.ts +0 -2
  727. package/src/theme/contrast.ts +8 -9
  728. package/src/theme/palette-store.ts +9 -9
  729. package/src/theme/text-size.ts +6 -8
  730. package/src/theme/theme-store.ts +7 -9
  731. package/src/tour/tour-catalogue.tsx +26 -15
  732. package/src/tour/tour-dock.ts +0 -2
  733. package/src/tour/tour-gates.ts +23 -0
  734. package/src/tour/tour-overlay.tsx +1170 -0
  735. package/src/tour/tour.tsx +108 -1202
  736. package/src/tour/use-tour-progress.ts +1 -1
  737. package/src/wizard/stepper-nav.tsx +3 -1
  738. package/src/wizard/types.ts +11 -2
  739. package/src/wizard/use-wizard.ts +2 -1
  740. package/src/wizard/validation.ts +8 -2
  741. package/tokens.css +6 -5
  742. package/dist/components/field-sync-labels.d.ts +0 -2
  743. package/dist/components/field-sync-labels.js +0 -12
  744. package/dist/components/field-sync-labels.js.map +0 -1
  745. package/dist/components/field-sync.d.ts +0 -164
  746. package/dist/components/field-sync.js +0 -269
  747. package/dist/components/field-sync.js.map +0 -1
  748. package/dist/components/section-nav.d.ts +0 -37
  749. package/dist/components/section-nav.js +0 -33
  750. package/dist/components/section-nav.js.map +0 -1
  751. package/dist/components/sign-chip-labels.d.ts +0 -2
  752. package/dist/components/sign-chip-labels.js +0 -11
  753. package/dist/components/sign-chip-labels.js.map +0 -1
  754. package/dist/components/sign-chip.d.ts +0 -68
  755. package/dist/components/sign-chip.js +0 -47
  756. package/dist/components/sign-chip.js.map +0 -1
  757. package/dist/components/ui-labels.js.map +0 -1
  758. package/dist/lib/isin.d.ts +0 -28
  759. package/dist/lib/isin.js +0 -36
  760. package/dist/lib/isin.js.map +0 -1
  761. package/dist/shell/dev-switchers.d.ts +0 -6
  762. package/dist/shell/dev-switchers.js +0 -12
  763. package/dist/shell/dev-switchers.js.map +0 -1
  764. package/dist/shell/role-switcher.d.ts +0 -21
  765. package/dist/shell/role-switcher.js +0 -31
  766. package/dist/shell/role-switcher.js.map +0 -1
  767. package/src/components/field-sync-labels.ts +0 -13
  768. package/src/components/field-sync.tsx +0 -482
  769. package/src/components/section-nav.tsx +0 -65
  770. package/src/components/sign-chip-labels.ts +0 -12
  771. package/src/components/sign-chip.tsx +0 -124
  772. package/src/components/ui-labels.ts +0 -24
  773. package/src/lib/isin.ts +0 -62
  774. package/src/shell/dev-switchers.tsx +0 -18
  775. package/src/shell/role-switcher.tsx +0 -46
@@ -1,9 +1,10 @@
1
- import { useId, useLayoutEffect, useRef, useState } from "react";
2
- import type { ComponentPropsWithoutRef, MouseEvent, ReactNode, Ref } from "react";
1
+ import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
2
+ import type { ComponentPropsWithoutRef, MouseEvent, ReactNode, Ref, RefObject } from "react";
3
3
  import { AlertTriangle, CheckCircle2, ChevronRight, Info, X } from "lucide-react";
4
4
  import { cn } from "../lib/cn";
5
5
  import { FOCUS_RING } from "./focus-ring";
6
6
  import { DEFAULT_COMMON_LABELS, useKitLabels } from "../i18n/kit-labels";
7
+ import { focusFirst, focusIsLost, tabStopsAround, whenFocusLeftForNothing } from "../lib/focus-left";
7
8
 
8
9
  /** `info` (0.8.0) is the sky family, for news that is neither good nor bad: keksdose's
9
10
  * preview and beta-performance banners, which borrowed `warning` for want of it and
@@ -185,10 +186,21 @@ interface AlertBannerBaseProps extends Omit<ComponentPropsWithoutRef<"div">, "on
185
186
  /** Replaces the tone's glyph — a `<Spinner label={null} />` for "Solving…", say.
186
187
  * `null` draws none. Always decorative; the text carries the message. */
187
188
  icon?: ReactNode;
188
- /** Renders an × at the end that calls this. The banner does not hide itself — the
189
- * caller removes it (and remembers that it did), as with every other kit control
190
- * whose state is app state. */
189
+ /**
190
+ * Renders an × at the end that calls this. The banner does not hide itself — the
191
+ * caller removes it (and remembers that it did), as with every other kit control
192
+ * whose state is app state.
193
+ *
194
+ * The focus (0.52, refactor audit 2026-10-30, DSP-15): the × that held it goes with the
195
+ * banner, and it fell to `<body>`. When the banner unmounts after its × was pressed and
196
+ * the focus has nowhere else, it moves on to the next control after the banner — where
197
+ * Tab from the × would have gone — or, at the end of the page, the one before it; to
198
+ * {@link dismissFocusRef} when given. A banner the caller keeps keeps its ×'s focus.
199
+ */
191
200
  onDismiss?: () => void;
201
+ /** Where the focus goes once the × has removed the banner (DSP-15) — a heading, the
202
+ * control the banner was about. Default: the next control after the banner. */
203
+ dismissFocusRef?: RefObject<HTMLElement | null>;
192
204
  /** Accessible name of the ×. Default: `common.dismiss` from the
193
205
  * {@link UiKitProvider}, else "Dismiss". */
194
206
  dismissLabel?: string;
@@ -278,6 +290,7 @@ export function AlertBanner({
278
290
  action,
279
291
  icon,
280
292
  onDismiss,
293
+ dismissFocusRef,
281
294
  dismissLabel,
282
295
  clampMobile = false,
283
296
  actionPlacement = "own-line",
@@ -318,6 +331,36 @@ export function AlertBanner({
318
331
  return () => ro?.disconnect();
319
332
  }, [clamp, expanded, children]);
320
333
 
334
+ // The ×'s focus, once the caller has removed the banner (DSP-15). Where it can go is read
335
+ // at the press, while the banner still has a place in the page to measure from; whether
336
+ // it must is decided at the unmount, in the cleanup of a passive effect — after React has
337
+ // taken the banner's DOM away, when a focus that fell with it is on <body>.
338
+ const dismissedFrom = useRef<HTMLElement[] | null>(null);
339
+ const dismissTarget = useRef(dismissFocusRef);
340
+ useLayoutEffect(() => {
341
+ dismissTarget.current = dismissFocusRef;
342
+ });
343
+ // The note lives until the focus lands outside the banner (0.52 code review,
344
+ // alert-banner.tsx). A caller that keeps the banner — an async dismiss that failed, a
345
+ // snooze — left it standing, and the banner's unmount long after, a route change with
346
+ // the focus on <body>, sent the focus to a stop noted at the press, over the app's own
347
+ // handling of a new page. A `focusin` on the document, not a `focusout` on the banner:
348
+ // a browser that blurs a removed node would drop the note at the very removal it is for.
349
+ // The one `focusout` heard is a move to nothing — a click on the page — and only once a
350
+ // microtask has shown the × still in place (`whenFocusLeftForNothing`). A focus that
351
+ // stays on the × keeps the note, however late the removal: that is DSP-15's own case,
352
+ // an async dismiss whose request resolves.
353
+ const unwatchFocus = useRef<(() => void) | null>(null);
354
+ useEffect(
355
+ () => () => {
356
+ unwatchFocus.current?.();
357
+ const around = dismissedFrom.current;
358
+ if (!around || !focusIsLost()) return;
359
+ focusFirst([dismissTarget.current?.current, ...around]);
360
+ },
361
+ [],
362
+ );
363
+
321
364
  const Glyph = TONE_GLYPH[tone];
322
365
  // Onto the first line of a box's text: 2px at 14px/20px, 1px at 12px/16px.
323
366
  const nudge = inlineRow || strip ? undefined : sm ? "mt-px" : "mt-0.5";
@@ -448,6 +491,40 @@ export function AlertBanner({
448
491
  // A dismiss inside a whole-row banner must not also follow the row.
449
492
  e.preventDefault();
450
493
  e.stopPropagation();
494
+ const frame = e.currentTarget.parentElement;
495
+ // Only a × that holds the focus has one to lose (0.52 code review, alert-banner.tsx):
496
+ // Safari focuses no button on a click, nor on an iOS tap, and with the focus on
497
+ // <body> already the removal sent it on — a ring, on a form field the on-screen
498
+ // keyboard, on a page the user was reading with the pointer.
499
+ if (frame && e.currentTarget.ownerDocument.activeElement === e.currentTarget) {
500
+ const { after, before } = tabStopsAround(frame);
501
+ dismissedFrom.current = [...after, ...before];
502
+ unwatchFocus.current?.();
503
+ const doc = frame.ownerDocument;
504
+ const forget = () => {
505
+ dismissedFrom.current = null;
506
+ unwatch();
507
+ };
508
+ const onFocusIn = (event: FocusEvent) => {
509
+ if (event.target instanceof Node && frame.contains(event.target)) return;
510
+ forget();
511
+ };
512
+ // Focus that left for nothing — a click on the page — took no `focusin` with it
513
+ // (0.52 code review): the user moved on all the same.
514
+ const onFocusOut = (event: FocusEvent) => {
515
+ whenFocusLeftForNothing(event, () => {
516
+ if (unwatchFocus.current === unwatch) forget();
517
+ });
518
+ };
519
+ const unwatch = () => {
520
+ doc.removeEventListener("focusin", onFocusIn, true);
521
+ frame.removeEventListener("focusout", onFocusOut);
522
+ unwatchFocus.current = null;
523
+ };
524
+ doc.addEventListener("focusin", onFocusIn, true);
525
+ frame.addEventListener("focusout", onFocusOut);
526
+ unwatchFocus.current = unwatch;
527
+ }
451
528
  onDismiss();
452
529
  }}
453
530
  className={cn(
@@ -338,17 +338,28 @@ function settleAmount(text: string, digits: number | undefined, min?: number, ma
338
338
  // `lib/decimal-marks.ts`, read through the engine NumberInput and this field share
339
339
  // (`numeric-field.ts`, DUP-10).
340
340
 
341
- // The consuming app's ONE money palette (`--money-expense` / `--money-income`),
342
- // not a bespoke rose/emerald pairing: a figure being typed has to wear the same
343
- // colour the same figure will wear once it is a row in the table behind the form
344
- // (Keksdose dev#434). The utilities are unlayered and theme-aware, so no `dark:`
345
- // variant is needed and they win over the field's own text colour regardless of
346
- // where `cn` puts them.
347
- const TONE_CLASS: Record<"neutral" | "outflow" | "inflow", string> = {
341
+ /**
342
+ * The figure's colour per {@link AmountInputProps.tone} — public as `AMOUNT_TONE_CLASS`,
343
+ * for a table cell or a test that must paint the same figure the same way (keksdose's
344
+ * receipt lines, dev#556).
345
+ *
346
+ * The consuming app's ONE money palette (`--money-expense` / `--money-income`), not a
347
+ * bespoke rose/emerald pairing: a figure being typed has to wear the same colour the same
348
+ * figure will wear once it is a row in the table behind the form (Keksdose dev#434). The
349
+ * utilities are unlayered and theme-aware, so no `dark:` variant is needed and they win
350
+ * over the field's own text colour regardless of where `cn` puts them.
351
+ */
352
+ const TONE_CLASS: Readonly<Record<NonNullable<AmountInputProps["tone"]>, string>> = {
348
353
  neutral: "",
349
354
  outflow: "text-money-neg",
350
355
  inflow: "text-money-pos",
351
356
  };
357
+ // Exported in 0.52 because keksdose's tests read it out of dist/components/amount-input.js
358
+ // with a regex for `const TONE_CLASS` — the one reach into the kit's dist in three apps,
359
+ // which a rename or a moved module breaks with no public change (MOD-8, refactor audit
360
+ // 2026-10-30; 0.34's move failed 91 of keksdose's test files that way). The local name
361
+ // stays until keksdose imports the export, so the bump itself breaks nothing.
362
+ export { TONE_CLASS as AMOUNT_TONE_CLASS };
352
363
 
353
364
  // The shared chrome-less treatment at money size. `tabular-nums` so digits don't
354
365
  // reflow the figure as they are typed; FIELD_DISPLAY's own muted placeholder
@@ -1,7 +1,8 @@
1
- import { Fragment, useId, useRef, useState } from "react";
1
+ import { useId, useRef, useState } from "react";
2
2
  import type { ComponentPropsWithoutRef, ReactNode, Ref } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { cn } from "../lib/cn";
5
+ import { hasContent } from "../lib/presence";
5
6
  import { FIELD_BASE, FIELD_FLOATING_PAD, FIELD_INVALID } from "./field-base";
6
7
  import { Spinner } from "./spinner";
7
8
  import { useAnchoredPanel } from "../hooks/use-anchored-panel";
@@ -10,10 +11,12 @@ import { isImeEnter } from "../lib/ime";
10
11
  import { isQuietFocus } from "../lib/quiet-focus";
11
12
  import { useAnchorDir } from "./use-anchor-dir";
12
13
  import {
14
+ anchoredHeadingClass,
13
15
  ComboboxFieldLabel,
14
16
  DISABLED_ROW_CLASS,
15
17
  endHintRowProps,
16
- GROUP_HEADING_STICKY,
18
+ GroupedOptions,
19
+ groupRuns,
17
20
  isOptionEnabled,
18
21
  stepEnabled,
19
22
  SUGGESTION_LIST_CLASS,
@@ -293,7 +296,7 @@ export function Autocomplete<V extends string | number = string>(props: Autocomp
293
296
  active: live,
294
297
  });
295
298
 
296
- const hasStatus = status !== undefined && status !== null && status !== false && status !== "";
299
+ const hasStatus = hasContent(status);
297
300
  // The visible line under the rows. Nothing below `minChars` unless the caller has
298
301
  // something to say — an empty field opening a box that says "type more" is noise.
299
302
  const statusLine: ReactNode = hasStatus
@@ -559,75 +562,66 @@ export function Autocomplete<V extends string | number = string>(props: Autocomp
559
562
  aria-labelledby={labelledBy}
560
563
  className="min-h-0 flex-1 overflow-y-auto py-1"
561
564
  >
562
- {results.map((o, i) => {
563
- const startsGroup = o.group != null && o.group !== results[i - 1]?.group;
564
- return (
565
- // Keyed by group AND value, as in the core panel: an option may
566
- // appear once per group.
567
- <Fragment key={`${o.group ?? ""}|${String(o.value)}`}>
568
- {startsGroup && (
569
- <li
570
- role="presentation"
571
- className={cn(
572
- GROUP_HEADING_STICKY,
573
- "px-3 pb-0.5 pt-2 text-caption font-semibold uppercase tracking-wide text-muted first:pt-1",
574
- )}
575
- >
576
- {o.group}
577
- </li>
578
- )}
579
- <li role="presentation">
580
- <button
581
- type="button"
582
- id={optionId(i)}
583
- role="option"
584
- // CHOSEN, never "highlighted" — the family's rule. This
585
- // field holds no value, so the only row that can be chosen
586
- // is the one whose label the text already is.
587
- aria-selected={fillOnSelect && o.label === value}
588
- // Listed, announced as unavailable, never taken: `take`
589
- // refuses it and the arrows pass it over.
590
- aria-disabled={o.disabled || undefined}
591
- tabIndex={-1}
592
- onClick={() => take(o)}
593
- onMouseEnter={() => {
594
- if (!o.disabled) setActive(i);
595
- }}
596
- className={cn(
597
- // The family's suggestion row (DUP-4): it hand-rolled its
598
- // own with a plain `truncate`, so a long address was cut at
599
- // Large, where Combobox's rows wrap (text size §10.17, T1).
600
- // A flex row here, for the icon and the two lines.
601
- suggestionRowClass(i === active && !o.disabled),
602
- "flex items-center gap-2",
603
- small && "py-1 text-xs",
604
- o.disabled && DISABLED_ROW_CLASS,
605
- o.group != null && "ps-6",
606
- )}
607
- >
608
- {o.icon && (
609
- <span aria-hidden className="shrink-0 text-muted">
610
- {o.icon}
565
+ {/* One heading per group, a `role="group"` it names, as in the core panel
566
+ (refactor audit 2026-10-30, INP-8), drawn by the same renderer: an
567
+ option may appear once per group. */}
568
+ <GroupedOptions
569
+ runs={groupRuns(results, (o) => o.group)}
570
+ listboxId={listboxId}
571
+ rowKey={(o) => String(o.value)}
572
+ headingClassName={(run) => anchoredHeadingClass(run)}
573
+ row={(o, i) => (
574
+ <li role="presentation">
575
+ <button
576
+ type="button"
577
+ id={optionId(i)}
578
+ role="option"
579
+ // CHOSEN, never "highlighted" — the family's rule. This
580
+ // field holds no value, so the only row that can be chosen
581
+ // is the one whose label the text already is.
582
+ aria-selected={fillOnSelect && o.label === value}
583
+ // Listed, announced as unavailable, never taken: `take`
584
+ // refuses it and the arrows pass it over.
585
+ aria-disabled={o.disabled || undefined}
586
+ tabIndex={-1}
587
+ onClick={() => take(o)}
588
+ onMouseEnter={() => {
589
+ if (!o.disabled) setActive(i);
590
+ }}
591
+ className={cn(
592
+ // The family's suggestion row (DUP-4): it hand-rolled its
593
+ // own with a plain `truncate`, so a long address was cut at
594
+ // Large, where Combobox's rows wrap (text size §10.17, T1).
595
+ // A flex row here, for the icon and the two lines.
596
+ suggestionRowClass(i === active && !o.disabled),
597
+ "flex items-center gap-2",
598
+ small && "py-1 text-xs",
599
+ o.disabled && DISABLED_ROW_CLASS,
600
+ o.group && "ps-6",
601
+ )}
602
+ >
603
+ {o.icon && (
604
+ <span aria-hidden className="shrink-0 text-muted">
605
+ {o.icon}
606
+ </span>
607
+ )}
608
+ <span className="min-w-0 flex-1">
609
+ <span className="block truncate-until-large text-primary">{o.label}</span>
610
+ {o.sublabel && (
611
+ <span
612
+ className={cn(
613
+ "block truncate-until-large text-placeholder",
614
+ small ? "text-caption" : "text-xs",
615
+ )}
616
+ >
617
+ {o.sublabel}
611
618
  </span>
612
619
  )}
613
- <span className="min-w-0 flex-1">
614
- <span className="block truncate-until-large text-primary">{o.label}</span>
615
- {o.sublabel && (
616
- <span
617
- className={cn(
618
- "block truncate-until-large text-placeholder",
619
- small ? "text-caption" : "text-xs",
620
- )}
621
- >
622
- {o.sublabel}
623
- </span>
624
- )}
625
- </span>
626
- </button>
627
- </li>
628
- </Fragment>
629
- );
630
- })}
620
+ </span>
621
+ </button>
622
+ </li>
623
+ )}
624
+ />
631
625
  </ul>
632
626
  )}
633
627
  {statusLine !== null && (
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useLayoutEffect, useRef, useState } from "react";
2
- import type { ComponentPropsWithRef, CSSProperties, KeyboardEvent, ReactNode } from "react";
2
+ import type { ComponentPropsWithRef, CSSProperties, FocusEvent, KeyboardEvent, ReactNode, RefObject } from "react";
3
3
  import { X } from "lucide-react";
4
4
 
5
5
  import { cn } from "../lib/cn";
@@ -8,6 +8,8 @@ import { useKitLabels, type LabelOverride } from "../i18n/kit-labels";
8
8
  import { useAnnounce } from "../hooks/use-announce";
9
9
  import { useBreakpoint } from "../hooks/use-breakpoint";
10
10
  import { OVERLAY_EXIT_MS, skipsExitAnimation } from "../hooks/use-close-transition";
11
+ import { focusFirst, focusIsLost, whenFocusLeftForNothing } from "../lib/focus-left";
12
+ import { hasContent } from "../lib/presence";
11
13
  import { Button } from "./button";
12
14
  import { IconButton } from "./icon-button";
13
15
  import { DEFAULT_BULK_ACTION_BAR_LABELS } from "./bulk-action-bar-labels";
@@ -124,6 +126,16 @@ export interface BulkActionBarProps extends Omit<ComponentPropsWithRef<"div">, "
124
126
  panel?: ReactNode;
125
127
  /** Default: `bulkActionBar` from the {@link UiKitProvider}, else English. */
126
128
  labels?: LabelOverride<BulkActionBarLabels>;
129
+ /**
130
+ * Where the focus goes when the bar leaves while it holds it — after Clear, or an action
131
+ * that empties the selection (0.52, refactor audit 2026-10-30, DSP-15). Without it, the
132
+ * control the focus came into the bar from (the row's box, say), if that is still on the
133
+ * page; with it, this first. Pass one where the selection takes that control with it — a
134
+ * bulk delete removes the rows whose boxes were ticked: the table, the list's heading,
135
+ * the "Select" button that entered selection mode. A bar that leaves without the focus
136
+ * in it moves nothing.
137
+ */
138
+ returnFocusRef?: RefObject<HTMLElement | null>;
127
139
  }
128
140
 
129
141
  /** Where the floating bar sits: clear of the phone nav and of the home indicator,
@@ -192,10 +204,12 @@ export function BulkActionBar({
192
204
  open,
193
205
  panel,
194
206
  labels: labelsProp,
207
+ returnFocusRef,
195
208
  className,
196
209
  style,
197
210
  onKeyDown,
198
211
  onFocus,
212
+ onBlur,
199
213
  ...rest
200
214
  }: BulkActionBarProps) {
201
215
  const labels = useKitLabels("bulkActionBar", DEFAULT_BULK_ACTION_BAR_LABELS, labelsProp);
@@ -245,6 +259,51 @@ export function BulkActionBar({
245
259
  rove(barRef.current, active);
246
260
  });
247
261
 
262
+ // Leaving with the focus in it (refactor audit 2026-10-30, DSP-15): the bar goes `inert`,
263
+ // which blurs the control it holds, then unmounts — and the focus fell to <body>; on a
264
+ // phone VoiceOver's cursor jumped to the top of the page (keksdose's register). Noted on
265
+ // the bar's outermost element, so the `panel`'s fields count: where the focus came in
266
+ // from (the first entry since the bar showed, replaced once that control is gone), and
267
+ // whether it is in the bar. Focus that leaves for nothing because the page took it —
268
+ // inert, a removal — still counts as in it; a real element elsewhere takes it out, and
269
+ // so does a click on the page that left it nowhere (0.52 code review,
270
+ // bulk-action-bar.tsx: the note outlived it, and the bar's hide long after — the last row
271
+ // unticked with the pointer — pulled the focus and the scroll back to the row's box).
272
+ const cameFrom = useRef<HTMLElement | null>(null);
273
+ const holdsFocus = useRef(false);
274
+ const trackFocus = {
275
+ onFocus: (e: FocusEvent<HTMLDivElement>) => {
276
+ holdsFocus.current = true;
277
+ const from = e.relatedTarget;
278
+ if (from instanceof HTMLElement && !e.currentTarget.contains(from) && !cameFrom.current?.isConnected)
279
+ cameFrom.current = from;
280
+ },
281
+ onBlur: (e: FocusEvent<HTMLDivElement>) => {
282
+ onBlur?.(e);
283
+ const to = e.relatedTarget;
284
+ if (to instanceof Element && !e.currentTarget.contains(to)) holdsFocus.current = false;
285
+ whenFocusLeftForNothing(e, () => {
286
+ holdsFocus.current = false;
287
+ });
288
+ },
289
+ };
290
+ // The commit that hides the bar: the focus goes back while the control holding it is
291
+ // still in place — before `inert` takes it, or, with no exit to play, before <body> does.
292
+ const wasShown = useRef(shown);
293
+ useLayoutEffect(() => {
294
+ if (wasShown.current === shown) return;
295
+ wasShown.current = shown;
296
+ const from = cameFrom.current;
297
+ const held = holdsFocus.current;
298
+ cameFrom.current = null;
299
+ holdsFocus.current = false;
300
+ if (shown) return;
301
+ // The outermost element: the toolbar, or with a `panel` the surface round both.
302
+ const surface = barRef.current?.closest("[data-bulk-action-bar]");
303
+ const inBar = !!surface && surface.contains(document.activeElement);
304
+ if (inBar || (held && focusIsLost())) focusFirst([returnFocusRef?.current, from]);
305
+ }, [shown, returnFocusRef]);
306
+
248
307
  const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
249
308
  onKeyDown?.(e);
250
309
  if (e.defaultPrevented || ownsArrows(e.target as Element)) return;
@@ -279,7 +338,9 @@ export function BulkActionBar({
279
338
  </Button>
280
339
  );
281
340
 
282
- const hasPanel = panel !== undefined && panel !== null && panel !== false;
341
+ // `hasContent` (refactor audit 2026-10-30, DUP-14): an empty-string panel drew an empty
342
+ // bordered strip under the bar.
343
+ const hasPanel = hasContent(panel);
283
344
  // The surface — where the bar sits and what it looks like — and the toolbar row's
284
345
  // own layout. Without a panel they are one element, exactly as before 0.11.0; with
285
346
  // one the surface wraps the row and the panel, so both share its tint and position.
@@ -314,6 +375,7 @@ export function BulkActionBar({
314
375
  onKeyDown={handleKeyDown}
315
376
  onFocus={(e) => {
316
377
  onFocus?.(e);
378
+ if (!hasPanel) trackFocus.onFocus(e);
317
379
  // Clicking a control is using it: the stop follows the pointer too.
318
380
  const hit = itemsOf(barRef.current).find((el) => el === e.target);
319
381
  if (hit) {
@@ -321,6 +383,7 @@ export function BulkActionBar({
321
383
  rove(barRef.current, active);
322
384
  }
323
385
  }}
386
+ onBlur={hasPanel ? undefined : trackFocus.onBlur}
324
387
  style={hasPanel ? undefined : surfaceStyle}
325
388
  className={hasPanel ? rowClass : cn(rowClass, surfaceClass, className)}
326
389
  >
@@ -340,8 +403,13 @@ export function BulkActionBar({
340
403
  <>
341
404
  <span {...regionProps} />
342
405
  {hasPanel ? (
406
+ /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- the focus is
407
+ only noted here (DSP-15): where it came from, whether it is in the bar; the
408
+ controls are the toolbar's and the panel's own. */
343
409
  <div
344
410
  {...rest}
411
+ onFocus={trackFocus.onFocus}
412
+ onBlur={trackFocus.onBlur}
345
413
  data-bulk-action-bar={variant}
346
414
  inert={!shown || undefined}
347
415
  style={surfaceStyle}
@@ -9,8 +9,8 @@ import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
9
9
  * and its router link. Both now draw from this module, which renders nothing, so neither
10
10
  * imports the other.
11
11
  *
12
- * Public through ./ui by name: `buttonClasses`, `TOUCH_TARGET_LARGE` and the four types.
13
- * The other exports are the two buttons' shared parts (`@internal`).
12
+ * Public through the barrel (src/index.ts) by name: `buttonClasses`, `TOUCH_TARGET_LARGE`
13
+ * and the four types. The other exports are the two buttons' shared parts (`@internal`).
14
14
  */
15
15
 
16
16
  /**
@@ -24,9 +24,9 @@ import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
24
24
  * (theme/palette-derive.ts), which no button paints.
25
25
  * - `secondary`: an outline. `ghost`: text or glyph only. `danger`: solid `--danger`,
26
26
  * for an action that destroys something. `link`: a text link that acts (a `<button>`).
27
- * - `primary` and `brand` are **deprecated** aliases of `default` and `accent` (0.49,
28
- * same look; a development build warns once per name). They are removed in 0.51, and
29
- * `primary` will not come back with another meaning.
27
+ *
28
+ * 0.49's `primary` and `brand` (aliases of `default` and `accent`) were removed in 0.51;
29
+ * `primary` will not come back with another meaning.
30
30
  */
31
31
  export type ButtonVariant = "default" | "accent" | "secondary" | "ghost" | "danger" | "link";
32
32
 
@@ -25,8 +25,8 @@ import type { ButtonSize, ButtonTone, ButtonVariant } from "./button-look";
25
25
  */
26
26
  export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
27
27
  /** `default` (the quiet action, what a Button without `variant` draws), `accent` (the
28
- * strong call to action, solid brand), `secondary`, `ghost`, `danger` or `link`.
29
- * `primary` / `brand` are deprecated aliases of `default` / `accent`. See {@link ButtonVariant}. */
28
+ * strong call to action, solid brand), `secondary`, `ghost`, `danger` or `link`. See
29
+ * {@link ButtonVariant}. */
30
30
  variant?: ButtonVariant;
31
31
  /** `md` (default) is the page's action button. `sm` is the compact one — 12px text
32
32
  * and `px-2 py-1` — for the secondary actions in a toolbar, a card header or a
@@ -1,7 +1,8 @@
1
1
  // The live side of `chartEdge` (theme/chart-palette.ts): which colours a FILLED mark is
2
2
  // painted with and which grounds it sits on, read off <html> as the page resolves them —
3
3
  // so the 1px ink edge follows a theme toggle and a palette switch without the chart
4
- // knowing either store. Shared by SeriesChart (bars, areas) and Treemap (tiles).
4
+ // knowing either store. And WHETHER a mark gets one: `useEdgesInForce`, the contrast
5
+ // setting's switch. Shared by SeriesChart (bars, areas) and Treemap (tiles).
5
6
  import { useCallback, useSyncExternalStore } from "react";
6
7
  import { chartEdge } from "../theme/chart-palette";
7
8
  import { parseHex } from "../theme/color";
@@ -71,3 +72,35 @@ export function useChartEdge(): (fill: string | undefined) => string | undefined
71
72
  [snapshot],
72
73
  );
73
74
  }
75
+
76
+ /**
77
+ * How filled marks are parted — see `TreemapProps.edges` (0.51.6, keksdose dev #642).
78
+ * `"auto"`: `"uniform"` at Standard contrast, `"contrast"` while More contrast is in force.
79
+ */
80
+ export type ChartEdges = "auto" | "uniform" | "contrast";
81
+
82
+ // `<html data-contrast>`, followed live: what `"auto"` keys on. Read here rather than
83
+ // through theme/contrast, whose store would come into the chart entry with it.
84
+ function subscribeContrast(onChange: () => void): () => void {
85
+ if (typeof MutationObserver === "undefined" || typeof document === "undefined") return () => {};
86
+ const observer = new MutationObserver(onChange);
87
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-contrast"] });
88
+ return () => observer.disconnect();
89
+ }
90
+ const readMoreContrast = () =>
91
+ typeof document !== "undefined" && document.documentElement.getAttribute("data-contrast") === "more";
92
+
93
+ /**
94
+ * The edges a filled mark draws: `"auto"` resolved against the contrast in force, live.
95
+ *
96
+ * Under `"contrast"` a mark whose hue is under 3:1 against the ground gets the 1px ink
97
+ * edge ({@link useChartEdge}); under `"uniform"` none does. The tiles took this rule in
98
+ * 0.51.6 (#642: "The borders do not look nice when changing from tile to tile"), and
99
+ * SeriesChart's bars and areas in 0.52 (refactor audit 2026-10-30, DSP-18, Marcel's
100
+ * decision 3): at Standard contrast the edge fell on 5 of 8 hues in light, and in dark on
101
+ * chart-5 and chart-7 alone — the same unevenness, in a bar chart.
102
+ */
103
+ export function useEdgesInForce(edges: ChartEdges = "auto"): "uniform" | "contrast" {
104
+ const more = useSyncExternalStore(subscribeContrast, readMoreContrast, () => false);
105
+ return edges === "auto" ? (more ? "contrast" : "uniform") : edges;
106
+ }
@@ -0,0 +1,35 @@
1
+ // A chart's accessible description — internal, not exported from the barrel.
2
+ //
3
+ // A chart is a `role="figure"` named by its `aria-label` and described by a sentence of
4
+ // what it shows (0.52, refactor audit 2026-10-30, DSP-5). SeriesChart and Treemap each
5
+ // built the `hidden` sentence and the `aria-describedby` that points at it, and already
6
+ // differed: Treemap kept a caller's own ids, SeriesChart had none to keep. Both put the
7
+ // sentence AFTER the figure, as its sibling, so the chart stopped being its parent's last
8
+ // child and a stack's `space-y-*` or `divide-y` gave it a margin or a rule underneath (0.52
9
+ // code review, deep, charts, findings 7 and 14). One copy now, rendered inside the root.
10
+ import { useId, type ReactNode } from "react";
11
+
12
+ export interface FigureDescription {
13
+ /** The root's `aria-describedby`: the caller's own ids, then the description's. */
14
+ describedBy: string | undefined;
15
+ /** The description, to render INSIDE the root; `null` when there is none. */
16
+ node: ReactNode;
17
+ }
18
+
19
+ /**
20
+ * The description of a figure, and the `aria-describedby` that reads it — after the
21
+ * caller's own ids (`describedBy`), which it keeps. The sentence is `hidden`: it is read
22
+ * through the reference, never a second time in browse mode, and takes no room.
23
+ */
24
+ export function useFigureDescription(description: string | undefined, describedBy?: string): FigureDescription {
25
+ const id = useId();
26
+ if (!description) return { describedBy, node: null };
27
+ return {
28
+ describedBy: describedBy ? `${describedBy} ${id}` : id,
29
+ node: (
30
+ <span id={id} hidden>
31
+ {description}
32
+ </span>
33
+ ),
34
+ };
35
+ }
@@ -466,7 +466,6 @@ export function withChartZoom<P extends ZoomTarget>(Chart: ComponentType<P>) {
466
466
  <ZoomLayer
467
467
  axisIds={axes.map((axis) => axis.id)}
468
468
  only={only}
469
- label={labels.zoomHint}
470
469
  onZoom={(selection) => {
471
470
  const next = zoomAfter(state, selection, source);
472
471
  if (next.x !== xWindow) setXWindow(next.x);
@@ -506,17 +505,20 @@ export function withChartZoom<P extends ZoomTarget>(Chart: ComponentType<P>) {
506
505
  * A transparent rectangle over the plot area that reads the drag. It does NOT stop the
507
506
  * event: recharts drives its tooltip from the same pointer move bubbling up to its own
508
507
  * wrapper, so swallowing it here would trade the tooltip for the zoom.
508
+ *
509
+ * Nothing to assistive technology (0.52, refactor audit 2026-10-30, DSP-5): it was a
510
+ * `role="application"` named by the drag hint, which could not take focus — the one name
511
+ * on an otherwise unnamed chart. The hint is in the chart's description now
512
+ * (`SeriesChart`'s figure), and `data-zoom-surface` is how a test finds the rect.
509
513
  */
510
514
  function ZoomLayer({
511
515
  axisIds,
512
516
  only,
513
- label,
514
517
  onZoom,
515
518
  onReset,
516
519
  }: {
517
520
  axisIds: string[];
518
521
  only?: "x" | "y";
519
- label: string;
520
522
  onZoom: (selection: ZoomSelection) => void;
521
523
  onReset: () => void;
522
524
  }) {
@@ -578,8 +580,7 @@ function ZoomLayer({
578
580
  height={plot.height}
579
581
  // `transparent`, not `none`: an unpainted shape takes no pointer events.
580
582
  fill="transparent"
581
- role="application"
582
- aria-label={label}
583
+ data-zoom-surface=""
583
584
  style={{ cursor: "crosshair" }}
584
585
  onPointerDown={(event) => {
585
586
  if (event.button !== 0) return;