@eifi1/ui-kit 0.31.1 → 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 (538) hide show
  1. package/README.md +51 -28
  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/invitations-panel.d.ts +8 -3
  5. package/dist/admin/invitations-panel.js +29 -26
  6. package/dist/admin/invitations-panel.js.map +1 -1
  7. package/dist/admin/reviewer-scope-editor.d.ts +8 -3
  8. package/dist/admin/role-select.d.ts +10 -5
  9. package/dist/admin/transfer-ownership-dialog.d.ts +8 -3
  10. package/dist/admin/user-roster.d.ts +10 -5
  11. package/dist/admin/user-roster.js +8 -47
  12. package/dist/admin/user-roster.js.map +1 -1
  13. package/dist/auth/auth-errors.d.ts +43 -8
  14. package/dist/auth/auth-errors.js +24 -1
  15. package/dist/auth/auth-errors.js.map +1 -1
  16. package/dist/auth/register-form.d.ts +8 -3
  17. package/dist/billing/billing-banner.d.ts +184 -0
  18. package/dist/billing/billing-banner.js +98 -0
  19. package/dist/billing/billing-banner.js.map +1 -0
  20. package/dist/billing/billing-labels.d.ts +100 -0
  21. package/dist/billing/billing-labels.js +74 -0
  22. package/dist/billing/billing-labels.js.map +1 -0
  23. package/dist/billing/billing-lock.d.ts +158 -0
  24. package/dist/billing/billing-lock.js +17 -0
  25. package/dist/billing/billing-lock.js.map +1 -0
  26. package/dist/billing/billing-standing.d.ts +149 -0
  27. package/dist/billing/billing-standing.js +28 -0
  28. package/dist/billing/billing-standing.js.map +1 -0
  29. package/dist/billing/plan-card.d.ts +248 -0
  30. package/dist/billing/plan-card.js +248 -0
  31. package/dist/billing/plan-card.js.map +1 -0
  32. package/dist/billing/plan-limit-notice.d.ts +156 -0
  33. package/dist/billing/plan-limit-notice.js +76 -0
  34. package/dist/billing/plan-limit-notice.js.map +1 -0
  35. package/dist/billing/plan-limit.d.ts +34 -0
  36. package/dist/billing/plan-limit.js +20 -0
  37. package/dist/billing/plan-limit.js.map +1 -0
  38. package/dist/billing/plan-price.d.ts +61 -0
  39. package/dist/billing/plan-price.js +45 -0
  40. package/dist/billing/plan-price.js.map +1 -0
  41. package/dist/billing/subscription-actions.d.ts +133 -0
  42. package/dist/billing/subscription-actions.js +28 -0
  43. package/dist/billing/subscription-actions.js.map +1 -0
  44. package/dist/billing/subscription-status-chip.d.ts +149 -0
  45. package/dist/billing/subscription-status-chip.js +48 -0
  46. package/dist/billing/subscription-status-chip.js.map +1 -0
  47. package/dist/chart.d.ts +9 -3
  48. package/dist/components/account-chips.d.ts +16 -4
  49. package/dist/components/account-chips.js +21 -3
  50. package/dist/components/account-chips.js.map +1 -1
  51. package/dist/components/alert-banner.d.ts +6 -1
  52. package/dist/components/alert-banner.js +4 -3
  53. package/dist/components/alert-banner.js.map +1 -1
  54. package/dist/components/amount-input.d.ts +8 -3
  55. package/dist/components/amount-input.js +5 -5
  56. package/dist/components/amount-input.js.map +1 -1
  57. package/dist/components/appearance-labels.d.ts +32 -0
  58. package/dist/components/appearance-labels.js +18 -0
  59. package/dist/components/appearance-labels.js.map +1 -0
  60. package/dist/components/autocomplete.js +2 -2
  61. package/dist/components/autocomplete.js.map +1 -1
  62. package/dist/components/breadcrumbs.js +3 -2
  63. package/dist/components/breadcrumbs.js.map +1 -1
  64. package/dist/components/bulk-action-bar.d.ts +2 -2
  65. package/dist/components/bulk-action-bar.js +7 -12
  66. package/dist/components/bulk-action-bar.js.map +1 -1
  67. package/dist/components/button-group.d.ts +8 -3
  68. package/dist/components/calculator.d.ts +8 -3
  69. package/dist/components/calculator.js +4 -3
  70. package/dist/components/calculator.js.map +1 -1
  71. package/dist/components/calendar-heatmap.js +5 -5
  72. package/dist/components/calendar-heatmap.js.map +1 -1
  73. package/dist/components/chart-height.d.ts +23 -0
  74. package/dist/components/chart-height.js +15 -0
  75. package/dist/components/chart-height.js.map +1 -0
  76. package/dist/components/chart-zoom.js +6 -1
  77. package/dist/components/chart-zoom.js.map +1 -1
  78. package/dist/components/checkbox-group.js +2 -2
  79. package/dist/components/checkbox-group.js.map +1 -1
  80. package/dist/components/checkbox.d.ts +10 -0
  81. package/dist/components/checkbox.js +25 -10
  82. package/dist/components/checkbox.js.map +1 -1
  83. package/dist/components/chip.d.ts +19 -3
  84. package/dist/components/chip.js +47 -21
  85. package/dist/components/chip.js.map +1 -1
  86. package/dist/components/choice-card.d.ts +1 -1
  87. package/dist/components/choice-card.js +8 -7
  88. package/dist/components/choice-card.js.map +1 -1
  89. package/dist/components/column-mapper.js +1 -1
  90. package/dist/components/column-mapper.js.map +1 -1
  91. package/dist/components/combobox-core.js +8 -7
  92. package/dist/components/combobox-core.js.map +1 -1
  93. package/dist/components/combobox.js +8 -8
  94. package/dist/components/combobox.js.map +1 -1
  95. package/dist/components/confirm-dialog.d.ts +8 -3
  96. package/dist/components/copy-button.d.ts +10 -5
  97. package/dist/components/country-select.js +1 -1
  98. package/dist/components/country-select.js.map +1 -1
  99. package/dist/components/currency-select.js +3 -3
  100. package/dist/components/currency-select.js.map +1 -1
  101. package/dist/components/danger-confirm.d.ts +8 -3
  102. package/dist/components/data-table-cells.d.ts +1 -1
  103. package/dist/components/data-table-filter-popover.d.ts +1 -1
  104. package/dist/components/data-table-filter-popover.js +4 -1
  105. package/dist/components/data-table-filter-popover.js.map +1 -1
  106. package/dist/components/data-table-filters.d.ts +1 -1
  107. package/dist/components/data-table-labels.d.ts +1 -1
  108. package/dist/components/data-table-pagination.d.ts +1 -1
  109. package/dist/components/data-table.d.ts +1 -1
  110. package/dist/components/data-table.js +123 -52
  111. package/dist/components/data-table.js.map +1 -1
  112. package/dist/components/date-picker.d.ts +1 -1
  113. package/dist/components/date-picker.js +7 -5
  114. package/dist/components/date-picker.js.map +1 -1
  115. package/dist/components/description-list.js +2 -2
  116. package/dist/components/description-list.js.map +1 -1
  117. package/dist/components/dialog-frame.js +2 -1
  118. package/dist/components/dialog-frame.js.map +1 -1
  119. package/dist/components/disclosure.js +3 -2
  120. package/dist/components/disclosure.js.map +1 -1
  121. package/dist/components/dropdown.d.ts +1 -1
  122. package/dist/components/dropdown.js +7 -1
  123. package/dist/components/dropdown.js.map +1 -1
  124. package/dist/components/facing-pair.d.ts +19 -6
  125. package/dist/components/facing-pair.js +3 -2
  126. package/dist/components/facing-pair.js.map +1 -1
  127. package/dist/components/field-parts.d.ts +102 -6
  128. package/dist/components/field-parts.js +81 -6
  129. package/dist/components/field-parts.js.map +1 -1
  130. package/dist/components/field-sync.d.ts +4 -1
  131. package/dist/components/field-sync.js +12 -2
  132. package/dist/components/field-sync.js.map +1 -1
  133. package/dist/components/field.js +2 -2
  134. package/dist/components/field.js.map +1 -1
  135. package/dist/components/file-button.d.ts +8 -3
  136. package/dist/components/file-dropzone.d.ts +8 -3
  137. package/dist/components/floating-panel.js +7 -4
  138. package/dist/components/floating-panel.js.map +1 -1
  139. package/dist/components/focus-ring.d.ts +12 -0
  140. package/dist/components/focus-ring.js +8 -0
  141. package/dist/components/focus-ring.js.map +1 -0
  142. package/dist/components/form-actions.d.ts +8 -3
  143. package/dist/components/form-actions.js +33 -19
  144. package/dist/components/form-actions.js.map +1 -1
  145. package/dist/components/full-bleed-dialog.d.ts +11 -1
  146. package/dist/components/full-bleed-dialog.js +3 -1
  147. package/dist/components/full-bleed-dialog.js.map +1 -1
  148. package/dist/components/iban-input.d.ts +8 -3
  149. package/dist/components/image-grid.js +1 -1
  150. package/dist/components/image-grid.js.map +1 -1
  151. package/dist/components/inline-edit-field.js +6 -3
  152. package/dist/components/inline-edit-field.js.map +1 -1
  153. package/dist/components/language-select.d.ts +8 -3
  154. package/dist/components/legal-page.d.ts +26 -5
  155. package/dist/components/legal-page.js +3 -1
  156. package/dist/components/legal-page.js.map +1 -1
  157. package/dist/components/legal.d.ts +8 -3
  158. package/dist/components/legal.js +2 -1
  159. package/dist/components/legal.js.map +1 -1
  160. package/dist/components/lightbox.js +24 -2
  161. package/dist/components/lightbox.js.map +1 -1
  162. package/dist/components/line-items.js +2 -1
  163. package/dist/components/line-items.js.map +1 -1
  164. package/dist/components/list.d.ts +5 -0
  165. package/dist/components/list.js +13 -9
  166. package/dist/components/list.js.map +1 -1
  167. package/dist/components/measured-grid.d.ts +3 -1
  168. package/dist/components/measured-grid.js +38 -20
  169. package/dist/components/measured-grid.js.map +1 -1
  170. package/dist/components/menu-item.js +2 -1
  171. package/dist/components/menu-item.js.map +1 -1
  172. package/dist/components/mini-calendar.js +8 -4
  173. package/dist/components/mini-calendar.js.map +1 -1
  174. package/dist/components/money-field.d.ts +8 -3
  175. package/dist/components/month-picker.js +9 -5
  176. package/dist/components/month-picker.js.map +1 -1
  177. package/dist/components/nav-pills.js +1 -1
  178. package/dist/components/nav-pills.js.map +1 -1
  179. package/dist/components/number-field.d.ts +8 -3
  180. package/dist/components/number-input.d.ts +11 -4
  181. package/dist/components/number-input.js +8 -6
  182. package/dist/components/number-input.js.map +1 -1
  183. package/dist/components/numpad-sheet.d.ts +8 -3
  184. package/dist/components/numpad-sheet.js +2 -1
  185. package/dist/components/numpad-sheet.js.map +1 -1
  186. package/dist/components/one-time-code-input.js +2 -2
  187. package/dist/components/one-time-code-input.js.map +1 -1
  188. package/dist/components/page-contents.js +2 -2
  189. package/dist/components/page-contents.js.map +1 -1
  190. package/dist/components/phone-input.d.ts +8 -3
  191. package/dist/components/phone-input.js +2 -2
  192. package/dist/components/phone-input.js.map +1 -1
  193. package/dist/components/pie-chart.d.ts +8 -2
  194. package/dist/components/pie-chart.js +11 -7
  195. package/dist/components/pie-chart.js.map +1 -1
  196. package/dist/components/popover.d.ts +9 -1
  197. package/dist/components/popover.js +8 -4
  198. package/dist/components/popover.js.map +1 -1
  199. package/dist/components/row-actions.d.ts +100 -0
  200. package/dist/components/row-actions.js +165 -0
  201. package/dist/components/row-actions.js.map +1 -0
  202. package/dist/components/scroll-area.js +1 -1
  203. package/dist/components/scroll-area.js.map +1 -1
  204. package/dist/components/search-field.js +3 -2
  205. package/dist/components/search-field.js.map +1 -1
  206. package/dist/components/series-chart-budget.d.ts +30 -7
  207. package/dist/components/series-chart-budget.js +12 -4
  208. package/dist/components/series-chart-budget.js.map +1 -1
  209. package/dist/components/series-chart-ticks.d.ts +7 -1
  210. package/dist/components/series-chart-ticks.js +6 -0
  211. package/dist/components/series-chart-ticks.js.map +1 -1
  212. package/dist/components/series-chart.d.ts +58 -28
  213. package/dist/components/series-chart.js +42 -32
  214. package/dist/components/series-chart.js.map +1 -1
  215. package/dist/components/settings-fields.d.ts +51 -6
  216. package/dist/components/settings-fields.js +37 -0
  217. package/dist/components/settings-fields.js.map +1 -1
  218. package/dist/components/share-card.d.ts +11 -6
  219. package/dist/components/share-card.js +2 -0
  220. package/dist/components/share-card.js.map +1 -1
  221. package/dist/components/sign-chip.d.ts +4 -0
  222. package/dist/components/sign-chip.js +17 -4
  223. package/dist/components/sign-chip.js.map +1 -1
  224. package/dist/components/signature-pad.d.ts +3 -1
  225. package/dist/components/signature-pad.js +8 -5
  226. package/dist/components/signature-pad.js.map +1 -1
  227. package/dist/components/slider.js +7 -6
  228. package/dist/components/slider.js.map +1 -1
  229. package/dist/components/sparkline.d.ts +5 -1
  230. package/dist/components/sparkline.js +4 -2
  231. package/dist/components/sparkline.js.map +1 -1
  232. package/dist/components/stat-tile.d.ts +17 -1
  233. package/dist/components/stat-tile.js +48 -20
  234. package/dist/components/stat-tile.js.map +1 -1
  235. package/dist/components/status-dot.d.ts +4 -0
  236. package/dist/components/swipeable-row.js +2 -1
  237. package/dist/components/swipeable-row.js.map +1 -1
  238. package/dist/components/switch.d.ts +21 -7
  239. package/dist/components/switch.js +24 -11
  240. package/dist/components/switch.js.map +1 -1
  241. package/dist/components/table.js +2 -2
  242. package/dist/components/table.js.map +1 -1
  243. package/dist/components/text-link.d.ts +8 -3
  244. package/dist/components/text-link.js +1 -1
  245. package/dist/components/text-link.js.map +1 -1
  246. package/dist/components/text.d.ts +7 -2
  247. package/dist/components/text.js +5 -5
  248. package/dist/components/text.js.map +1 -1
  249. package/dist/components/tile-radio.js +2 -1
  250. package/dist/components/tile-radio.js.map +1 -1
  251. package/dist/components/time-input.d.ts +8 -3
  252. package/dist/components/toast.d.ts +18 -2
  253. package/dist/components/toast.js +8 -5
  254. package/dist/components/toast.js.map +1 -1
  255. package/dist/components/toggle-group.d.ts +16 -1
  256. package/dist/components/toggle-group.js +40 -11
  257. package/dist/components/toggle-group.js.map +1 -1
  258. package/dist/components/toggle-legend.d.ts +10 -1
  259. package/dist/components/toggle-legend.js +21 -7
  260. package/dist/components/toggle-legend.js.map +1 -1
  261. package/dist/components/translation-review-parts.d.ts +4 -0
  262. package/dist/components/translation-review.d.ts +4 -0
  263. package/dist/components/translation-review.js +3 -3
  264. package/dist/components/translation-review.js.map +1 -1
  265. package/dist/components/tree-view.js +3 -2
  266. package/dist/components/tree-view.js.map +1 -1
  267. package/dist/components/treemap.d.ts +5 -2
  268. package/dist/components/treemap.js +33 -16
  269. package/dist/components/treemap.js.map +1 -1
  270. package/dist/components/ui.d.ts +8 -3
  271. package/dist/components/ui.js +202 -114
  272. package/dist/components/ui.js.map +1 -1
  273. package/dist/components/use-table-state.d.ts +1 -1
  274. package/dist/components/user-avatar.d.ts +5 -1
  275. package/dist/components/user-avatar.js +1 -1
  276. package/dist/components/user-avatar.js.map +1 -1
  277. package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
  278. package/dist/data-table.d.ts +1 -1
  279. package/dist/{delete-account-setting-CpIhzxxZ.d.ts → delete-account-setting-BvdQdbv3.d.ts} +605 -58
  280. package/dist/demo/demo-banner.js +1 -0
  281. package/dist/demo/demo-banner.js.map +1 -1
  282. package/dist/feedback/feedback-attachment.d.ts +8 -3
  283. package/dist/feedback/feedback-context.d.ts +8 -3
  284. package/dist/feedback/feedback-crash.d.ts +8 -3
  285. package/dist/feedback/feedback-dialog.d.ts +8 -3
  286. package/dist/feedback/feedback-dialog.js +3 -3
  287. package/dist/feedback/feedback-dialog.js.map +1 -1
  288. package/dist/feedback/feedback-inbox.d.ts +8 -3
  289. package/dist/feedback/feedback-inbox.js +2 -2
  290. package/dist/feedback/feedback-inbox.js.map +1 -1
  291. package/dist/feedback/feedback-labels.d.ts +8 -3
  292. package/dist/feedback/feedback-menu.d.ts +9 -4
  293. package/dist/feedback/feedback-record.d.ts +8 -3
  294. package/dist/feedback/feedback-row-detail.d.ts +8 -3
  295. package/dist/feedback/feedback-status-undo.d.ts +8 -3
  296. package/dist/feedback/feedback-submit.d.ts +8 -3
  297. package/dist/feedback/feedback-swipe.d.ts +8 -3
  298. package/dist/feedback/feedback-table.d.ts +8 -3
  299. package/dist/feedback/feedback-thread.d.ts +8 -3
  300. package/dist/feedback/feedback-thread.js +3 -3
  301. package/dist/feedback/feedback-thread.js.map +1 -1
  302. package/dist/feedback.d.ts +8 -3
  303. package/dist/hooks/use-anchored-panel.d.ts +32 -3
  304. package/dist/hooks/use-anchored-panel.js +21 -3
  305. package/dist/hooks/use-anchored-panel.js.map +1 -1
  306. package/dist/hooks/use-breakpoint.d.ts +77 -0
  307. package/dist/hooks/use-breakpoint.js +50 -0
  308. package/dist/hooks/use-breakpoint.js.map +1 -0
  309. package/dist/hooks/use-file-drop.d.ts +8 -3
  310. package/dist/hooks/use-large-text.d.ts +29 -0
  311. package/dist/hooks/use-large-text.js +20 -0
  312. package/dist/hooks/use-large-text.js.map +1 -0
  313. package/dist/hooks/use-windowed-rows.d.ts +52 -8
  314. package/dist/hooks/use-windowed-rows.js +94 -5
  315. package/dist/hooks/use-windowed-rows.js.map +1 -1
  316. package/dist/i18n/defaults.d.ts +8 -3
  317. package/dist/i18n/defaults.js +8 -0
  318. package/dist/i18n/defaults.js.map +1 -1
  319. package/dist/i18n/german.d.ts +8 -3
  320. package/dist/i18n/german.js +89 -2
  321. package/dist/i18n/german.js.map +1 -1
  322. package/dist/i18n/kit-labels.d.ts +8 -3
  323. package/dist/i18n/kit-labels.js.map +1 -1
  324. package/dist/i18n/languages.d.ts +10 -5
  325. package/dist/i18n/locales/de-CH.d.ts +8 -3
  326. package/dist/i18n/locales/en.d.ts +8 -3
  327. package/dist/i18n/locales/es.d.ts +8 -3
  328. package/dist/i18n/locales/es.js +84 -2
  329. package/dist/i18n/locales/es.js.map +1 -1
  330. package/dist/i18n/locales/fr.d.ts +8 -3
  331. package/dist/i18n/locales/fr.js +86 -2
  332. package/dist/i18n/locales/fr.js.map +1 -1
  333. package/dist/i18n/locales/hu.d.ts +8 -3
  334. package/dist/i18n/locales/hu.js +86 -2
  335. package/dist/i18n/locales/hu.js.map +1 -1
  336. package/dist/i18n/locales/it.d.ts +8 -3
  337. package/dist/i18n/locales/it.js +87 -2
  338. package/dist/i18n/locales/it.js.map +1 -1
  339. package/dist/i18n/locales/zh.d.ts +8 -3
  340. package/dist/i18n/locales/zh.js +84 -2
  341. package/dist/i18n/locales/zh.js.map +1 -1
  342. package/dist/i18n/review.d.ts +8 -3
  343. package/dist/i18n/review.js +15 -1
  344. package/dist/i18n/review.js.map +1 -1
  345. package/dist/index.d.ts +27 -11
  346. package/dist/index.js +75 -4
  347. package/dist/index.js.map +1 -1
  348. package/dist/landing/landing-actions.d.ts +8 -3
  349. package/dist/landing/landing-sections.d.ts +8 -3
  350. package/dist/landing/public-header.d.ts +9 -4
  351. package/dist/landing/public-header.js +3 -3
  352. package/dist/landing/public-header.js.map +1 -1
  353. package/dist/landing/routing.d.ts +8 -3
  354. package/dist/lib/cn.js +3 -2
  355. package/dist/lib/cn.js.map +1 -1
  356. package/dist/rhf/fields.d.ts +8 -3
  357. package/dist/rhf/fields.js +1 -1
  358. package/dist/rhf/fields.js.map +1 -1
  359. package/dist/rhf/form.d.ts +8 -3
  360. package/dist/rhf/form.js +2 -2
  361. package/dist/rhf/form.js.map +1 -1
  362. package/dist/rhf.d.ts +8 -3
  363. package/dist/search/command-palette.d.ts +1 -1
  364. package/dist/search/command-palette.js +9 -8
  365. package/dist/search/command-palette.js.map +1 -1
  366. package/dist/settings/settings-layout.js +2 -1
  367. package/dist/settings/settings-layout.js.map +1 -1
  368. package/dist/settings/settings-section.d.ts +8 -3
  369. package/dist/settings/use-account-appearance.d.ts +92 -0
  370. package/dist/settings/use-account-appearance.js +63 -0
  371. package/dist/settings/use-account-appearance.js.map +1 -0
  372. package/dist/settings/use-settings-route.d.ts +1 -1
  373. package/dist/settings/use-settings-route.js +2 -3
  374. package/dist/settings/use-settings-route.js.map +1 -1
  375. package/dist/shell/app-shell.d.ts +12 -171
  376. package/dist/shell/app-shell.js +144 -28
  377. package/dist/shell/app-shell.js.map +1 -1
  378. package/dist/shell/auth-layout.js +1 -1
  379. package/dist/shell/auth-layout.js.map +1 -1
  380. package/dist/shell/option-switcher-menu.js +1 -1
  381. package/dist/shell/option-switcher-menu.js.map +1 -1
  382. package/dist/shell/top-bar-brand.d.ts +8 -3
  383. package/dist/shell/top-bar-brand.js +3 -1
  384. package/dist/shell/top-bar-brand.js.map +1 -1
  385. package/dist/shell/topbar-action-menu.d.ts +4 -0
  386. package/dist/shell/topbar-action-menu.js +3 -3
  387. package/dist/shell/topbar-action-menu.js.map +1 -1
  388. package/dist/shell/topbar-controls.js +4 -4
  389. package/dist/shell/topbar-controls.js.map +1 -1
  390. package/dist/shell.d.ts +9 -5
  391. package/dist/theme/contrast-tokens.d.ts +41 -0
  392. package/dist/theme/contrast-tokens.js +59 -0
  393. package/dist/theme/contrast-tokens.js.map +1 -0
  394. package/dist/theme/contrast.d.ts +109 -0
  395. package/dist/theme/contrast.js +128 -0
  396. package/dist/theme/contrast.js.map +1 -0
  397. package/dist/theme/palette-presets.d.ts +12 -1
  398. package/dist/theme/palette-presets.js +9 -4
  399. package/dist/theme/palette-presets.js.map +1 -1
  400. package/dist/theme/palette-store.js.map +1 -1
  401. package/dist/theme/text-size.d.ts +157 -0
  402. package/dist/theme/text-size.js +121 -0
  403. package/dist/theme/text-size.js.map +1 -0
  404. package/dist/wizard/stepper-nav.d.ts +8 -3
  405. package/dist/wizard.d.ts +8 -3
  406. package/package.json +1 -1
  407. package/src/admin/invitations-panel.tsx +36 -29
  408. package/src/admin/user-roster.tsx +17 -72
  409. package/src/auth/auth-errors.ts +68 -7
  410. package/src/billing/billing-banner.tsx +205 -0
  411. package/src/billing/billing-labels.ts +224 -0
  412. package/src/billing/billing-lock.ts +75 -0
  413. package/src/billing/billing-standing.ts +72 -0
  414. package/src/billing/plan-card.tsx +458 -0
  415. package/src/billing/plan-limit-notice.tsx +145 -0
  416. package/src/billing/plan-limit.ts +53 -0
  417. package/src/billing/plan-price.ts +101 -0
  418. package/src/billing/subscription-actions.tsx +76 -0
  419. package/src/billing/subscription-status-chip.tsx +95 -0
  420. package/src/components/account-chips.tsx +36 -10
  421. package/src/components/alert-banner.tsx +11 -6
  422. package/src/components/amount-input.tsx +5 -5
  423. package/src/components/appearance-labels.ts +40 -0
  424. package/src/components/autocomplete.tsx +2 -2
  425. package/src/components/breadcrumbs.tsx +3 -3
  426. package/src/components/bulk-action-bar.tsx +11 -14
  427. package/src/components/calculator.tsx +4 -3
  428. package/src/components/calendar-heatmap.tsx +5 -5
  429. package/src/components/chart-height.ts +30 -0
  430. package/src/components/chart-zoom.tsx +6 -1
  431. package/src/components/checkbox-group.tsx +2 -2
  432. package/src/components/checkbox.tsx +62 -20
  433. package/src/components/chip.tsx +105 -24
  434. package/src/components/choice-card.tsx +15 -10
  435. package/src/components/column-mapper.tsx +1 -1
  436. package/src/components/combobox-core.tsx +8 -7
  437. package/src/components/combobox.tsx +8 -8
  438. package/src/components/country-select.tsx +1 -1
  439. package/src/components/currency-select.tsx +3 -3
  440. package/src/components/data-table-filter-popover.tsx +7 -1
  441. package/src/components/data-table.tsx +160 -43
  442. package/src/components/date-picker.tsx +12 -9
  443. package/src/components/description-list.tsx +2 -2
  444. package/src/components/dialog-frame.tsx +2 -1
  445. package/src/components/disclosure.tsx +3 -2
  446. package/src/components/dropdown.tsx +10 -2
  447. package/src/components/facing-pair.ts +12 -5
  448. package/src/components/field-parts.tsx +190 -7
  449. package/src/components/field-sync.tsx +17 -3
  450. package/src/components/field.tsx +2 -2
  451. package/src/components/floating-panel.tsx +7 -4
  452. package/src/components/focus-ring.ts +12 -0
  453. package/src/components/form-actions.tsx +55 -18
  454. package/src/components/full-bleed-dialog.tsx +18 -4
  455. package/src/components/image-grid.tsx +1 -1
  456. package/src/components/inline-edit-field.tsx +6 -3
  457. package/src/components/legal-page.tsx +28 -2
  458. package/src/components/legal.tsx +19 -1
  459. package/src/components/lightbox.tsx +24 -2
  460. package/src/components/line-items.tsx +7 -1
  461. package/src/components/list.tsx +36 -10
  462. package/src/components/measured-grid.tsx +49 -24
  463. package/src/components/menu-item.tsx +2 -2
  464. package/src/components/mini-calendar.tsx +8 -4
  465. package/src/components/month-picker.tsx +9 -5
  466. package/src/components/nav-pills.tsx +1 -1
  467. package/src/components/number-input.tsx +14 -7
  468. package/src/components/numpad-sheet.tsx +2 -2
  469. package/src/components/one-time-code-input.tsx +2 -2
  470. package/src/components/page-contents.tsx +2 -2
  471. package/src/components/phone-input.tsx +2 -2
  472. package/src/components/pie-chart.tsx +19 -9
  473. package/src/components/popover.tsx +22 -5
  474. package/src/components/row-actions.tsx +344 -0
  475. package/src/components/scroll-area.tsx +1 -1
  476. package/src/components/search-field.tsx +4 -2
  477. package/src/components/series-chart-budget.ts +40 -8
  478. package/src/components/series-chart-ticks.ts +10 -0
  479. package/src/components/series-chart.tsx +123 -72
  480. package/src/components/settings-fields.tsx +81 -2
  481. package/src/components/share-card.tsx +10 -0
  482. package/src/components/sign-chip.tsx +28 -4
  483. package/src/components/signature-pad.tsx +13 -7
  484. package/src/components/slider.tsx +22 -9
  485. package/src/components/sparkline.tsx +11 -3
  486. package/src/components/stat-tile.tsx +70 -14
  487. package/src/components/swipeable-row.tsx +2 -1
  488. package/src/components/switch.tsx +57 -23
  489. package/src/components/table.tsx +2 -2
  490. package/src/components/text-link.tsx +1 -1
  491. package/src/components/text.tsx +11 -6
  492. package/src/components/tile-radio.tsx +4 -1
  493. package/src/components/toast.tsx +27 -7
  494. package/src/components/toggle-group.tsx +99 -12
  495. package/src/components/toggle-legend.tsx +53 -8
  496. package/src/components/translation-review.tsx +10 -5
  497. package/src/components/tree-view.tsx +3 -2
  498. package/src/components/treemap.tsx +27 -10
  499. package/src/components/ui.tsx +331 -145
  500. package/src/components/user-avatar.tsx +1 -1
  501. package/src/demo/demo-banner.tsx +5 -0
  502. package/src/feedback/feedback-dialog.tsx +3 -3
  503. package/src/feedback/feedback-inbox.tsx +2 -2
  504. package/src/feedback/feedback-thread.tsx +3 -3
  505. package/src/hooks/use-anchored-panel.ts +67 -5
  506. package/src/hooks/use-breakpoint.ts +121 -0
  507. package/src/hooks/use-large-text.ts +41 -0
  508. package/src/hooks/use-windowed-rows.ts +168 -12
  509. package/src/i18n/defaults.ts +8 -0
  510. package/src/i18n/german.ts +103 -0
  511. package/src/i18n/kit-labels.tsx +12 -0
  512. package/src/i18n/locales/es.ts +94 -0
  513. package/src/i18n/locales/fr.ts +98 -0
  514. package/src/i18n/locales/hu.ts +96 -0
  515. package/src/i18n/locales/it.ts +99 -0
  516. package/src/i18n/locales/zh.ts +90 -0
  517. package/src/i18n/review.ts +14 -0
  518. package/src/index.ts +74 -6
  519. package/src/landing/public-header.tsx +4 -4
  520. package/src/lib/cn.ts +13 -2
  521. package/src/rhf/fields.tsx +1 -1
  522. package/src/rhf/form.tsx +2 -2
  523. package/src/search/command-palette.tsx +10 -9
  524. package/src/settings/settings-layout.tsx +2 -1
  525. package/src/settings/use-account-appearance.ts +148 -0
  526. package/src/settings/use-settings-route.ts +3 -4
  527. package/src/shell/app-shell.tsx +278 -54
  528. package/src/shell/auth-layout.tsx +1 -1
  529. package/src/shell/option-switcher-menu.tsx +1 -1
  530. package/src/shell/top-bar-brand.tsx +3 -1
  531. package/src/shell/topbar-action-menu.tsx +3 -3
  532. package/src/shell/topbar-controls.tsx +4 -4
  533. package/src/theme/contrast-tokens.ts +112 -0
  534. package/src/theme/contrast.ts +224 -0
  535. package/src/theme/palette-presets.ts +24 -4
  536. package/src/theme/palette-store.ts +6 -1
  537. package/src/theme/text-size.ts +267 -0
  538. package/tokens.css +143 -1
@@ -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-CpIhzxxZ.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-CpIhzxxZ.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.1",
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.",
@@ -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;
@@ -0,0 +1,205 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from "react";
2
+ import { CreditCard, Hourglass, PencilOff } from "lucide-react";
3
+
4
+ import type { LabelOverride } from "../i18n/kit-labels";
5
+ import { AlertBanner } from "../components/alert-banner";
6
+ import type { AlertSize, AlertTone } from "../components/alert-banner";
7
+ import { Button, Spinner } from "../components/ui";
8
+ import { useBillingLabels } from "./billing-labels";
9
+ import type { BillingLabels } from "./billing-labels";
10
+
11
+ /**
12
+ * The billing banners (docs/billing-harmonization.md §3.3, §7, §12.6, §12.21):
13
+ *
14
+ * - `trial-ending` — from 7 days before `trial_ends_at` (§3.3);
15
+ * - `grant-ending` — from 7 days before `comped_until`: a beta user's 12 months, an
16
+ * operator's grant;
17
+ * - `payment-failed` — from the first failed payment (`past_due`), while the provider
18
+ * retries;
19
+ * - `plan-ended` — out of good standing: read-only, never locked (§2.3);
20
+ * - `guest` — a guest in someone else's read-only item (§12.6);
21
+ * - `processing` — back from checkout before the webhook landed (§12.21).
22
+ */
23
+ export type BillingBannerKind =
24
+ | "trial-ending"
25
+ | "grant-ending"
26
+ | "payment-failed"
27
+ | "plan-ended"
28
+ | "guest"
29
+ | "processing";
30
+
31
+ interface BillingBannerBaseProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "onClick"> {
32
+ /**
33
+ * Over the kind's tone: `trial-ending` and `grant-ending` are `info` until the last
34
+ * three days and `warning` in them; `payment-failed` is `danger` (money did not
35
+ * arrive — kastlan's past-due strip was red); `plan-ended` is `warning`, a state with
36
+ * everything still there rather than an error; `guest` and `processing` are `info`.
37
+ */
38
+ tone?: AlertTone;
39
+ /** `strip` (default): the band under the top bar, on every page. `box`: the same
40
+ * message on the subscription page itself. */
41
+ variant?: "strip" | "box";
42
+ /** Default `sm` for a strip (the demo's banner), `md` for a box. */
43
+ size?: AlertSize;
44
+ /**
45
+ * Where the action goes — the subscription page, say. The app's action, as an `href`
46
+ * or as {@link onAction}; without either there is no button. Never a write-locked
47
+ * commit: billing itself always works (§3.3).
48
+ */
49
+ actionHref?: string;
50
+ /** What the action does — the app's portal call (`POST /billing/portal`, then off to
51
+ * its `url`). The kit sends no request. */
52
+ onAction?: () => void;
53
+ /**
54
+ * Over the kind's action words: "Choose a plan" (trial, grant, plan ended), "Update
55
+ * payment method" (payment failed), "Check again" (processing). The guest banner has
56
+ * none of its own: an action there needs this.
57
+ */
58
+ actionLabel?: string;
59
+ /** The action is under way: the button's spinner, no second press. */
60
+ actionPending?: boolean;
61
+ /** An × that calls this; the app removes the banner and remembers that it did. */
62
+ onDismiss?: () => void;
63
+ dismissLabel?: string;
64
+ /** Prop > `<UiKitProvider labels={{ billing }}>` > English. */
65
+ labels?: LabelOverride<BillingLabels>;
66
+ }
67
+
68
+ export type BillingBannerProps = BillingBannerBaseProps &
69
+ (
70
+ | {
71
+ kind: "trial-ending" | "grant-ending";
72
+ /** Calendar days left — `billingDaysLeft(trial_ends_at)`. 0 is today. */
73
+ daysLeft: number;
74
+ item?: never;
75
+ }
76
+ | {
77
+ kind: "guest";
78
+ /** The item's own name ("Household 2026"), or nothing. NEVER the owner's status:
79
+ * the banner says "for now" and nothing more (§12.6). */
80
+ item?: string;
81
+ daysLeft?: never;
82
+ }
83
+ | {
84
+ kind: "payment-failed" | "plan-ended" | "processing";
85
+ daysLeft?: never;
86
+ item?: never;
87
+ }
88
+ );
89
+
90
+ /** The last days, in which an ending trial or grant turns from news into a warning. */
91
+ const LAST_DAYS = 3;
92
+
93
+ function defaultTone(kind: BillingBannerKind, daysLeft: number | undefined): AlertTone {
94
+ switch (kind) {
95
+ case "trial-ending":
96
+ case "grant-ending":
97
+ return (daysLeft ?? 0) > LAST_DAYS ? "info" : "warning";
98
+ case "payment-failed":
99
+ return "danger";
100
+ case "plan-ended":
101
+ return "warning";
102
+ default:
103
+ return "info";
104
+ }
105
+ }
106
+
107
+ /**
108
+ * One of the billing banners, as a kit `AlertBanner` strip (§7): the kind's sentence, its
109
+ * glyph and tone, and the app's action. Carries `data-billing-banner` with the kind.
110
+ *
111
+ * The app decides WHICH banner shows, from `GET /billing/overview` (and hides all of them
112
+ * while billing is off, §4); the kit only says it. A guest's banner takes no status at
113
+ * all — the type does not let one in — so the owner's payment state cannot leak into it
114
+ * by a wrong prop (§12.6).
115
+ *
116
+ * `processing` is a live region (`role="status"`): it appears when the person comes back
117
+ * from checkout and goes when the overview catches up (§12.21), and a screen reader
118
+ * should hear both. The others are page furniture, role-less as the strip is.
119
+ */
120
+ export function BillingBanner(props: BillingBannerProps) {
121
+ const {
122
+ kind,
123
+ daysLeft,
124
+ item,
125
+ tone,
126
+ variant = "strip",
127
+ size,
128
+ actionHref,
129
+ onAction,
130
+ actionLabel,
131
+ actionPending,
132
+ onDismiss,
133
+ dismissLabel,
134
+ labels: labelsProp,
135
+ role,
136
+ ...rest
137
+ } = props;
138
+ const labels = useBillingLabels(labelsProp);
139
+ const days = Math.max(0, Math.floor(daysLeft ?? 0));
140
+
141
+ let text: string;
142
+ let icon: ReactNode;
143
+ let ownAction: string | undefined;
144
+ switch (kind) {
145
+ case "trial-ending":
146
+ text = labels.trialEnding(days);
147
+ icon = <Hourglass />;
148
+ ownAction = labels.choosePlan;
149
+ break;
150
+ case "grant-ending":
151
+ text = labels.grantEnding(days);
152
+ icon = <Hourglass />;
153
+ ownAction = labels.choosePlan;
154
+ break;
155
+ case "payment-failed":
156
+ text = labels.paymentFailed;
157
+ icon = <CreditCard />;
158
+ ownAction = labels.updatePayment;
159
+ break;
160
+ case "plan-ended":
161
+ text = labels.planEnded;
162
+ icon = <PencilOff />;
163
+ ownAction = labels.choosePlan;
164
+ break;
165
+ case "guest":
166
+ text = labels.guestReadOnly(item);
167
+ icon = <PencilOff />;
168
+ ownAction = undefined;
169
+ break;
170
+ case "processing":
171
+ text = labels.processing;
172
+ icon = <Spinner label={null} />;
173
+ ownAction = labels.checkAgain;
174
+ break;
175
+ }
176
+
177
+ const words = actionLabel ?? ownAction;
178
+ const action =
179
+ words === undefined || (actionHref === undefined && onAction === undefined) ? undefined : actionHref !== undefined ? (
180
+ <Button href={actionHref} variant="secondary" size="sm">
181
+ {words}
182
+ </Button>
183
+ ) : (
184
+ <Button type="button" variant="secondary" size="sm" onClick={onAction} pending={actionPending}>
185
+ {words}
186
+ </Button>
187
+ );
188
+
189
+ return (
190
+ <AlertBanner
191
+ {...rest}
192
+ role={role ?? (kind === "processing" ? "status" : undefined)}
193
+ variant={variant}
194
+ size={size ?? (variant === "strip" ? "sm" : "md")}
195
+ tone={tone ?? defaultTone(kind, days)}
196
+ icon={icon}
197
+ action={action}
198
+ onDismiss={onDismiss}
199
+ dismissLabel={dismissLabel}
200
+ data-billing-banner={kind}
201
+ >
202
+ {text}
203
+ </AlertBanner>
204
+ );
205
+ }