@eifi1/ui-kit 0.54.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (847) hide show
  1. package/README.md +90 -76
  2. package/dist/admin/plan-change.js +2 -2
  3. package/dist/admin/plan-change.js.map +1 -1
  4. package/dist/admin/user-roster.d.ts +6 -0
  5. package/dist/admin/user-roster.js +4 -0
  6. package/dist/admin/user-roster.js.map +1 -1
  7. package/dist/billing/billing-labels.d.ts +26 -0
  8. package/dist/billing/billing-labels.js +9 -0
  9. package/dist/billing/billing-labels.js.map +1 -1
  10. package/dist/billing/subscription-standing.d.ts +61 -0
  11. package/dist/billing/subscription-standing.js +51 -0
  12. package/dist/billing/subscription-standing.js.map +1 -0
  13. package/dist/billing/usage-meter.d.ts +52 -0
  14. package/dist/billing/usage-meter.js +52 -0
  15. package/dist/billing/usage-meter.js.map +1 -0
  16. package/dist/chart.d.ts +1 -0
  17. package/dist/chart.js +4 -0
  18. package/dist/chart.js.map +1 -1
  19. package/dist/components/alert-banner.js +2 -3
  20. package/dist/components/alert-banner.js.map +1 -1
  21. package/dist/components/amount-input.js +3 -15
  22. package/dist/components/amount-input.js.map +1 -1
  23. package/dist/components/autocomplete.js +2 -2
  24. package/dist/components/autocomplete.js.map +1 -1
  25. package/dist/components/bulk-action-bar.js +2 -1
  26. package/dist/components/bulk-action-bar.js.map +1 -1
  27. package/dist/components/button.d.ts +35 -1
  28. package/dist/components/button.js +5 -9
  29. package/dist/components/button.js.map +1 -1
  30. package/dist/components/calendar-heatmap.js +2 -2
  31. package/dist/components/calendar-heatmap.js.map +1 -1
  32. package/dist/components/camera-button.d.ts +89 -0
  33. package/dist/components/camera-button.js +122 -0
  34. package/dist/components/camera-button.js.map +1 -0
  35. package/dist/components/camera-capture.d.ts +66 -0
  36. package/dist/components/camera-capture.js +190 -0
  37. package/dist/components/camera-capture.js.map +1 -0
  38. package/dist/components/camera-labels.d.ts +20 -0
  39. package/dist/components/camera-labels.js +15 -0
  40. package/dist/components/camera-labels.js.map +1 -0
  41. package/dist/components/chart-edge.d.ts +9 -0
  42. package/dist/components/chart-edge.js +14 -3
  43. package/dist/components/chart-edge.js.map +1 -1
  44. package/dist/components/chart-zoom-core.js +34 -29
  45. package/dist/components/chart-zoom-core.js.map +1 -1
  46. package/dist/components/checkbox-group.js +1 -1
  47. package/dist/components/checkbox-group.js.map +1 -1
  48. package/dist/components/combobox-core.js +3 -2
  49. package/dist/components/combobox-core.js.map +1 -1
  50. package/dist/components/combobox.js +2 -2
  51. package/dist/components/combobox.js.map +1 -1
  52. package/dist/components/consent-again-notice-labels.d.ts +25 -0
  53. package/dist/components/consent-again-notice-labels.js +15 -0
  54. package/dist/components/consent-again-notice-labels.js.map +1 -0
  55. package/dist/components/consent-again-notice.d.ts +85 -0
  56. package/dist/components/consent-again-notice.js +94 -0
  57. package/dist/components/consent-again-notice.js.map +1 -0
  58. package/dist/components/country-select.js +2 -7
  59. package/dist/components/country-select.js.map +1 -1
  60. package/dist/components/currency-select.js +2 -2
  61. package/dist/components/currency-select.js.map +1 -1
  62. package/dist/components/danger-confirm.js +2 -2
  63. package/dist/components/danger-confirm.js.map +1 -1
  64. package/dist/components/data-table-column-settings.js +2 -2
  65. package/dist/components/data-table-column-settings.js.map +1 -1
  66. package/dist/components/data-table-fit.js.map +1 -1
  67. package/dist/components/data-table-virtual.js +13 -3
  68. package/dist/components/data-table-virtual.js.map +1 -1
  69. package/dist/components/date-field.js +2 -2
  70. package/dist/components/date-field.js.map +1 -1
  71. package/dist/components/date-picker.js +2 -2
  72. package/dist/components/date-picker.js.map +1 -1
  73. package/dist/components/date-range-picker.js +2 -6
  74. package/dist/components/date-range-picker.js.map +1 -1
  75. package/dist/components/dialog-frame.d.ts +3 -1
  76. package/dist/components/dialog-frame.js.map +1 -1
  77. package/dist/components/dropdown.js +7 -3
  78. package/dist/components/dropdown.js.map +1 -1
  79. package/dist/components/entity-combobox.js +2 -2
  80. package/dist/components/entity-combobox.js.map +1 -1
  81. package/dist/components/field-parts.d.ts +108 -6
  82. package/dist/components/field-parts.js +59 -6
  83. package/dist/components/field-parts.js.map +1 -1
  84. package/dist/components/field-suggestion-labels.d.ts +2 -0
  85. package/dist/components/field-suggestion-labels.js +10 -0
  86. package/dist/components/field-suggestion-labels.js.map +1 -0
  87. package/dist/components/field.js +1 -1
  88. package/dist/components/field.js.map +1 -1
  89. package/dist/components/file-button.d.ts +1 -1
  90. package/dist/components/file-button.js.map +1 -1
  91. package/dist/components/floating-panel.d.ts +7 -6
  92. package/dist/components/floating-panel.js +3 -17
  93. package/dist/components/floating-panel.js.map +1 -1
  94. package/dist/components/form-actions.d.ts +3 -2
  95. package/dist/components/form-actions.js.map +1 -1
  96. package/dist/components/hover-menu.js +3 -2
  97. package/dist/components/hover-menu.js.map +1 -1
  98. package/dist/components/icon-button.d.ts +23 -5
  99. package/dist/components/icon-button.js +6 -8
  100. package/dist/components/icon-button.js.map +1 -1
  101. package/dist/components/inline-confirm.js +2 -1
  102. package/dist/components/inline-confirm.js.map +1 -1
  103. package/dist/components/inline-entity-combobox.js +2 -2
  104. package/dist/components/inline-entity-combobox.js.map +1 -1
  105. package/dist/components/input.js +2 -2
  106. package/dist/components/input.js.map +1 -1
  107. package/dist/components/jump-calendar.js +3 -3
  108. package/dist/components/jump-calendar.js.map +1 -1
  109. package/dist/components/legal.d.ts +13 -1
  110. package/dist/components/legal.js +12 -0
  111. package/dist/components/legal.js.map +1 -1
  112. package/dist/components/legended-series-chart.d.ts +80 -0
  113. package/dist/components/legended-series-chart.js +70 -0
  114. package/dist/components/legended-series-chart.js.map +1 -0
  115. package/dist/components/lightbox.js +2 -1
  116. package/dist/components/lightbox.js.map +1 -1
  117. package/dist/components/mini-calendar.js +13 -5
  118. package/dist/components/mini-calendar.js.map +1 -1
  119. package/dist/components/modal.d.ts +23 -2
  120. package/dist/components/modal.js +24 -6
  121. package/dist/components/modal.js.map +1 -1
  122. package/dist/components/month-picker.d.ts +1 -1
  123. package/dist/components/month-picker.js +6 -6
  124. package/dist/components/month-picker.js.map +1 -1
  125. package/dist/components/multi-entity-combobox.js +2 -2
  126. package/dist/components/multi-entity-combobox.js.map +1 -1
  127. package/dist/components/multi-select.js +2 -6
  128. package/dist/components/multi-select.js.map +1 -1
  129. package/dist/components/number-input.js +2 -2
  130. package/dist/components/number-input.js.map +1 -1
  131. package/dist/components/page-header-layout.d.ts +18 -0
  132. package/dist/components/page-header-layout.js +23 -0
  133. package/dist/components/page-header-layout.js.map +1 -0
  134. package/dist/components/page-header-skeleton.d.ts +41 -0
  135. package/dist/components/page-header-skeleton.js +52 -0
  136. package/dist/components/page-header-skeleton.js.map +1 -0
  137. package/dist/components/page-header.js +4 -17
  138. package/dist/components/page-header.js.map +1 -1
  139. package/dist/components/popover.js +7 -2
  140. package/dist/components/popover.js.map +1 -1
  141. package/dist/components/release-notes-labels.d.ts +16 -0
  142. package/dist/components/release-notes-labels.js +18 -0
  143. package/dist/components/release-notes-labels.js.map +1 -0
  144. package/dist/components/release-notes.d.ts +65 -0
  145. package/dist/components/release-notes.js +65 -0
  146. package/dist/components/release-notes.js.map +1 -0
  147. package/dist/components/select.js +2 -2
  148. package/dist/components/select.js.map +1 -1
  149. package/dist/components/series-chart-labels.d.ts +9 -0
  150. package/dist/components/series-chart-labels.js +3 -1
  151. package/dist/components/series-chart-labels.js.map +1 -1
  152. package/dist/components/series-chart.d.ts +5 -0
  153. package/dist/components/series-chart.js +89 -36
  154. package/dist/components/series-chart.js.map +1 -1
  155. package/dist/components/toggle-group.js +3 -2
  156. package/dist/components/toggle-group.js.map +1 -1
  157. package/dist/components/treemap-layout.js.map +1 -1
  158. package/dist/components/treemap.d.ts +17 -15
  159. package/dist/components/treemap.js +167 -85
  160. package/dist/components/treemap.js.map +1 -1
  161. package/dist/eslint/index.js +1 -1
  162. package/dist/eslint/role-utilities.d.ts +2 -2
  163. package/dist/eslint/role-utilities.js.map +1 -1
  164. package/dist/feedback/feedback-inbox-page.d.ts +260 -0
  165. package/dist/feedback/feedback-inbox-page.js +207 -0
  166. package/dist/feedback/feedback-inbox-page.js.map +1 -0
  167. package/dist/feedback/feedback-vocabulary.js +2 -2
  168. package/dist/feedback/feedback-vocabulary.js.map +1 -1
  169. package/dist/feedback.d.ts +1 -0
  170. package/dist/feedback.js +6 -0
  171. package/dist/feedback.js.map +1 -1
  172. package/dist/hooks/use-debounce.d.ts +32 -5
  173. package/dist/hooks/use-debounce.js +6 -5
  174. package/dist/hooks/use-debounce.js.map +1 -1
  175. package/dist/hooks/use-file-picker.d.ts +3 -1
  176. package/dist/hooks/use-file-picker.js.map +1 -1
  177. package/dist/hooks/use-focus-trap.d.ts +19 -3
  178. package/dist/hooks/use-focus-trap.js +15 -5
  179. package/dist/hooks/use-focus-trap.js.map +1 -1
  180. package/dist/hooks/use-has-camera.d.ts +36 -0
  181. package/dist/hooks/use-has-camera.js +53 -0
  182. package/dist/hooks/use-has-camera.js.map +1 -0
  183. package/dist/hooks/use-hotkey.js +1 -10
  184. package/dist/hooks/use-hotkey.js.map +1 -1
  185. package/dist/hooks/use-overlay-history.d.ts +6 -1
  186. package/dist/hooks/use-overlay-history.js +10 -2
  187. package/dist/hooks/use-overlay-history.js.map +1 -1
  188. package/dist/i18n/defaults.js +11 -1
  189. package/dist/i18n/defaults.js.map +1 -1
  190. package/dist/i18n/german.js +75 -3
  191. package/dist/i18n/german.js.map +1 -1
  192. package/dist/i18n/kit-labels.d.ts +20 -0
  193. package/dist/i18n/kit-labels.js.map +1 -1
  194. package/dist/i18n/locales/en.js +92 -4
  195. package/dist/i18n/locales/en.js.map +3 -3
  196. package/dist/i18n/locales/es.js +71 -3
  197. package/dist/i18n/locales/es.js.map +1 -1
  198. package/dist/i18n/locales/fr.js +72 -2
  199. package/dist/i18n/locales/fr.js.map +1 -1
  200. package/dist/i18n/locales/hu.js +74 -3
  201. package/dist/i18n/locales/hu.js.map +1 -1
  202. package/dist/i18n/locales/it.js +74 -3
  203. package/dist/i18n/locales/it.js.map +1 -1
  204. package/dist/i18n/locales/zh.js +71 -3
  205. package/dist/i18n/locales/zh.js.map +1 -1
  206. package/dist/i18n/review.js +22 -1
  207. package/dist/i18n/review.js.map +1 -1
  208. package/dist/index.d.ts +24 -5
  209. package/dist/index.js +35 -1
  210. package/dist/index.js.map +1 -1
  211. package/dist/landing/landing-labels.d.ts +8 -0
  212. package/dist/landing/landing-labels.js +2 -1
  213. package/dist/landing/landing-labels.js.map +1 -1
  214. package/dist/lib/changelog.d.ts +130 -0
  215. package/dist/lib/changelog.js +137 -0
  216. package/dist/lib/changelog.js.map +1 -0
  217. package/dist/lib/clipboard-minus.js +2 -2
  218. package/dist/lib/clipboard-minus.js.map +1 -1
  219. package/dist/lib/dates.d.ts +29 -3
  220. package/dist/lib/dates.js +41 -12
  221. package/dist/lib/dates.js.map +1 -1
  222. package/dist/lib/editable-content.d.ts +33 -0
  223. package/dist/lib/editable-content.js +15 -0
  224. package/dist/lib/editable-content.js.map +1 -0
  225. package/dist/lib/focus-left.d.ts +47 -1
  226. package/dist/lib/focus-left.js +11 -2
  227. package/dist/lib/focus-left.js.map +1 -1
  228. package/dist/lib/server-wake.d.ts +49 -9
  229. package/dist/lib/server-wake.js +25 -14
  230. package/dist/lib/server-wake.js.map +1 -1
  231. package/dist/lib/session-refresh.d.ts +53 -0
  232. package/dist/lib/session-refresh.js +27 -1
  233. package/dist/lib/session-refresh.js.map +1 -1
  234. package/dist/rhf/fields.js +3 -3
  235. package/dist/rhf/fields.js.map +1 -1
  236. package/dist/shell/app-shell.d.ts +25 -1
  237. package/dist/shell/app-shell.js +3 -1
  238. package/dist/shell/app-shell.js.map +1 -1
  239. package/dist/shell/dev-menu.d.ts +3 -0
  240. package/dist/shell/dev-menu.js +4 -2
  241. package/dist/shell/dev-menu.js.map +1 -1
  242. package/dist/shell/held-changes-banner-labels.d.ts +23 -0
  243. package/dist/shell/held-changes-banner-labels.js +14 -0
  244. package/dist/shell/held-changes-banner-labels.js.map +1 -0
  245. package/dist/shell/held-changes-banner.d.ts +75 -0
  246. package/dist/shell/held-changes-banner.js +106 -0
  247. package/dist/shell/held-changes-banner.js.map +1 -0
  248. package/dist/shell/sync-status-indicator.d.ts +27 -12
  249. package/dist/shell/sync-status-indicator.js +13 -4
  250. package/dist/shell/sync-status-indicator.js.map +1 -1
  251. package/dist/shell/top-bar-parts-labels.d.ts +5 -0
  252. package/dist/shell/top-bar-parts-labels.js +3 -1
  253. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  254. package/dist/shell.d.ts +2 -0
  255. package/dist/shell.js +3 -0
  256. package/dist/shell.js.map +1 -1
  257. package/dist/sync/engine.d.ts +11 -2
  258. package/dist/sync/engine.js +13 -5
  259. package/dist/sync/engine.js.map +1 -1
  260. package/dist/sync/hooks.d.ts +3 -2
  261. package/dist/sync/hooks.js +4 -2
  262. package/dist/sync/hooks.js.map +1 -1
  263. package/dist/testing/css-variables.generated.d.ts +2 -2
  264. package/dist/testing/css-variables.generated.js +1 -1
  265. package/dist/testing/css-variables.generated.js.map +1 -1
  266. package/dist/theme/chart-palette.d.ts +18 -0
  267. package/dist/theme/chart-palette.js +17 -1
  268. package/dist/theme/chart-palette.js.map +1 -1
  269. package/dist/theme/contrast-tokens.d.ts +3 -3
  270. package/dist/theme/contrast-tokens.js.map +1 -1
  271. package/dist/theme/contrast.d.ts +4 -4
  272. package/dist/theme/contrast.js.map +1 -1
  273. package/dist/tour/tour-overlay.js +2 -1
  274. package/dist/tour/tour-overlay.js.map +1 -1
  275. package/package.json +5 -9
  276. package/tokens.css +19 -54
  277. package/src/account/account-parts.ts +0 -47
  278. package/src/account/account-settings-labels.ts +0 -240
  279. package/src/account/account-settings.tsx +0 -698
  280. package/src/account/data-export-setting-labels.ts +0 -20
  281. package/src/account/data-export-setting.tsx +0 -200
  282. package/src/account/delete-account-setting-labels.ts +0 -33
  283. package/src/account/delete-account-setting.tsx +0 -342
  284. package/src/account/email-change-setting-labels.ts +0 -31
  285. package/src/account/email-change-setting.tsx +0 -443
  286. package/src/account/passkeys-setting.tsx +0 -445
  287. package/src/account/sessions-setting-labels.ts +0 -24
  288. package/src/account/sessions-setting.tsx +0 -283
  289. package/src/admin/admin-action-confirm-labels.ts +0 -19
  290. package/src/admin/admin-action-confirm.tsx +0 -509
  291. package/src/admin/admin-action-log-labels.ts +0 -83
  292. package/src/admin/admin-action-log.tsx +0 -464
  293. package/src/admin/admin-area-labels.ts +0 -114
  294. package/src/admin/admin-forbidden.tsx +0 -75
  295. package/src/admin/admin-overview.tsx +0 -258
  296. package/src/admin/admin-parts.ts +0 -68
  297. package/src/admin/admin-sections.tsx +0 -208
  298. package/src/admin/admin-tool-links.tsx +0 -90
  299. package/src/admin/invitations-panel-labels.ts +0 -31
  300. package/src/admin/invitations-panel.tsx +0 -516
  301. package/src/admin/mail-test-card-labels.ts +0 -19
  302. package/src/admin/mail-test-card.tsx +0 -189
  303. package/src/admin/plan-change-labels.ts +0 -5
  304. package/src/admin/plan-change.tsx +0 -474
  305. package/src/admin/reviewer-scope-editor-labels.ts +0 -16
  306. package/src/admin/reviewer-scope-editor.tsx +0 -224
  307. package/src/admin/role-select-labels.ts +0 -16
  308. package/src/admin/role-select.tsx +0 -282
  309. package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
  310. package/src/admin/transfer-ownership-dialog.tsx +0 -162
  311. package/src/admin/user-roster-labels.ts +0 -17
  312. package/src/admin/user-roster.tsx +0 -559
  313. package/src/auth/accept-invitation-labels.ts +0 -21
  314. package/src/auth/accept-invitation.tsx +0 -210
  315. package/src/auth/auth-errors-labels.ts +0 -23
  316. package/src/auth/auth-errors.ts +0 -439
  317. package/src/auth/complete-name-dialog-labels.ts +0 -16
  318. package/src/auth/complete-name-dialog.tsx +0 -253
  319. package/src/auth/email-tag.ts +0 -75
  320. package/src/auth/forgot-password-form-labels.ts +0 -20
  321. package/src/auth/forgot-password-form.tsx +0 -221
  322. package/src/auth/form-rules.ts +0 -40
  323. package/src/auth/new-password-fields.tsx +0 -85
  324. package/src/auth/not-found-page-labels.ts +0 -12
  325. package/src/auth/not-found-page.tsx +0 -95
  326. package/src/auth/register-form-labels.ts +0 -31
  327. package/src/auth/register-form.tsx +0 -456
  328. package/src/auth/reset-password-form-labels.ts +0 -27
  329. package/src/auth/reset-password-form.tsx +0 -341
  330. package/src/auth/sign-in-form-labels.ts +0 -44
  331. package/src/auth/sign-in-form.tsx +0 -858
  332. package/src/auth/status-parts.tsx +0 -132
  333. package/src/auth/verify-email-labels.ts +0 -26
  334. package/src/auth/verify-email.tsx +0 -445
  335. package/src/billing/billing-banner.tsx +0 -258
  336. package/src/billing/billing-labels.ts +0 -381
  337. package/src/billing/billing-lock.ts +0 -108
  338. package/src/billing/billing-overview.ts +0 -157
  339. package/src/billing/billing-standing.ts +0 -80
  340. package/src/billing/cancel-request.ts +0 -97
  341. package/src/billing/cancel-subscription.tsx +0 -98
  342. package/src/billing/checkout-processing.ts +0 -394
  343. package/src/billing/checkout-return.ts +0 -90
  344. package/src/billing/pay-page.ts +0 -330
  345. package/src/billing/plan-card.tsx +0 -562
  346. package/src/billing/plan-change-labels.ts +0 -66
  347. package/src/billing/plan-limit-notice.tsx +0 -148
  348. package/src/billing/plan-limit-toast.tsx +0 -90
  349. package/src/billing/plan-limit.ts +0 -52
  350. package/src/billing/plan-price.ts +0 -98
  351. package/src/billing/plan-switch.tsx +0 -216
  352. package/src/billing/provider-page.ts +0 -84
  353. package/src/billing/subscription-actions.tsx +0 -247
  354. package/src/billing/subscription-status-chip.tsx +0 -95
  355. package/src/chart.ts +0 -103
  356. package/src/components/account-chips-labels.ts +0 -16
  357. package/src/components/account-chips.tsx +0 -474
  358. package/src/components/alert-banner.tsx +0 -670
  359. package/src/components/amount-input.tsx +0 -816
  360. package/src/components/appearance-labels.ts +0 -63
  361. package/src/components/authed-image-labels.ts +0 -11
  362. package/src/components/authed-image.tsx +0 -209
  363. package/src/components/autocomplete.tsx +0 -645
  364. package/src/components/breadcrumbs-labels.ts +0 -10
  365. package/src/components/breadcrumbs.tsx +0 -205
  366. package/src/components/bulk-action-bar-labels.ts +0 -11
  367. package/src/components/bulk-action-bar.tsx +0 -443
  368. package/src/components/button-group.tsx +0 -223
  369. package/src/components/button-look.ts +0 -256
  370. package/src/components/button.tsx +0 -339
  371. package/src/components/calculator.tsx +0 -341
  372. package/src/components/calendar-heatmap-labels.ts +0 -15
  373. package/src/components/calendar-heatmap.tsx +0 -600
  374. package/src/components/card.tsx +0 -377
  375. package/src/components/chart-edge.ts +0 -106
  376. package/src/components/chart-figure.tsx +0 -35
  377. package/src/components/chart-height.ts +0 -30
  378. package/src/components/chart-zoom-core.tsx +0 -627
  379. package/src/components/chart-zoom.tsx +0 -22
  380. package/src/components/chart.tsx +0 -554
  381. package/src/components/checkbox-group.tsx +0 -247
  382. package/src/components/checkbox.tsx +0 -346
  383. package/src/components/chip-input.tsx +0 -370
  384. package/src/components/chip-labels.ts +0 -13
  385. package/src/components/chip.tsx +0 -843
  386. package/src/components/choice-card.tsx +0 -544
  387. package/src/components/column-mapper-labels.ts +0 -38
  388. package/src/components/column-mapper.tsx +0 -655
  389. package/src/components/combobox-core.tsx +0 -1321
  390. package/src/components/combobox-suggestions.tsx +0 -131
  391. package/src/components/combobox.tsx +0 -716
  392. package/src/components/confirm-context.ts +0 -7
  393. package/src/components/confirm-dialog-labels.ts +0 -13
  394. package/src/components/confirm-dialog.tsx +0 -341
  395. package/src/components/copy-button-labels.ts +0 -13
  396. package/src/components/copy-button.tsx +0 -192
  397. package/src/components/country-select-labels.ts +0 -11
  398. package/src/components/country-select.tsx +0 -539
  399. package/src/components/currency-option-list.tsx +0 -188
  400. package/src/components/currency-select.tsx +0 -343
  401. package/src/components/danger-confirm-labels.ts +0 -20
  402. package/src/components/danger-confirm.tsx +0 -987
  403. package/src/components/data-table-cells.tsx +0 -118
  404. package/src/components/data-table-column-settings.tsx +0 -198
  405. package/src/components/data-table-filter-popover.tsx +0 -289
  406. package/src/components/data-table-filters.ts +0 -315
  407. package/src/components/data-table-fit.ts +0 -301
  408. package/src/components/data-table-groups.tsx +0 -288
  409. package/src/components/data-table-head.tsx +0 -280
  410. package/src/components/data-table-labels.ts +0 -198
  411. package/src/components/data-table-leading-row.tsx +0 -58
  412. package/src/components/data-table-mobile.tsx +0 -1117
  413. package/src/components/data-table-pagination.tsx +0 -202
  414. package/src/components/data-table-resize.ts +0 -188
  415. package/src/components/data-table-row-actions.tsx +0 -302
  416. package/src/components/data-table-row.tsx +0 -496
  417. package/src/components/data-table-selection.ts +0 -71
  418. package/src/components/data-table-shared.tsx +0 -287
  419. package/src/components/data-table-sort.ts +0 -163
  420. package/src/components/data-table-virtual.ts +0 -576
  421. package/src/components/data-table.tsx +0 -2598
  422. package/src/components/date-field.tsx +0 -684
  423. package/src/components/date-format-setting.tsx +0 -64
  424. package/src/components/date-picker.tsx +0 -404
  425. package/src/components/date-range-picker.tsx +0 -518
  426. package/src/components/description-list-labels.ts +0 -9
  427. package/src/components/description-list.tsx +0 -386
  428. package/src/components/dialog-frame-labels.ts +0 -7
  429. package/src/components/dialog-frame.tsx +0 -284
  430. package/src/components/disclosure.tsx +0 -476
  431. package/src/components/dropdown.tsx +0 -461
  432. package/src/components/empty-state.tsx +0 -136
  433. package/src/components/entity-combobox.tsx +0 -422
  434. package/src/components/error-boundary-labels.ts +0 -20
  435. package/src/components/error-boundary.tsx +0 -675
  436. package/src/components/facing-pair.ts +0 -93
  437. package/src/components/field-base-labels.ts +0 -20
  438. package/src/components/field-base.tsx +0 -656
  439. package/src/components/field-parts.tsx +0 -825
  440. package/src/components/field-strip.tsx +0 -129
  441. package/src/components/field.tsx +0 -206
  442. package/src/components/file-button.tsx +0 -295
  443. package/src/components/file-dropzone.tsx +0 -559
  444. package/src/components/floating-panel-labels.ts +0 -10
  445. package/src/components/floating-panel.tsx +0 -828
  446. package/src/components/focus-ring.ts +0 -16
  447. package/src/components/form-actions-labels.ts +0 -11
  448. package/src/components/form-actions.tsx +0 -578
  449. package/src/components/full-bleed-dialog.tsx +0 -294
  450. package/src/components/grouped-picker.tsx +0 -184
  451. package/src/components/help-popover.tsx +0 -117
  452. package/src/components/hover-menu.tsx +0 -343
  453. package/src/components/iban-input-labels.ts +0 -14
  454. package/src/components/iban-input.tsx +0 -236
  455. package/src/components/icon-button.tsx +0 -959
  456. package/src/components/icon-picker-labels.ts +0 -13
  457. package/src/components/icon-picker.tsx +0 -232
  458. package/src/components/image-grid-labels.ts +0 -11
  459. package/src/components/image-grid.tsx +0 -202
  460. package/src/components/inline-confirm.tsx +0 -116
  461. package/src/components/inline-edit-field-labels.ts +0 -11
  462. package/src/components/inline-edit-field.tsx +0 -552
  463. package/src/components/inline-entity-combobox.tsx +0 -849
  464. package/src/components/input.tsx +0 -394
  465. package/src/components/jump-calendar.tsx +0 -265
  466. package/src/components/language-select.tsx +0 -93
  467. package/src/components/legal-labels.ts +0 -105
  468. package/src/components/legal-page.tsx +0 -711
  469. package/src/components/legal.tsx +0 -668
  470. package/src/components/lightbox-labels.ts +0 -18
  471. package/src/components/lightbox.tsx +0 -399
  472. package/src/components/line-items-labels.ts +0 -14
  473. package/src/components/line-items.tsx +0 -602
  474. package/src/components/list-labels.ts +0 -12
  475. package/src/components/list.tsx +0 -870
  476. package/src/components/loading-state.tsx +0 -78
  477. package/src/components/measured-grid-labels.ts +0 -25
  478. package/src/components/measured-grid.tsx +0 -837
  479. package/src/components/menu-item.tsx +0 -241
  480. package/src/components/mini-calendar-labels.ts +0 -17
  481. package/src/components/mini-calendar.tsx +0 -792
  482. package/src/components/modal.tsx +0 -534
  483. package/src/components/money-draft.ts +0 -83
  484. package/src/components/money-field.tsx +0 -76
  485. package/src/components/month-grid.tsx +0 -229
  486. package/src/components/month-picker-labels.ts +0 -22
  487. package/src/components/month-picker.tsx +0 -1087
  488. package/src/components/multi-entity-combobox.tsx +0 -369
  489. package/src/components/multi-select.tsx +0 -376
  490. package/src/components/nav-pills.tsx +0 -198
  491. package/src/components/number-field.tsx +0 -327
  492. package/src/components/number-input.tsx +0 -559
  493. package/src/components/numeric-field.ts +0 -169
  494. package/src/components/numpad-sheet.tsx +0 -285
  495. package/src/components/one-time-code-input.tsx +0 -270
  496. package/src/components/page-contents-labels.ts +0 -9
  497. package/src/components/page-contents.tsx +0 -282
  498. package/src/components/page-header-context.ts +0 -8
  499. package/src/components/page-header.tsx +0 -298
  500. package/src/components/password-strength-labels.ts +0 -22
  501. package/src/components/password-strength.tsx +0 -241
  502. package/src/components/phone-input-labels.ts +0 -10
  503. package/src/components/phone-input.tsx +0 -282
  504. package/src/components/picker-sheet.tsx +0 -330
  505. package/src/components/pie-chart-labels.ts +0 -40
  506. package/src/components/pie-chart.tsx +0 -480
  507. package/src/components/popover-labels.ts +0 -14
  508. package/src/components/popover.tsx +0 -268
  509. package/src/components/progress-bar-labels.ts +0 -10
  510. package/src/components/progress-bar.tsx +0 -479
  511. package/src/components/qr-code.tsx +0 -76
  512. package/src/components/reauth-dialog-labels.ts +0 -13
  513. package/src/components/reauth-dialog.tsx +0 -265
  514. package/src/components/row-actions-labels.ts +0 -12
  515. package/src/components/row-actions.tsx +0 -795
  516. package/src/components/scroll-area.tsx +0 -123
  517. package/src/components/search-field.tsx +0 -192
  518. package/src/components/select.tsx +0 -294
  519. package/src/components/separator.tsx +0 -33
  520. package/src/components/series-chart-axes.ts +0 -342
  521. package/src/components/series-chart-budget.ts +0 -150
  522. package/src/components/series-chart-labels.ts +0 -33
  523. package/src/components/series-chart-thin.ts +0 -180
  524. package/src/components/series-chart-ticks.ts +0 -194
  525. package/src/components/series-chart.tsx +0 -1906
  526. package/src/components/server-wake-labels.ts +0 -11
  527. package/src/components/server-wake.tsx +0 -129
  528. package/src/components/settings-fields.tsx +0 -237
  529. package/src/components/share-card-labels.ts +0 -30
  530. package/src/components/share-card.tsx +0 -675
  531. package/src/components/signature-pad-labels.ts +0 -24
  532. package/src/components/signature-pad.tsx +0 -747
  533. package/src/components/signed-amount-labels.ts +0 -10
  534. package/src/components/signed-amount.tsx +0 -336
  535. package/src/components/skeleton.tsx +0 -80
  536. package/src/components/slider.tsx +0 -338
  537. package/src/components/sparkline-labels.ts +0 -14
  538. package/src/components/sparkline.tsx +0 -322
  539. package/src/components/spinner.tsx +0 -81
  540. package/src/components/stat-tile-labels.ts +0 -15
  541. package/src/components/stat-tile.tsx +0 -754
  542. package/src/components/status-dot.tsx +0 -170
  543. package/src/components/swatch-picker-labels.ts +0 -10
  544. package/src/components/swatch-picker.tsx +0 -283
  545. package/src/components/swipeable-row.tsx +0 -386
  546. package/src/components/switch.tsx +0 -273
  547. package/src/components/table-skeleton.tsx +0 -136
  548. package/src/components/table.tsx +0 -647
  549. package/src/components/tabs-labels.ts +0 -13
  550. package/src/components/tabs.tsx +0 -693
  551. package/src/components/text-link.tsx +0 -359
  552. package/src/components/text.tsx +0 -150
  553. package/src/components/textarea.tsx +0 -330
  554. package/src/components/tile-radio.tsx +0 -332
  555. package/src/components/time-input.tsx +0 -121
  556. package/src/components/toast-labels.ts +0 -12
  557. package/src/components/toast.tsx +0 -519
  558. package/src/components/toggle-group.tsx +0 -752
  559. package/src/components/toggle-legend.tsx +0 -394
  560. package/src/components/tooltip.tsx +0 -1149
  561. package/src/components/translation-review-editor.tsx +0 -290
  562. package/src/components/translation-review-labels.ts +0 -181
  563. package/src/components/translation-review-parts.tsx +0 -272
  564. package/src/components/translation-review.tsx +0 -1240
  565. package/src/components/tree-view.tsx +0 -589
  566. package/src/components/treemap-labels.ts +0 -47
  567. package/src/components/treemap-layout.ts +0 -210
  568. package/src/components/treemap.tsx +0 -1184
  569. package/src/components/trigger-aria.ts +0 -44
  570. package/src/components/use-anchor-dir.ts +0 -20
  571. package/src/components/use-mobile-reveal.ts +0 -62
  572. package/src/components/use-table-state.ts +0 -753
  573. package/src/components/user-avatar.tsx +0 -116
  574. package/src/components/wizard-stepper.tsx +0 -42
  575. package/src/components/write-lock-labels.ts +0 -10
  576. package/src/components/write-lock.tsx +0 -253
  577. package/src/currencies.ts +0 -90
  578. package/src/data-table.ts +0 -42
  579. package/src/demo/demo-banner.tsx +0 -153
  580. package/src/demo/demo-countdown.ts +0 -136
  581. package/src/demo/demo-ended.tsx +0 -90
  582. package/src/demo/demo-labels.ts +0 -78
  583. package/src/demo/demo-session.ts +0 -32
  584. package/src/demo/demo-start.tsx +0 -156
  585. package/src/eslint/index.ts +0 -161
  586. package/src/eslint/role-utilities.generated.ts +0 -146
  587. package/src/eslint/role-utilities.ts +0 -233
  588. package/src/feedback/feedback-attachment-download.tsx +0 -77
  589. package/src/feedback/feedback-attachment-labels.ts +0 -37
  590. package/src/feedback/feedback-attachment.tsx +0 -1239
  591. package/src/feedback/feedback-capture.ts +0 -287
  592. package/src/feedback/feedback-comment-thread.tsx +0 -353
  593. package/src/feedback/feedback-context-labels.ts +0 -11
  594. package/src/feedback/feedback-context.tsx +0 -245
  595. package/src/feedback/feedback-crash.ts +0 -547
  596. package/src/feedback/feedback-diagnostics.tsx +0 -41
  597. package/src/feedback/feedback-dialog-labels.ts +0 -29
  598. package/src/feedback/feedback-dialog.tsx +0 -452
  599. package/src/feedback/feedback-entry-file.tsx +0 -150
  600. package/src/feedback/feedback-inbox.tsx +0 -552
  601. package/src/feedback/feedback-labels.ts +0 -144
  602. package/src/feedback/feedback-menu-labels.ts +0 -12
  603. package/src/feedback/feedback-menu.tsx +0 -177
  604. package/src/feedback/feedback-mutation.ts +0 -20
  605. package/src/feedback/feedback-page-link.ts +0 -90
  606. package/src/feedback/feedback-public-labels.ts +0 -81
  607. package/src/feedback/feedback-public-list.tsx +0 -524
  608. package/src/feedback/feedback-public-switch.tsx +0 -102
  609. package/src/feedback/feedback-public-types.ts +0 -101
  610. package/src/feedback/feedback-record.ts +0 -428
  611. package/src/feedback/feedback-replies-context.ts +0 -9
  612. package/src/feedback/feedback-replies.tsx +0 -343
  613. package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
  614. package/src/feedback/feedback-row-detail-labels.ts +0 -31
  615. package/src/feedback/feedback-row-detail.tsx +0 -871
  616. package/src/feedback/feedback-status-undo.ts +0 -109
  617. package/src/feedback/feedback-submit-dialog.tsx +0 -131
  618. package/src/feedback/feedback-submit.tsx +0 -270
  619. package/src/feedback/feedback-swipe.tsx +0 -192
  620. package/src/feedback/feedback-table-labels.ts +0 -27
  621. package/src/feedback/feedback-table.tsx +0 -912
  622. package/src/feedback/feedback-thread-labels.ts +0 -22
  623. package/src/feedback/feedback-thread-parts-labels.ts +0 -41
  624. package/src/feedback/feedback-thread.tsx +0 -905
  625. package/src/feedback/feedback-vocabulary.ts +0 -263
  626. package/src/feedback/feedback-vote-button.tsx +0 -124
  627. package/src/feedback.ts +0 -116
  628. package/src/hooks/use-anchored-panel.ts +0 -396
  629. package/src/hooks/use-anchored-rect.ts +0 -77
  630. package/src/hooks/use-announce.ts +0 -85
  631. package/src/hooks/use-authed-src.ts +0 -167
  632. package/src/hooks/use-body-scroll-lock.ts +0 -119
  633. package/src/hooks/use-breakpoint.ts +0 -133
  634. package/src/hooks/use-close-transition.ts +0 -107
  635. package/src/hooks/use-copy-to-clipboard.ts +0 -122
  636. package/src/hooks/use-debounce.ts +0 -114
  637. package/src/hooks/use-dismiss.ts +0 -371
  638. package/src/hooks/use-document-title.ts +0 -33
  639. package/src/hooks/use-file-drop.ts +0 -121
  640. package/src/hooks/use-file-picker-labels.ts +0 -28
  641. package/src/hooks/use-file-picker.tsx +0 -359
  642. package/src/hooks/use-focus-trap.ts +0 -199
  643. package/src/hooks/use-hotkey.ts +0 -212
  644. package/src/hooks/use-large-text.ts +0 -41
  645. package/src/hooks/use-long-press.ts +0 -140
  646. package/src/hooks/use-media-query.ts +0 -39
  647. package/src/hooks/use-mounted.ts +0 -39
  648. package/src/hooks/use-noindex.ts +0 -37
  649. package/src/hooks/use-overlay-history.ts +0 -714
  650. package/src/hooks/use-row-swipe.ts +0 -207
  651. package/src/hooks/use-search-param-state.ts +0 -434
  652. package/src/hooks/use-windowed-rows.ts +0 -225
  653. package/src/i18n/defaults.ts +0 -267
  654. package/src/i18n/german.ts +0 -2059
  655. package/src/i18n/kit-labels.tsx +0 -1003
  656. package/src/i18n/languages.ts +0 -349
  657. package/src/i18n/locales/de-CH.ts +0 -24
  658. package/src/i18n/locales/en.ts +0 -267
  659. package/src/i18n/locales/es.ts +0 -1999
  660. package/src/i18n/locales/fr.ts +0 -2037
  661. package/src/i18n/locales/hu.ts +0 -2004
  662. package/src/i18n/locales/it.ts +0 -2014
  663. package/src/i18n/locales/zh.ts +0 -1912
  664. package/src/i18n/plurals.ts +0 -86
  665. package/src/i18n/review.ts +0 -475
  666. package/src/i18n/shared-labels.ts +0 -138
  667. package/src/i18n/swiss.ts +0 -29
  668. package/src/index.ts +0 -1191
  669. package/src/landing/access.ts +0 -93
  670. package/src/landing/landing-actions.tsx +0 -97
  671. package/src/landing/landing-labels.ts +0 -59
  672. package/src/landing/landing-sections.tsx +0 -341
  673. package/src/landing/page-seo.ts +0 -261
  674. package/src/landing/public-header.tsx +0 -121
  675. package/src/landing/public-page.tsx +0 -106
  676. package/src/landing/routing.tsx +0 -225
  677. package/src/lib/broadcast.ts +0 -52
  678. package/src/lib/calc.ts +0 -323
  679. package/src/lib/chip-edge-snap.ts +0 -212
  680. package/src/lib/clipboard-minus.ts +0 -107
  681. package/src/lib/clipping.ts +0 -34
  682. package/src/lib/cn.ts +0 -28
  683. package/src/lib/column-mapping.ts +0 -234
  684. package/src/lib/countries.ts +0 -91
  685. package/src/lib/data-attributes.ts +0 -14
  686. package/src/lib/date-format.ts +0 -227
  687. package/src/lib/dates.ts +0 -288
  688. package/src/lib/day-format.ts +0 -96
  689. package/src/lib/decimal-marks.ts +0 -108
  690. package/src/lib/describe-failure.ts +0 -18
  691. package/src/lib/device-settings.ts +0 -147
  692. package/src/lib/direction.ts +0 -74
  693. package/src/lib/document-navigation.ts +0 -17
  694. package/src/lib/document-title.ts +0 -41
  695. package/src/lib/download.ts +0 -172
  696. package/src/lib/float-clearance.ts +0 -70
  697. package/src/lib/floating-stack.ts +0 -70
  698. package/src/lib/focus-left.ts +0 -156
  699. package/src/lib/format.ts +0 -585
  700. package/src/lib/http-status.ts +0 -38
  701. package/src/lib/iban.ts +0 -251
  702. package/src/lib/ime.ts +0 -24
  703. package/src/lib/intl-cache.ts +0 -27
  704. package/src/lib/iso-day.ts +0 -13
  705. package/src/lib/lazy-with-fallback.ts +0 -82
  706. package/src/lib/logger.ts +0 -69
  707. package/src/lib/minus.ts +0 -50
  708. package/src/lib/path-pattern.ts +0 -55
  709. package/src/lib/person-name.ts +0 -155
  710. package/src/lib/phone.ts +0 -208
  711. package/src/lib/plain-text.ts +0 -22
  712. package/src/lib/presence.ts +0 -18
  713. package/src/lib/qr-encode.ts +0 -444
  714. package/src/lib/quiet-focus.ts +0 -32
  715. package/src/lib/reduced-motion.ts +0 -22
  716. package/src/lib/refs.ts +0 -22
  717. package/src/lib/round.ts +0 -43
  718. package/src/lib/safe-storage.ts +0 -119
  719. package/src/lib/server-wake.ts +0 -315
  720. package/src/lib/session-refresh.ts +0 -472
  721. package/src/lib/share-format.ts +0 -18
  722. package/src/lib/status-tone.ts +0 -43
  723. package/src/lib/strip-fade.ts +0 -287
  724. package/src/lib/table-text.ts +0 -554
  725. package/src/lib/text-case.ts +0 -19
  726. package/src/lib/text-match.ts +0 -169
  727. package/src/lib/thenable.ts +0 -19
  728. package/src/lib/translation-review.ts +0 -769
  729. package/src/notifications/device-zone-report.ts +0 -67
  730. package/src/notifications/feedback-notification-types-labels.ts +0 -33
  731. package/src/notifications/feedback-notification-types.ts +0 -71
  732. package/src/notifications/notification-bell.tsx +0 -119
  733. package/src/notifications/notification-inbox.tsx +0 -309
  734. package/src/notifications/notification-parts.tsx +0 -81
  735. package/src/notifications/notification-types-setting.tsx +0 -406
  736. package/src/notifications/notification-types.ts +0 -89
  737. package/src/notifications/notifications-labels.ts +0 -287
  738. package/src/notifications/push-device-setting.tsx +0 -230
  739. package/src/notifications/push-devices-setting.tsx +0 -374
  740. package/src/notifications/push-worker.ts +0 -168
  741. package/src/notifications/quiet-hours-setting.tsx +0 -238
  742. package/src/notifications/unsubscribe-panel.tsx +0 -200
  743. package/src/notifications/use-push-device.ts +0 -448
  744. package/src/offline/local-first.ts +0 -100
  745. package/src/offline/pwa-update-prompt-labels.ts +0 -9
  746. package/src/offline/pwa-update-prompt.tsx +0 -78
  747. package/src/offline/query-persistence.ts +0 -391
  748. package/src/offline/service-worker-updates.ts +0 -81
  749. package/src/offline/shared-computer-labels.ts +0 -8
  750. package/src/offline/shared-computer.tsx +0 -87
  751. package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
  752. package/src/offline/sign-out-pending-dialog.tsx +0 -135
  753. package/src/offline.ts +0 -37
  754. package/src/push-worker.ts +0 -25
  755. package/src/rhf/fields.tsx +0 -2187
  756. package/src/rhf/form-core.tsx +0 -324
  757. package/src/rhf/form.tsx +0 -12
  758. package/src/rhf/line-items.tsx +0 -135
  759. package/src/rhf/use-rhf-wizard-step.ts +0 -104
  760. package/src/rhf.ts +0 -21
  761. package/src/search/command-palette-labels.ts +0 -16
  762. package/src/search/command-palette.tsx +0 -624
  763. package/src/search/global-search-labels.ts +0 -13
  764. package/src/search/global-search.tsx +0 -660
  765. package/src/search/search-index.ts +0 -283
  766. package/src/search/use-command-key.ts +0 -23
  767. package/src/search.ts +0 -14
  768. package/src/settings/device-reset-setting-labels.ts +0 -20
  769. package/src/settings/device-reset-setting.tsx +0 -116
  770. package/src/settings/settings-card-classes.ts +0 -11
  771. package/src/settings/settings-catalogue.ts +0 -150
  772. package/src/settings/settings-context.ts +0 -33
  773. package/src/settings/settings-heading.tsx +0 -67
  774. package/src/settings/settings-labels.ts +0 -87
  775. package/src/settings/settings-layout.tsx +0 -471
  776. package/src/settings/settings-section.tsx +0 -108
  777. package/src/settings/use-account-appearance.ts +0 -148
  778. package/src/settings/use-account-language.ts +0 -146
  779. package/src/settings/use-settings-focus.ts +0 -128
  780. package/src/settings/use-settings-route.ts +0 -258
  781. package/src/shell/account-menu-language.tsx +0 -130
  782. package/src/shell/account-menu-text-size.tsx +0 -80
  783. package/src/shell/admin-menu.tsx +0 -183
  784. package/src/shell/app-shell-labels.ts +0 -11
  785. package/src/shell/app-shell.tsx +0 -1560
  786. package/src/shell/auth-layout.tsx +0 -186
  787. package/src/shell/company-switcher-labels.ts +0 -13
  788. package/src/shell/company-switcher.tsx +0 -136
  789. package/src/shell/dev-menu.tsx +0 -257
  790. package/src/shell/fill-height.tsx +0 -45
  791. package/src/shell/language-code.ts +0 -5
  792. package/src/shell/option-switcher-menu.tsx +0 -84
  793. package/src/shell/sync-status-indicator.tsx +0 -303
  794. package/src/shell/top-bar-actions.tsx +0 -87
  795. package/src/shell/top-bar-brand.tsx +0 -78
  796. package/src/shell/top-bar-parts-labels.ts +0 -148
  797. package/src/shell/top-bar.tsx +0 -40
  798. package/src/shell/topbar-action-menu.tsx +0 -567
  799. package/src/shell/topbar-controls.tsx +0 -269
  800. package/src/shell/tour-menu.tsx +0 -187
  801. package/src/shell.ts +0 -108
  802. package/src/sync/db.ts +0 -129
  803. package/src/sync/engine.ts +0 -1011
  804. package/src/sync/hooks.ts +0 -98
  805. package/src/sync/testing.ts +0 -73
  806. package/src/sync/wire.ts +0 -61
  807. package/src/sync.ts +0 -37
  808. package/src/table-text.ts +0 -8
  809. package/src/testing/css-variables.generated.ts +0 -112
  810. package/src/testing/css-variables.ts +0 -189
  811. package/src/testing/index.ts +0 -29
  812. package/src/testing/react.ts +0 -58
  813. package/src/testing/tour.ts +0 -41
  814. package/src/testing/viewport.ts +0 -357
  815. package/src/theme/chart-palette.ts +0 -232
  816. package/src/theme/color.ts +0 -272
  817. package/src/theme/contrast-tokens.ts +0 -268
  818. package/src/theme/contrast.ts +0 -250
  819. package/src/theme/palette-derive.ts +0 -825
  820. package/src/theme/palette-presets.ts +0 -544
  821. package/src/theme/palette-store.ts +0 -108
  822. package/src/theme/text-size.ts +0 -289
  823. package/src/theme/theme-store.ts +0 -157
  824. package/src/tour/tour-catalogue-labels.ts +0 -75
  825. package/src/tour/tour-catalogue.tsx +0 -335
  826. package/src/tour/tour-dock.ts +0 -134
  827. package/src/tour/tour-engine.ts +0 -37
  828. package/src/tour/tour-gates.ts +0 -218
  829. package/src/tour/tour-guide-button.tsx +0 -64
  830. package/src/tour/tour-guide-labels.ts +0 -15
  831. package/src/tour/tour-info.ts +0 -144
  832. package/src/tour/tour-labels.ts +0 -23
  833. package/src/tour/tour-overlay.tsx +0 -1170
  834. package/src/tour/tour.tsx +0 -750
  835. package/src/tour/use-tour-progress.ts +0 -671
  836. package/src/tour/use-tour-url-sync.ts +0 -266
  837. package/src/tour.ts +0 -21
  838. package/src/vite-env.d.ts +0 -23
  839. package/src/wizard/stepper-nav.tsx +0 -360
  840. package/src/wizard/types.ts +0 -275
  841. package/src/wizard/use-wizard.ts +0 -560
  842. package/src/wizard/validation.ts +0 -39
  843. package/src/wizard/wizard-context.tsx +0 -74
  844. package/src/wizard/wizard-labels.ts +0 -29
  845. package/src/wizard/wizard-step.tsx +0 -76
  846. package/src/wizard/wizard-summary.tsx +0 -84
  847. package/src/wizard.ts +0 -15
@@ -1,1117 +0,0 @@
1
- import { Fragment, isValidElement, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
2
- import type { MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent, ReactNode } from "react";
3
- import { ArrowDownWideNarrow, ArrowUpNarrowWide, ChevronDown, ChevronRight, Filter } from "lucide-react";
4
- import { FOCUS_RING } from "./focus-ring";
5
- import { IconButton } from "./icon-button";
6
- import { Select } from "./select";
7
- import { cn } from "../lib/cn";
8
- import { dirOf, type Direction } from "../lib/direction";
9
- import { defaultFilterState, isFilterActive, resolveFilter } from "./data-table-filters";
10
- import type { ColumnFilter, FilterValue } from "./data-table-filters";
11
- import type { SortCycle, SortState } from "./data-table-sort";
12
- import { FilterPopover } from "./data-table-filter-popover";
13
- import { SwipeableRow, type SwipeAction } from "./swipeable-row";
14
- import { DialogFrame } from "./dialog-frame";
15
- import { Checkbox } from "./checkbox";
16
- import { Chip } from "./chip";
17
- import { BooleanMark } from "./data-table-cells";
18
- import { EMPTY_FORMATTED_VALUE } from "../lib/format";
19
- import { useLongPress } from "../hooks/use-long-press";
20
- import type { DataTableLabels } from "./data-table-labels";
21
- import { RowLink, cleanAttrs, columnLabel, fromOutside, fromOwnControl } from "./data-table-shared";
22
- import type {
23
- DataTableCellProps,
24
- DataTableColumn,
25
- DataTableProps,
26
- DataTableSortLabels,
27
- FilterState,
28
- MobileSwipeActions,
29
- } from "./data-table";
30
-
31
- // DataTable's phone layout: the card a row becomes, the fields inside it, and the sort
32
- // and filter controls above the card list. Moved out of data-table.tsx whole (refactor
33
- // audit 2026-10, MOD-3 step 4); the list itself, its pager and its row dialog stay in
34
- // DataTable, which owns the state they share with the wide-screen table.
35
-
36
- /** Only the `data-*` attributes of a cell's props — what a phone card field takes. */
37
- function dataAttrsOf(props: DataTableCellProps | undefined): Record<string, unknown> | undefined {
38
- if (!props) return undefined;
39
- const out: Record<string, unknown> = {};
40
- for (const [k, v] of Object.entries(props)) if (k.startsWith("data-") && k !== "data-col") out[k] = v;
41
- return out;
42
- }
43
-
44
- /** The logical sides onto the physical drag directions {@link SwipeableRow} takes. */
45
- function physicalSwipe(actions: MobileSwipeActions, dir: Direction): { left?: SwipeAction[]; right?: SwipeAction[] } {
46
- const rtl = dir === "rtl";
47
- return {
48
- left: (rtl ? actions.end : actions.start) ?? actions.left,
49
- right: (rtl ? actions.start : actions.end) ?? actions.right,
50
- };
51
- }
52
-
53
- // ---------- Phone card at Large (0.32) ----------
54
-
55
- /**
56
- * §4 "nothing truncates" in a phone card at Large and Extra large: a cell's own
57
- * `truncate` (a kit cell, a chip, the app's span) wraps instead — and breaks inside a
58
- * word too long for the card rather than running out of it. A descendant rule, so it
59
- * reaches the cells without the table knowing what they render; `large:`, so Normal
60
- * keeps every ellipsis it had.
61
- */
62
- const CARD_NO_TRUNCATE = "large:[&_.truncate]:whitespace-normal large:[&_.truncate]:[overflow-wrap:anywhere]";
63
-
64
- /**
65
- * Columns as label-above-value pairs: the phone card's fields at Large (§4), and the
66
- * opened row's `mobileDetailsInRow` at every size. Each pair is a `<div>` in the
67
- * `<dl>` — the grouping HTML allows — so a pair keeps together and the gap falls
68
- * BETWEEN facts rather than between a label and its own value. `relative` holds a
69
- * header's `sr-only` text (an actions column's), as every kit container must.
70
- */
71
- export function StackedFields<T>({
72
- columns,
73
- row,
74
- compact,
75
- liftOwnClicks = false,
76
- }: {
77
- columns: readonly DataTableColumn<T>[];
78
- row: T;
79
- compact: boolean;
80
- /** On a stretched card (0.50 K4): lift the own-click fields above its title link. */
81
- liftOwnClicks?: boolean;
82
- }) {
83
- const fields = filledFields(columns, row);
84
- if (fields.length === 0) return null;
85
- return (
86
- <dl
87
- data-card-fields="stacked"
88
- className={cn(
89
- "relative flex min-w-0 flex-col",
90
- compact ? "gap-1.5 text-xs" : "gap-2.5 text-sm",
91
- CARD_NO_TRUNCATE,
92
- )}
93
- >
94
- {fields.map(({ col, value }) => (
95
- <div key={col.key} className="min-w-0">
96
- <dt className="text-xs uppercase tracking-wide text-muted">{col.header}</dt>
97
- <dd
98
- {...dataAttrsOf(col.cellProps?.(row))}
99
- className={cn(
100
- "mt-0.5 min-w-0 break-words text-secondary",
101
- liftOwnClicks && keepsOwnClicks(col) && LIFTED_FIELD,
102
- )}
103
- >
104
- {value}
105
- </dd>
106
- </div>
107
- ))}
108
- </dl>
109
- );
110
- }
111
-
112
- /**
113
- * A stretched card's own-click cell (0.50 K4): above the title control's overlay
114
- * (`after:z-[1]`), so its link or buttons take their own taps. `w-fit` keeps the lifted
115
- * box to the cell's content, so the field's empty rest still opens the row.
116
- */
117
- const LIFTED_CELL = "relative z-[2]";
118
- const LIFTED_FIELD = cn(LIFTED_CELL, "w-fit max-w-full justify-self-start");
119
-
120
- /**
121
- * The keyboard frame of a stretched card (0.50 K4): FOCUS_RING's, inside the card,
122
- * while its title control (`data-card-link`) has the focus.
123
- */
124
- const STRETCHED_RING =
125
- "has-[[data-card-link]:focus-visible]:ring-inset has-[[data-card-link]:focus-visible]:ring-focus has-[[data-card-link]:focus-visible]:ring-brand";
126
-
127
- /** The most fields the default phone card shows under its title (0.49 KIT-7). */
128
- export const MOBILE_CARD_FIELDS = 3;
129
-
130
- /**
131
- * The default phone card's columns (0.49 KIT-7), out of the phone's columns (visible,
132
- * not `mobileHidden`): the title — `mobile: "primary"` or `mobilePrimary`, else the
133
- * first that is not a status — the `role: "status"` columns for beside it, at most
134
- * {@link MOBILE_CARD_FIELDS} fields: those marked `mobile: "secondary"` when any is,
135
- * else the first after the title, and the `role: "actions"` columns, drawn after the
136
- * fields. An actions column is never the title and never one of the fields, so the
137
- * fields and the marks leave it on the card; only `mobileHidden` / `mobile: "hidden"`
138
- * (which keep it out of `columns`) remove it.
139
- */
140
- export function mobileCardColumns<T>(columns: readonly DataTableColumn<T>[]): {
141
- primary: DataTableColumn<T> | null;
142
- status: DataTableColumn<T>[];
143
- fields: DataTableColumn<T>[];
144
- actions: DataTableColumn<T>[];
145
- } {
146
- const titles = columns.filter((c) => c.role !== "actions");
147
- const primary =
148
- titles.find((c) => c.mobile === "primary") ??
149
- titles.find((c) => c.mobilePrimary) ??
150
- titles.find((c) => c.role !== "status") ??
151
- titles[0] ??
152
- null;
153
- const status = columns.filter((c) => c !== primary && c.role === "status");
154
- const actions = columns.filter((c) => c.role === "actions");
155
- const rest = titles.filter((c) => c !== primary && c.role !== "status");
156
- const marked = rest.filter((c) => c.mobile === "secondary");
157
- return { primary, status, fields: (marked.length > 0 ? marked : rest).slice(0, MOBILE_CARD_FIELDS), actions };
158
- }
159
-
160
- /** A column whose cell keeps its own clicks — `noRowLink`, or `role: "actions"`: no row
161
- * link may wrap it, and a card that draws it is not a link itself. */
162
- export function keepsOwnClicks<T>(col: DataTableColumn<T>): boolean {
163
- return !!col.noRowLink || col.role === "actions";
164
- }
165
-
166
- /** The kit's dev switch, as `lib/logger.ts` reads it: a development build, never a
167
- * production one, never under vitest. Read per call so a test can switch it on. */
168
- function devWarnings(): boolean {
169
- return Boolean(import.meta.env?.DEV) && !import.meta.env?.VITEST;
170
- }
171
-
172
- /**
173
- * A development build's note on the default phone card (0.49 KIT-7): a `noRowLink`
174
- * column past its three fields. Such a column holds the cell's own controls — a per-row
175
- * delete, a link — and the card leaves it out with them, so on a phone the action is
176
- * gone. Never for a `role: "actions"` column, which the card draws after its fields.
177
- * Read off the declared `columns` (not the ones a person or the width hid), and only
178
- * while the table lets the card choose: `enabled` is false with a `mobileCard` or
179
- * `mobileDetailsInRow` (the column is then in the opened row), and a column marked
180
- * `mobile: "secondary"` means the app chose. Once per table and column key; `name`
181
- * (the table's `storageKey` or `urlSync` prefix) says which table, else its title
182
- * column does.
183
- */
184
- export function useCardColumnWarnings<T>(
185
- columns: readonly DataTableColumn<T>[],
186
- enabled: boolean,
187
- name?: string,
188
- ): void {
189
- const warned = useRef<Set<string> | null>(null);
190
- useEffect(() => {
191
- if (!enabled || !devWarnings()) return;
192
- const phone = columns.filter((c) => !c.mobileHidden && c.mobile !== "hidden");
193
- if (phone.some((c) => c.mobile === "secondary")) return;
194
- const { primary, status, fields, actions } = mobileCardColumns(phone);
195
- const onCard = new Set<DataTableColumn<T> | null>([primary, ...status, ...fields, ...actions]);
196
- const table = name ? ` "${name}"` : primary ? ` (title column "${primary.key}")` : "";
197
- warned.current ??= new Set();
198
- for (const col of phone) {
199
- if (onCard.has(col) || !col.noRowLink || warned.current.has(col.key)) continue;
200
- warned.current.add(col.key);
201
- console.warn(
202
- `[ui-kit] DataTable${table}: column "${col.key}" (noRowLink) is past the phone card's three fields and ` +
203
- 'leaves the card — give it role: "actions" (drawn after the fields), or move its action to rowActions.',
204
- );
205
- }
206
- }, [columns, enabled, name]);
207
- }
208
-
209
- /**
210
- * Whether a rendered cell shows nothing worth a line (0.49 KIT-7): `null`, `undefined`,
211
- * a boolean (React draws neither), `""` or blank text, and the formatters' empty value
212
- * "—" ({@link EMPTY_FORMATTED_VALUE}), and `booleanColumn`'s mark for an unset value.
213
- * Any other element is never empty here — its output is the component's — so a column
214
- * whose empty value should drop off the card returns `null` (or "—") from `cell` for it.
215
- */
216
- export function isEmptyCell(value: ReactNode): boolean {
217
- if (value === null || value === undefined || typeof value === "boolean") return true;
218
- if (isValidElement<{ value?: unknown }>(value) && value.type === BooleanMark) return value.props.value == null;
219
- if (typeof value === "string") {
220
- const text = value.trim();
221
- return text === "" || text === EMPTY_FORMATTED_VALUE;
222
- }
223
- return false;
224
- }
225
-
226
- /** Each column's rendered cell, without the empty ones. */
227
- function filledFields<T>(
228
- columns: readonly DataTableColumn<T>[],
229
- row: T,
230
- ): { col: DataTableColumn<T>; value: ReactNode }[] {
231
- return columns.map((col) => ({ col, value: col.cell(row) })).filter(({ value }) => !isEmptyCell(value));
232
- }
233
-
234
- /** A status column's value beside the card's title: text in a neutral chip, an element
235
- * (the app's own Chip or StatusDot) as it is. */
236
- function StatusChip({ value }: { value: ReactNode }) {
237
- return typeof value === "string" || typeof value === "number" ? <Chip size="sm">{value}</Chip> : <>{value}</>;
238
- }
239
-
240
- // ---------- Phone sort control (keksdose K10) ----------
241
-
242
- /**
243
- * The card list's sort control: a "Sort by" select and a direction button.
244
- *
245
- * keksdose K10 (inputs audit 2026-10-02 §5; its aggregated report tables, #258): a
246
- * table is sorted by its header row and the phone card list has none, so the two
247
- * decisions the header carries — which column, which direction — were unreachable
248
- * below the breakpoint. keksdose hand-built this row over its DataTable to answer
249
- * "who did I pay the most" on the device it was asked on; this is that row, driven by
250
- * the SAME sort state as the headers (controlled `sorts` included), so turning the
251
- * phone sideways keeps the order and a sort chosen here is the one the URL and the
252
- * storage key hold.
253
- *
254
- * The rules are the header's: a newly picked column starts in its `firstSort`, the
255
- * direction button flips the primary sort (secondary ones stay), and "Default order"
256
- * — the third click of a `"tri"` cycle — is offered only under `"tri"`, since a
257
- * `"toggle"` table promises it is always ranked by something. The select's label is
258
- * a real `<label>` beside it, and the button sits outside that label: a click inside
259
- * a label is forwarded to the control it names, so a nested button would flip AND
260
- * open the select.
261
- */
262
- export function MobileSort<T>({
263
- columns,
264
- sorts,
265
- sortCycle,
266
- compact,
267
- labels,
268
- onPick,
269
- onFlip,
270
- }: {
271
- columns: readonly DataTableColumn<T>[];
272
- sorts: readonly SortState[];
273
- sortCycle: SortCycle;
274
- compact: boolean;
275
- labels: DataTableLabels;
276
- onPick: (key: string | null) => void;
277
- onFlip: () => void;
278
- }) {
279
- const selectId = useId();
280
- const sortLabels: DataTableSortLabels = labels;
281
- if (columns.length === 0) return null;
282
- // A persisted or URL sort can name a column with no `sortBy` (or a hidden one): the
283
- // sort chain drops it, and so does the control rather than claiming it.
284
- const primary = sorts[0] && columns.some((c) => c.key === sorts[0].key) ? sorts[0] : null;
285
- const offerDefault = sortCycle === "tri" || primary === null;
286
- const desc = primary?.dir === "desc";
287
- return (
288
- <div
289
- data-table-mobile-sort=""
290
- className={cn(
291
- // At Large the direction button shows its words (IconButton's label, §4); the
292
- // row wraps so the select keeps a usable width on a 240 px card.
293
- "flex flex-wrap items-center gap-2 border-b border-subtle",
294
- compact ? "px-3 py-1.5" : "px-4 py-2",
295
- )}
296
- >
297
- <label htmlFor={selectId} className="shrink-0 text-xs text-muted">
298
- {sortLabels.sortBy}
299
- </label>
300
- <Select
301
- id={selectId}
302
- size={compact ? "sm" : "md"}
303
- className="min-w-0 flex-1 large:basis-40"
304
- value={primary?.key ?? ""}
305
- onChange={(e) => onPick(e.target.value || null)}
306
- >
307
- {offerDefault && <option value="">{sortLabels.sortDefault}</option>}
308
- {columns.map((col) => (
309
- <option key={col.key} value={col.key}>
310
- {columnLabel(col, labels)}
311
- </option>
312
- ))}
313
- </Select>
314
- <IconButton
315
- size={compact ? "xs" : "md"}
316
- variant="secondary"
317
- label={desc ? sortLabels.sortDescending : sortLabels.sortAscending}
318
- disabled={primary === null}
319
- onClick={onFlip}
320
- >
321
- {desc ? <ArrowDownWideNarrow /> : <ArrowUpNarrowWide />}
322
- </IconButton>
323
- </div>
324
- );
325
- }
326
-
327
- // ---------- Phone card ----------
328
-
329
- /** One phone card: what DataTable's card list draws for a row. */
330
- export interface MobileRowProps<T> {
331
- row: T;
332
- isExpanded: DataTableProps<T>["isExpanded"];
333
- /** What the row shows when opened — DataTable's `openedRowContent`. */
334
- openedRowContent: (row: T) => ReactNode;
335
- /** Whether an opened row has anything to show at all. */
336
- opensRow: boolean;
337
- rowClassName: DataTableProps<T>["rowClassName"];
338
- rowAttributes: DataTableProps<T>["rowAttributes"];
339
- onRowClick: DataTableProps<T>["onRowClick"];
340
- rowHref: DataTableProps<T>["rowHref"];
341
- /** `mobileSwipeActions`, or the swipes derived from `rowActions`. */
342
- swipeActionsFor: ((row: T) => MobileSwipeActions | null) | undefined;
343
- /** The card list's reading direction, for which physical drag a swipe side means. */
344
- mobileDir: Direction;
345
- /** No column the card renders owns a link, so the card itself may be one. */
346
- mobileCardLinkable: boolean;
347
- /**
348
- * The card draws cells that keep their own clicks, so it cannot be one control (0.50
349
- * K4): its TITLE is the row's link (`rowHref`) or button (`onRowClick`), stretched over
350
- * the card, and those cells sit above it. Only for the default card, never a
351
- * `mobileCard`, and never when the title itself keeps its own clicks.
352
- */
353
- mobileCardStretched: boolean;
354
- mobileCard: DataTableProps<T>["mobileCard"];
355
- mobilePrimaryCol: DataTableColumn<T> | null;
356
- /** The `role: "status"` columns, drawn as chips beside the title (0.49 KIT-7). */
357
- mobileStatusColumns: DataTableColumn<T>[];
358
- mobileSecondaryColumns: DataTableColumn<T>[];
359
- /** The `role: "actions"` columns, drawn after the fields at the card's end (0.49 KIT-7). */
360
- mobileActionColumns: DataTableColumn<T>[];
361
- mobileExpandAsDialog: boolean;
362
- compact: boolean;
363
- /** The text size is Large or Extra large (§4). */
364
- large: boolean;
365
- /** Phone selection (0.40, keksdose live #421); null without DataTable's `selection`. */
366
- select: MobileRowSelect | null;
367
- /** The card's place in the list, on its `<li>` — where the table finds the card that
368
- * takes the focus when a focused one leaves (refactor audit 2026-10-30, DSP-4). */
369
- rowIndex?: number;
370
- /** The focus came into the card, or into anything it opened (a portal included). */
371
- onFocusIn?: () => void;
372
- }
373
-
374
- /** A phone card's part in DataTable's `selection` (0.40, keksdose live #421). */
375
- export interface MobileRowSelect {
376
- /** The card list is in selection mode: the card shows its box and a tap toggles it. */
377
- active: boolean;
378
- /** `selection.isSelectable` for this row. */
379
- selectable: boolean;
380
- selected: boolean;
381
- onToggle: (checked: boolean) => void;
382
- /** A long press outside the mode: enter it with this row selected. */
383
- onLongPress: () => void;
384
- }
385
-
386
- /** One mobile card. Shared by the flat list and the grouped list, so both draw
387
- * identical row markup. */
388
- export function MobileRow<T>({
389
- row,
390
- isExpanded,
391
- openedRowContent,
392
- opensRow,
393
- rowClassName,
394
- rowAttributes,
395
- onRowClick,
396
- rowHref,
397
- swipeActionsFor,
398
- mobileDir,
399
- mobileCardLinkable,
400
- mobileCardStretched,
401
- mobileCard,
402
- mobilePrimaryCol,
403
- mobileStatusColumns,
404
- mobileSecondaryColumns,
405
- mobileActionColumns,
406
- mobileExpandAsDialog,
407
- compact,
408
- large,
409
- select,
410
- rowIndex,
411
- onFocusIn,
412
- }: MobileRowProps<T>) {
413
- const expanded = isExpanded?.(row) ?? false;
414
- // Selection mode (#421): the whole card is the row's checkbox — a tap toggles it
415
- // instead of opening the row, and the swipe and the long press stand down, so no
416
- // gesture means two things at once (keksdose's register list, its feedback #153).
417
- const selecting = !!select?.active;
418
- // Not on an open card, as in kk's register: its editor and a selection are two modes
419
- // fighting over the same row.
420
- const pressable = !!select && select.selectable && !selecting && !expanded;
421
- const longPress = useLongPress(() => select?.onLongPress(), { enabled: pressable });
422
- const expansion = expanded ? openedRowContent(row) : null;
423
- const tint = rowClassName?.(row);
424
- // Use div+role="button" rather than a real <button> so cells that
425
- // contain their own interactive controls (status toggles, action
426
- // icons) don't end up as illegal nested buttons.
427
- const interactive = !!onRowClick;
428
- // `rowHref` without `onRowClick` (refactor audit 2026-10-30, DSP-23 b): nothing opens the
429
- // row in place, so the card — or its stretched title — is a link that is followed. It
430
- // was neither, and the phone had no way into the row at all.
431
- const rowUrl = rowHref?.(row);
432
- const linkOnly = !interactive && rowUrl !== undefined;
433
- // Say that the card OPENS something (Keksdose feedback #163). A phone card had
434
- // only a cursor and a tap-tint to advertise its editor — on the invoice review
435
- // screen, whose entire purpose is correcting mis-read lines, that read as "these
436
- // values are not editable". A chevron is the affordance every list on a phone
437
- // uses for exactly this, and it points the way the row actually opens: right into
438
- // a sheet, or down into an inline panel that flips it when expanded.
439
- const opensDetail = interactive && opensRow;
440
- const swipeActions = swipeActionsFor?.(row);
441
- const swipe = swipeActions ? physicalSwipe(swipeActions, mobileDir) : null;
442
- // An OPEN card swipes too (0.40, keksdose live #416, Marcel: "faster than scrolling
443
- // down to the buttons"). Only the card's header — the summary line — is inside
444
- // SwipeableRow; the opened body below is a sibling, so its fields, sliders and
445
- // scrolling never meet the gesture. Until 0.40 an open card silently lost it.
446
- // In selection mode a sideways drag would be a second meaning for a selecting touch.
447
- const swipeEnabled = !!swipe && !selecting;
448
- // The stretched card (0.50 K4): the title is the control, the card a plain box.
449
- // Selection mode keeps its own card (the whole card is the toggle there).
450
- const stretched = mobileCardStretched && (interactive || linkOnly) && !selecting && !mobileCard && !!mobilePrimaryCol;
451
- const href = mobileCardLinkable || stretched ? rowUrl : undefined;
452
- // The card opens something on a tap: the row (`onRowClick`), or its link.
453
- const opens = interactive || (linkOnly && href !== undefined);
454
- const cardClass = cn(
455
- "w-full text-start flex items-center",
456
- // §4: more room per row at Large — a taller row is a bigger tap target too.
457
- compact ? "px-3 py-2 gap-2 text-sm large:py-3" : "px-4 py-3 gap-3 large:py-4",
458
- // Live #320's other half: the card acknowledges the touch before the sheet
459
- // arrives. On a cold route the data can take a beat, and an unacknowledged tap
460
- // reads as "did that register?" — which is most of what "abrupt" means here.
461
- // `origin-center` + a 0.5% squeeze is deliberately almost subliminal: this fires
462
- // on every row of a long list, so anything larger becomes the list's personality
463
- // rather than feedback. `transition-transform` alone, so the existing background
464
- // flip stays instant.
465
- opens &&
466
- "cursor-pointer transition-transform duration-100 active:scale-[0.995] active:bg-active motion-reduce:transition-none motion-reduce:active:scale-100",
467
- // The keyboard's frame drawn INSIDE the card, as the desktop row's is (keksdose live
468
- // #394): the browser's own ring and tap highlight sit on the card's outer edge, and
469
- // three ancestors clip it — SwipeableRow's `overflow-hidden`, the card list, and
470
- // AppShell main's `overflow-x-clip` — so the frame went missing on one side. A press
471
- // keeps the fill and the squeeze above; an outline on every tap of a long list would
472
- // be louder than the acknowledgement it replaces.
473
- (opens || selecting) &&
474
- !stretched &&
475
- cn("[-webkit-tap-highlight-color:transparent] focus-visible:ring-inset", FOCUS_RING),
476
- // A stretched card (0.50 K4) draws the same frame, inside the card, while its TITLE
477
- // control has the keyboard: the ring outlines the whole tap target, as on every other
478
- // card, not the title's line alone. The own-click cells draw their own.
479
- stretched && cn("relative [-webkit-tap-highlight-color:transparent]", STRETCHED_RING),
480
- selecting && "cursor-pointer",
481
- select?.selected && selecting && "bg-brand-soft",
482
- // A 500 ms hold would otherwise start a text selection, and iOS's link callout —
483
- // which only yields to this CSS. The trade kk made on its register: the card's text
484
- // is not selectable on a phone where the table offers selection.
485
- select && "select-none [-webkit-touch-callout:none]",
486
- );
487
- // The card's content is written once and worn by either tag below. Note that
488
- // the primary cell is rendered RAW here, never through `linkColumn` — the card
489
- // itself is the anchor, and routing it through `linkColumn` too would nest an
490
- // <a> inside an <a>. Desktop and mobile are mutually exclusive (`isMdUp`), so
491
- // exactly one anchor exists per row.
492
- // The default card's filled fields and status values (0.49 KIT-7): an empty one
493
- // gets no line. Not computed for a `mobileCard`, which draws its own.
494
- // The stretched card's title control (0.50 K4): the row's link, or a real <button>
495
- // when the table only has `onRowClick` — a plain control, since nothing interactive
496
- // sits inside it any more. Its `::after` covers the whole card, so a tap anywhere
497
- // but on an own-click cell (lifted above it) lands on it, and a long press on the
498
- // card is a long press on the link ("open in new tab"). Its accessible name is the
499
- // title alone; the fields read as text after it. `data-row-link` marks it as the
500
- // row itself for `fromOwnControl`, so the long press still arms on it.
501
- const stretchedClass =
502
- "block w-full text-start focus-visible:outline-none after:absolute after:inset-0 after:z-[1] after:content-['']";
503
- const titleControl = (title: ReactNode) =>
504
- href ? (
505
- <RowLink
506
- href={href}
507
- data-card-link=""
508
- data-row-link=""
509
- navigate={linkOnly}
510
- onActivate={interactive ? () => onRowClick!(row) : undefined}
511
- aria-expanded={opensRow && interactive ? expanded : undefined}
512
- className={stretchedClass}
513
- // Enter follows RowLink's own onClick; Space opens the row too, not the list's scroll.
514
- onKeyDown={
515
- interactive
516
- ? (e) => {
517
- if (e.key !== " ") return;
518
- e.preventDefault();
519
- onRowClick!(row);
520
- }
521
- : undefined
522
- }
523
- >
524
- {title}
525
- </RowLink>
526
- ) : (
527
- <button
528
- type="button"
529
- data-card-link=""
530
- data-row-link=""
531
- aria-expanded={opensRow ? expanded : undefined}
532
- onClick={() => onRowClick!(row)}
533
- className={cn(stretchedClass, "font-[inherit]")}
534
- >
535
- {title}
536
- </button>
537
- );
538
- const fields = mobileCard ? [] : filledFields(mobileSecondaryColumns, row);
539
- const statuses = mobileCard ? [] : filledFields(mobileStatusColumns, row);
540
- const actionCells = mobileCard ? [] : filledFields(mobileActionColumns, row);
541
- const cardInner = (
542
- <>
543
- {/* The card's own content keeps the column it always had; the chevron sits
544
- beside it rather than inside, so a caller's `mobileCard` is untouched. */}
545
- <div
546
- className={cn(
547
- "flex min-w-0 flex-1 flex-col",
548
- compact ? "gap-1 large:gap-2" : "gap-2 large:gap-3",
549
- // §4 "nothing truncates": at Large a cell's own `truncate` wraps instead —
550
- // the reader who chose bigger type is the one an ellipsis fails. A caller's
551
- // `mobileCard` is its own layout and keeps what it chose.
552
- !mobileCard && CARD_NO_TRUNCATE,
553
- )}
554
- >
555
- {mobileCard ? (
556
- mobileCard(row)
557
- ) : (
558
- <>
559
- {/* Only the `data-*` half of `cellProps` — see its note. The title row
560
- carries the status chips at its end (0.49 KIT-7). */}
561
- {(mobilePrimaryCol || statuses.length > 0) && (
562
- <div data-card-head="" className="flex min-w-0 items-start justify-between gap-2">
563
- {mobilePrimaryCol && (
564
- <div
565
- {...dataAttrsOf(mobilePrimaryCol.cellProps?.(row))}
566
- data-card-title=""
567
- className="min-w-0 flex-1 break-words font-medium"
568
- >
569
- {stretched ? titleControl(mobilePrimaryCol.cell(row)) : mobilePrimaryCol.cell(row)}
570
- </div>
571
- )}
572
- {statuses.length > 0 && (
573
- <div className="flex shrink-0 flex-wrap items-center justify-end gap-1">
574
- {statuses.map(({ col, value }) => (
575
- <span
576
- key={col.key}
577
- {...dataAttrsOf(col.cellProps?.(row))}
578
- data-card-status=""
579
- className="inline-flex"
580
- >
581
- <StatusChip value={value} />
582
- </span>
583
- ))}
584
- </div>
585
- )}
586
- </div>
587
- )}
588
- {fields.length > 0 && large ? (
589
- // §4: each label ABOVE its value at Large. Side by side, a label column
590
- // sized to the widest label leaves a value a sliver of a 240 px card.
591
- <StackedFields columns={mobileSecondaryColumns} row={row} compact={compact} liftOwnClicks={stretched} />
592
- ) : (
593
- fields.length > 0 && (
594
- <dl
595
- className={cn("grid grid-cols-[auto_1fr] gap-x-3", compact ? "gap-y-0.5 text-xs" : "gap-y-1 text-sm")}
596
- >
597
- {fields.map(({ col, value }) => (
598
- <Fragment key={col.key}>
599
- <dt className="text-xs uppercase tracking-wide text-muted self-center">{col.header}</dt>
600
- <dd
601
- {...dataAttrsOf(col.cellProps?.(row))}
602
- className={cn(
603
- "min-w-0 text-secondary self-center",
604
- stretched && keepsOwnClicks(col) && LIFTED_FIELD,
605
- )}
606
- >
607
- {value}
608
- </dd>
609
- </Fragment>
610
- ))}
611
- </dl>
612
- )
613
- )}
614
- {/* The row's actions after the fields, at the card's end (0.49 KIT-7): its
615
- buttons keep their own clicks, and RowActions folds into "⋯" at Large. */}
616
- {actionCells.length > 0 && (
617
- <div data-card-actions="" className="flex min-w-0 flex-wrap items-center justify-end gap-2">
618
- {actionCells.map(({ col, value }) => (
619
- <div
620
- key={col.key}
621
- {...dataAttrsOf(col.cellProps?.(row))}
622
- className={cn("flex min-w-0 justify-end", stretched && LIFTED_CELL)}
623
- >
624
- {value}
625
- </div>
626
- ))}
627
- </div>
628
- )}
629
- </>
630
- )}
631
- </div>
632
- {opensDetail &&
633
- !selecting &&
634
- // `aria-hidden`: the row already announces itself as a button with
635
- // `aria-expanded`, so the icon would only add a second, wordless stop.
636
- (mobileExpandAsDialog ? (
637
- // Points INTO the sheet, which is the reading-forward direction.
638
- <ChevronRight aria-hidden className="size-4 shrink-0 text-placeholder rtl:-scale-x-100" />
639
- ) : (
640
- <ChevronDown
641
- aria-hidden
642
- className={cn("size-4 shrink-0 text-placeholder transition-transform", expanded && "rotate-180")}
643
- />
644
- ))}
645
- </>
646
- );
647
- // The long press rides the card itself; the click it leaves behind is swallowed on
648
- // the `<li>` below, in the capture phase, before the card (or RowLink) sees it.
649
- // Attached whenever the table selects, the hook's `enabled` deciding whether a press
650
- // arms: its pointerdown also clears a stale swallow latch, which must happen in the
651
- // mode too, or a long press whose click never came would eat the first toggle.
652
- //
653
- // A press that starts on a control in a cell (or in a portal one opened) is that
654
- // control's, not a way into the mode (keksdose live #428): it only clears the latch.
655
- const press = select
656
- ? {
657
- ...longPress.handlers,
658
- onPointerDown: (e: ReactPointerEvent<HTMLElement>) => {
659
- if (fromOutside(e.target, e.currentTarget) || fromOwnControl(e.target, e.currentTarget)) {
660
- longPress.consumeClick();
661
- return;
662
- }
663
- longPress.handlers.onPointerDown(e);
664
- },
665
- onContextMenu: (e: ReactMouseEvent<HTMLElement>) => {
666
- if (fromOutside(e.target, e.currentTarget) || fromOwnControl(e.target, e.currentTarget)) return;
667
- longPress.handlers.onContextMenu(e);
668
- },
669
- }
670
- : undefined;
671
- const body = selecting ? (
672
- // In the mode a card is a toggle — kk's register row: `role="button"` with
673
- // `aria-pressed`, the box beside it only a picture of that state (the card is the
674
- // one control, so the two can never fight or fire twice).
675
- /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- role="button",
676
- tabIndex 0 and Enter/Space are all here; the rule cannot follow the conditional role. */
677
- <div
678
- role={select!.selectable ? "button" : undefined}
679
- tabIndex={select!.selectable ? 0 : undefined}
680
- aria-pressed={select!.selectable ? select!.selected : undefined}
681
- aria-disabled={select!.selectable ? undefined : true}
682
- data-card-selecting=""
683
- {...press}
684
- onClick={(e) => {
685
- if (!select!.selectable || fromOutside(e.target, e.currentTarget)) return;
686
- // A tap on a control in a cell is that control's (keksdose live #428).
687
- if (fromOwnControl(e.target, e.currentTarget)) return;
688
- select!.onToggle(!select!.selected);
689
- }}
690
- onKeyDown={(e) => {
691
- if (!select!.selectable || e.target !== e.currentTarget) return;
692
- if (e.key === "Enter" || e.key === " ") {
693
- e.preventDefault();
694
- select!.onToggle(!select!.selected);
695
- }
696
- }}
697
- className={cardClass}
698
- >
699
- <Checkbox
700
- readOnly
701
- tabIndex={-1}
702
- aria-hidden
703
- checked={select!.selected}
704
- disabled={!select!.selectable}
705
- className="pointer-events-none shrink-0 self-center"
706
- inputClassName="pointer-events-none"
707
- />
708
- {cardInner}
709
- </div>
710
- ) : stretched ? (
711
- // The stretched card (0.50 K4): a plain box; its title is the control (above).
712
- <div {...press} data-card-stretched="" className={cardClass}>
713
- {cardInner}
714
- </div>
715
- ) : opens && href ? (
716
- // A card with a URL of its own is a LINK, not a div wearing role="button"
717
- // (feedback #451): the phone's long-press "open in new tab" and a screen
718
- // reader's link semantics both come free, and the fake role goes away.
719
- <RowLink
720
- href={href}
721
- {...press}
722
- navigate={linkOnly}
723
- onActivate={interactive ? () => onRowClick!(row) : undefined}
724
- aria-expanded={opensRow && interactive ? expanded : undefined}
725
- className={cardClass}
726
- // Enter already activates a link natively, and that routes through
727
- // RowLink's own onClick — handling it here too would open the row twice.
728
- // Space is the only key left, and only so it opens the row instead of
729
- // scrolling the list.
730
- onKeyDown={
731
- interactive
732
- ? (e) => {
733
- if (e.key !== " ") return;
734
- e.preventDefault();
735
- onRowClick!(row);
736
- }
737
- : undefined
738
- }
739
- >
740
- {cardInner}
741
- </RowLink>
742
- ) : (
743
- /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- the handlers are
744
- only attached when `interactive`, and then so are role="button", tabIndex 0 and the
745
- Enter/Space handler below; the rule cannot follow the conditional role. */
746
- <div
747
- {...press}
748
- role={interactive ? "button" : undefined}
749
- tabIndex={interactive ? 0 : undefined}
750
- onClick={
751
- interactive
752
- ? (e) => {
753
- if (fromOutside(e.target, e.currentTarget)) return;
754
- // A tap on a control in a cell (a select, a button) is that
755
- // control's, as on the desktop `<tr>` (keksdose live #428).
756
- if (fromOwnControl(e.target, e.currentTarget)) return;
757
- onRowClick!(row);
758
- }
759
- : undefined
760
- }
761
- onKeyDown={
762
- interactive
763
- ? (e) => {
764
- // A key press inside a nested control is that control's.
765
- if (e.target !== e.currentTarget) return;
766
- if (e.key === "Enter" || e.key === " ") {
767
- e.preventDefault();
768
- onRowClick!(row);
769
- }
770
- }
771
- : undefined
772
- }
773
- aria-expanded={opensRow ? expanded : undefined}
774
- className={cardClass}
775
- >
776
- {cardInner}
777
- </div>
778
- );
779
- return (
780
- /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- `onFocus`
781
- only notes that the focus is in this card (DSP-4); the card inside is the control. */
782
- <li
783
- className={cn(tint)}
784
- {...cleanAttrs(rowAttributes?.(row))}
785
- data-row-index={rowIndex}
786
- onFocus={onFocusIn}
787
- data-selected={selecting && select!.selected ? "" : undefined}
788
- onClickCapture={
789
- select
790
- ? (e) => {
791
- if (!longPress.consumeClick()) return;
792
- e.preventDefault();
793
- e.stopPropagation();
794
- }
795
- : undefined
796
- }
797
- >
798
- {swipeEnabled ? (
799
- <SwipeableRow enabled left={swipe!.left} right={swipe!.right}>
800
- {body}
801
- </SwipeableRow>
802
- ) : (
803
- body
804
- )}
805
- {expansion && !mobileExpandAsDialog && (
806
- <div className={cn("bg-surface-2 border-t border-subtle", compact ? "px-3 py-2" : "px-4 py-3")}>
807
- {expansion}
808
- </div>
809
- )}
810
- </li>
811
- );
812
- }
813
-
814
- // ---------- The card list's leading row (0.51.7, audit 2026-10-30 DSP-2) ----------
815
-
816
- /**
817
- * DataTable's `leadingRow` on a phone: the card list's first item. It was desktop-only
818
- * and the card list dropped it without a word — kastlan's inline "Add meter" editor
819
- * never appeared (the page's button disabled itself, its only create path gone), and
820
- * keksdose kept an `md:hidden` copy of its hidden-accounts toggle above the table.
821
- *
822
- * A plain `<li>`: the list is a `<ul>`, and axe's `list` rule fails an `<li
823
- * role="none">` in one (checked against axe 4.13), so it is one more item, as the
824
- * section headers, the reveal sentinel and the load-more footer are. It is no card — no
825
- * row link, swipe or long press, no toggle in selection mode, no `rowAttributes` — and
826
- * selection, the reveal and the row count work on the rows, not on the list's items.
827
- *
828
- * `sticky`: pinned at the top of whatever scrolls the list (the list itself under a
829
- * bounded `fillHeight`, else the page), opaque over the cards passing beneath it, and
830
- * over the section headers (`z-10`) — which stand under it, at the height `onHeight`
831
- * reports, measured and followed (an inline editor wraps at Large text). The desktop
832
- * stacks the same way: its leading row over the group bands.
833
- */
834
- export function MobileLeadingRow({
835
- sticky,
836
- onHeight,
837
- children,
838
- }: {
839
- sticky: boolean;
840
- /** Under `sticky`: the row's height, and each change of it. */
841
- onHeight?: (height: number) => void;
842
- children: ReactNode;
843
- }) {
844
- const ref = useRef<HTMLLIElement>(null);
845
- useLayoutEffect(() => {
846
- const el = ref.current;
847
- if (!sticky || !onHeight || !el) return;
848
- const measure = () => onHeight(el.getBoundingClientRect().height);
849
- measure();
850
- if (typeof ResizeObserver === "undefined") return;
851
- const observer = new ResizeObserver(measure);
852
- observer.observe(el);
853
- return () => observer.disconnect();
854
- }, [sticky, onHeight]);
855
- return (
856
- <li ref={ref} data-leading-row={sticky ? "sticky" : ""} className={cn(sticky && "sticky top-0 z-[11] bg-surface")}>
857
- {children}
858
- </li>
859
- );
860
- }
861
-
862
- // ---------- Phone selection bar (0.40, keksdose live #421) ----------
863
-
864
- /**
865
- * The card list's way into and out of selection mode for a keyboard and a screen
866
- * reader — the long press is a touch gesture, and a gesture alone is no path at all.
867
- *
868
- * Outside the mode it is one button, "Select rows", visually hidden until it takes
869
- * focus (SwipeableRow's keyboard buttons, the same rule): the card list keeps the look
870
- * kk's register has, where the long press is the way in, and Tab or a screen reader's
871
- * cursor still finds it. In the mode it is a visible row: "Select all rows" — the
872
- * desktop header's box — and Cancel, which clears the selection and leaves, as the
873
- * bulk bar's Clear does. The owner's bulk bar is the owner's, as on desktop: it
874
- * appears because the selection is no longer empty.
875
- */
876
- export function MobileSelectBar({
877
- active,
878
- allSelected,
879
- someSelected,
880
- compact,
881
- labels,
882
- onEnter,
883
- onToggleAll,
884
- onCancel,
885
- }: {
886
- active: boolean;
887
- allSelected: boolean;
888
- someSelected: boolean;
889
- compact: boolean;
890
- labels: DataTableLabels;
891
- onEnter: () => void;
892
- onToggleAll: (checked: boolean) => void;
893
- onCancel: () => void;
894
- }) {
895
- // The control that was pressed unmounts with the mode it switched, so focus is
896
- // handed across: into the bar's box on the way in, back to the button on the way out.
897
- const handOff = useRef(false);
898
- const allBox = useRef<HTMLInputElement>(null);
899
- const enterButton = useRef<HTMLButtonElement>(null);
900
- useEffect(() => {
901
- if (!handOff.current) return;
902
- handOff.current = false;
903
- (active ? allBox.current : enterButton.current)?.focus();
904
- }, [active]);
905
- if (!active)
906
- return (
907
- <button
908
- ref={enterButton}
909
- type="button"
910
- data-table-mobile-select=""
911
- onClick={() => {
912
- handOff.current = true;
913
- onEnter();
914
- }}
915
- className={cn(
916
- "sr-only rounded-md text-sm font-medium text-secondary",
917
- // Padding only once shown: unconditional, it beats sr-only's `padding: 0`
918
- // (SwipeableRow's 0.32.1 note).
919
- "focus:not-sr-only focus:m-2 focus:inline-flex focus:px-2 focus:py-1",
920
- FOCUS_RING,
921
- )}
922
- >
923
- {labels.selectRows}
924
- </button>
925
- );
926
- return (
927
- <div
928
- data-table-mobile-select=""
929
- className={cn(
930
- "flex flex-wrap items-center justify-between gap-2 border-b border-subtle",
931
- compact ? "px-3 py-1.5" : "px-4 py-2",
932
- )}
933
- >
934
- <Checkbox
935
- ref={allBox}
936
- label={labels.selectAllRows}
937
- checked={allSelected}
938
- indeterminate={someSelected && !allSelected}
939
- onChange={(e) => onToggleAll(e.target.checked)}
940
- />
941
- <button
942
- type="button"
943
- onClick={() => {
944
- handOff.current = true;
945
- onCancel();
946
- }}
947
- className={cn("rounded-md px-2 py-1 text-sm text-muted hover:text-secondary", FOCUS_RING)}
948
- >
949
- {labels.cancelSelection}
950
- </button>
951
- </div>
952
- );
953
- }
954
-
955
- // ---------- Mobile filter sheet ----------
956
-
957
- /**
958
- * Filter access for the mobile card list (feedback #299). The desktop table
959
- * houses per-column filters in its header; the card list has no header, so the
960
- * same filters were unreachable on phones. This renders a compact "Filters" bar
961
- * that opens a bottom sheet listing each filterable column as a collapsible
962
- * section, reusing the very same {@link FilterPopover} and filter state the
963
- * desktop table drives — so it filters identically and stays URL-synced.
964
- *
965
- * The sheet is a {@link DialogFrame} (round 0.34 BUG-7b = DUP-6). It was the last
966
- * hand-rolled modal in the kit: `role="dialog" aria-modal="true"` on the backdrop, with
967
- * copies of the scroll lock, the backdrop close and the Back entry — and no name, no
968
- * focus moved in, no trap and no Escape, so a reader was told the page was hidden while
969
- * focus stayed on it. On a phone `Modal` is a bottom sheet already; it now brings all
970
- * of that, and the exit motion, from the one place every other dialog gets it.
971
- */
972
- export function MobileFilters<T>({
973
- columns,
974
- filters,
975
- selectOptionsByKey,
976
- isServer,
977
- hasFilterCallback,
978
- onSetFilter,
979
- onClearFilter,
980
- onClearAll,
981
- labels,
982
- locale,
983
- }: {
984
- columns: readonly DataTableColumn<T>[];
985
- filters: FilterState;
986
- selectOptionsByKey: Record<string, { value: string; label: string }[]>;
987
- isServer: boolean;
988
- hasFilterCallback: boolean;
989
- onSetFilter: (key: string, value: FilterValue) => void;
990
- onClearFilter: (key: string) => void;
991
- onClearAll: () => void;
992
- labels: DataTableLabels;
993
- locale?: string;
994
- }) {
995
- const [open, setOpen] = useState(false);
996
- // The sheet is portalled to <body>, out from under whatever `dir` the table sits
997
- // in, so it carries the trigger's direction with it — read at the moment of opening,
998
- // from the element the user actually pressed.
999
- const [dir, setDir] = useState<Direction>("ltr");
1000
- const [expanded, setExpanded] = useState<string | null>(null);
1001
-
1002
- const filterable = useMemo(
1003
- () =>
1004
- columns
1005
- .map((col) => ({ col, filter: !isServer || hasFilterCallback ? resolveFilter(col) : null }))
1006
- .filter((x): x is { col: DataTableColumn<T>; filter: ColumnFilter<T> } => !!x.filter),
1007
- [columns, isServer, hasFilterCallback],
1008
- );
1009
-
1010
- if (filterable.length === 0) return null;
1011
-
1012
- const activeCount = filterable.filter(({ col }) => {
1013
- const s = filters[col.key];
1014
- return s && isFilterActive(s);
1015
- }).length;
1016
-
1017
- return (
1018
- <div className="flex items-center justify-between gap-2 border-b border-subtle px-4 py-2">
1019
- <button
1020
- type="button"
1021
- onClick={(e) => {
1022
- setDir(dirOf(e.currentTarget));
1023
- setOpen(true);
1024
- }}
1025
- className="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium text-secondary hover:bg-hover"
1026
- >
1027
- <Filter className="size-4" />
1028
- <span>{labels.filters}</span>
1029
- {activeCount > 0 && (
1030
- <span className="inline-flex min-w-4 items-center justify-center rounded-full bg-info-soft px-1 text-caption font-semibold text-info">
1031
- {activeCount}
1032
- </span>
1033
- )}
1034
- </button>
1035
- {activeCount > 0 && (
1036
- <button type="button" onClick={onClearAll} className="text-xs text-muted hover:text-secondary">
1037
- {labels.clearAll}
1038
- </button>
1039
- )}
1040
- {/* Escape, the backdrop, Back (Keksdose feedback #172: the filters are already
1041
- in the URL, so closing never loses anything), the scroll lock, focus in and
1042
- back to this bar's button — all `Modal`'s. `fullBleed` lets the sheet meet the
1043
- screen's edges on a phone, as it always did. */}
1044
- <DialogFrame
1045
- open={open}
1046
- onClose={() => setOpen(false)}
1047
- title={labels.filters}
1048
- closeButton
1049
- closeLabel={labels.close}
1050
- headerDivider
1051
- fullBleed
1052
- // Portalled to <body>, so the direction read from the button goes with it.
1053
- dir={dir}
1054
- className="max-h-[85vh] rounded-b-none md:rounded-b-lg"
1055
- // The sections run edge to edge with a rule between them, as before.
1056
- bodyClassName="space-y-0 divide-y divide-subtle px-0 pt-0 pb-0"
1057
- actions={(close) => (
1058
- <>
1059
- <button
1060
- type="button"
1061
- onClick={onClearAll}
1062
- disabled={activeCount === 0}
1063
- // `me-auto` keeps Clear all at the start and Done at the end.
1064
- className="me-auto text-sm text-muted hover:text-secondary disabled:opacity-40"
1065
- >
1066
- {labels.clearAll}
1067
- </button>
1068
- <button
1069
- type="button"
1070
- onClick={close}
1071
- className="rounded-md bg-inverse px-4 py-2 text-sm font-medium text-inverse"
1072
- >
1073
- {labels.done}
1074
- </button>
1075
- </>
1076
- )}
1077
- >
1078
- {filterable.map(({ col, filter }) => {
1079
- const state = filters[col.key];
1080
- const active = !!state && isFilterActive(state);
1081
- const isItemOpen = expanded === col.key;
1082
- return (
1083
- <div key={col.key}>
1084
- <button
1085
- type="button"
1086
- onClick={() => setExpanded(isItemOpen ? null : col.key)}
1087
- aria-expanded={isItemOpen}
1088
- className="flex w-full items-center justify-between gap-2 px-4 py-3 text-start"
1089
- >
1090
- <span className="flex items-center gap-2 text-sm font-medium text-secondary">
1091
- {col.header}
1092
- {active && <span className="size-1.5 rounded-full bg-brand" />}
1093
- </span>
1094
- <ChevronDown
1095
- className={cn("size-4 shrink-0 text-placeholder transition-transform", isItemOpen && "rotate-180")}
1096
- />
1097
- </button>
1098
- {isItemOpen && (
1099
- <div className="px-4 pb-3">
1100
- <FilterPopover
1101
- column={col}
1102
- state={state ?? defaultFilterState(filter)}
1103
- onChange={(next) => onSetFilter(col.key, next)}
1104
- onClear={() => onClearFilter(col.key)}
1105
- selectOptions={selectOptionsByKey[col.key] ?? []}
1106
- labels={labels}
1107
- locale={locale}
1108
- />
1109
- </div>
1110
- )}
1111
- </div>
1112
- );
1113
- })}
1114
- </DialogFrame>
1115
- </div>
1116
- );
1117
- }