@eifi1/ui-kit 0.31.0 → 0.32.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 (565) hide show
  1. package/README.md +53 -30
  2. package/dist/account/delete-account-setting.d.ts +8 -3
  3. package/dist/admin/admin-action-confirm.d.ts +8 -3
  4. package/dist/admin/admin-parts.d.ts +8 -1
  5. package/dist/admin/admin-parts.js +1 -1
  6. package/dist/admin/admin-parts.js.map +1 -1
  7. package/dist/admin/invitations-panel.d.ts +8 -3
  8. package/dist/admin/invitations-panel.js +29 -26
  9. package/dist/admin/invitations-panel.js.map +1 -1
  10. package/dist/admin/reviewer-scope-editor.d.ts +8 -3
  11. package/dist/admin/role-select.d.ts +10 -5
  12. package/dist/admin/transfer-ownership-dialog.d.ts +8 -3
  13. package/dist/admin/user-roster.d.ts +10 -5
  14. package/dist/admin/user-roster.js +8 -47
  15. package/dist/admin/user-roster.js.map +1 -1
  16. package/dist/auth/auth-errors.d.ts +43 -8
  17. package/dist/auth/auth-errors.js +24 -1
  18. package/dist/auth/auth-errors.js.map +1 -1
  19. package/dist/auth/register-form.d.ts +8 -3
  20. package/dist/auth/reset-password-form.d.ts +1 -1
  21. package/dist/auth/reset-password-form.js +2 -1
  22. package/dist/auth/reset-password-form.js.map +1 -1
  23. package/dist/auth/verify-email.d.ts +4 -0
  24. package/dist/auth/verify-email.js +3 -2
  25. package/dist/auth/verify-email.js.map +1 -1
  26. package/dist/billing/billing-banner.d.ts +184 -0
  27. package/dist/billing/billing-banner.js +98 -0
  28. package/dist/billing/billing-banner.js.map +1 -0
  29. package/dist/billing/billing-labels.d.ts +100 -0
  30. package/dist/billing/billing-labels.js +74 -0
  31. package/dist/billing/billing-labels.js.map +1 -0
  32. package/dist/billing/billing-lock.d.ts +158 -0
  33. package/dist/billing/billing-lock.js +17 -0
  34. package/dist/billing/billing-lock.js.map +1 -0
  35. package/dist/billing/billing-standing.d.ts +149 -0
  36. package/dist/billing/billing-standing.js +28 -0
  37. package/dist/billing/billing-standing.js.map +1 -0
  38. package/dist/billing/plan-card.d.ts +248 -0
  39. package/dist/billing/plan-card.js +248 -0
  40. package/dist/billing/plan-card.js.map +1 -0
  41. package/dist/billing/plan-limit-notice.d.ts +156 -0
  42. package/dist/billing/plan-limit-notice.js +76 -0
  43. package/dist/billing/plan-limit-notice.js.map +1 -0
  44. package/dist/billing/plan-limit.d.ts +34 -0
  45. package/dist/billing/plan-limit.js +20 -0
  46. package/dist/billing/plan-limit.js.map +1 -0
  47. package/dist/billing/plan-price.d.ts +61 -0
  48. package/dist/billing/plan-price.js +45 -0
  49. package/dist/billing/plan-price.js.map +1 -0
  50. package/dist/billing/subscription-actions.d.ts +133 -0
  51. package/dist/billing/subscription-actions.js +28 -0
  52. package/dist/billing/subscription-actions.js.map +1 -0
  53. package/dist/billing/subscription-status-chip.d.ts +149 -0
  54. package/dist/billing/subscription-status-chip.js +48 -0
  55. package/dist/billing/subscription-status-chip.js.map +1 -0
  56. package/dist/chart.d.ts +9 -3
  57. package/dist/components/account-chips.d.ts +16 -4
  58. package/dist/components/account-chips.js +21 -3
  59. package/dist/components/account-chips.js.map +1 -1
  60. package/dist/components/alert-banner.d.ts +6 -1
  61. package/dist/components/alert-banner.js +4 -3
  62. package/dist/components/alert-banner.js.map +1 -1
  63. package/dist/components/amount-input.d.ts +8 -3
  64. package/dist/components/amount-input.js +5 -5
  65. package/dist/components/amount-input.js.map +1 -1
  66. package/dist/components/appearance-labels.d.ts +32 -0
  67. package/dist/components/appearance-labels.js +18 -0
  68. package/dist/components/appearance-labels.js.map +1 -0
  69. package/dist/components/autocomplete.js +2 -2
  70. package/dist/components/autocomplete.js.map +1 -1
  71. package/dist/components/breadcrumbs.js +3 -2
  72. package/dist/components/breadcrumbs.js.map +1 -1
  73. package/dist/components/bulk-action-bar.d.ts +2 -2
  74. package/dist/components/bulk-action-bar.js +7 -12
  75. package/dist/components/bulk-action-bar.js.map +1 -1
  76. package/dist/components/button-group.d.ts +8 -3
  77. package/dist/components/calculator.d.ts +8 -3
  78. package/dist/components/calculator.js +4 -3
  79. package/dist/components/calculator.js.map +1 -1
  80. package/dist/components/calendar-heatmap.js +5 -5
  81. package/dist/components/calendar-heatmap.js.map +1 -1
  82. package/dist/components/chart-height.d.ts +23 -0
  83. package/dist/components/chart-height.js +15 -0
  84. package/dist/components/chart-height.js.map +1 -0
  85. package/dist/components/chart-zoom.js +6 -1
  86. package/dist/components/chart-zoom.js.map +1 -1
  87. package/dist/components/checkbox-group.js +2 -2
  88. package/dist/components/checkbox-group.js.map +1 -1
  89. package/dist/components/checkbox.d.ts +10 -0
  90. package/dist/components/checkbox.js +25 -10
  91. package/dist/components/checkbox.js.map +1 -1
  92. package/dist/components/chip.d.ts +19 -3
  93. package/dist/components/chip.js +47 -21
  94. package/dist/components/chip.js.map +1 -1
  95. package/dist/components/choice-card.d.ts +1 -1
  96. package/dist/components/choice-card.js +8 -7
  97. package/dist/components/choice-card.js.map +1 -1
  98. package/dist/components/column-mapper.js +1 -1
  99. package/dist/components/column-mapper.js.map +1 -1
  100. package/dist/components/combobox-core.js +8 -7
  101. package/dist/components/combobox-core.js.map +1 -1
  102. package/dist/components/combobox.js +8 -8
  103. package/dist/components/combobox.js.map +1 -1
  104. package/dist/components/confirm-dialog.d.ts +8 -3
  105. package/dist/components/confirm-dialog.js +7 -1
  106. package/dist/components/confirm-dialog.js.map +1 -1
  107. package/dist/components/copy-button.d.ts +10 -5
  108. package/dist/components/country-select.js +1 -1
  109. package/dist/components/country-select.js.map +1 -1
  110. package/dist/components/currency-select.js +3 -3
  111. package/dist/components/currency-select.js.map +1 -1
  112. package/dist/components/danger-confirm.d.ts +8 -3
  113. package/dist/components/data-table-cells.d.ts +1 -1
  114. package/dist/components/data-table-filter-popover.d.ts +1 -1
  115. package/dist/components/data-table-filter-popover.js +4 -1
  116. package/dist/components/data-table-filter-popover.js.map +1 -1
  117. package/dist/components/data-table-filters.d.ts +1 -1
  118. package/dist/components/data-table-labels.d.ts +1 -1
  119. package/dist/components/data-table-pagination.d.ts +1 -1
  120. package/dist/components/data-table.d.ts +1 -1
  121. package/dist/components/data-table.js +123 -52
  122. package/dist/components/data-table.js.map +1 -1
  123. package/dist/components/date-picker.d.ts +1 -1
  124. package/dist/components/date-picker.js +7 -5
  125. package/dist/components/date-picker.js.map +1 -1
  126. package/dist/components/description-list.js +2 -2
  127. package/dist/components/description-list.js.map +1 -1
  128. package/dist/components/dialog-frame.js +2 -1
  129. package/dist/components/dialog-frame.js.map +1 -1
  130. package/dist/components/disclosure.js +3 -2
  131. package/dist/components/disclosure.js.map +1 -1
  132. package/dist/components/dropdown.d.ts +1 -1
  133. package/dist/components/dropdown.js +7 -1
  134. package/dist/components/dropdown.js.map +1 -1
  135. package/dist/components/facing-pair.d.ts +19 -6
  136. package/dist/components/facing-pair.js +3 -2
  137. package/dist/components/facing-pair.js.map +1 -1
  138. package/dist/components/field-parts.d.ts +102 -6
  139. package/dist/components/field-parts.js +81 -6
  140. package/dist/components/field-parts.js.map +1 -1
  141. package/dist/components/field-sync.d.ts +4 -1
  142. package/dist/components/field-sync.js +12 -2
  143. package/dist/components/field-sync.js.map +1 -1
  144. package/dist/components/field.js +2 -2
  145. package/dist/components/field.js.map +1 -1
  146. package/dist/components/file-button.d.ts +8 -3
  147. package/dist/components/file-dropzone.d.ts +8 -3
  148. package/dist/components/floating-panel.js +7 -4
  149. package/dist/components/floating-panel.js.map +1 -1
  150. package/dist/components/focus-ring.d.ts +12 -0
  151. package/dist/components/focus-ring.js +8 -0
  152. package/dist/components/focus-ring.js.map +1 -0
  153. package/dist/components/form-actions.d.ts +8 -3
  154. package/dist/components/form-actions.js +33 -19
  155. package/dist/components/form-actions.js.map +1 -1
  156. package/dist/components/full-bleed-dialog.d.ts +11 -1
  157. package/dist/components/full-bleed-dialog.js +3 -1
  158. package/dist/components/full-bleed-dialog.js.map +1 -1
  159. package/dist/components/iban-input.d.ts +8 -3
  160. package/dist/components/image-grid.js +1 -1
  161. package/dist/components/image-grid.js.map +1 -1
  162. package/dist/components/inline-edit-field.js +6 -3
  163. package/dist/components/inline-edit-field.js.map +1 -1
  164. package/dist/components/language-select.d.ts +8 -3
  165. package/dist/components/legal-page.d.ts +26 -5
  166. package/dist/components/legal-page.js +3 -1
  167. package/dist/components/legal-page.js.map +1 -1
  168. package/dist/components/legal.d.ts +8 -3
  169. package/dist/components/legal.js +2 -1
  170. package/dist/components/legal.js.map +1 -1
  171. package/dist/components/lightbox.js +24 -2
  172. package/dist/components/lightbox.js.map +1 -1
  173. package/dist/components/line-items.js +2 -1
  174. package/dist/components/line-items.js.map +1 -1
  175. package/dist/components/list.d.ts +5 -0
  176. package/dist/components/list.js +13 -9
  177. package/dist/components/list.js.map +1 -1
  178. package/dist/components/measured-grid.d.ts +3 -1
  179. package/dist/components/measured-grid.js +38 -20
  180. package/dist/components/measured-grid.js.map +1 -1
  181. package/dist/components/menu-item.js +2 -1
  182. package/dist/components/menu-item.js.map +1 -1
  183. package/dist/components/mini-calendar.js +8 -4
  184. package/dist/components/mini-calendar.js.map +1 -1
  185. package/dist/components/money-field.d.ts +8 -3
  186. package/dist/components/month-picker.js +9 -5
  187. package/dist/components/month-picker.js.map +1 -1
  188. package/dist/components/nav-pills.js +1 -1
  189. package/dist/components/nav-pills.js.map +1 -1
  190. package/dist/components/number-field.d.ts +8 -3
  191. package/dist/components/number-input.d.ts +11 -4
  192. package/dist/components/number-input.js +8 -6
  193. package/dist/components/number-input.js.map +1 -1
  194. package/dist/components/numpad-sheet.d.ts +8 -3
  195. package/dist/components/numpad-sheet.js +2 -1
  196. package/dist/components/numpad-sheet.js.map +1 -1
  197. package/dist/components/one-time-code-input.js +2 -2
  198. package/dist/components/one-time-code-input.js.map +1 -1
  199. package/dist/components/page-contents.js +2 -2
  200. package/dist/components/page-contents.js.map +1 -1
  201. package/dist/components/phone-input.d.ts +8 -3
  202. package/dist/components/phone-input.js +2 -2
  203. package/dist/components/phone-input.js.map +1 -1
  204. package/dist/components/pie-chart.d.ts +8 -2
  205. package/dist/components/pie-chart.js +11 -7
  206. package/dist/components/pie-chart.js.map +1 -1
  207. package/dist/components/popover.d.ts +9 -1
  208. package/dist/components/popover.js +8 -4
  209. package/dist/components/popover.js.map +1 -1
  210. package/dist/components/row-actions.d.ts +100 -0
  211. package/dist/components/row-actions.js +165 -0
  212. package/dist/components/row-actions.js.map +1 -0
  213. package/dist/components/scroll-area.js +1 -1
  214. package/dist/components/scroll-area.js.map +1 -1
  215. package/dist/components/search-field.js +3 -2
  216. package/dist/components/search-field.js.map +1 -1
  217. package/dist/components/series-chart-budget.d.ts +30 -7
  218. package/dist/components/series-chart-budget.js +12 -4
  219. package/dist/components/series-chart-budget.js.map +1 -1
  220. package/dist/components/series-chart-ticks.d.ts +7 -1
  221. package/dist/components/series-chart-ticks.js +6 -0
  222. package/dist/components/series-chart-ticks.js.map +1 -1
  223. package/dist/components/series-chart.d.ts +58 -28
  224. package/dist/components/series-chart.js +42 -32
  225. package/dist/components/series-chart.js.map +1 -1
  226. package/dist/components/settings-fields.d.ts +51 -6
  227. package/dist/components/settings-fields.js +37 -0
  228. package/dist/components/settings-fields.js.map +1 -1
  229. package/dist/components/share-card.d.ts +11 -6
  230. package/dist/components/share-card.js +2 -0
  231. package/dist/components/share-card.js.map +1 -1
  232. package/dist/components/sign-chip.d.ts +4 -0
  233. package/dist/components/sign-chip.js +17 -4
  234. package/dist/components/sign-chip.js.map +1 -1
  235. package/dist/components/signature-pad.d.ts +3 -1
  236. package/dist/components/signature-pad.js +8 -5
  237. package/dist/components/signature-pad.js.map +1 -1
  238. package/dist/components/slider.js +7 -6
  239. package/dist/components/slider.js.map +1 -1
  240. package/dist/components/sparkline.d.ts +5 -1
  241. package/dist/components/sparkline.js +4 -2
  242. package/dist/components/sparkline.js.map +1 -1
  243. package/dist/components/stat-tile.d.ts +17 -1
  244. package/dist/components/stat-tile.js +48 -20
  245. package/dist/components/stat-tile.js.map +1 -1
  246. package/dist/components/status-dot.d.ts +4 -0
  247. package/dist/components/swipeable-row.js +2 -1
  248. package/dist/components/swipeable-row.js.map +1 -1
  249. package/dist/components/switch.d.ts +21 -7
  250. package/dist/components/switch.js +24 -11
  251. package/dist/components/switch.js.map +1 -1
  252. package/dist/components/table.js +2 -2
  253. package/dist/components/table.js.map +1 -1
  254. package/dist/components/text-link.d.ts +8 -3
  255. package/dist/components/text-link.js +1 -1
  256. package/dist/components/text-link.js.map +1 -1
  257. package/dist/components/text.d.ts +7 -2
  258. package/dist/components/text.js +5 -5
  259. package/dist/components/text.js.map +1 -1
  260. package/dist/components/tile-radio.js +2 -1
  261. package/dist/components/tile-radio.js.map +1 -1
  262. package/dist/components/time-input.d.ts +8 -3
  263. package/dist/components/toast.d.ts +18 -2
  264. package/dist/components/toast.js +8 -5
  265. package/dist/components/toast.js.map +1 -1
  266. package/dist/components/toggle-group.d.ts +16 -1
  267. package/dist/components/toggle-group.js +40 -11
  268. package/dist/components/toggle-group.js.map +1 -1
  269. package/dist/components/toggle-legend.d.ts +10 -1
  270. package/dist/components/toggle-legend.js +21 -7
  271. package/dist/components/toggle-legend.js.map +1 -1
  272. package/dist/components/translation-review-parts.d.ts +4 -0
  273. package/dist/components/translation-review.d.ts +4 -0
  274. package/dist/components/translation-review.js +3 -3
  275. package/dist/components/translation-review.js.map +1 -1
  276. package/dist/components/tree-view.js +3 -2
  277. package/dist/components/tree-view.js.map +1 -1
  278. package/dist/components/treemap.d.ts +5 -2
  279. package/dist/components/treemap.js +33 -16
  280. package/dist/components/treemap.js.map +1 -1
  281. package/dist/components/ui.d.ts +8 -3
  282. package/dist/components/ui.js +202 -114
  283. package/dist/components/ui.js.map +1 -1
  284. package/dist/components/use-table-state.d.ts +1 -1
  285. package/dist/components/user-avatar.d.ts +5 -1
  286. package/dist/components/user-avatar.js +1 -1
  287. package/dist/components/user-avatar.js.map +1 -1
  288. package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
  289. package/dist/data-table.d.ts +1 -1
  290. package/dist/{delete-account-setting-DCFS5Vof.d.ts → delete-account-setting-BvdQdbv3.d.ts} +613 -58
  291. package/dist/demo/demo-banner.js +1 -0
  292. package/dist/demo/demo-banner.js.map +1 -1
  293. package/dist/feedback/feedback-attachment.d.ts +8 -3
  294. package/dist/feedback/feedback-context.d.ts +8 -3
  295. package/dist/feedback/feedback-crash.d.ts +8 -3
  296. package/dist/feedback/feedback-dialog.d.ts +8 -3
  297. package/dist/feedback/feedback-dialog.js +3 -3
  298. package/dist/feedback/feedback-dialog.js.map +1 -1
  299. package/dist/feedback/feedback-inbox.d.ts +8 -3
  300. package/dist/feedback/feedback-inbox.js +2 -2
  301. package/dist/feedback/feedback-inbox.js.map +1 -1
  302. package/dist/feedback/feedback-labels.d.ts +8 -3
  303. package/dist/feedback/feedback-menu.d.ts +9 -4
  304. package/dist/feedback/feedback-record.d.ts +8 -3
  305. package/dist/feedback/feedback-row-detail.d.ts +8 -3
  306. package/dist/feedback/feedback-status-undo.d.ts +8 -3
  307. package/dist/feedback/feedback-submit.d.ts +8 -3
  308. package/dist/feedback/feedback-swipe.d.ts +8 -3
  309. package/dist/feedback/feedback-table.d.ts +8 -3
  310. package/dist/feedback/feedback-thread.d.ts +8 -3
  311. package/dist/feedback/feedback-thread.js +3 -3
  312. package/dist/feedback/feedback-thread.js.map +1 -1
  313. package/dist/feedback.d.ts +8 -3
  314. package/dist/hooks/use-anchored-panel.d.ts +32 -3
  315. package/dist/hooks/use-anchored-panel.js +21 -3
  316. package/dist/hooks/use-anchored-panel.js.map +1 -1
  317. package/dist/hooks/use-breakpoint.d.ts +77 -0
  318. package/dist/hooks/use-breakpoint.js +50 -0
  319. package/dist/hooks/use-breakpoint.js.map +1 -0
  320. package/dist/hooks/use-file-drop.d.ts +8 -3
  321. package/dist/hooks/use-large-text.d.ts +29 -0
  322. package/dist/hooks/use-large-text.js +20 -0
  323. package/dist/hooks/use-large-text.js.map +1 -0
  324. package/dist/hooks/use-windowed-rows.d.ts +52 -8
  325. package/dist/hooks/use-windowed-rows.js +94 -5
  326. package/dist/hooks/use-windowed-rows.js.map +1 -1
  327. package/dist/i18n/defaults.d.ts +8 -3
  328. package/dist/i18n/defaults.js +8 -0
  329. package/dist/i18n/defaults.js.map +1 -1
  330. package/dist/i18n/german.d.ts +8 -3
  331. package/dist/i18n/german.js +91 -3
  332. package/dist/i18n/german.js.map +1 -1
  333. package/dist/i18n/kit-labels.d.ts +8 -3
  334. package/dist/i18n/kit-labels.js.map +1 -1
  335. package/dist/i18n/languages.d.ts +10 -5
  336. package/dist/i18n/locales/de-CH.d.ts +8 -3
  337. package/dist/i18n/locales/en.d.ts +8 -3
  338. package/dist/i18n/locales/es.d.ts +8 -3
  339. package/dist/i18n/locales/es.js +86 -3
  340. package/dist/i18n/locales/es.js.map +1 -1
  341. package/dist/i18n/locales/fr.d.ts +8 -3
  342. package/dist/i18n/locales/fr.js +88 -3
  343. package/dist/i18n/locales/fr.js.map +1 -1
  344. package/dist/i18n/locales/hu.d.ts +8 -3
  345. package/dist/i18n/locales/hu.js +88 -3
  346. package/dist/i18n/locales/hu.js.map +1 -1
  347. package/dist/i18n/locales/it.d.ts +8 -3
  348. package/dist/i18n/locales/it.js +89 -3
  349. package/dist/i18n/locales/it.js.map +1 -1
  350. package/dist/i18n/locales/zh.d.ts +8 -3
  351. package/dist/i18n/locales/zh.js +86 -3
  352. package/dist/i18n/locales/zh.js.map +1 -1
  353. package/dist/i18n/review.d.ts +8 -3
  354. package/dist/i18n/review.js +16 -1
  355. package/dist/i18n/review.js.map +1 -1
  356. package/dist/index.d.ts +29 -13
  357. package/dist/index.js +77 -5
  358. package/dist/index.js.map +1 -1
  359. package/dist/landing/landing-actions.d.ts +12 -5
  360. package/dist/landing/landing-actions.js +1 -1
  361. package/dist/landing/landing-actions.js.map +1 -1
  362. package/dist/landing/landing-sections.d.ts +8 -3
  363. package/dist/landing/public-header.d.ts +9 -4
  364. package/dist/landing/public-header.js +3 -3
  365. package/dist/landing/public-header.js.map +1 -1
  366. package/dist/landing/routing.d.ts +27 -9
  367. package/dist/landing/routing.js +5 -2
  368. package/dist/landing/routing.js.map +1 -1
  369. package/dist/lib/cn.js +3 -2
  370. package/dist/lib/cn.js.map +1 -1
  371. package/dist/rhf/fields.d.ts +8 -3
  372. package/dist/rhf/fields.js +1 -1
  373. package/dist/rhf/fields.js.map +1 -1
  374. package/dist/rhf/form.d.ts +8 -3
  375. package/dist/rhf/form.js +2 -2
  376. package/dist/rhf/form.js.map +1 -1
  377. package/dist/rhf.d.ts +8 -3
  378. package/dist/search/command-palette.d.ts +1 -1
  379. package/dist/search/command-palette.js +9 -8
  380. package/dist/search/command-palette.js.map +1 -1
  381. package/dist/settings/settings-labels.d.ts +8 -1
  382. package/dist/settings/settings-labels.js +7 -2
  383. package/dist/settings/settings-labels.js.map +1 -1
  384. package/dist/settings/settings-layout.js +2 -1
  385. package/dist/settings/settings-layout.js.map +1 -1
  386. package/dist/settings/settings-section.d.ts +8 -3
  387. package/dist/settings/use-account-appearance.d.ts +92 -0
  388. package/dist/settings/use-account-appearance.js +63 -0
  389. package/dist/settings/use-account-appearance.js.map +1 -0
  390. package/dist/settings/use-settings-route.d.ts +1 -1
  391. package/dist/settings/use-settings-route.js +2 -3
  392. package/dist/settings/use-settings-route.js.map +1 -1
  393. package/dist/shell/app-shell.d.ts +12 -171
  394. package/dist/shell/app-shell.js +144 -28
  395. package/dist/shell/app-shell.js.map +1 -1
  396. package/dist/shell/auth-layout.js +1 -1
  397. package/dist/shell/auth-layout.js.map +1 -1
  398. package/dist/shell/option-switcher-menu.js +1 -1
  399. package/dist/shell/option-switcher-menu.js.map +1 -1
  400. package/dist/shell/top-bar-brand.d.ts +8 -3
  401. package/dist/shell/top-bar-brand.js +3 -1
  402. package/dist/shell/top-bar-brand.js.map +1 -1
  403. package/dist/shell/topbar-action-menu.d.ts +4 -0
  404. package/dist/shell/topbar-action-menu.js +3 -3
  405. package/dist/shell/topbar-action-menu.js.map +1 -1
  406. package/dist/shell/topbar-controls.js +4 -4
  407. package/dist/shell/topbar-controls.js.map +1 -1
  408. package/dist/shell.d.ts +10 -6
  409. package/dist/shell.js.map +1 -1
  410. package/dist/theme/contrast-tokens.d.ts +41 -0
  411. package/dist/theme/contrast-tokens.js +59 -0
  412. package/dist/theme/contrast-tokens.js.map +1 -0
  413. package/dist/theme/contrast.d.ts +109 -0
  414. package/dist/theme/contrast.js +128 -0
  415. package/dist/theme/contrast.js.map +1 -0
  416. package/dist/theme/palette-presets.d.ts +12 -1
  417. package/dist/theme/palette-presets.js +9 -4
  418. package/dist/theme/palette-presets.js.map +1 -1
  419. package/dist/theme/palette-store.js.map +1 -1
  420. package/dist/theme/text-size.d.ts +157 -0
  421. package/dist/theme/text-size.js +121 -0
  422. package/dist/theme/text-size.js.map +1 -0
  423. package/dist/wizard/stepper-nav.d.ts +8 -3
  424. package/dist/wizard.d.ts +8 -3
  425. package/package.json +1 -1
  426. package/src/admin/admin-parts.ts +9 -2
  427. package/src/admin/invitations-panel.tsx +36 -29
  428. package/src/admin/user-roster.tsx +17 -72
  429. package/src/auth/auth-errors.ts +68 -7
  430. package/src/auth/reset-password-form.tsx +6 -2
  431. package/src/auth/verify-email.tsx +14 -2
  432. package/src/billing/billing-banner.tsx +205 -0
  433. package/src/billing/billing-labels.ts +224 -0
  434. package/src/billing/billing-lock.ts +75 -0
  435. package/src/billing/billing-standing.ts +72 -0
  436. package/src/billing/plan-card.tsx +458 -0
  437. package/src/billing/plan-limit-notice.tsx +145 -0
  438. package/src/billing/plan-limit.ts +53 -0
  439. package/src/billing/plan-price.ts +101 -0
  440. package/src/billing/subscription-actions.tsx +76 -0
  441. package/src/billing/subscription-status-chip.tsx +95 -0
  442. package/src/components/account-chips.tsx +36 -10
  443. package/src/components/alert-banner.tsx +11 -6
  444. package/src/components/amount-input.tsx +5 -5
  445. package/src/components/appearance-labels.ts +40 -0
  446. package/src/components/autocomplete.tsx +2 -2
  447. package/src/components/breadcrumbs.tsx +3 -3
  448. package/src/components/bulk-action-bar.tsx +11 -14
  449. package/src/components/calculator.tsx +4 -3
  450. package/src/components/calendar-heatmap.tsx +5 -5
  451. package/src/components/chart-height.ts +30 -0
  452. package/src/components/chart-zoom.tsx +6 -1
  453. package/src/components/checkbox-group.tsx +2 -2
  454. package/src/components/checkbox.tsx +62 -20
  455. package/src/components/chip.tsx +105 -24
  456. package/src/components/choice-card.tsx +15 -10
  457. package/src/components/column-mapper.tsx +1 -1
  458. package/src/components/combobox-core.tsx +8 -7
  459. package/src/components/combobox.tsx +8 -8
  460. package/src/components/confirm-dialog.tsx +20 -1
  461. package/src/components/country-select.tsx +1 -1
  462. package/src/components/currency-select.tsx +3 -3
  463. package/src/components/data-table-filter-popover.tsx +7 -1
  464. package/src/components/data-table.tsx +160 -43
  465. package/src/components/date-picker.tsx +12 -9
  466. package/src/components/description-list.tsx +2 -2
  467. package/src/components/dialog-frame.tsx +2 -1
  468. package/src/components/disclosure.tsx +3 -2
  469. package/src/components/dropdown.tsx +10 -2
  470. package/src/components/facing-pair.ts +12 -5
  471. package/src/components/field-parts.tsx +190 -7
  472. package/src/components/field-sync.tsx +17 -3
  473. package/src/components/field.tsx +2 -2
  474. package/src/components/floating-panel.tsx +7 -4
  475. package/src/components/focus-ring.ts +12 -0
  476. package/src/components/form-actions.tsx +55 -18
  477. package/src/components/full-bleed-dialog.tsx +18 -4
  478. package/src/components/image-grid.tsx +1 -1
  479. package/src/components/inline-edit-field.tsx +6 -3
  480. package/src/components/legal-page.tsx +28 -2
  481. package/src/components/legal.tsx +19 -1
  482. package/src/components/lightbox.tsx +24 -2
  483. package/src/components/line-items.tsx +7 -1
  484. package/src/components/list.tsx +36 -10
  485. package/src/components/measured-grid.tsx +49 -24
  486. package/src/components/menu-item.tsx +2 -2
  487. package/src/components/mini-calendar.tsx +8 -4
  488. package/src/components/month-picker.tsx +9 -5
  489. package/src/components/nav-pills.tsx +1 -1
  490. package/src/components/number-input.tsx +14 -7
  491. package/src/components/numpad-sheet.tsx +2 -2
  492. package/src/components/one-time-code-input.tsx +2 -2
  493. package/src/components/page-contents.tsx +2 -2
  494. package/src/components/phone-input.tsx +2 -2
  495. package/src/components/pie-chart.tsx +19 -9
  496. package/src/components/popover.tsx +22 -5
  497. package/src/components/row-actions.tsx +344 -0
  498. package/src/components/scroll-area.tsx +1 -1
  499. package/src/components/search-field.tsx +4 -2
  500. package/src/components/series-chart-budget.ts +40 -8
  501. package/src/components/series-chart-ticks.ts +10 -0
  502. package/src/components/series-chart.tsx +123 -72
  503. package/src/components/settings-fields.tsx +81 -2
  504. package/src/components/share-card.tsx +10 -0
  505. package/src/components/sign-chip.tsx +28 -4
  506. package/src/components/signature-pad.tsx +13 -7
  507. package/src/components/slider.tsx +22 -9
  508. package/src/components/sparkline.tsx +11 -3
  509. package/src/components/stat-tile.tsx +70 -14
  510. package/src/components/swipeable-row.tsx +2 -1
  511. package/src/components/switch.tsx +57 -23
  512. package/src/components/table.tsx +2 -2
  513. package/src/components/text-link.tsx +1 -1
  514. package/src/components/text.tsx +11 -6
  515. package/src/components/tile-radio.tsx +4 -1
  516. package/src/components/toast.tsx +27 -7
  517. package/src/components/toggle-group.tsx +99 -12
  518. package/src/components/toggle-legend.tsx +53 -8
  519. package/src/components/translation-review.tsx +10 -5
  520. package/src/components/tree-view.tsx +3 -2
  521. package/src/components/treemap.tsx +27 -10
  522. package/src/components/ui.tsx +331 -145
  523. package/src/components/user-avatar.tsx +1 -1
  524. package/src/demo/demo-banner.tsx +5 -0
  525. package/src/feedback/feedback-dialog.tsx +3 -3
  526. package/src/feedback/feedback-inbox.tsx +2 -2
  527. package/src/feedback/feedback-thread.tsx +3 -3
  528. package/src/hooks/use-anchored-panel.ts +67 -5
  529. package/src/hooks/use-breakpoint.ts +121 -0
  530. package/src/hooks/use-large-text.ts +41 -0
  531. package/src/hooks/use-windowed-rows.ts +168 -12
  532. package/src/i18n/defaults.ts +8 -0
  533. package/src/i18n/german.ts +105 -1
  534. package/src/i18n/kit-labels.tsx +12 -0
  535. package/src/i18n/locales/es.ts +96 -1
  536. package/src/i18n/locales/fr.ts +100 -1
  537. package/src/i18n/locales/hu.ts +98 -1
  538. package/src/i18n/locales/it.ts +101 -1
  539. package/src/i18n/locales/zh.ts +92 -1
  540. package/src/i18n/review.ts +15 -0
  541. package/src/index.ts +82 -8
  542. package/src/landing/landing-actions.tsx +9 -5
  543. package/src/landing/public-header.tsx +4 -4
  544. package/src/landing/routing.tsx +22 -5
  545. package/src/lib/cn.ts +13 -2
  546. package/src/rhf/fields.tsx +1 -1
  547. package/src/rhf/form.tsx +2 -2
  548. package/src/search/command-palette.tsx +10 -9
  549. package/src/settings/settings-labels.ts +13 -1
  550. package/src/settings/settings-layout.tsx +2 -1
  551. package/src/settings/use-account-appearance.ts +148 -0
  552. package/src/settings/use-settings-route.ts +3 -4
  553. package/src/shell/app-shell.tsx +278 -54
  554. package/src/shell/auth-layout.tsx +1 -1
  555. package/src/shell/option-switcher-menu.tsx +1 -1
  556. package/src/shell/top-bar-brand.tsx +3 -1
  557. package/src/shell/topbar-action-menu.tsx +3 -3
  558. package/src/shell/topbar-controls.tsx +4 -4
  559. package/src/shell.ts +7 -1
  560. package/src/theme/contrast-tokens.ts +112 -0
  561. package/src/theme/contrast.ts +224 -0
  562. package/src/theme/palette-presets.ts +24 -4
  563. package/src/theme/palette-store.ts +6 -1
  564. package/src/theme/text-size.ts +267 -0
  565. package/tokens.css +143 -1
@@ -0,0 +1,121 @@
1
+ "use client";
2
+ import { useEffect, useMemo, useSyncExternalStore } from "react";
3
+ import { create } from "zustand";
4
+ import { persist } from "zustand/middleware";
5
+ import { logger } from "../lib/logger.js";
6
+ import { readStored } from "../lib/safe-storage.js";
7
+ const TEXT_SIZES = ["normal", "large", "xlarge"];
8
+ const TEXT_SCALE = { normal: 1, large: 1.25, xlarge: 1.5 };
9
+ const TEXT_SIZE_ATTRIBUTE = "data-text-size";
10
+ function isTextSize(value) {
11
+ return value === "normal" || value === "large" || value === "xlarge";
12
+ }
13
+ function resolveTextSize(device, account) {
14
+ if (isTextSize(device)) return device;
15
+ if (isTextSize(account)) return account;
16
+ return "normal";
17
+ }
18
+ const listeners = /* @__PURE__ */ new Set();
19
+ let observer = null;
20
+ function notify() {
21
+ for (const listener of listeners) listener();
22
+ }
23
+ function subscribe(listener) {
24
+ listeners.add(listener);
25
+ if (!observer && typeof MutationObserver !== "undefined" && typeof document !== "undefined") {
26
+ observer = new MutationObserver(notify);
27
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: [TEXT_SIZE_ATTRIBUTE] });
28
+ }
29
+ return () => {
30
+ listeners.delete(listener);
31
+ if (listeners.size === 0) {
32
+ observer?.disconnect();
33
+ observer = null;
34
+ }
35
+ };
36
+ }
37
+ function readTextSize() {
38
+ if (typeof document === "undefined") return "normal";
39
+ const value = document.documentElement.getAttribute(TEXT_SIZE_ATTRIBUTE);
40
+ return isTextSize(value) ? value : "normal";
41
+ }
42
+ function applyTextSize(size) {
43
+ if (typeof document === "undefined") return;
44
+ const root = document.documentElement;
45
+ if (root.getAttribute(TEXT_SIZE_ATTRIBUTE) === size) return;
46
+ root.setAttribute(TEXT_SIZE_ATTRIBUTE, size);
47
+ notify();
48
+ }
49
+ function useTextSize() {
50
+ const size = useSyncExternalStore(subscribe, readTextSize, () => "normal");
51
+ return useMemo(() => ({ size, scale: TEXT_SCALE[size] }), [size]);
52
+ }
53
+ function readPersistedTextSize(storageKey) {
54
+ try {
55
+ const blob = JSON.parse(readStored(storageKey) ?? "null");
56
+ const size = blob?.state?.size;
57
+ return isTextSize(size) ? size : null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+ function readAccount(account) {
63
+ if (!account) return void 0;
64
+ try {
65
+ return account();
66
+ } catch {
67
+ return void 0;
68
+ }
69
+ }
70
+ function applyPersistedTextSize(storageKey, options = {}) {
71
+ const size = resolveTextSize(readPersistedTextSize(storageKey), readAccount(options.account));
72
+ applyTextSize(size);
73
+ return size;
74
+ }
75
+ const TEXT_SIZE_INLINE_SCRIPT = "(function(k,a){function ok(v){return v==='normal'||v==='large'||v==='xlarge'}var v=null;try{var s=JSON.parse(localStorage.getItem(k)||'null');v=s&&s.state&&s.state.size}catch(e){}if(!ok(v)){v=null;try{v=a?a():null}catch(e){}}try{document.documentElement.setAttribute('data-text-size',ok(v)?v:'normal')}catch(e){}})";
76
+ function createTextSizeStore(storageKey) {
77
+ const useTextSizeStore = create()(
78
+ logger(
79
+ persist(
80
+ (set) => ({
81
+ size: null,
82
+ setSize: (size) => set({ size: isTextSize(size) ? size : null })
83
+ }),
84
+ {
85
+ name: storageKey,
86
+ version: 1,
87
+ partialize: (s) => ({ size: s.size }),
88
+ // A hand-edited or future value must not reach <html> as an unknown size.
89
+ merge: (persisted, current) => ({
90
+ ...current,
91
+ size: isTextSize(persisted?.size) ? persisted.size : null
92
+ })
93
+ }
94
+ ),
95
+ "text-size"
96
+ )
97
+ );
98
+ function useApplyTextSize({ account } = {}) {
99
+ const device = useTextSizeStore((s) => s.size);
100
+ const size = resolveTextSize(device, account);
101
+ useEffect(() => {
102
+ applyTextSize(size);
103
+ }, [size]);
104
+ return size;
105
+ }
106
+ return { useTextSizeStore, useApplyTextSize };
107
+ }
108
+ export {
109
+ TEXT_SCALE,
110
+ TEXT_SIZES,
111
+ TEXT_SIZE_INLINE_SCRIPT,
112
+ applyPersistedTextSize,
113
+ applyTextSize,
114
+ createTextSizeStore,
115
+ isTextSize,
116
+ readPersistedTextSize,
117
+ readTextSize,
118
+ resolveTextSize,
119
+ useTextSize
120
+ };
121
+ //# sourceMappingURL=text-size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/theme/text-size.ts"],"sourcesContent":["import { useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport { logger } from \"../lib/logger\";\nimport { readStored } from \"../lib/safe-storage\";\n\n/**\n * The text size (docs/text-size-harmonization.md §2.1, §3.1, §10.3, §10.6).\n *\n * ONE scale on `<html>`: `data-text-size=\"normal|large|xlarge\"`, and `tokens.css` turns\n * it into `font-size: 100% / 125% / 150%` — percent, never px, so \"Normal\" keeps\n * whatever size the person set in the browser. Tailwind's spacing and type are rem, so\n * about 90 % of every screen grows with that one declaration; the breakpoints follow too\n * (tokens.css redefines `sm:` … `3xl:` per size, {@link useBreakpoint} answers the same\n * in JS).\n *\n * The names below are the mechanism's public vocabulary and are FROZEN: an app's\n * storage, its `/auth/me` column (server-kit's `TEXT_SIZES`) and the inline boot snippet\n * all spell them this way.\n */\n\n/** The three steps, in order (§2.1, §10.6): server-kit's `TEXT_SIZES`, value for value. */\nexport const TEXT_SIZES = [\"normal\", \"large\", \"xlarge\"] as const;\n\nexport type TextSize = (typeof TEXT_SIZES)[number];\n\n/** The root font size each step sets, as a factor of the browser's own (tokens.css). For\n * what CSS cannot reach: a canvas or SVG chart's px constants, a windowed list's row\n * estimate, a popover width in px (§3.2). */\nexport const TEXT_SCALE: Readonly<Record<TextSize, number>> = { normal: 1, large: 1.25, xlarge: 1.5 };\n\n/** The attribute on `<html>` that carries the size. */\nconst TEXT_SIZE_ATTRIBUTE = \"data-text-size\";\n\nexport function isTextSize(value: unknown): value is TextSize {\n return value === \"normal\" || value === \"large\" || value === \"xlarge\";\n}\n\n/**\n * The size in force: **this device's own choice → the account's → Normal** (§2.2, §2.6).\n * Each is taken only when it is one of {@link TEXT_SIZES}; anything else (a stale value,\n * `null` = never chosen) falls through to the next. The one order the pre-paint call, the\n * apply hook and `useAccountAppearance` all use, so the three can never disagree.\n */\nexport function resolveTextSize(device: unknown, account?: unknown): TextSize {\n if (isTextSize(device)) return device;\n if (isTextSize(account)) return account;\n return \"normal\";\n}\n\n/* ── <html data-text-size> and the subscribers that read it ─────────────── */\n\nconst listeners = new Set<() => void>();\nlet observer: MutationObserver | null = null;\n\nfunction notify(): void {\n for (const listener of listeners) listener();\n}\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener);\n // Also catches writers that are not this module — the inline boot snippet, an app\n // setting the attribute by hand, a test.\n if (!observer && typeof MutationObserver !== \"undefined\" && typeof document !== \"undefined\") {\n observer = new MutationObserver(notify);\n observer.observe(document.documentElement, { attributes: true, attributeFilter: [TEXT_SIZE_ATTRIBUTE] });\n }\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n observer?.disconnect();\n observer = null;\n }\n };\n}\n\n/** The size `<html>` carries now; Normal without one (or outside a browser). */\nexport function readTextSize(): TextSize {\n if (typeof document === \"undefined\") return \"normal\";\n const value = document.documentElement.getAttribute(TEXT_SIZE_ATTRIBUTE);\n return isTextSize(value) ? value : \"normal\";\n}\n\n/** Put `size` on `<html>`. Every {@link useTextSize} re-renders with it. */\nexport function applyTextSize(size: TextSize): void {\n if (typeof document === \"undefined\") return;\n const root = document.documentElement;\n if (root.getAttribute(TEXT_SIZE_ATTRIBUTE) === size) return;\n root.setAttribute(TEXT_SIZE_ATTRIBUTE, size);\n notify();\n}\n\nexport interface TextSizeInfo {\n size: TextSize;\n /** {@link TEXT_SCALE} for `size`: 1, 1.25 or 1.5. */\n scale: number;\n}\n\n/**\n * The size in force on this page, and its scale (§3.1) — read from `<html>`, so it needs\n * no store and works in every kit component: whoever applied the size (the app's store,\n * the pre-paint call, the inline snippet), this follows it.\n *\n * Only for what CSS cannot do. A class that should differ at Large is a `large:`\n * variant; a layout switch is a breakpoint, which already scales.\n */\nexport function useTextSize(): TextSizeInfo {\n const size = useSyncExternalStore(subscribe, readTextSize, () => \"normal\" as const);\n return useMemo(() => ({ size, scale: TEXT_SCALE[size] }), [size]);\n}\n\n/* ── Storage: the frozen format, the pre-paint call ─────────────────────── */\n\n/**\n * What a text-size store persists under its key — zustand's persist envelope, and FROZEN\n * (§10.3) because an app's inline `<head>` snippet reads it before any module loads:\n *\n * {\"state\":{\"size\":\"large\"},\"version\":1}\n *\n * `size` is one of {@link TEXT_SIZES}, or `null` / absent when this device never chose.\n * A future change adds a key; it never renames `state.size` or changes its values.\n */\nexport interface PersistedTextSize {\n state?: { size?: TextSize | null };\n version?: number;\n}\n\n/** This device's own choice under `storageKey`, or `null` when it never chose (or the\n * storage is blocked, malformed, or holds a value that is not a size). */\nexport function readPersistedTextSize(storageKey: string): TextSize | null {\n try {\n const blob = JSON.parse(readStored(storageKey) ?? \"null\") as PersistedTextSize | null;\n const size = blob?.state?.size;\n return isTextSize(size) ? size : null;\n } catch {\n return null;\n }\n}\n\nexport interface PersistedAppearanceOptions {\n /**\n * The account's last known value, for a device without a choice of its own (§10.3):\n * what the app persisted with its user (keksdose keeps `/auth/me` in localStorage), so\n * a cold boot paints the account's size instead of Normal and then jumping — offline\n * too. Called once, guarded: a throw counts as \"no account value\".\n */\n account?: () => string | null | undefined;\n}\n\nfunction readAccount(account: PersistedAppearanceOptions[\"account\"]): unknown {\n if (!account) return undefined;\n try {\n return account();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Pre-paint (§3.1, §10.3): read this device's choice under `storageKey` — else the\n * account's last known value — and put it on `<html>` BEFORE `createRoot`, next to\n * `applyPersistedTheme`, so the first paint already has the right size. Returns the size\n * applied.\n *\n * ```ts\n * applyPersistedTextSize(\"keksdose-text-size\", {\n * account: () => readPersistedUser()?.text_size,\n * });\n * ```\n */\nexport function applyPersistedTextSize(storageKey: string, options: PersistedAppearanceOptions = {}): TextSize {\n const size = resolveTextSize(readPersistedTextSize(storageKey), readAccount(options.account));\n applyTextSize(size);\n return size;\n}\n\n/**\n * The same pre-paint as {@link applyPersistedTextSize}, as an inline `<head>` script for\n * a boot splash painted from `index.html` before any module runs (§10.3). A function\n * EXPRESSION: call it with the storage key and, optionally, a function returning the\n * account's last known value.\n *\n * ```html\n * <script>\n * (function(k,a){…})(\"keksdose-text-size\", function () {\n * return JSON.parse(localStorage.getItem(\"keksdose-auth\")).state.user.text_size;\n * });\n * </script>\n * ```\n *\n * Paste the constant's value in place of `(function(k,a){…})`. It reads the frozen\n * {@link PersistedTextSize} format, never throws, and sets only `data-text-size`; the\n * splash itself sizes in rem so the attribute reaches it (tokens.css, or the splash's own\n * `html[data-text-size=large]{font-size:125%}` if it paints before the stylesheet).\n */\nexport const TEXT_SIZE_INLINE_SCRIPT =\n \"(function(k,a){function ok(v){return v==='normal'||v==='large'||v==='xlarge'}\" +\n \"var v=null;try{var s=JSON.parse(localStorage.getItem(k)||'null');v=s&&s.state&&s.state.size}catch(e){}\" +\n \"if(!ok(v)){v=null;try{v=a?a():null}catch(e){}}\" +\n \"try{document.documentElement.setAttribute('data-text-size',ok(v)?v:'normal')}catch(e){}})\";\n\n/* ── The store ──────────────────────────────────────────────────────────── */\n\nexport interface TextSizeState {\n /** This device's own choice (persisted), or `null` when it never chose — then the\n * account's value applies (§2.2). */\n size: TextSize | null;\n /** Store this device's choice; `null` forgets it, so the account's applies again. */\n setSize: (size: TextSize | null) => void;\n}\n\nexport interface ApplyTextSizeOptions {\n /** The account's value (`/auth/me`'s `text_size`): it applies while this device has no\n * choice of its own. Leave it out where there is no account. */\n account?: string | null;\n}\n\n/**\n * Build a text-size store bound to a consumer-supplied localStorage key, shaped like\n * `createThemeStore` (§6). It holds the DEVICE's choice only; the account's value comes\n * in through `useApplyTextSize({ account })` and `useAccountAppearance`, and the size in\n * force is always {@link resolveTextSize} of the two.\n *\n * - `useTextSizeStore` — the zustand hook: `size` (device choice or null) and `setSize`.\n * - `useApplyTextSize({ account })` — the effect that puts the size in force on `<html>`.\n * Mount it once near the root, beside `useApplyTheme`.\n *\n * The persisted format is {@link PersistedTextSize}, and frozen.\n */\nexport function createTextSizeStore(storageKey: string) {\n const useTextSizeStore = create<TextSizeState>()(\n logger(\n persist(\n (set) => ({\n size: null,\n setSize: (size) => set({ size: isTextSize(size) ? size : null }),\n }),\n {\n name: storageKey,\n version: 1,\n partialize: (s) => ({ size: s.size }),\n // A hand-edited or future value must not reach <html> as an unknown size.\n merge: (persisted, current) => ({\n ...current,\n size: isTextSize((persisted as { size?: unknown } | null)?.size)\n ? ((persisted as { size: TextSize }).size)\n : null,\n }),\n },\n ),\n \"text-size\",\n ),\n );\n\n function useApplyTextSize({ account }: ApplyTextSizeOptions = {}): TextSize {\n const device = useTextSizeStore((s) => s.size);\n const size = resolveTextSize(device, account);\n useEffect(() => {\n applyTextSize(size);\n }, [size]);\n return size;\n }\n\n return { useTextSizeStore, useApplyTextSize };\n}\n\nexport type TextSizeStore = ReturnType<typeof createTextSizeStore>;\n"],"mappings":";AAAA,SAAS,WAAW,SAAS,4BAA4B;AACzD,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAkBpB,MAAM,aAAa,CAAC,UAAU,SAAS,QAAQ;AAO/C,MAAM,aAAiD,EAAE,QAAQ,GAAG,OAAO,MAAM,QAAQ,IAAI;AAGpG,MAAM,sBAAsB;AAErB,SAAS,WAAW,OAAmC;AAC5D,SAAO,UAAU,YAAY,UAAU,WAAW,UAAU;AAC9D;AAQO,SAAS,gBAAgB,QAAiB,SAA6B;AAC5E,MAAI,WAAW,MAAM,EAAG,QAAO;AAC/B,MAAI,WAAW,OAAO,EAAG,QAAO;AAChC,SAAO;AACT;AAIA,MAAM,YAAY,oBAAI,IAAgB;AACtC,IAAI,WAAoC;AAExC,SAAS,SAAe;AACtB,aAAW,YAAY,UAAW,UAAS;AAC7C;AAEA,SAAS,UAAU,UAAkC;AACnD,YAAU,IAAI,QAAQ;AAGtB,MAAI,CAAC,YAAY,OAAO,qBAAqB,eAAe,OAAO,aAAa,aAAa;AAC3F,eAAW,IAAI,iBAAiB,MAAM;AACtC,aAAS,QAAQ,SAAS,iBAAiB,EAAE,YAAY,MAAM,iBAAiB,CAAC,mBAAmB,EAAE,CAAC;AAAA,EACzG;AACA,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AACzB,QAAI,UAAU,SAAS,GAAG;AACxB,gBAAU,WAAW;AACrB,iBAAW;AAAA,IACb;AAAA,EACF;AACF;AAGO,SAAS,eAAyB;AACvC,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,QAAQ,SAAS,gBAAgB,aAAa,mBAAmB;AACvE,SAAO,WAAW,KAAK,IAAI,QAAQ;AACrC;AAGO,SAAS,cAAc,MAAsB;AAClD,MAAI,OAAO,aAAa,YAAa;AACrC,QAAM,OAAO,SAAS;AACtB,MAAI,KAAK,aAAa,mBAAmB,MAAM,KAAM;AACrD,OAAK,aAAa,qBAAqB,IAAI;AAC3C,SAAO;AACT;AAgBO,SAAS,cAA4B;AAC1C,QAAM,OAAO,qBAAqB,WAAW,cAAc,MAAM,QAAiB;AAClF,SAAO,QAAQ,OAAO,EAAE,MAAM,OAAO,WAAW,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;AAClE;AAoBO,SAAS,sBAAsB,YAAqC;AACzE,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,WAAW,UAAU,KAAK,MAAM;AACxD,UAAM,OAAO,MAAM,OAAO;AAC1B,WAAO,WAAW,IAAI,IAAI,OAAO;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYA,SAAS,YAAY,SAAyD;AAC5E,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,WAAO,QAAQ;AAAA,EACjB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAcO,SAAS,uBAAuB,YAAoB,UAAsC,CAAC,GAAa;AAC7G,QAAM,OAAO,gBAAgB,sBAAsB,UAAU,GAAG,YAAY,QAAQ,OAAO,CAAC;AAC5F,gBAAc,IAAI;AAClB,SAAO;AACT;AAqBO,MAAM,0BACX;AAiCK,SAAS,oBAAoB,YAAoB;AACtD,QAAM,mBAAmB,OAAsB;AAAA,IAC7C;AAAA,MACE;AAAA,QACE,CAAC,SAAS;AAAA,UACR,MAAM;AAAA,UACN,SAAS,CAAC,SAAS,IAAI,EAAE,MAAM,WAAW,IAAI,IAAI,OAAO,KAAK,CAAC;AAAA,QACjE;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,UACT,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK;AAAA;AAAA,UAEnC,OAAO,CAAC,WAAW,aAAa;AAAA,YAC9B,GAAG;AAAA,YACH,MAAM,WAAY,WAAyC,IAAI,IACzD,UAAiC,OACnC;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,WAAS,iBAAiB,EAAE,QAAQ,IAA0B,CAAC,GAAa;AAC1E,UAAM,SAAS,iBAAiB,CAAC,MAAM,EAAE,IAAI;AAC7C,UAAM,OAAO,gBAAgB,QAAQ,OAAO;AAC5C,cAAU,MAAM;AACd,oBAAc,IAAI;AAAA,IACpB,GAAG,CAAC,IAAI,CAAC;AACT,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,kBAAkB,iBAAiB;AAC9C;","names":[]}
@@ -1,18 +1,20 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { bH as ButtonVariant } from '../delete-account-setting-DCFS5Vof.js';
3
+ import { bO as ButtonVariant } from '../delete-account-setting-BvdQdbv3.js';
4
4
  import { UseWizardReturn, WizardLabels } from './types.js';
5
5
  import '../components/tooltip.js';
6
6
  import '../hooks/use-anchored-rect.js';
7
7
  import '../lib/clipping.js';
8
- import '../data-table-labels-DUELeXpa.js';
8
+ import '../data-table-labels-kpAAVi6g.js';
9
9
  import '../components/data-table-sort.js';
10
10
  import '../components/swipeable-row.js';
11
11
  import '../components/mini-calendar.js';
12
12
  import '../components/calendar-heatmap.js';
13
13
  import '../components/popover.js';
14
+ import '../hooks/use-breakpoint.js';
14
15
  import '../components/chip.js';
15
16
  import 'lucide-react';
17
+ import '../components/field-parts.js';
16
18
  import '../components/field-sync.js';
17
19
  import '../tour/tour.js';
18
20
  import '../search/command-palette.js';
@@ -80,6 +82,9 @@ import '../admin/admin-parts.js';
80
82
  import '../admin/admin-action-log.js';
81
83
  import '../settings/settings-labels.js';
82
84
  import '../demo/demo-labels.js';
85
+ import '../components/appearance-labels.js';
86
+ import '../theme/contrast.js';
87
+ import '../theme/text-size.js';
83
88
  import '../components/reauth-dialog.js';
84
89
  import '../components/server-wake.js';
85
90
  import '../lib/server-wake.js';
@@ -93,7 +98,7 @@ import '../components/sign-chip.js';
93
98
  import '../components/column-mapper.js';
94
99
  import '../lib/column-mapping.js';
95
100
  import '../lib/table-text.js';
96
- import '../components/field-parts.js';
101
+ import '../components/focus-ring.js';
97
102
 
98
103
  /**
99
104
  * The chrome around a wizard: the step indicator, the step content, and the
package/dist/wizard.d.ts CHANGED
@@ -6,18 +6,20 @@ export { WizardStep, WizardStepProps } from './wizard/wizard-step.js';
6
6
  export { WizardSummary } from './wizard/wizard-summary.js';
7
7
  export { StepperNav } from './wizard/stepper-nav.js';
8
8
  import 'react';
9
- import './delete-account-setting-DCFS5Vof.js';
9
+ import './delete-account-setting-BvdQdbv3.js';
10
10
  import './components/tooltip.js';
11
11
  import './hooks/use-anchored-rect.js';
12
12
  import './lib/clipping.js';
13
- import './data-table-labels-DUELeXpa.js';
13
+ import './data-table-labels-kpAAVi6g.js';
14
14
  import './components/data-table-sort.js';
15
15
  import './components/swipeable-row.js';
16
16
  import './components/mini-calendar.js';
17
17
  import './components/calendar-heatmap.js';
18
18
  import './components/popover.js';
19
+ import './hooks/use-breakpoint.js';
19
20
  import './components/chip.js';
20
21
  import 'lucide-react';
22
+ import './components/field-parts.js';
21
23
  import './components/field-sync.js';
22
24
  import './tour/tour.js';
23
25
  import './search/command-palette.js';
@@ -85,6 +87,9 @@ import './admin/admin-parts.js';
85
87
  import './admin/admin-action-log.js';
86
88
  import './settings/settings-labels.js';
87
89
  import './demo/demo-labels.js';
90
+ import './components/appearance-labels.js';
91
+ import './theme/contrast.js';
92
+ import './theme/text-size.js';
88
93
  import './components/reauth-dialog.js';
89
94
  import './components/server-wake.js';
90
95
  import './lib/server-wake.js';
@@ -98,4 +103,4 @@ import './components/sign-chip.js';
98
103
  import './components/column-mapper.js';
99
104
  import './lib/column-mapping.js';
100
105
  import './lib/table-text.js';
101
- import './components/field-parts.js';
106
+ import './components/focus-ring.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eifi1/ui-kit",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "type": "module",
5
5
  "description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
6
6
  "_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives. Re-checked when `flag-icons` left `dependencies` and tsup started emitting a `\"use client\"` banner: neither touches this. The flag stylesheet was never imported from JS in the first place (that was the finding), and a directive prologue is a string literal every bundler lifts out before it decides whether a module is inert.",
@@ -16,11 +16,18 @@ import { formatPersonName } from "../lib/person-name";
16
16
  export type MaybePromise<T = unknown> = void | Promise<T>;
17
17
 
18
18
  /** A person as the admin parts show one: the two name parts of the auth contract
19
- * (§3.2), an older whole `name` as the fallback, and the address. */
19
+ * (§3.2), an older whole `name` as the fallback, and the address. The name parts are
20
+ * read in either spelling — the kit's `first` / `last`, or server-kit's `PersonRef`
21
+ * as it serialises (`first_name` / `last_name`), so an app hands the server's object
22
+ * over as it came (keksdose's and kastlan's 0.30 adoptions mapped one to the other). */
20
23
  export interface AdminPerson {
21
24
  id?: string | number | null;
22
25
  first?: string | null;
23
26
  last?: string | null;
27
+ /** server-kit `PersonRef.first_name`; `first` wins when both are given. */
28
+ first_name?: string | null;
29
+ /** server-kit `PersonRef.last_name`; `last` wins when both are given. */
30
+ last_name?: string | null;
24
31
  /** A whole name, for an API that still sends `display_name` only. */
25
32
  name?: string | null;
26
33
  email?: string | null;
@@ -39,7 +46,7 @@ export function hasMessage(node: ReactNode): boolean {
39
46
  export function personLabel(person: AdminPerson | null | undefined, locale: string | undefined): string {
40
47
  if (!person) return "";
41
48
  return (
42
- formatPersonName({ first: person.first, last: person.last }, locale) ||
49
+ formatPersonName({ first: person.first ?? person.first_name, last: person.last ?? person.last_name }, locale) ||
43
50
  (person.name ?? "").trim() ||
44
51
  (person.email ?? "").trim()
45
52
  );
@@ -14,7 +14,8 @@ import type { ChipTone } from "../components/chip";
14
14
  import { CopyButton } from "../components/copy-button";
15
15
  import { LanguageSelect } from "../components/language-select";
16
16
  import { SectionLabel, Caption } from "../components/text";
17
- import { Button, IconButton, Input, Select, Spinner } from "../components/ui";
17
+ import { RowActions } from "../components/row-actions";
18
+ import { Button, Input, Select, Spinner } from "../components/ui";
18
19
  import { useWriteLock } from "../components/write-lock";
19
20
  import { hasMessage, settle, useDayText, usePersonLabel } from "./admin-parts";
20
21
  import type { AdminPerson, MaybePromise } from "./admin-parts";
@@ -480,35 +481,41 @@ export function InvitationsPanel<R extends string = string>({
480
481
  </div>
481
482
  {(link || resendable || revocable) && (
482
483
  <div className="ms-auto flex shrink-0 items-center gap-1">
484
+ {/* The copy stays its own icon: it says "Copied" where it was
485
+ pressed, which a menu entry that closes cannot. */}
483
486
  {link && open && <CopyButton size="xs" tone="muted" text={link} label={labels.copyLink} />}
484
- {resendable && (
485
- <IconButton
486
- size="xs"
487
- commit
488
- label={labels.resend(invitation.email)}
489
- pending={busy === `resend:${key}`}
490
- disabled={busy !== null}
491
- onClick={async () => {
492
- const answer = await run(`resend:${key}`, () => onResend(invitation));
493
- if (answer !== FAILED) took(answer, invitation.email, invitation.id);
494
- }}
495
- >
496
- <RefreshCw />
497
- </IconButton>
498
- )}
499
- {revocable && (
500
- <IconButton
501
- size="xs"
502
- tone="danger"
503
- commit
504
- label={labels.revoke(invitation.email)}
505
- pending={busy === `revoke:${key}`}
506
- disabled={busy !== null}
507
- onClick={() => void run(`revoke:${key}`, () => onRevoke(invitation))}
508
- >
509
- <Trash2 />
510
- </IconButton>
511
- )}
487
+ {/* Resend and revoke are the row's actions (docs/text-size-
488
+ harmonization.md §10.8): two icons at Normal, one "⋯" menu at
489
+ Large, where both names as text ("Send … a new link") would
490
+ not fit a phone's row beside the copy icon. */}
491
+ <RowActions
492
+ name={invitation.email}
493
+ actions={[
494
+ resendable && {
495
+ key: "resend",
496
+ label: labels.resend(invitation.email),
497
+ icon: RefreshCw,
498
+ commit: true,
499
+ pending: busy === `resend:${key}`,
500
+ disabled: busy !== null,
501
+ onSelect: () =>
502
+ void (async () => {
503
+ const answer = await run(`resend:${key}`, () => onResend(invitation));
504
+ if (answer !== FAILED) took(answer, invitation.email, invitation.id);
505
+ })(),
506
+ },
507
+ revocable && {
508
+ key: "revoke",
509
+ label: labels.revoke(invitation.email),
510
+ icon: Trash2,
511
+ tone: "danger",
512
+ commit: true,
513
+ pending: busy === `revoke:${key}`,
514
+ disabled: busy !== null,
515
+ onSelect: () => void run(`revoke:${key}`, () => onRevoke(invitation)),
516
+ },
517
+ ]}
518
+ />
512
519
  </div>
513
520
  )}
514
521
  </div>
@@ -1,7 +1,5 @@
1
1
  import { useMemo } from "react";
2
2
  import type { ReactNode } from "react";
3
- import { MoreHorizontal } from "lucide-react";
4
- import type { LucideIcon } from "lucide-react";
5
3
  import { cn } from "../lib/cn";
6
4
  import type { DateInput } from "../lib/format";
7
5
  import type { PersonName } from "../lib/person-name";
@@ -12,8 +10,8 @@ import type { ChipTone } from "../components/chip";
12
10
  import type { DataTableColumn } from "../components/data-table";
13
11
  import { encodeSorts } from "../components/data-table-sort";
14
12
  import type { SortState } from "../components/data-table-sort";
15
- import { Popover } from "../components/popover";
16
- import { Button, IconButton } from "../components/ui";
13
+ import { RowActions } from "../components/row-actions";
14
+ import type { RowAction, RowActionList } from "../components/row-actions";
17
15
  import { UserAvatar } from "../components/user-avatar";
18
16
  import { personLabel } from "./admin-parts";
19
17
  import { RoleSelect } from "./role-select";
@@ -213,27 +211,13 @@ export function UserIdentityCell({
213
211
 
214
212
  /* ── The row's actions ──────────────────────────────────────────────────────── */
215
213
 
216
- /** One entry of a row's action menu (§4). */
217
- export interface UserRowAction {
218
- /** React key. Default: the label. */
219
- key?: string;
220
- label: string;
221
- /** Runs it — usually opens `AdminActionConfirm` with the level the server states. */
222
- onSelect: () => void;
223
- icon?: LucideIcon;
224
- /** `danger` for deactivate, transfer, erase. */
225
- tone?: "default" | "danger";
226
- /** Why it is not available for this row ("You can’t deactivate yourself"). The entry
227
- * stays in the menu, focusable, and says so — an action that silently vanished
228
- * would leave the admin looking for it. */
229
- disabledReason?: ReactNode;
230
- /** Leave it out for this row. */
231
- hidden?: boolean;
232
- }
214
+ /** One entry of a row's action menu (§4) — the kit's {@link RowAction} since 0.32,
215
+ * which generalised it for every dense row (docs/text-size-harmonization.md §10.8). */
216
+ export type UserRowAction = RowAction;
233
217
 
234
218
  /** What `actions(row)` may return: entries, and `false` / `null` for the ones a
235
219
  * condition left out (`row.is_active && {…}`). */
236
- export type UserRowActionList = readonly (UserRowAction | false | null | undefined)[];
220
+ export type UserRowActionList = RowActionList;
237
221
 
238
222
  export interface UserRowActionsProps {
239
223
  actions: UserRowActionList;
@@ -248,59 +232,19 @@ export interface UserRowActionsProps {
248
232
  * a phone card, and both grow with every action §2.4 adds. The menu is portalled, so a
249
233
  * table's scroller cannot clip it; Escape and an outside press close it, and focus goes
250
234
  * back to the button, which is where a confirmation dialog then returns it.
235
+ *
236
+ * Since 0.32 this is {@link RowActions} with `collapse="menu"` — the menu at every size —
237
+ * named by the roster's own `actionsFor`.
251
238
  */
252
239
  export function UserRowActions({ actions, name, labels: labelsProp }: UserRowActionsProps) {
253
240
  const labels = useKitLabels("userRoster", DEFAULT_USER_ROSTER_LABELS, labelsProp);
254
- const entries = actions.filter((a): a is UserRowAction => Boolean(a) && !(a as UserRowAction).hidden);
255
- if (entries.length === 0) return null;
256
- const title = labels.actionsFor(name);
257
241
  return (
258
- <Popover
259
- width={240}
260
- aria-label={title}
261
- className="p-1"
262
- trigger={({ open, toggle, ref }) => (
263
- <IconButton
264
- ref={ref}
265
- size="xs"
266
- label={title}
267
- aria-haspopup="dialog"
268
- aria-expanded={open}
269
- stopPropagation
270
- onClick={toggle}
271
- >
272
- <MoreHorizontal />
273
- </IconButton>
274
- )}
275
- >
276
- {(close) => (
277
- <ul className="space-y-0.5">
278
- {entries.map((action) => {
279
- const Icon = action.icon;
280
- return (
281
- <li key={action.key ?? action.label}>
282
- <Button
283
- type="button"
284
- variant="ghost"
285
- size="sm"
286
- stretch
287
- tone={action.tone === "danger" ? "danger" : undefined}
288
- disabledReason={action.disabledReason}
289
- className="justify-start"
290
- onClick={() => {
291
- close();
292
- action.onSelect();
293
- }}
294
- >
295
- {Icon && <Icon className="size-4 shrink-0" aria-hidden />}
296
- {action.label}
297
- </Button>
298
- </li>
299
- );
300
- })}
301
- </ul>
302
- )}
303
- </Popover>
242
+ <RowActions
243
+ actions={actions}
244
+ name={name}
245
+ collapse="menu"
246
+ labels={{ actionsFor: labels.actionsFor }}
247
+ />
304
248
  );
305
249
  }
306
250
 
@@ -507,7 +451,8 @@ export function userRosterColumns<T extends UserRosterRow, R extends string = st
507
451
  column: { mobileHidden: true },
508
452
  }),
509
453
  // How long ago rather than a date — the question is "recently?" — with the exact
510
- // time in a tooltip (keksdose's last-seen column, Kurvenschmiede's).
454
+ // time beside it (keksdose's last-seen column, Kurvenschmiede's; 0.32 §4: never only
455
+ // in a tooltip).
511
456
  dateColumn<T>({
512
457
  key: "last_login",
513
458
  header: labels.lastLogin,
@@ -86,9 +86,35 @@ export type DemoErrorCode =
86
86
  | "demo_read_only"
87
87
  | "demo_refused";
88
88
 
89
- /** Every code the kit reads: the sign-in refusals, the account ones (0.30.0) and the
90
- * demo's (0.31.0). */
91
- export type KitErrorCode = AuthErrorCode | AccountErrorCode | DemoErrorCode;
89
+ /**
90
+ * 0.32.0: billing's coded refusals (docs/billing-harmonization.md §3.3, §3.4, §4, §12.3) —
91
+ * server-kit 0.6's `BillingError` and `PlanLimitError`, value for value, answered as
92
+ * `{detail, code}` like the others and read by the same {@link authErrorCode}.
93
+ *
94
+ * - `billing_disabled` — `404`: the billing switch is off (§2.9), so every billing route
95
+ * but `GET /billing/status` is "not there", as a switched-off demo is.
96
+ * - `billing_read_only` — `402`: a write by (or into the data of) a payer out of good
97
+ * standing (§3.3, §12.1). Inside a sync reply it marks the changes that were refused
98
+ * and not applied (§12.5). The app's write lock and banner already say why, so — like
99
+ * `demo_read_only` — it needs no toast of its own.
100
+ * - `plan_limit` — `402`: a create past the plan's limit (§3.4), with the extra fields
101
+ * `dimension`, `plan`, `limit` and `used` — read them with `isPlanLimit`. A lapsed payer
102
+ * creating gets `billing_read_only` instead, never this (§12.3).
103
+ * - `billing_not_configured` — billing is switched on but the provider's settings are
104
+ * missing (the secrets, a price id): the operator's mistake, not the payer's.
105
+ */
106
+ export type BillingErrorCode = "billing_disabled" | "billing_read_only" | "plan_limit" | "billing_not_configured";
107
+
108
+ /** Every code the kit reads: the sign-in refusals, the account ones (0.30.0), the demo's
109
+ * (0.31.0) and billing's (0.32.0). */
110
+ export type KitErrorCode = AuthErrorCode | AccountErrorCode | DemoErrorCode | BillingErrorCode;
111
+
112
+ const BILLING_CODES: ReadonlySet<BillingErrorCode> = new Set<BillingErrorCode>([
113
+ "billing_disabled",
114
+ "billing_read_only",
115
+ "plan_limit",
116
+ "billing_not_configured",
117
+ ]);
92
118
 
93
119
  const CODES: ReadonlySet<string> = new Set<KitErrorCode>([
94
120
  "invalid_credentials",
@@ -112,6 +138,7 @@ const CODES: ReadonlySet<string> = new Set<KitErrorCode>([
112
138
  "demo_not_ready",
113
139
  "demo_read_only",
114
140
  "demo_refused",
141
+ ...BILLING_CODES,
115
142
  ]);
116
143
 
117
144
  type Bag = Record<string, unknown>;
@@ -132,8 +159,8 @@ function codeOfBody(body: unknown): KitErrorCode | undefined {
132
159
 
133
160
  /**
134
161
  * The code an error carries — an {@link AuthErrorCode}, since 0.30.0 an
135
- * {@link AccountErrorCode}, since 0.31.0 a {@link DemoErrorCode} — or `undefined` when it
136
- * carries none.
162
+ * {@link AccountErrorCode}, since 0.31.0 a {@link DemoErrorCode}, since 0.32.0 a
163
+ * {@link BillingErrorCode} — or `undefined` when it carries none.
137
164
  *
138
165
  * Reads the parsed response body wherever the apps' HTTP clients put it, without
139
166
  * depending on any of them:
@@ -148,7 +175,7 @@ function codeOfBody(body: unknown): KitErrorCode | undefined {
148
175
  * it is one of the codes above: axios writes its OWN codes there (`ERR_NETWORK`,
149
176
  * `ECONNABORTED`, `ERR_BAD_REQUEST`), and so does Node, so an unknown value is never
150
177
  * read as a refusal. The HTTP status is not checked: the code is the contract, and the
151
- * status differs per refusal (401, 403, 409, 410).
178
+ * status differs per refusal (401, 402, 403, 404, 409, 410).
152
179
  */
153
180
  export function authErrorCode(err: unknown): KitErrorCode | undefined {
154
181
  if (!isBag(err)) return undefined;
@@ -173,13 +200,47 @@ export function authErrorCode(err: unknown): KitErrorCode | undefined {
173
200
  * with, so an app's `onSubmit` can simply let its client's error through. See
174
201
  * {@link authErrorCode} for the shapes it reads. Since 0.30.0 it knows the account codes
175
202
  * too: `isAuthError(err, "last_admin")`; since 0.31.0 the demo's:
176
- * `isAuthError(err, "demo_read_only")`.
203
+ * `isAuthError(err, "demo_read_only")`; since 0.32.0 billing's:
204
+ * `isAuthError(err, "billing_read_only")` (or {@link isBillingError}).
177
205
  */
178
206
  export function isAuthError(err: unknown, code?: KitErrorCode): boolean {
179
207
  const found = authErrorCode(err);
180
208
  return found !== undefined && (code === undefined || found === code);
181
209
  }
182
210
 
211
+ /**
212
+ * Whether `err` is one of billing's coded refusals (0.32.0, docs/billing-harmonization.md
213
+ * §10) — any of the four, or the one named. {@link isAuthError} answers the same for a
214
+ * named code; this one also answers "is it billing's at all", which an app's error
215
+ * handler asks before it decides between the write lock, a `PlanLimitNotice` and a toast.
216
+ */
217
+ export function isBillingError(err: unknown, code?: BillingErrorCode): boolean {
218
+ const found = authErrorCode(err);
219
+ return found !== undefined && BILLING_CODES.has(found as BillingErrorCode) && (code === undefined || found === code);
220
+ }
221
+
222
+ /**
223
+ * The parsed body that carries a code, wherever {@link authErrorCode} would find it (in
224
+ * the same order) — the object with the `code`, so FastAPI's nested `detail` when the code
225
+ * is there. For a reader of a refusal's EXTRA fields (`isPlanLimit`: `dimension`, `limit`,
226
+ * `used` sit beside the code). `accept` decides which codes count; default, the known ones.
227
+ *
228
+ * @internal Not part of the barrel.
229
+ */
230
+ export function errorBodyWithCode(
231
+ err: unknown,
232
+ accept: (code: unknown) => boolean = (code) => known(code) !== undefined,
233
+ ): Record<string, unknown> | undefined {
234
+ if (!isBag(err)) return undefined;
235
+ const response = err.response;
236
+ for (const body of [isBag(response) ? response.data : undefined, err.data, err.body, err]) {
237
+ if (!isBag(body)) continue;
238
+ if (accept(body.code)) return body;
239
+ if (isBag(body.detail) && accept(body.detail.code)) return body.detail;
240
+ }
241
+ return undefined;
242
+ }
243
+
183
244
  /** An HTTP status as a number, or undefined. */
184
245
  function statusOf(value: unknown): number | undefined {
185
246
  return typeof value === "number" && Number.isInteger(value) ? value : undefined;
@@ -144,7 +144,7 @@ export interface ResetPasswordFormProps
144
144
  forgotHref?: string;
145
145
  /** The app's words for a failed save (the device offline), or `undefined` for the
146
146
  * kit's: `rateLimited` for a bare `429` (0.30.0), else `failed`. Not asked for
147
- * `token_invalid`, which is a state. */
147
+ * `token_invalid` or `token_expired`, which are a state. */
148
148
  describeError?: (error: unknown) => ReactNode | undefined;
149
149
  /** The heading. Default `labels.title`; `null` draws none, for a page that puts it
150
150
  * in `AuthLayout`'s `title` instead. */
@@ -299,7 +299,11 @@ export function ResetPasswordForm({
299
299
  },
300
300
  (error: unknown) => {
301
301
  setPending(false);
302
- if (authErrorCode(error) === "token_invalid") {
302
+ // A link that died between the check and the save — used, or run out — is the
303
+ // dead-link state, under either code (0.31.1: keksdose mapped `token_expired`
304
+ // to it itself, `asDeadLink`).
305
+ const code = authErrorCode(error);
306
+ if (code === "token_invalid" || code === "token_expired") {
303
307
  setPassword("");
304
308
  setConfirm("");
305
309
  setPhase({ token, phase: { kind: "invalid" } });