@eifi1/ui-kit 0.51.7 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (746) hide show
  1. package/README.md +141 -23
  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 +20 -8
  60. package/dist/billing/cancel-subscription.js +7 -5
  61. package/dist/billing/cancel-subscription.js.map +1 -1
  62. package/dist/billing/dialog-close.d.ts +29 -0
  63. package/dist/billing/dialog-close.js +8 -0
  64. package/dist/billing/dialog-close.js.map +1 -0
  65. package/dist/billing/plan-card.d.ts +14 -4
  66. package/dist/billing/plan-card.js +15 -8
  67. package/dist/billing/plan-card.js.map +1 -1
  68. package/dist/billing/plan-change-labels.d.ts +42 -0
  69. package/dist/billing/plan-change-labels.js +22 -0
  70. package/dist/billing/plan-change-labels.js.map +1 -0
  71. package/dist/billing/plan-switch.d.ts +19 -7
  72. package/dist/billing/plan-switch.js +8 -4
  73. package/dist/billing/plan-switch.js.map +1 -1
  74. package/dist/chart.d.ts +12 -0
  75. package/dist/chart.js +16 -1
  76. package/dist/chart.js.map +1 -1
  77. package/dist/components/account-chips.d.ts +5 -5
  78. package/dist/components/account-chips.js +11 -13
  79. package/dist/components/account-chips.js.map +1 -1
  80. package/dist/components/alert-banner.d.ts +16 -5
  81. package/dist/components/alert-banner.js +46 -1
  82. package/dist/components/alert-banner.js.map +1 -1
  83. package/dist/components/amount-input.d.ts +13 -0
  84. package/dist/components/amount-input.js +1 -0
  85. package/dist/components/amount-input.js.map +1 -1
  86. package/dist/components/autocomplete.js +57 -65
  87. package/dist/components/autocomplete.js.map +1 -1
  88. package/dist/components/bulk-action-bar.d.ts +12 -2
  89. package/dist/components/bulk-action-bar.js +65 -21
  90. package/dist/components/bulk-action-bar.js.map +1 -1
  91. package/dist/components/button-look.d.ts +5 -5
  92. package/dist/components/button-look.js.map +1 -1
  93. package/dist/components/button.d.ts +2 -2
  94. package/dist/components/button.js.map +1 -1
  95. package/dist/components/chart-edge.d.ts +16 -0
  96. package/dist/components/chart-edge.js +14 -1
  97. package/dist/components/chart-edge.js.map +1 -1
  98. package/dist/components/chart-figure.d.ts +13 -0
  99. package/dist/components/chart-figure.js +15 -0
  100. package/dist/components/chart-figure.js.map +1 -0
  101. package/dist/components/chart-zoom-core.js +1 -4
  102. package/dist/components/chart-zoom-core.js.map +1 -1
  103. package/dist/components/chart.d.ts +8 -1
  104. package/dist/components/chart.js +19 -10
  105. package/dist/components/chart.js.map +1 -1
  106. package/dist/components/combobox-core.d.ts +111 -2
  107. package/dist/components/combobox-core.js +148 -42
  108. package/dist/components/combobox-core.js.map +1 -1
  109. package/dist/components/combobox.d.ts +1 -1
  110. package/dist/components/combobox.js +141 -130
  111. package/dist/components/combobox.js.map +1 -1
  112. package/dist/components/copy-button.d.ts +6 -5
  113. package/dist/components/copy-button.js +5 -3
  114. package/dist/components/copy-button.js.map +1 -1
  115. package/dist/components/country-select.js +2 -2
  116. package/dist/components/country-select.js.map +1 -1
  117. package/dist/components/data-table-column-settings.d.ts +1 -1
  118. package/dist/components/data-table-column-settings.js +33 -7
  119. package/dist/components/data-table-column-settings.js.map +1 -1
  120. package/dist/components/data-table-filters.d.ts +4 -2
  121. package/dist/components/data-table-filters.js +2 -2
  122. package/dist/components/data-table-filters.js.map +1 -1
  123. package/dist/components/data-table-fit.d.ts +6 -4
  124. package/dist/components/data-table-fit.js +4 -1
  125. package/dist/components/data-table-fit.js.map +1 -1
  126. package/dist/components/data-table-groups.d.ts +1 -1
  127. package/dist/components/data-table-groups.js.map +1 -1
  128. package/dist/components/data-table-head.d.ts +1 -1
  129. package/dist/components/data-table-head.js +15 -12
  130. package/dist/components/data-table-head.js.map +1 -1
  131. package/dist/components/data-table-mobile.d.ts +12 -7
  132. package/dist/components/data-table-mobile.js +43 -30
  133. package/dist/components/data-table-mobile.js.map +1 -1
  134. package/dist/components/data-table-pagination.js +11 -6
  135. package/dist/components/data-table-pagination.js.map +1 -1
  136. package/dist/components/data-table-resize.d.ts +1 -1
  137. package/dist/components/data-table-resize.js.map +1 -1
  138. package/dist/components/data-table-row-actions.d.ts +2 -2
  139. package/dist/components/data-table-row-actions.js.map +1 -1
  140. package/dist/components/data-table-row.d.ts +6 -2
  141. package/dist/components/data-table-row.js +17 -10
  142. package/dist/components/data-table-row.js.map +1 -1
  143. package/dist/components/data-table-selection.d.ts +1 -1
  144. package/dist/components/data-table-selection.js.map +1 -1
  145. package/dist/components/data-table-shared.d.ts +10 -2
  146. package/dist/components/data-table-shared.js +18 -0
  147. package/dist/components/data-table-shared.js.map +1 -1
  148. package/dist/components/data-table-sort.d.ts +21 -2
  149. package/dist/components/data-table-sort.js +24 -0
  150. package/dist/components/data-table-sort.js.map +1 -1
  151. package/dist/components/data-table-virtual.d.ts +1 -1
  152. package/dist/components/data-table-virtual.js +18 -6
  153. package/dist/components/data-table-virtual.js.map +1 -1
  154. package/dist/components/data-table.d.ts +13 -4
  155. package/dist/components/data-table.js +139 -36
  156. package/dist/components/data-table.js.map +1 -1
  157. package/dist/components/date-field.d.ts +24 -2
  158. package/dist/components/date-field.js +33 -8
  159. package/dist/components/date-field.js.map +1 -1
  160. package/dist/components/date-picker.d.ts +15 -2
  161. package/dist/components/date-picker.js +55 -26
  162. package/dist/components/date-picker.js.map +1 -1
  163. package/dist/components/date-range-picker.d.ts +47 -20
  164. package/dist/components/date-range-picker.js +23 -7
  165. package/dist/components/date-range-picker.js.map +1 -1
  166. package/dist/components/error-boundary.js +37 -30
  167. package/dist/components/error-boundary.js.map +1 -1
  168. package/dist/components/{ui-labels.d.ts → field-base-labels.d.ts} +0 -2
  169. package/dist/components/{ui-labels.js → field-base-labels.js} +2 -9
  170. package/dist/components/field-base-labels.js.map +1 -0
  171. package/dist/components/field-base.d.ts +4 -3
  172. package/dist/components/field-base.js +3 -3
  173. package/dist/components/field-base.js.map +1 -1
  174. package/dist/components/field-sync-labels.d.ts +1 -0
  175. package/dist/components/field-sync-labels.js.map +1 -1
  176. package/dist/components/field-sync.d.ts +23 -1
  177. package/dist/components/field-sync.js.map +1 -1
  178. package/dist/components/form-actions.js +11 -5
  179. package/dist/components/form-actions.js.map +1 -1
  180. package/dist/components/hover-menu.d.ts +3 -0
  181. package/dist/components/hover-menu.js +5 -3
  182. package/dist/components/hover-menu.js.map +1 -1
  183. package/dist/components/icon-button.js +14 -9
  184. package/dist/components/icon-button.js.map +1 -1
  185. package/dist/components/inline-confirm.d.ts +43 -0
  186. package/dist/components/inline-confirm.js +67 -0
  187. package/dist/components/inline-confirm.js.map +1 -0
  188. package/dist/components/inline-entity-combobox.d.ts +4 -3
  189. package/dist/components/inline-entity-combobox.js +148 -140
  190. package/dist/components/inline-entity-combobox.js.map +1 -1
  191. package/dist/components/input.js +1 -1
  192. package/dist/components/input.js.map +1 -1
  193. package/dist/components/jump-calendar.js +28 -112
  194. package/dist/components/jump-calendar.js.map +1 -1
  195. package/dist/components/legal-page.d.ts +3 -1
  196. package/dist/components/legal-page.js +3 -2
  197. package/dist/components/legal-page.js.map +1 -1
  198. package/dist/components/legal.d.ts +4 -2
  199. package/dist/components/legal.js.map +1 -1
  200. package/dist/components/lightbox.d.ts +2 -2
  201. package/dist/components/lightbox.js +17 -6
  202. package/dist/components/lightbox.js.map +1 -1
  203. package/dist/components/list.d.ts +6 -2
  204. package/dist/components/list.js +3 -2
  205. package/dist/components/list.js.map +1 -1
  206. package/dist/components/measured-grid.d.ts +1 -1
  207. package/dist/components/measured-grid.js +2 -1
  208. package/dist/components/measured-grid.js.map +1 -1
  209. package/dist/components/mini-calendar.js +3 -5
  210. package/dist/components/mini-calendar.js.map +1 -1
  211. package/dist/components/modal.js +3 -1
  212. package/dist/components/modal.js.map +1 -1
  213. package/dist/components/month-grid.d.ts +55 -0
  214. package/dist/components/month-grid.js +148 -0
  215. package/dist/components/month-grid.js.map +1 -0
  216. package/dist/components/month-picker.d.ts +36 -4
  217. package/dist/components/month-picker.js +123 -146
  218. package/dist/components/month-picker.js.map +1 -1
  219. package/dist/components/multi-select.d.ts +2 -2
  220. package/dist/components/multi-select.js +11 -6
  221. package/dist/components/multi-select.js.map +1 -1
  222. package/dist/components/picker-sheet.js +2 -1
  223. package/dist/components/picker-sheet.js.map +1 -1
  224. package/dist/components/pie-chart-labels.d.ts +7 -0
  225. package/dist/components/pie-chart-labels.js +1 -0
  226. package/dist/components/pie-chart-labels.js.map +1 -1
  227. package/dist/components/pie-chart.d.ts +5 -3
  228. package/dist/components/pie-chart.js +7 -22
  229. package/dist/components/pie-chart.js.map +1 -1
  230. package/dist/components/popover.d.ts +3 -0
  231. package/dist/components/popover.js +7 -1
  232. package/dist/components/popover.js.map +1 -1
  233. package/dist/components/row-actions.js +2 -1
  234. package/dist/components/row-actions.js.map +1 -1
  235. package/dist/components/section-nav.d.ts +7 -0
  236. package/dist/components/section-nav.js.map +1 -1
  237. package/dist/components/series-chart-axes.d.ts +3 -3
  238. package/dist/components/series-chart-axes.js.map +1 -1
  239. package/dist/components/series-chart-labels.d.ts +7 -4
  240. package/dist/components/series-chart-labels.js.map +1 -1
  241. package/dist/components/series-chart.d.ts +31 -7
  242. package/dist/components/series-chart.js +72 -29
  243. package/dist/components/series-chart.js.map +1 -1
  244. package/dist/components/share-card.js +3 -3
  245. package/dist/components/share-card.js.map +1 -1
  246. package/dist/components/sign-chip-labels.d.ts +1 -0
  247. package/dist/components/sign-chip-labels.js.map +1 -1
  248. package/dist/components/sign-chip.d.ts +10 -1
  249. package/dist/components/sign-chip.js.map +1 -1
  250. package/dist/components/tabs-labels.d.ts +2 -0
  251. package/dist/components/tabs-labels.js +11 -0
  252. package/dist/components/tabs-labels.js.map +1 -0
  253. package/dist/components/tabs.d.ts +1 -1
  254. package/dist/components/tabs.js +1 -1
  255. package/dist/components/tabs.js.map +1 -1
  256. package/dist/components/toggle-legend.d.ts +2 -2
  257. package/dist/components/toggle-legend.js +6 -6
  258. package/dist/components/toggle-legend.js.map +1 -1
  259. package/dist/components/translation-review-editor.d.ts +4 -2
  260. package/dist/components/translation-review-editor.js +2 -3
  261. package/dist/components/translation-review-editor.js.map +1 -1
  262. package/dist/components/translation-review.d.ts +2 -1
  263. package/dist/components/translation-review.js +2 -2
  264. package/dist/components/translation-review.js.map +1 -1
  265. package/dist/components/treemap-labels.d.ts +26 -0
  266. package/dist/components/treemap-labels.js +10 -0
  267. package/dist/components/treemap-labels.js.map +1 -0
  268. package/dist/components/treemap-layout.d.ts +62 -0
  269. package/dist/components/treemap-layout.js +126 -0
  270. package/dist/components/treemap-layout.js.map +1 -0
  271. package/dist/components/treemap.d.ts +140 -17
  272. package/dist/components/treemap.js +324 -97
  273. package/dist/components/treemap.js.map +1 -1
  274. package/dist/components/use-table-state.d.ts +5 -5
  275. package/dist/components/use-table-state.js +4 -1
  276. package/dist/components/use-table-state.js.map +1 -1
  277. package/dist/data-table.d.ts +6 -2
  278. package/dist/data-table.js +7 -2
  279. package/dist/data-table.js.map +1 -1
  280. package/dist/eslint/index.js +1 -1
  281. package/dist/eslint/role-utilities.d.ts +36 -7
  282. package/dist/eslint/role-utilities.js +42 -9
  283. package/dist/eslint/role-utilities.js.map +1 -1
  284. package/dist/feedback/feedback-dialog.d.ts +25 -16
  285. package/dist/feedback/feedback-dialog.js +6 -2
  286. package/dist/feedback/feedback-dialog.js.map +1 -1
  287. package/dist/feedback/feedback-mutation.d.ts +30 -0
  288. package/dist/feedback/feedback-mutation.js +16 -0
  289. package/dist/feedback/feedback-mutation.js.map +1 -0
  290. package/dist/feedback/feedback-page-link.d.ts +53 -0
  291. package/dist/feedback/feedback-page-link.js +37 -0
  292. package/dist/feedback/feedback-page-link.js.map +1 -0
  293. package/dist/feedback/feedback-public-list.d.ts +12 -3
  294. package/dist/feedback/feedback-public-list.js +2 -1
  295. package/dist/feedback/feedback-public-list.js.map +1 -1
  296. package/dist/feedback/feedback-replies.d.ts +3 -1
  297. package/dist/feedback/feedback-replies.js +5 -4
  298. package/dist/feedback/feedback-replies.js.map +1 -1
  299. package/dist/feedback/feedback-row-detail-labels.js +5 -0
  300. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  301. package/dist/feedback/feedback-row-detail.d.ts +64 -28
  302. package/dist/feedback/feedback-row-detail.js +30 -28
  303. package/dist/feedback/feedback-row-detail.js.map +1 -1
  304. package/dist/feedback/feedback-status-undo.d.ts +30 -11
  305. package/dist/feedback/feedback-status-undo.js +10 -15
  306. package/dist/feedback/feedback-status-undo.js.map +1 -1
  307. package/dist/feedback/feedback-submit-dialog.d.ts +1 -1
  308. package/dist/feedback/feedback-submit-dialog.js +1 -1
  309. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  310. package/dist/feedback/feedback-submit.js +9 -3
  311. package/dist/feedback/feedback-submit.js.map +1 -1
  312. package/dist/feedback/feedback-swipe.d.ts +1 -1
  313. package/dist/feedback/feedback-swipe.js.map +1 -1
  314. package/dist/feedback/feedback-table.d.ts +21 -9
  315. package/dist/feedback/feedback-table.js +45 -25
  316. package/dist/feedback/feedback-table.js.map +1 -1
  317. package/dist/feedback/feedback-thread-labels.d.ts +2 -2
  318. package/dist/feedback/feedback-thread-labels.js.map +1 -1
  319. package/dist/feedback/feedback-thread.d.ts +24 -23
  320. package/dist/feedback/feedback-thread.js +2 -1
  321. package/dist/feedback/feedback-thread.js.map +1 -1
  322. package/dist/feedback.d.ts +34 -0
  323. package/dist/feedback.js +42 -0
  324. package/dist/feedback.js.map +1 -1
  325. package/dist/hooks/use-focus-trap.d.ts +35 -0
  326. package/dist/hooks/use-focus-trap.js +6 -4
  327. package/dist/hooks/use-focus-trap.js.map +1 -1
  328. package/dist/hooks/use-hotkey.d.ts +5 -2
  329. package/dist/hooks/use-hotkey.js +5 -4
  330. package/dist/hooks/use-hotkey.js.map +1 -1
  331. package/dist/hooks/use-long-press.d.ts +7 -4
  332. package/dist/hooks/use-long-press.js +3 -0
  333. package/dist/hooks/use-long-press.js.map +1 -1
  334. package/dist/hooks/use-media-query.js +12 -4
  335. package/dist/hooks/use-media-query.js.map +1 -1
  336. package/dist/hooks/use-mounted.d.ts +22 -0
  337. package/dist/hooks/use-mounted.js +24 -0
  338. package/dist/hooks/use-mounted.js.map +1 -0
  339. package/dist/hooks/use-overlay-history.js +19 -3
  340. package/dist/hooks/use-overlay-history.js.map +1 -1
  341. package/dist/i18n/defaults.js +4 -5
  342. package/dist/i18n/defaults.js.map +1 -1
  343. package/dist/i18n/german.js +7 -1
  344. package/dist/i18n/german.js.map +1 -1
  345. package/dist/i18n/kit-labels.d.ts +9 -2
  346. package/dist/i18n/kit-labels.js.map +1 -1
  347. package/dist/i18n/locales/en.js +20 -41
  348. package/dist/i18n/locales/en.js.map +3 -3
  349. package/dist/i18n/locales/es.js +7 -1
  350. package/dist/i18n/locales/es.js.map +1 -1
  351. package/dist/i18n/locales/fr.js +20 -10
  352. package/dist/i18n/locales/fr.js.map +1 -1
  353. package/dist/i18n/locales/hu.js +9 -1
  354. package/dist/i18n/locales/hu.js.map +1 -1
  355. package/dist/i18n/locales/it.js +7 -1
  356. package/dist/i18n/locales/it.js.map +1 -1
  357. package/dist/i18n/locales/zh.js +8 -1
  358. package/dist/i18n/locales/zh.js.map +1 -1
  359. package/dist/i18n/review.js +5 -0
  360. package/dist/i18n/review.js.map +1 -1
  361. package/dist/index.d.ts +64 -17
  362. package/dist/index.js +77 -12
  363. package/dist/index.js.map +1 -1
  364. package/dist/landing/routing.d.ts +3 -1
  365. package/dist/landing/routing.js +4 -7
  366. package/dist/landing/routing.js.map +1 -1
  367. package/dist/lib/broadcast.d.ts +17 -0
  368. package/dist/lib/broadcast.js +30 -0
  369. package/dist/lib/broadcast.js.map +1 -0
  370. package/dist/lib/date-format.d.ts +37 -2
  371. package/dist/lib/date-format.js +34 -8
  372. package/dist/lib/date-format.js.map +1 -1
  373. package/dist/lib/day-format.d.ts +46 -0
  374. package/dist/lib/day-format.js +46 -0
  375. package/dist/lib/day-format.js.map +1 -0
  376. package/dist/lib/describe-failure.d.ts +14 -0
  377. package/dist/lib/describe-failure.js +9 -0
  378. package/dist/lib/describe-failure.js.map +1 -0
  379. package/dist/lib/direction.d.ts +16 -0
  380. package/dist/lib/direction.js +9 -1
  381. package/dist/lib/direction.js.map +1 -1
  382. package/dist/lib/focus-left.d.ts +63 -0
  383. package/dist/lib/focus-left.js +67 -0
  384. package/dist/lib/focus-left.js.map +1 -0
  385. package/dist/lib/format.js +5 -14
  386. package/dist/lib/format.js.map +1 -1
  387. package/dist/lib/http-status.d.ts +24 -0
  388. package/dist/lib/http-status.js +14 -0
  389. package/dist/lib/http-status.js.map +1 -0
  390. package/dist/lib/intl-cache.d.ts +14 -0
  391. package/dist/lib/intl-cache.js +15 -0
  392. package/dist/lib/intl-cache.js.map +1 -0
  393. package/dist/lib/isin.d.ts +10 -0
  394. package/dist/lib/isin.js.map +1 -1
  395. package/dist/lib/iso-day.d.ts +11 -0
  396. package/dist/lib/iso-day.js +8 -0
  397. package/dist/lib/iso-day.js.map +1 -0
  398. package/dist/lib/lazy-with-fallback.d.ts +31 -0
  399. package/dist/lib/lazy-with-fallback.js +40 -0
  400. package/dist/lib/lazy-with-fallback.js.map +1 -0
  401. package/dist/lib/path-pattern.d.ts +34 -0
  402. package/dist/lib/path-pattern.js +24 -0
  403. package/dist/lib/path-pattern.js.map +1 -0
  404. package/dist/lib/reduced-motion.d.ts +2 -1
  405. package/dist/lib/reduced-motion.js.map +1 -1
  406. package/dist/lib/round.d.ts +2 -1
  407. package/dist/lib/round.js +2 -1
  408. package/dist/lib/round.js.map +1 -1
  409. package/dist/lib/safe-storage.d.ts +24 -5
  410. package/dist/lib/safe-storage.js +27 -2
  411. package/dist/lib/safe-storage.js.map +1 -1
  412. package/dist/lib/session-refresh.js +1 -1
  413. package/dist/lib/session-refresh.js.map +1 -1
  414. package/dist/lib/share-format.d.ts +13 -0
  415. package/dist/lib/share-format.js +10 -0
  416. package/dist/lib/share-format.js.map +1 -0
  417. package/dist/lib/text-case.d.ts +17 -0
  418. package/dist/lib/text-case.js +8 -0
  419. package/dist/lib/text-case.js.map +1 -0
  420. package/dist/lib/text-match.d.ts +29 -0
  421. package/dist/lib/text-match.js +20 -2
  422. package/dist/lib/text-match.js.map +1 -1
  423. package/dist/lib/thenable.d.ts +13 -0
  424. package/dist/lib/thenable.js +8 -0
  425. package/dist/lib/thenable.js.map +1 -0
  426. package/dist/lib/translation-review.d.ts +7 -2
  427. package/dist/lib/translation-review.js +4 -4
  428. package/dist/lib/translation-review.js.map +1 -1
  429. package/dist/notifications/device-zone-report.js +1 -1
  430. package/dist/notifications/device-zone-report.js.map +1 -1
  431. package/dist/notifications/notification-bell.d.ts +2 -1
  432. package/dist/notifications/notification-bell.js +3 -3
  433. package/dist/notifications/notification-bell.js.map +1 -1
  434. package/dist/notifications/notification-inbox.d.ts +10 -6
  435. package/dist/notifications/notification-inbox.js +26 -48
  436. package/dist/notifications/notification-inbox.js.map +1 -1
  437. package/dist/notifications/notification-parts.d.ts +3 -2
  438. package/dist/notifications/notification-parts.js +1 -3
  439. package/dist/notifications/notification-parts.js.map +1 -1
  440. package/dist/notifications/notification-types-setting.js +1 -1
  441. package/dist/notifications/notification-types-setting.js.map +1 -1
  442. package/dist/notifications/notifications-labels.d.ts +6 -0
  443. package/dist/notifications/notifications-labels.js +1 -0
  444. package/dist/notifications/notifications-labels.js.map +1 -1
  445. package/dist/notifications/push-device-setting.js +1 -1
  446. package/dist/notifications/push-device-setting.js.map +1 -1
  447. package/dist/notifications/push-devices-setting.d.ts +2 -1
  448. package/dist/notifications/push-devices-setting.js +27 -49
  449. package/dist/notifications/push-devices-setting.js.map +1 -1
  450. package/dist/notifications/quiet-hours-setting.d.ts +10 -3
  451. package/dist/notifications/quiet-hours-setting.js +65 -26
  452. package/dist/notifications/quiet-hours-setting.js.map +1 -1
  453. package/dist/notifications/unsubscribe-panel.js +4 -7
  454. package/dist/notifications/unsubscribe-panel.js.map +1 -1
  455. package/dist/notifications/use-push-device.d.ts +39 -1
  456. package/dist/notifications/use-push-device.js +28 -7
  457. package/dist/notifications/use-push-device.js.map +1 -1
  458. package/dist/offline/query-persistence.d.ts +3 -1
  459. package/dist/offline/query-persistence.js +19 -2
  460. package/dist/offline/query-persistence.js.map +1 -1
  461. package/dist/rhf/fields.d.ts +11 -4
  462. package/dist/rhf/fields.js.map +1 -1
  463. package/dist/search/command-palette.js +1 -1
  464. package/dist/search/command-palette.js.map +1 -1
  465. package/dist/search/global-search.js +19 -9
  466. package/dist/search/global-search.js.map +1 -1
  467. package/dist/search/use-command-key.d.ts +12 -1
  468. package/dist/search/use-command-key.js +3 -12
  469. package/dist/search/use-command-key.js.map +1 -1
  470. package/dist/search.d.ts +7 -0
  471. package/dist/search.js +10 -0
  472. package/dist/search.js.map +1 -1
  473. package/dist/settings/use-settings-focus.js +1 -0
  474. package/dist/settings/use-settings-focus.js.map +1 -1
  475. package/dist/shell/account-menu-language.d.ts +21 -5
  476. package/dist/shell/account-menu-language.js +3 -1
  477. package/dist/shell/account-menu-language.js.map +1 -1
  478. package/dist/shell/account-menu-text-size.d.ts +26 -9
  479. package/dist/shell/account-menu-text-size.js +3 -2
  480. package/dist/shell/account-menu-text-size.js.map +1 -1
  481. package/dist/shell/dev-menu.js +5 -18
  482. package/dist/shell/dev-menu.js.map +1 -1
  483. package/dist/shell/role-switcher.d.ts +7 -0
  484. package/dist/shell/role-switcher.js.map +1 -1
  485. package/dist/shell/top-bar-actions.d.ts +1 -1
  486. package/dist/shell/top-bar-actions.js.map +1 -1
  487. package/dist/shell/top-bar-parts-labels.d.ts +0 -2
  488. package/dist/shell/top-bar-parts-labels.js +0 -1
  489. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  490. package/dist/shell.d.ts +27 -8
  491. package/dist/shell.js +28 -7
  492. package/dist/shell.js.map +1 -1
  493. package/dist/sync/engine.d.ts +30 -4
  494. package/dist/sync/engine.js +172 -52
  495. package/dist/sync/engine.js.map +1 -1
  496. package/dist/sync.d.ts +6 -2
  497. package/dist/sync.js +6 -1
  498. package/dist/sync.js.map +1 -1
  499. package/dist/testing/css-variables.js +3 -8
  500. package/dist/testing/css-variables.js.map +1 -1
  501. package/dist/theme/contrast.d.ts +2 -1
  502. package/dist/theme/contrast.js +3 -8
  503. package/dist/theme/contrast.js.map +1 -1
  504. package/dist/theme/palette-store.js +6 -6
  505. package/dist/theme/palette-store.js.map +1 -1
  506. package/dist/theme/text-size.js +3 -8
  507. package/dist/theme/text-size.js.map +1 -1
  508. package/dist/theme/theme-store.js +3 -6
  509. package/dist/theme/theme-store.js.map +1 -1
  510. package/dist/tour/tour-catalogue.d.ts +7 -4
  511. package/dist/tour/tour-catalogue.js +12 -10
  512. package/dist/tour/tour-catalogue.js.map +1 -1
  513. package/dist/tour/tour-dock.d.ts +0 -2
  514. package/dist/tour/tour-dock.js +0 -2
  515. package/dist/tour/tour-dock.js.map +1 -1
  516. package/dist/tour/tour-gates.d.ts +4 -0
  517. package/dist/tour/tour-gates.js +14 -0
  518. package/dist/tour/tour-gates.js.map +1 -1
  519. package/dist/tour/tour-overlay.d.ts +27 -0
  520. package/dist/tour/tour-overlay.js +741 -0
  521. package/dist/tour/tour-overlay.js.map +1 -0
  522. package/dist/tour/tour.d.ts +13 -3
  523. package/dist/tour/tour.js +59 -781
  524. package/dist/tour/tour.js.map +1 -1
  525. package/dist/tour/use-tour-progress.js +1 -1
  526. package/dist/tour/use-tour-progress.js.map +1 -1
  527. package/dist/wizard/stepper-nav.d.ts +5 -1
  528. package/dist/wizard/stepper-nav.js.map +1 -1
  529. package/dist/wizard/types.d.ts +6 -2
  530. package/dist/wizard/types.js.map +1 -1
  531. package/dist/wizard/use-wizard.d.ts +3 -1
  532. package/dist/wizard/use-wizard.js.map +1 -1
  533. package/dist/wizard/validation.d.ts +3 -1
  534. package/dist/wizard/validation.js.map +1 -1
  535. package/package.json +10 -6
  536. package/src/account/account-parts.ts +5 -25
  537. package/src/account/data-export-setting-labels.ts +1 -1
  538. package/src/account/delete-account-setting-labels.ts +1 -1
  539. package/src/account/email-change-setting-labels.ts +1 -1
  540. package/src/account/passkeys-setting.tsx +60 -38
  541. package/src/account/sessions-setting.tsx +18 -12
  542. package/src/admin/admin-action-confirm.tsx +1 -5
  543. package/src/admin/admin-parts.ts +11 -25
  544. package/src/admin/mail-test-card-labels.ts +1 -1
  545. package/src/admin/plan-change-labels.ts +5 -23
  546. package/src/admin/plan-change.tsx +7 -44
  547. package/src/admin/reviewer-scope-editor.tsx +3 -3
  548. package/src/auth/auth-errors-labels.ts +23 -0
  549. package/src/auth/auth-errors.ts +11 -38
  550. package/src/auth/complete-name-dialog.tsx +4 -1
  551. package/src/auth/forgot-password-form-labels.ts +1 -1
  552. package/src/auth/forgot-password-form.tsx +11 -6
  553. package/src/auth/register-form-labels.ts +1 -1
  554. package/src/auth/reset-password-form-labels.ts +1 -1
  555. package/src/auth/reset-password-form.tsx +8 -4
  556. package/src/auth/sign-in-form-labels.ts +1 -1
  557. package/src/auth/status-parts.tsx +0 -6
  558. package/src/auth/verify-email-labels.ts +1 -1
  559. package/src/auth/verify-email.tsx +11 -6
  560. package/src/billing/cancel-subscription.tsx +26 -12
  561. package/src/billing/dialog-close.ts +34 -0
  562. package/src/billing/plan-card.tsx +36 -11
  563. package/src/billing/plan-change-labels.ts +66 -0
  564. package/src/billing/plan-switch.tsx +26 -12
  565. package/src/chart.ts +21 -3
  566. package/src/components/account-chips.tsx +20 -23
  567. package/src/components/alert-banner.tsx +82 -5
  568. package/src/components/amount-input.tsx +18 -7
  569. package/src/components/autocomplete.tsx +64 -70
  570. package/src/components/bulk-action-bar.tsx +70 -2
  571. package/src/components/button-look.ts +5 -5
  572. package/src/components/button.tsx +2 -2
  573. package/src/components/chart-edge.ts +34 -1
  574. package/src/components/chart-figure.tsx +35 -0
  575. package/src/components/chart-zoom-core.tsx +6 -5
  576. package/src/components/chart.tsx +26 -5
  577. package/src/components/combobox-core.tsx +293 -62
  578. package/src/components/combobox.tsx +183 -153
  579. package/src/components/copy-button.tsx +34 -23
  580. package/src/components/country-select.tsx +16 -11
  581. package/src/components/data-table-column-settings.tsx +52 -10
  582. package/src/components/data-table-filters.ts +6 -4
  583. package/src/components/data-table-fit.ts +19 -5
  584. package/src/components/data-table-groups.tsx +1 -1
  585. package/src/components/data-table-head.tsx +32 -22
  586. package/src/components/data-table-mobile.tsx +61 -26
  587. package/src/components/data-table-pagination.tsx +21 -6
  588. package/src/components/data-table-resize.ts +1 -1
  589. package/src/components/data-table-row-actions.tsx +3 -3
  590. package/src/components/data-table-row.tsx +25 -14
  591. package/src/components/data-table-selection.ts +1 -1
  592. package/src/components/data-table-shared.tsx +33 -1
  593. package/src/components/data-table-sort.ts +57 -2
  594. package/src/components/data-table-virtual.ts +32 -8
  595. package/src/components/data-table.tsx +239 -68
  596. package/src/components/date-field.tsx +65 -8
  597. package/src/components/date-picker.tsx +125 -48
  598. package/src/components/date-range-picker.tsx +82 -31
  599. package/src/components/error-boundary.tsx +53 -39
  600. package/src/components/field-base-labels.ts +20 -0
  601. package/src/components/field-base.tsx +13 -7
  602. package/src/components/field-sync-labels.ts +1 -0
  603. package/src/components/field-sync.tsx +23 -1
  604. package/src/components/form-actions.tsx +20 -5
  605. package/src/components/hover-menu.tsx +18 -8
  606. package/src/components/icon-button.tsx +14 -9
  607. package/src/components/inline-confirm.tsx +116 -0
  608. package/src/components/inline-entity-combobox.tsx +197 -153
  609. package/src/components/input.tsx +1 -1
  610. package/src/components/jump-calendar.tsx +30 -146
  611. package/src/components/legal-page.tsx +10 -7
  612. package/src/components/legal.tsx +4 -2
  613. package/src/components/lightbox.tsx +21 -8
  614. package/src/components/list.tsx +10 -4
  615. package/src/components/measured-grid.tsx +8 -4
  616. package/src/components/mini-calendar.tsx +6 -5
  617. package/src/components/modal.tsx +6 -1
  618. package/src/components/month-grid.tsx +229 -0
  619. package/src/components/month-picker.tsx +192 -200
  620. package/src/components/multi-select.tsx +21 -9
  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/section-nav.tsx +7 -0
  627. package/src/components/series-chart-axes.ts +3 -3
  628. package/src/components/series-chart-labels.ts +7 -4
  629. package/src/components/series-chart.tsx +178 -69
  630. package/src/components/share-card.tsx +6 -6
  631. package/src/components/sign-chip-labels.ts +1 -0
  632. package/src/components/sign-chip.tsx +10 -1
  633. package/src/components/tabs-labels.ts +13 -0
  634. package/src/components/tabs.tsx +2 -2
  635. package/src/components/toggle-legend.tsx +16 -11
  636. package/src/components/translation-review-editor.tsx +6 -5
  637. package/src/components/translation-review.tsx +4 -3
  638. package/src/components/treemap-labels.ts +39 -0
  639. package/src/components/treemap-layout.ts +210 -0
  640. package/src/components/treemap.tsx +722 -133
  641. package/src/components/use-table-state.ts +12 -9
  642. package/src/data-table.ts +9 -2
  643. package/src/eslint/role-utilities.ts +97 -19
  644. package/src/feedback/feedback-dialog.tsx +30 -19
  645. package/src/feedback/feedback-mutation.ts +41 -0
  646. package/src/feedback/feedback-page-link.ts +90 -0
  647. package/src/feedback/feedback-public-list.tsx +17 -5
  648. package/src/feedback/feedback-replies.tsx +13 -6
  649. package/src/feedback/feedback-row-detail-labels.ts +5 -0
  650. package/src/feedback/feedback-row-detail.tsx +103 -67
  651. package/src/feedback/feedback-status-undo.ts +41 -27
  652. package/src/feedback/feedback-submit-dialog.tsx +2 -2
  653. package/src/feedback/feedback-submit.tsx +21 -7
  654. package/src/feedback/feedback-swipe.tsx +1 -1
  655. package/src/feedback/feedback-table.tsx +86 -38
  656. package/src/feedback/feedback-thread-labels.ts +2 -2
  657. package/src/feedback/feedback-thread.tsx +27 -25
  658. package/src/feedback.ts +42 -0
  659. package/src/hooks/use-focus-trap.ts +8 -24
  660. package/src/hooks/use-hotkey.ts +24 -7
  661. package/src/hooks/use-long-press.ts +16 -2
  662. package/src/hooks/use-media-query.ts +20 -4
  663. package/src/hooks/use-mounted.ts +39 -0
  664. package/src/hooks/use-overlay-history.ts +32 -3
  665. package/src/i18n/defaults.ts +4 -5
  666. package/src/i18n/german.ts +8 -2
  667. package/src/i18n/kit-labels.tsx +9 -2
  668. package/src/i18n/locales/es.ts +8 -2
  669. package/src/i18n/locales/fr.ts +21 -13
  670. package/src/i18n/locales/hu.ts +10 -2
  671. package/src/i18n/locales/it.ts +8 -2
  672. package/src/i18n/locales/zh.ts +9 -2
  673. package/src/i18n/review.ts +5 -0
  674. package/src/index.ts +103 -13
  675. package/src/landing/routing.tsx +14 -10
  676. package/src/lib/broadcast.ts +52 -0
  677. package/src/lib/date-format.ts +89 -13
  678. package/src/lib/day-format.ts +96 -0
  679. package/src/lib/describe-failure.ts +18 -0
  680. package/src/lib/direction.ts +39 -0
  681. package/src/lib/focus-left.ts +156 -0
  682. package/src/lib/format.ts +5 -17
  683. package/src/lib/http-status.ts +38 -0
  684. package/src/lib/intl-cache.ts +25 -0
  685. package/src/lib/isin.ts +10 -0
  686. package/src/lib/iso-day.ts +13 -0
  687. package/src/lib/lazy-with-fallback.ts +82 -0
  688. package/src/lib/path-pattern.ts +55 -0
  689. package/src/lib/reduced-motion.ts +2 -1
  690. package/src/lib/round.ts +7 -2
  691. package/src/lib/safe-storage.ts +48 -6
  692. package/src/lib/session-refresh.ts +1 -1
  693. package/src/lib/share-format.ts +18 -0
  694. package/src/lib/text-case.ts +19 -0
  695. package/src/lib/text-match.ts +57 -2
  696. package/src/lib/thenable.ts +19 -0
  697. package/src/lib/translation-review.ts +13 -5
  698. package/src/notifications/device-zone-report.ts +1 -1
  699. package/src/notifications/notification-bell.tsx +7 -4
  700. package/src/notifications/notification-inbox.tsx +40 -58
  701. package/src/notifications/notification-parts.tsx +3 -4
  702. package/src/notifications/notification-types-setting.tsx +1 -1
  703. package/src/notifications/notifications-labels.ts +7 -0
  704. package/src/notifications/push-device-setting.tsx +1 -1
  705. package/src/notifications/push-devices-setting.tsx +30 -55
  706. package/src/notifications/quiet-hours-setting.tsx +93 -31
  707. package/src/notifications/unsubscribe-panel.tsx +4 -7
  708. package/src/notifications/use-push-device.ts +76 -10
  709. package/src/offline/query-persistence.ts +35 -3
  710. package/src/rhf/fields.tsx +11 -4
  711. package/src/search/command-palette.tsx +2 -4
  712. package/src/search/global-search.tsx +36 -15
  713. package/src/search/use-command-key.ts +13 -12
  714. package/src/search.ts +9 -0
  715. package/src/settings/use-settings-focus.ts +3 -0
  716. package/src/shell/account-menu-language.tsx +27 -5
  717. package/src/shell/account-menu-text-size.tsx +32 -10
  718. package/src/shell/dev-menu.tsx +7 -21
  719. package/src/shell/role-switcher.tsx +7 -0
  720. package/src/shell/top-bar-actions.tsx +1 -1
  721. package/src/shell/top-bar-parts-labels.ts +0 -3
  722. package/src/shell.ts +40 -9
  723. package/src/sync/engine.ts +339 -61
  724. package/src/sync.ts +8 -1
  725. package/src/testing/css-variables.ts +8 -9
  726. package/src/theme/contrast.ts +8 -9
  727. package/src/theme/palette-store.ts +9 -9
  728. package/src/theme/text-size.ts +6 -8
  729. package/src/theme/theme-store.ts +7 -9
  730. package/src/tour/tour-catalogue.tsx +26 -15
  731. package/src/tour/tour-dock.ts +0 -2
  732. package/src/tour/tour-gates.ts +23 -0
  733. package/src/tour/tour-overlay.tsx +1170 -0
  734. package/src/tour/tour.tsx +108 -1202
  735. package/src/tour/use-tour-progress.ts +1 -1
  736. package/src/wizard/stepper-nav.tsx +3 -1
  737. package/src/wizard/types.ts +11 -2
  738. package/src/wizard/use-wizard.ts +2 -1
  739. package/src/wizard/validation.ts +8 -2
  740. package/tokens.css +2 -1
  741. package/dist/components/ui-labels.js.map +0 -1
  742. package/dist/shell/dev-switchers.d.ts +0 -6
  743. package/dist/shell/dev-switchers.js +0 -12
  744. package/dist/shell/dev-switchers.js.map +0 -1
  745. package/src/components/ui-labels.ts +0 -24
  746. package/src/shell/dev-switchers.tsx +0 -18
@@ -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;
@@ -13,6 +13,7 @@ import type { ComponentProps, CSSProperties, ReactNode, RefObject } from "react"
13
13
  import { Legend as RechartsLegend, ResponsiveContainer, Tooltip as RechartsTooltip } from "recharts";
14
14
  import { cn } from "../lib/cn";
15
15
  import { isRtl } from "../lib/direction";
16
+ import { useFigureDescription } from "./chart-figure";
16
17
  import { TYPOGRAPHIC_MINUS } from "../lib/minus";
17
18
 
18
19
  export interface ChartSeriesConfig {
@@ -33,6 +34,13 @@ export function useChart(): ChartConfig {
33
34
  interface ChartContainerProps extends ComponentProps<"div"> {
34
35
  config: ChartConfig;
35
36
  children: ComponentProps<typeof ResponsiveContainer>["children"];
37
+ /**
38
+ * What the chart SHOWS, in a sentence, as its accessible description (0.52): read through
39
+ * `aria-describedby`, after a caller's own ids, and drawn nowhere. Inside the root, so
40
+ * the chart stays the one element its parent lays out. Pair it with `role="figure"` and
41
+ * an `aria-label`, as `Treemap` and `SeriesChart` do.
42
+ */
43
+ description?: string;
36
44
  }
37
45
 
38
46
  /**
@@ -49,8 +57,9 @@ interface ChartContainerProps extends ComponentProps<"div"> {
49
57
  * page's direction like any other markup. A consumer who does want a mirrored
50
58
  * category axis passes `reversed` on its own `XAxis`.
51
59
  */
52
- export function ChartContainer({ id, className, children, config, ...props }: ChartContainerProps) {
60
+ export function ChartContainer({ id, className, children, config, description, ...props }: ChartContainerProps) {
53
61
  const uid = useId();
62
+ const about = useFigureDescription(description, props["aria-describedby"]);
54
63
  // The id is pasted into an UNQUOTED `[data-chart=…]` selector below, so it has to be
55
64
  // a CSS identifier: one stray space or `#` and the browser discards the whole rule,
56
65
  // taking every series colour with it — silently, because an invalid rule is not an
@@ -81,11 +90,13 @@ export function ChartContainer({ id, className, children, config, ...props }: Ch
81
90
  className,
82
91
  )}
83
92
  {...props}
93
+ aria-describedby={about.describedBy}
84
94
  >
85
95
  <ChartStyle id={chartId} config={config} />
86
96
  <ResponsiveContainer width="100%" height="100%">
87
97
  {children}
88
98
  </ResponsiveContainer>
99
+ {about.node}
89
100
  </div>
90
101
  </ChartContext.Provider>
91
102
  );
@@ -489,15 +500,22 @@ export function ChartLegendContent({ payload, onItemClick, hiddenKeys, activeKey
489
500
  const label = series?.label ?? vv ?? cfgKey;
490
501
  const off = hidden?.has(cfgKey) ?? false;
491
502
  const dimmed = off || (activeKey != null && activeKey !== cfgKey);
503
+ // The label's cue, in a key and on a button alike: a hidden series struck through,
504
+ // and an entry an isolate legend dims in the muted ink — drawn still, so not struck.
505
+ // Only a toggle button had one, and a key or an isolate legend told an entry off by
506
+ // its 10px swatch at 40 % alone (0.52 code review, deep, charts, finding 9).
507
+ const cue = off ? "line-through" : dimmed ? "text-muted" : undefined;
492
508
  const className = cn(
493
509
  "flex items-center gap-1.5 text-secondary transition-opacity",
494
510
  onItemClick ? "cursor-pointer hover:opacity-80" : "cursor-default",
495
- dimmed && "opacity-40",
496
511
  );
512
+ // Only the swatch fades; the label keeps an ink of AA contrast (0.52, refactor audit
513
+ // 2026-10-30, DSP-9). The whole entry at 40 % put a switched-off label under 2:1,
514
+ // on a button that brings its series back and is named by nothing else.
497
515
  const swatch = (
498
516
  <span
499
517
  aria-hidden
500
- className="h-2.5 w-2.5 shrink-0 rounded-[3px]"
518
+ className={cn("h-2.5 w-2.5 shrink-0 rounded-[3px]", dimmed && "opacity-40")}
501
519
  style={{ backgroundColor: color } as CSSProperties}
502
520
  />
503
521
  );
@@ -519,10 +537,13 @@ export function ChartLegendContent({ payload, onItemClick, hiddenKeys, activeKey
519
537
  className={className}
520
538
  >
521
539
  {swatch}
522
- <span className={cn(off && "line-through")}>{label}</span>
540
+ <span className={cue}>{label}</span>
523
541
  </button>
524
542
  ) : (
525
- <span key={`${cfgKey}-${i}`} className={className}>
543
+ // On the entry, not round the label: the text is its own, so the key's DOM is the
544
+ // same with a cue as without. A line-through reaches the text only (the swatch
545
+ // holds none), and the ink colours no swatch.
546
+ <span key={`${cfgKey}-${i}`} className={cn(className, cue)}>
526
547
  {swatch}
527
548
  {label}
528
549
  </span>
@@ -11,6 +11,7 @@ import type {
11
11
  import { Check, Plus } from "lucide-react";
12
12
  import { cn } from "../lib/cn";
13
13
  import { CLIPS_ATTRIBUTE } from "../lib/clipping";
14
+ import { tabStops } from "../lib/focus-left";
14
15
  import { DropdownSearchHeader } from "./dropdown";
15
16
  import { PickerSheet, SHEET_ROW_CLASS } from "./picker-sheet";
16
17
  import { usePhoneLayout, useRemPx } from "../hooks/use-breakpoint";
@@ -471,10 +472,147 @@ export function useComboboxCore<V extends string | number>({
471
472
  * heading is the one thing that says where in the alphabet the thumb is. Opaque — the
472
473
  * list's own `bg-surface` — so the rows pass beneath it rather than through it. Shared
473
474
  * by every list that heads groups (the panel and the sheet, `Combobox`, `Autocomplete`,
474
- * `InlineEntityCombobox`).
475
+ * `InlineEntityCombobox`). Inside its {@link OptionGroup} since 0.52, it sticks while its
476
+ * own group is in view and the next group's heading pushes it out.
475
477
  */
476
478
  export const GROUP_HEADING_STICKY = "sticky top-0 z-10 bg-surface";
477
479
 
480
+ /** One run of rows that share a group, as {@link groupRuns} finds them: `group` is
481
+ * `null` for rows filed under none, which stand bare in the list; `start` is the index
482
+ * of the run's first row in the whole list (the option ids and the arrows count by it). */
483
+ export interface GroupRun<T> {
484
+ group: string | null;
485
+ start: number;
486
+ rows: T[];
487
+ }
488
+
489
+ /**
490
+ * A list's rows, cut where the group changes — the runs {@link OptionGroup} wraps. The
491
+ * lists keep their rows group-contiguous (the panel's source order, or the ranked list
492
+ * regrouped by its best member), so a run is "the rows up to the next change of group".
493
+ * `groupOf` is asked once per row. An empty group name is no group: a heading of nothing
494
+ * names nothing.
495
+ */
496
+ export function groupRuns<T>(rows: readonly T[], groupOf: (row: T) => string | null | undefined): GroupRun<T>[] {
497
+ const runs: GroupRun<T>[] = [];
498
+ rows.forEach((row, i) => {
499
+ const raw = groupOf(row);
500
+ const group = raw === undefined || raw === null || raw === "" ? null : raw;
501
+ const last = runs[runs.length - 1];
502
+ if (last && last.group === group) last.rows.push(row);
503
+ else runs.push({ group, start: i, rows: [row] });
504
+ });
505
+ return runs;
506
+ }
507
+
508
+ /**
509
+ * A group of options under its heading, inside a `role="listbox"`: a `role="group"`
510
+ * NAMED by the heading, so a reader moving through the list hears which group a row is
511
+ * in (refactor audit 2026-10-30, INP-8). The heading was loose text among the options,
512
+ * and focus stays in the search box with the row announced through
513
+ * `aria-activedescendant` — keksdose's "Groceries" under Recent and again under its own
514
+ * group were two identical names, and an account filed by its type never said the type.
515
+ * The heading stays a visible first item of the group (sticky within it); the columns of
516
+ * {@link ComboboxPanel} carry the same `role="group"`.
517
+ *
518
+ * `id` must be unique in the document (the list's id plus the run's start); `className`
519
+ * places the heading's text in its list.
520
+ */
521
+ export function OptionGroup({
522
+ id,
523
+ heading,
524
+ className,
525
+ children,
526
+ }: {
527
+ id: string;
528
+ heading: ReactNode;
529
+ className?: string;
530
+ children: ReactNode;
531
+ }) {
532
+ return (
533
+ <li role="presentation">
534
+ <ul role="group" aria-labelledby={id}>
535
+ <li
536
+ role="presentation"
537
+ id={id}
538
+ className={cn(GROUP_HEADING_STICKY, "font-semibold uppercase tracking-wide text-muted", className)}
539
+ >
540
+ {heading}
541
+ </li>
542
+ {children}
543
+ </ul>
544
+ </li>
545
+ );
546
+ }
547
+
548
+ /**
549
+ * A list's rows in their {@link groupRuns}: a run filed under no group stands bare, any
550
+ * other is an {@link OptionGroup} under its heading. The one drawing of it for
551
+ * {@link ComboboxPanel}, {@link Combobox}, {@link InlineEntityCombobox} and
552
+ * {@link Autocomplete}, which had a copy each (0.52 code review, combobox-core.tsx).
553
+ *
554
+ * A run is keyed by its group and how many runs of that group came before it — NOT by
555
+ * the index of its first row, which every keystroke that drops a row from an earlier
556
+ * group moves: each later group was re-keyed and React rebuilt it, every row of it, per
557
+ * keystroke. A row is keyed by `rowKey` among the rows of its run; an option may stand
558
+ * in two groups (keksdose's "Recent", live #203), never twice in one.
559
+ *
560
+ * `row(o, i, grouped)`: `i` is the row's index in the whole list (the option ids and the
561
+ * arrows count by it), `grouped` whether it stands under a heading. `headingClassName`
562
+ * places a run's heading text in its list. The group's id is the list's id and the run's
563
+ * first row, unique in the document.
564
+ */
565
+ export function GroupedOptions<T>({
566
+ runs,
567
+ listboxId,
568
+ rowKey,
569
+ row,
570
+ headingClassName,
571
+ }: {
572
+ runs: readonly GroupRun<T>[];
573
+ listboxId: string;
574
+ rowKey: (o: T) => string;
575
+ row: (o: T, index: number, grouped: boolean) => ReactNode;
576
+ headingClassName: (run: GroupRun<T>) => string;
577
+ }) {
578
+ return keyRuns(runs).map(({ run, key }) => {
579
+ const grouped = run.group !== null;
580
+ const rows = run.rows.map((o, k) => <Fragment key={rowKey(o)}>{row(o, run.start + k, grouped)}</Fragment>);
581
+ if (!grouped) return <Fragment key={key}>{rows}</Fragment>;
582
+ return (
583
+ <OptionGroup
584
+ key={key}
585
+ id={`${listboxId}-group-${run.start}`}
586
+ heading={run.group}
587
+ className={headingClassName(run)}
588
+ >
589
+ {rows}
590
+ </OptionGroup>
591
+ );
592
+ });
593
+ }
594
+
595
+ /** Each run with its key — see {@link GroupedOptions}: the group and how many runs of it
596
+ * came before, which a query narrowing an earlier group does not move. */
597
+ function keyRuns<T>(runs: readonly GroupRun<T>[]): { run: GroupRun<T>; key: string }[] {
598
+ const seen = new Map<string | null, number>();
599
+ return runs.map((run) => {
600
+ const nth = seen.get(run.group) ?? 0;
601
+ seen.set(run.group, nth + 1);
602
+ // The count first: it never holds a ":", so no group's name can make two keys equal.
603
+ return { run, key: run.group === null ? String(nth) : `${nth}:${run.group}` };
604
+ });
605
+ }
606
+
607
+ /** The anchored list's heading text: the first group's sits closer to the list's top
608
+ * edge, unless a row stands above it (`first: false`, Combobox's create row). */
609
+ export const anchoredHeadingClass = (run: GroupRun<unknown>, first = true) =>
610
+ cn("px-3 pb-0.5 text-caption", run.start === 0 && first ? "pt-1" : "pt-2");
611
+
612
+ /** The typed comboboxes' phone sheet's heading text ({@link Combobox},
613
+ * {@link InlineEntityCombobox}). */
614
+ export const SHEET_HEADING_CLASS = "px-4 pb-0.5 pt-3 text-caption";
615
+
478
616
  export const SUGGESTION_LIST_CLASS =
479
617
  "max-h-64 overflow-y-auto rounded-md border border-subtle bg-surface py-1 shadow-lg";
480
618
 
@@ -505,6 +643,63 @@ export function useActiveOptionScroll(activeId: string | undefined): void {
505
643
  }, [activeId]);
506
644
  }
507
645
 
646
+ /**
647
+ * What a TYPED combobox's list says — {@link Combobox} and {@link InlineEntityCombobox},
648
+ * which had a copy each, the second saying "see the twin" (0.52 code review,
649
+ * inline-entity-combobox.tsx; the rules are refactor audit 2026-10-30, INP-6):
650
+ *
651
+ * - `showing`: the anchored list is on screen — open, not inert, with a row to show (an
652
+ * option, or `extraRow`, the create row). The list is then a layer on the Escape stack
653
+ * (Wave 2, DUP-1). Never on a phone, where the sheet is the list and the layer.
654
+ * - `expanded`, the field's `aria-expanded`: a list on screen. An open list with nothing
655
+ * to show said "expanded" with no listbox in the document.
656
+ * - `activeOption`: the id of the row the keyboard is on — `active`, a TAKEABLE row's
657
+ * index, else -1 — in whichever list shows, kept scrolled into view.
658
+ * - `statusRegion`: a polite live region with the row count, or "No results" for a query
659
+ * that left nothing to show or do. Focus stays in the field, or in the sheet's search
660
+ * box, so a list changing under it was silent. A list opened on no query asked nothing.
661
+ * Render it outside the list, so the list going cannot take it along: on a phone inside
662
+ * the sheet, which hides from a reader what lies outside it. There it mounts with the
663
+ * sheet, so the count the sheet opens with is not announced — as a ComboboxPanel's is
664
+ * not, in either shell — and every change after it is.
665
+ */
666
+ export function useTypedListStatus({
667
+ isPhone,
668
+ open,
669
+ inert,
670
+ rowCount,
671
+ extraRow,
672
+ query,
673
+ active,
674
+ optionId,
675
+ labels,
676
+ }: {
677
+ isPhone: boolean;
678
+ open: boolean;
679
+ inert: boolean;
680
+ /** The options the list shows. */
681
+ rowCount: number;
682
+ /** A create row shows: the list is not empty, it offers to fill itself. */
683
+ extraRow: boolean;
684
+ query: string;
685
+ active: number;
686
+ optionId: (index: number) => string;
687
+ labels: { resultCount: (count: number) => string; noResults: string };
688
+ }): { showing: boolean; expanded: boolean; activeOption: string | undefined; statusRegion: ReactNode } {
689
+ const showing = !isPhone && open && !inert && (rowCount > 0 || extraRow);
690
+ const expanded = isPhone ? open && !inert : showing;
691
+ const activeOption = expanded && active >= 0 ? optionId(active) : undefined;
692
+ useActiveOptionScroll(activeOption);
693
+ const status =
694
+ !open || inert ? null : rowCount > 0 ? labels.resultCount(rowCount) : query && !extraRow ? labels.noResults : null;
695
+ const statusRegion = (
696
+ <div role="status" aria-live="polite" className="sr-only">
697
+ {status}
698
+ </div>
699
+ );
700
+ return { showing, expanded, activeOption, statusRegion };
701
+ }
702
+
508
703
  /** The anchored panel's least width, in rem: 220 px at Normal (§3.2). */
509
704
  const PANEL_MIN_REM = 13.75;
510
705
 
@@ -570,7 +765,9 @@ export interface ComboboxPanelProps<V extends string | number> extends Component
570
765
  */
571
766
  renderOption?: (o: ComboOption<V>) => ReactNode;
572
767
  /** Controls between the search box and the list, in the anchored panel and the phone
573
- * sheet alike — MultiSelect's "Select all" and "Clear". */
768
+ * sheet alike — MultiSelect's "Select all" and "Clear". In the anchored panel Tab from
769
+ * the search box walks them before it leaves (0.52); keep them focusable when they
770
+ * have nothing to do (`aria-disabled`), or a press drops their focus to <body>. */
574
771
  toolbar?: ReactNode;
575
772
  /** See {@link ComboboxPanelWidth}. */
576
773
  width?: ComboboxPanelWidth;
@@ -695,6 +892,44 @@ export function ComboboxPanel<V extends string | number>({
695
892
  return () => observer.disconnect();
696
893
  }, [fit, placed, panelRef]);
697
894
 
895
+ // The toolbar's own box, so the keyboard can tell its buttons from the search box.
896
+ const toolbarRef = useRef<HTMLDivElement>(null);
897
+ // The kit's one tab-stop rule (0.52 code review, lib/focus-left.ts): this had a third
898
+ // copy of the selector, without its `hidden`/`inert` half. An `aria-disabled` button
899
+ // stays a stop — it keeps its focus (INP-9).
900
+ const toolbarStops = () => (toolbarRef.current ? tabStops(toolbarRef.current, { includeSelf: false }) : []);
901
+ // A POINTER press on the toolbar leaves the focus in the search box, as a press on a
902
+ // row does: since a toolbar button's keys are its own (INP-9), a mouse press — which
903
+ // focuses a button in Chrome and Firefox — left the arrows on "Select all", where they
904
+ // did nothing; until 0.52 they moved the list's highlight (0.52 code review,
905
+ // combobox-core.tsx). Tab still reaches the buttons. A field in a toolbar keeps its
906
+ // press: it is there to be typed in.
907
+ const toolbarBox = hasContent(toolbar) && (
908
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- never focused itself: it only keeps a pointer press on its buttons from taking the focus out of the search box.
909
+ <div
910
+ ref={toolbarRef}
911
+ onMouseDown={(e) => {
912
+ if (!(e.target as Element).closest("input, select, textarea, [contenteditable]")) e.preventDefault();
913
+ }}
914
+ >
915
+ {toolbar}
916
+ </div>
917
+ );
918
+ /** Tab on a toolbar button in the anchored panel: Shift+Tab off the first goes back to
919
+ * the search box, Tab off the last leaves as the search box's Tab did; between two of
920
+ * them the browser's own Tab is right, as they stand next to each other. */
921
+ const tabInToolbar = (e: ReactKeyboardEvent) => {
922
+ const stops = toolbarStops();
923
+ const at = stops.indexOf(e.target as HTMLElement);
924
+ if (e.shiftKey && at === 0) {
925
+ e.preventDefault();
926
+ inputRef.current?.focus();
927
+ } else if (!e.shiftKey && at === stops.length - 1) {
928
+ e.preventDefault();
929
+ closeToTrigger();
930
+ }
931
+ };
932
+
698
933
  // The WAI-ARIA APG's listbox keyboard, in full: Up/Down step, Home/End jump, Enter
699
934
  // commits, Tab leaves, Escape closes. On the panel's search box — in the anchored
700
935
  // panel and, through `onSearchKeyDown`, in the phone sheet's header (BUG-5).
@@ -704,6 +939,14 @@ export function ComboboxPanel<V extends string | number>({
704
939
  // panel's events travel up the React tree through that dialog, portal or not, and
705
940
  // one press used to close the dialog and throw the form away, list still open.
706
941
  if (takeEscape(e)) return;
942
+ // A key on a toolbar button is the button's — Enter and Space press it — and not the
943
+ // list's: Enter there toggled the highlighted row instead of pressing "Select all".
944
+ // Only Tab is the panel's (refactor audit 2026-10-30, INP-9). The phone sheet hands
945
+ // this handler its search box's keys alone, and its trap keeps Tab.
946
+ if (toolbarRef.current?.contains(e.target as Node)) {
947
+ if (e.key === "Tab" && !isPhone) tabInToolbar(e);
948
+ return;
949
+ }
707
950
  // The Enter that confirms an IME composition is the IME's (BUG-9).
708
951
  if (isImeEnter(e)) return;
709
952
  // Disabled rows are passed over (`stepEnabled`), and each step starts from the
@@ -731,8 +974,12 @@ export function ComboboxPanel<V extends string | number>({
731
974
  // here to whatever follows the portal — i.e. off the end of the document. The
732
975
  // trade is one extra Tab (trigger, then onward) against never stranding focus
733
976
  // at the bottom of the page. The phone sheet is a dialog, and its trap keeps Tab.
977
+ // A panel with a toolbar is walked through it first: the rows are not tab stops,
978
+ // so MultiSelect's "Select all" and "Clear" were out of a keyboard's reach (INP-9).
734
979
  e.preventDefault();
735
- closeToTrigger();
980
+ const first = e.shiftKey ? undefined : toolbarStops()[0];
981
+ if (first) first.focus();
982
+ else closeToTrigger();
736
983
  }
737
984
  };
738
985
 
@@ -891,17 +1138,9 @@ export function ComboboxPanel<V extends string | number>({
891
1138
  </li>
892
1139
  );
893
1140
 
894
- // In columns, a block per run of one group; `results` keeps its source order, so a
895
- // run is the rows up to the next change of group.
896
- const blocks: { group: string; rows: { o: ComboOption<V>; i: number }[] }[] = [];
897
- if (columns && !isPhone) {
898
- results.forEach((o, i) => {
899
- const group = o.group ?? "";
900
- const last = blocks[blocks.length - 1];
901
- if (last && last.group === group) last.rows.push({ o, i });
902
- else blocks.push({ group, rows: [{ o, i }] });
903
- });
904
- }
1141
+ // The rows' group runs — `results` keeps its source order, so a run is the rows up to
1142
+ // the next change of group. In columns, a block per run.
1143
+ const runs = groupRuns(results, (o) => o.group);
905
1144
 
906
1145
  const list = (
907
1146
  <ul
@@ -921,51 +1160,39 @@ export function ComboboxPanel<V extends string | number>({
921
1160
  isPhone && "flex-none",
922
1161
  )}
923
1162
  >
924
- {columns && !isPhone
925
- ? blocks.map((block) => (
926
- // A column block: its heading is a picture of the group's name, which the
927
- // `role="group"` carries for a reader — the listbox may own options and
928
- // groups, and a heading of loose text in it is neither.
929
- <li key={`${block.group}|${block.rows[0].i}`} role="presentation" className="mb-3 break-inside-avoid">
930
- {block.group && (
931
- <div aria-hidden className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted">
932
- {block.group}
933
- </div>
934
- )}
935
- <ul role="group" aria-label={block.group || undefined} className="space-y-0.5">
936
- {block.rows.map(({ o, i }) => (
937
- <Fragment key={`${o.group ?? ""}|${String(o.value)}`}>{optionRow(o, i)}</Fragment>
938
- ))}
939
- </ul>
940
- </li>
941
- ))
942
- : results.map((o, i) => {
943
- // One heading per group rather than a grey suffix on every row (#136), and
944
- // the thing the native <select>'s <optgroup> used to give the account
945
- // picker for free (live #200). `results` keeps its source order, so a
946
- // boundary is simply "different from the row above".
947
- const startsGroup = o.group != null && o.group !== results[i - 1]?.group;
948
- return (
949
- // Keyed by group AND value: an option may deliberately appear twice —
950
- // Keksdose repeats recently-used categories in a "Recent" group at the
951
- // top (live #203) — and a bare value key would collide.
952
- <Fragment key={`${o.group ?? ""}|${String(o.value)}`}>
953
- {startsGroup && (
954
- <li
955
- role="presentation"
956
- className={cn(
957
- GROUP_HEADING_STICKY,
958
- "font-semibold uppercase tracking-wide text-muted",
959
- isPhone ? "px-4 pb-1 pt-3 text-xs" : "px-3 pb-0.5 pt-2 text-caption first:pt-1",
960
- )}
961
- >
962
- {o.group}
963
- </li>
964
- )}
965
- {optionRow(o, i)}
966
- </Fragment>
967
- );
968
- })}
1163
+ {columns && !isPhone ? (
1164
+ keyRuns(runs).map(({ run, key }) => (
1165
+ // A column block: its heading is a picture of the group's name, which the
1166
+ // `role="group"` carries for a reader — the listbox may own options and
1167
+ // groups, and a heading of loose text in it is neither. Keyed as
1168
+ // GroupedOptions keys a run, so a filter does not rebuild the later columns.
1169
+ <li key={key} role="presentation" className="mb-3 break-inside-avoid">
1170
+ {run.group !== null && (
1171
+ <div aria-hidden className="mb-1 text-xs font-semibold uppercase tracking-wide text-muted">
1172
+ {run.group}
1173
+ </div>
1174
+ )}
1175
+ <ul role="group" aria-label={run.group ?? undefined} className="space-y-0.5">
1176
+ {run.rows.map((o, k) => (
1177
+ <Fragment key={String(o.value)}>{optionRow(o, run.start + k)}</Fragment>
1178
+ ))}
1179
+ </ul>
1180
+ </li>
1181
+ ))
1182
+ ) : (
1183
+ // One heading per group rather than a grey suffix on every row (#136), and the
1184
+ // thing the native <select>'s <optgroup> used to give the account picker for
1185
+ // free (live #200) — a `role="group"` it names (INP-8). Keksdose repeats
1186
+ // recently-used categories in a "Recent" group at the top (live #203): a value
1187
+ // is a row's key within its group alone.
1188
+ <GroupedOptions
1189
+ runs={runs}
1190
+ listboxId={listboxId}
1191
+ rowKey={(o) => String(o.value)}
1192
+ row={optionRow}
1193
+ headingClassName={(run) => (isPhone ? "px-4 pb-1 pt-3 text-xs" : anchoredHeadingClass(run))}
1194
+ />
1195
+ )}
969
1196
  {createRow}
970
1197
  </ul>
971
1198
  );
@@ -986,7 +1213,11 @@ export function ComboboxPanel<V extends string | number>({
986
1213
  className={className}
987
1214
  style={style}
988
1215
  open
989
- onClose={() => core.setOpen(false)}
1216
+ // The X, Done, Escape and Back hand focus to the trigger, as taking a row does:
1217
+ // these triggers open on a click, never on focus, so focusing one re-opens
1218
+ // nothing — and the sheet, which cannot know that, leaves focus on <body>
1219
+ // (refactor audit 2026-10-30, INP-7).
1220
+ onClose={closeToTrigger}
990
1221
  title={sheetTitle}
991
1222
  query={query}
992
1223
  onQueryChange={setQuery}
@@ -1005,7 +1236,7 @@ export function ComboboxPanel<V extends string | number>({
1005
1236
  // A single pick closes the sheet itself.
1006
1237
  done={multi}
1007
1238
  >
1008
- {toolbar}
1239
+ {toolbarBox}
1009
1240
  <div className="relative">
1010
1241
  {list}
1011
1242
  {announcement}
@@ -1079,7 +1310,7 @@ export function ComboboxPanel<V extends string | number>({
1079
1310
  listboxId={listboxId}
1080
1311
  activeId={activeId}
1081
1312
  />
1082
- {toolbar}
1313
+ {toolbarBox}
1083
1314
  {/* One list, two containers: the desktop panel and the phone sheet render
1084
1315
  the same rows through the same handlers (live #200). */}
1085
1316
  {list}