@eifi1/ui-kit 0.31.1 → 0.32.1

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 +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/admin-action-log.d.ts +1 -1
  5. package/dist/admin/admin-action-log.js +4 -2
  6. package/dist/admin/admin-action-log.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/billing/billing-banner.d.ts +184 -0
  21. package/dist/billing/billing-banner.js +98 -0
  22. package/dist/billing/billing-banner.js.map +1 -0
  23. package/dist/billing/billing-labels.d.ts +100 -0
  24. package/dist/billing/billing-labels.js +76 -0
  25. package/dist/billing/billing-labels.js.map +1 -0
  26. package/dist/billing/billing-lock.d.ts +158 -0
  27. package/dist/billing/billing-lock.js +17 -0
  28. package/dist/billing/billing-lock.js.map +1 -0
  29. package/dist/billing/billing-standing.d.ts +149 -0
  30. package/dist/billing/billing-standing.js +28 -0
  31. package/dist/billing/billing-standing.js.map +1 -0
  32. package/dist/billing/plan-card.d.ts +248 -0
  33. package/dist/billing/plan-card.js +248 -0
  34. package/dist/billing/plan-card.js.map +1 -0
  35. package/dist/billing/plan-limit-notice.d.ts +156 -0
  36. package/dist/billing/plan-limit-notice.js +76 -0
  37. package/dist/billing/plan-limit-notice.js.map +1 -0
  38. package/dist/billing/plan-limit.d.ts +34 -0
  39. package/dist/billing/plan-limit.js +20 -0
  40. package/dist/billing/plan-limit.js.map +1 -0
  41. package/dist/billing/plan-price.d.ts +61 -0
  42. package/dist/billing/plan-price.js +45 -0
  43. package/dist/billing/plan-price.js.map +1 -0
  44. package/dist/billing/subscription-actions.d.ts +133 -0
  45. package/dist/billing/subscription-actions.js +28 -0
  46. package/dist/billing/subscription-actions.js.map +1 -0
  47. package/dist/billing/subscription-status-chip.d.ts +149 -0
  48. package/dist/billing/subscription-status-chip.js +48 -0
  49. package/dist/billing/subscription-status-chip.js.map +1 -0
  50. package/dist/chart.d.ts +9 -3
  51. package/dist/chart.js.map +1 -1
  52. package/dist/components/account-chips.d.ts +16 -4
  53. package/dist/components/account-chips.js +21 -3
  54. package/dist/components/account-chips.js.map +1 -1
  55. package/dist/components/alert-banner.d.ts +6 -1
  56. package/dist/components/alert-banner.js +5 -4
  57. package/dist/components/alert-banner.js.map +1 -1
  58. package/dist/components/amount-input.d.ts +8 -3
  59. package/dist/components/amount-input.js +5 -5
  60. package/dist/components/amount-input.js.map +1 -1
  61. package/dist/components/appearance-labels.d.ts +38 -0
  62. package/dist/components/appearance-labels.js +19 -0
  63. package/dist/components/appearance-labels.js.map +1 -0
  64. package/dist/components/autocomplete.js +2 -2
  65. package/dist/components/autocomplete.js.map +1 -1
  66. package/dist/components/breadcrumbs.js +3 -2
  67. package/dist/components/breadcrumbs.js.map +1 -1
  68. package/dist/components/bulk-action-bar.d.ts +2 -2
  69. package/dist/components/bulk-action-bar.js +7 -12
  70. package/dist/components/bulk-action-bar.js.map +1 -1
  71. package/dist/components/button-group.d.ts +8 -3
  72. package/dist/components/button-group.js +7 -1
  73. package/dist/components/button-group.js.map +1 -1
  74. package/dist/components/calculator.d.ts +8 -3
  75. package/dist/components/calculator.js +4 -3
  76. package/dist/components/calculator.js.map +1 -1
  77. package/dist/components/calendar-heatmap.js +5 -5
  78. package/dist/components/calendar-heatmap.js.map +1 -1
  79. package/dist/components/chart-height.d.ts +23 -0
  80. package/dist/components/chart-height.js +15 -0
  81. package/dist/components/chart-height.js.map +1 -0
  82. package/dist/components/chart-zoom.js +6 -1
  83. package/dist/components/chart-zoom.js.map +1 -1
  84. package/dist/components/checkbox-group.js +2 -2
  85. package/dist/components/checkbox-group.js.map +1 -1
  86. package/dist/components/checkbox.d.ts +10 -0
  87. package/dist/components/checkbox.js +25 -10
  88. package/dist/components/checkbox.js.map +1 -1
  89. package/dist/components/chip.d.ts +19 -3
  90. package/dist/components/chip.js +54 -23
  91. package/dist/components/chip.js.map +1 -1
  92. package/dist/components/choice-card.d.ts +1 -1
  93. package/dist/components/choice-card.js +9 -8
  94. package/dist/components/choice-card.js.map +1 -1
  95. package/dist/components/column-mapper.js +1 -1
  96. package/dist/components/column-mapper.js.map +1 -1
  97. package/dist/components/combobox-core.js +8 -7
  98. package/dist/components/combobox-core.js.map +1 -1
  99. package/dist/components/combobox.js +8 -8
  100. package/dist/components/combobox.js.map +1 -1
  101. package/dist/components/confirm-dialog.d.ts +8 -3
  102. package/dist/components/copy-button.d.ts +10 -5
  103. package/dist/components/country-select.js +1 -1
  104. package/dist/components/country-select.js.map +1 -1
  105. package/dist/components/currency-select.js +3 -3
  106. package/dist/components/currency-select.js.map +1 -1
  107. package/dist/components/danger-confirm.d.ts +8 -3
  108. package/dist/components/data-table-cells.d.ts +1 -1
  109. package/dist/components/data-table-filter-popover.d.ts +1 -1
  110. package/dist/components/data-table-filter-popover.js +4 -1
  111. package/dist/components/data-table-filter-popover.js.map +1 -1
  112. package/dist/components/data-table-filters.d.ts +1 -1
  113. package/dist/components/data-table-labels.d.ts +1 -1
  114. package/dist/components/data-table-pagination.d.ts +3 -2
  115. package/dist/components/data-table-pagination.js +15 -6
  116. package/dist/components/data-table-pagination.js.map +1 -1
  117. package/dist/components/data-table.d.ts +1 -1
  118. package/dist/components/data-table.js +123 -52
  119. package/dist/components/data-table.js.map +1 -1
  120. package/dist/components/date-picker.d.ts +1 -1
  121. package/dist/components/date-picker.js +18 -6
  122. package/dist/components/date-picker.js.map +1 -1
  123. package/dist/components/description-list.js +2 -2
  124. package/dist/components/description-list.js.map +1 -1
  125. package/dist/components/dialog-frame.js +2 -1
  126. package/dist/components/dialog-frame.js.map +1 -1
  127. package/dist/components/disclosure.js +3 -2
  128. package/dist/components/disclosure.js.map +1 -1
  129. package/dist/components/dropdown.d.ts +1 -1
  130. package/dist/components/dropdown.js +7 -1
  131. package/dist/components/dropdown.js.map +1 -1
  132. package/dist/components/facing-pair.d.ts +19 -6
  133. package/dist/components/facing-pair.js +3 -2
  134. package/dist/components/facing-pair.js.map +1 -1
  135. package/dist/components/field-parts.d.ts +102 -6
  136. package/dist/components/field-parts.js +81 -6
  137. package/dist/components/field-parts.js.map +1 -1
  138. package/dist/components/field-sync.d.ts +4 -1
  139. package/dist/components/field-sync.js +12 -2
  140. package/dist/components/field-sync.js.map +1 -1
  141. package/dist/components/field.js +2 -2
  142. package/dist/components/field.js.map +1 -1
  143. package/dist/components/file-button.d.ts +8 -3
  144. package/dist/components/file-dropzone.d.ts +8 -3
  145. package/dist/components/floating-panel.js +7 -4
  146. package/dist/components/floating-panel.js.map +1 -1
  147. package/dist/components/focus-ring.d.ts +12 -0
  148. package/dist/components/focus-ring.js +8 -0
  149. package/dist/components/focus-ring.js.map +1 -0
  150. package/dist/components/form-actions.d.ts +8 -3
  151. package/dist/components/form-actions.js +33 -19
  152. package/dist/components/form-actions.js.map +1 -1
  153. package/dist/components/full-bleed-dialog.d.ts +11 -1
  154. package/dist/components/full-bleed-dialog.js +3 -1
  155. package/dist/components/full-bleed-dialog.js.map +1 -1
  156. package/dist/components/iban-input.d.ts +8 -3
  157. package/dist/components/image-grid.js +1 -1
  158. package/dist/components/image-grid.js.map +1 -1
  159. package/dist/components/inline-edit-field.js +10 -4
  160. package/dist/components/inline-edit-field.js.map +1 -1
  161. package/dist/components/language-select.d.ts +8 -3
  162. package/dist/components/legal-page.d.ts +26 -5
  163. package/dist/components/legal-page.js +3 -1
  164. package/dist/components/legal-page.js.map +1 -1
  165. package/dist/components/legal.d.ts +8 -3
  166. package/dist/components/legal.js +2 -1
  167. package/dist/components/legal.js.map +1 -1
  168. package/dist/components/lightbox.js +24 -2
  169. package/dist/components/lightbox.js.map +1 -1
  170. package/dist/components/line-items.js +2 -1
  171. package/dist/components/line-items.js.map +1 -1
  172. package/dist/components/list.d.ts +5 -0
  173. package/dist/components/list.js +13 -9
  174. package/dist/components/list.js.map +1 -1
  175. package/dist/components/measured-grid.d.ts +3 -1
  176. package/dist/components/measured-grid.js +38 -20
  177. package/dist/components/measured-grid.js.map +1 -1
  178. package/dist/components/menu-item.js +2 -1
  179. package/dist/components/menu-item.js.map +1 -1
  180. package/dist/components/mini-calendar.js +8 -4
  181. package/dist/components/mini-calendar.js.map +1 -1
  182. package/dist/components/money-field.d.ts +8 -3
  183. package/dist/components/month-picker.js +50 -39
  184. package/dist/components/month-picker.js.map +1 -1
  185. package/dist/components/nav-pills.js +1 -1
  186. package/dist/components/nav-pills.js.map +1 -1
  187. package/dist/components/number-field.d.ts +8 -3
  188. package/dist/components/number-input.d.ts +11 -4
  189. package/dist/components/number-input.js +8 -6
  190. package/dist/components/number-input.js.map +1 -1
  191. package/dist/components/numpad-sheet.d.ts +8 -3
  192. package/dist/components/numpad-sheet.js +2 -1
  193. package/dist/components/numpad-sheet.js.map +1 -1
  194. package/dist/components/one-time-code-input.js +2 -2
  195. package/dist/components/one-time-code-input.js.map +1 -1
  196. package/dist/components/page-contents.js +2 -2
  197. package/dist/components/page-contents.js.map +1 -1
  198. package/dist/components/phone-input.d.ts +8 -3
  199. package/dist/components/phone-input.js +4 -4
  200. package/dist/components/phone-input.js.map +1 -1
  201. package/dist/components/pie-chart.d.ts +8 -2
  202. package/dist/components/pie-chart.js +11 -7
  203. package/dist/components/pie-chart.js.map +1 -1
  204. package/dist/components/popover.d.ts +9 -1
  205. package/dist/components/popover.js +8 -4
  206. package/dist/components/popover.js.map +1 -1
  207. package/dist/components/row-actions.d.ts +100 -0
  208. package/dist/components/row-actions.js +231 -0
  209. package/dist/components/row-actions.js.map +1 -0
  210. package/dist/components/scroll-area.js +1 -1
  211. package/dist/components/scroll-area.js.map +1 -1
  212. package/dist/components/search-field.js +3 -2
  213. package/dist/components/search-field.js.map +1 -1
  214. package/dist/components/series-chart-budget.d.ts +30 -7
  215. package/dist/components/series-chart-budget.js +12 -4
  216. package/dist/components/series-chart-budget.js.map +1 -1
  217. package/dist/components/series-chart-ticks.d.ts +7 -1
  218. package/dist/components/series-chart-ticks.js +6 -0
  219. package/dist/components/series-chart-ticks.js.map +1 -1
  220. package/dist/components/series-chart.d.ts +58 -28
  221. package/dist/components/series-chart.js +42 -32
  222. package/dist/components/series-chart.js.map +1 -1
  223. package/dist/components/settings-fields.d.ts +57 -8
  224. package/dist/components/settings-fields.js +39 -1
  225. package/dist/components/settings-fields.js.map +1 -1
  226. package/dist/components/share-card.d.ts +11 -6
  227. package/dist/components/share-card.js +8 -2
  228. package/dist/components/share-card.js.map +1 -1
  229. package/dist/components/sign-chip.d.ts +4 -0
  230. package/dist/components/sign-chip.js +17 -4
  231. package/dist/components/sign-chip.js.map +1 -1
  232. package/dist/components/signature-pad.d.ts +3 -1
  233. package/dist/components/signature-pad.js +8 -5
  234. package/dist/components/signature-pad.js.map +1 -1
  235. package/dist/components/slider.js +7 -6
  236. package/dist/components/slider.js.map +1 -1
  237. package/dist/components/sparkline.d.ts +5 -1
  238. package/dist/components/sparkline.js +4 -2
  239. package/dist/components/sparkline.js.map +1 -1
  240. package/dist/components/stat-tile.d.ts +17 -1
  241. package/dist/components/stat-tile.js +48 -20
  242. package/dist/components/stat-tile.js.map +1 -1
  243. package/dist/components/status-dot.d.ts +4 -0
  244. package/dist/components/status-dot.js +9 -2
  245. package/dist/components/status-dot.js.map +1 -1
  246. package/dist/components/swipeable-row.js +10 -2
  247. package/dist/components/swipeable-row.js.map +1 -1
  248. package/dist/components/switch.d.ts +21 -7
  249. package/dist/components/switch.js +24 -11
  250. package/dist/components/switch.js.map +1 -1
  251. package/dist/components/table.js +2 -2
  252. package/dist/components/table.js.map +1 -1
  253. package/dist/components/text-link.d.ts +8 -3
  254. package/dist/components/text-link.js +1 -1
  255. package/dist/components/text-link.js.map +1 -1
  256. package/dist/components/text.d.ts +7 -2
  257. package/dist/components/text.js +5 -5
  258. package/dist/components/text.js.map +1 -1
  259. package/dist/components/tile-radio.js +2 -1
  260. package/dist/components/tile-radio.js.map +1 -1
  261. package/dist/components/time-input.d.ts +8 -3
  262. package/dist/components/toast.d.ts +18 -2
  263. package/dist/components/toast.js +8 -5
  264. package/dist/components/toast.js.map +1 -1
  265. package/dist/components/toggle-group.d.ts +25 -3
  266. package/dist/components/toggle-group.js +40 -11
  267. package/dist/components/toggle-group.js.map +1 -1
  268. package/dist/components/toggle-legend.d.ts +10 -1
  269. package/dist/components/toggle-legend.js +21 -7
  270. package/dist/components/toggle-legend.js.map +1 -1
  271. package/dist/components/translation-review-editor.js +1 -1
  272. package/dist/components/translation-review-editor.js.map +1 -1
  273. package/dist/components/translation-review-parts.d.ts +4 -0
  274. package/dist/components/translation-review.d.ts +4 -0
  275. package/dist/components/translation-review.js +3 -3
  276. package/dist/components/translation-review.js.map +1 -1
  277. package/dist/components/tree-view.js +3 -2
  278. package/dist/components/tree-view.js.map +1 -1
  279. package/dist/components/treemap.d.ts +5 -2
  280. package/dist/components/treemap.js +33 -16
  281. package/dist/components/treemap.js.map +1 -1
  282. package/dist/components/ui.d.ts +8 -3
  283. package/dist/components/ui.js +207 -119
  284. package/dist/components/ui.js.map +1 -1
  285. package/dist/components/use-table-state.d.ts +1 -1
  286. package/dist/components/user-avatar.d.ts +5 -1
  287. package/dist/components/user-avatar.js +1 -1
  288. package/dist/components/user-avatar.js.map +1 -1
  289. package/dist/components/wizard-stepper.js +20 -14
  290. package/dist/components/wizard-stepper.js.map +1 -1
  291. package/dist/{data-table-labels-DUELeXpa.d.ts → data-table-labels-kpAAVi6g.d.ts} +26 -2
  292. package/dist/data-table.d.ts +1 -1
  293. package/dist/{delete-account-setting-CpIhzxxZ.d.ts → delete-account-setting-FLyJf-kZ.d.ts} +642 -60
  294. package/dist/demo/demo-banner.js +1 -0
  295. package/dist/demo/demo-banner.js.map +1 -1
  296. package/dist/feedback/feedback-attachment.d.ts +8 -3
  297. package/dist/feedback/feedback-context.d.ts +8 -3
  298. package/dist/feedback/feedback-crash.d.ts +8 -3
  299. package/dist/feedback/feedback-dialog.d.ts +8 -3
  300. package/dist/feedback/feedback-dialog.js +3 -3
  301. package/dist/feedback/feedback-dialog.js.map +1 -1
  302. package/dist/feedback/feedback-inbox.d.ts +8 -3
  303. package/dist/feedback/feedback-inbox.js +2 -2
  304. package/dist/feedback/feedback-inbox.js.map +1 -1
  305. package/dist/feedback/feedback-labels.d.ts +8 -3
  306. package/dist/feedback/feedback-menu.d.ts +9 -4
  307. package/dist/feedback/feedback-record.d.ts +8 -3
  308. package/dist/feedback/feedback-row-detail.d.ts +8 -3
  309. package/dist/feedback/feedback-status-undo.d.ts +8 -3
  310. package/dist/feedback/feedback-submit.d.ts +8 -3
  311. package/dist/feedback/feedback-swipe.d.ts +8 -3
  312. package/dist/feedback/feedback-table.d.ts +8 -3
  313. package/dist/feedback/feedback-thread.d.ts +8 -3
  314. package/dist/feedback/feedback-thread.js +3 -3
  315. package/dist/feedback/feedback-thread.js.map +1 -1
  316. package/dist/feedback.d.ts +8 -3
  317. package/dist/hooks/use-anchored-panel.d.ts +32 -3
  318. package/dist/hooks/use-anchored-panel.js +21 -3
  319. package/dist/hooks/use-anchored-panel.js.map +1 -1
  320. package/dist/hooks/use-breakpoint.d.ts +88 -0
  321. package/dist/hooks/use-breakpoint.js +50 -0
  322. package/dist/hooks/use-breakpoint.js.map +1 -0
  323. package/dist/hooks/use-file-drop.d.ts +8 -3
  324. package/dist/hooks/use-large-text.d.ts +29 -0
  325. package/dist/hooks/use-large-text.js +20 -0
  326. package/dist/hooks/use-large-text.js.map +1 -0
  327. package/dist/hooks/use-windowed-rows.d.ts +52 -8
  328. package/dist/hooks/use-windowed-rows.js +94 -5
  329. package/dist/hooks/use-windowed-rows.js.map +1 -1
  330. package/dist/i18n/defaults.d.ts +8 -3
  331. package/dist/i18n/defaults.js +8 -0
  332. package/dist/i18n/defaults.js.map +1 -1
  333. package/dist/i18n/german.d.ts +8 -3
  334. package/dist/i18n/german.js +94 -3
  335. package/dist/i18n/german.js.map +1 -1
  336. package/dist/i18n/kit-labels.d.ts +8 -3
  337. package/dist/i18n/kit-labels.js.map +1 -1
  338. package/dist/i18n/languages.d.ts +10 -5
  339. package/dist/i18n/locales/de-CH.d.ts +8 -3
  340. package/dist/i18n/locales/en.d.ts +8 -3
  341. package/dist/i18n/locales/es.d.ts +8 -3
  342. package/dist/i18n/locales/es.js +89 -3
  343. package/dist/i18n/locales/es.js.map +1 -1
  344. package/dist/i18n/locales/fr.d.ts +8 -3
  345. package/dist/i18n/locales/fr.js +91 -3
  346. package/dist/i18n/locales/fr.js.map +1 -1
  347. package/dist/i18n/locales/hu.d.ts +8 -3
  348. package/dist/i18n/locales/hu.js +91 -3
  349. package/dist/i18n/locales/hu.js.map +1 -1
  350. package/dist/i18n/locales/it.d.ts +8 -3
  351. package/dist/i18n/locales/it.js +92 -3
  352. package/dist/i18n/locales/it.js.map +1 -1
  353. package/dist/i18n/locales/zh.d.ts +8 -3
  354. package/dist/i18n/locales/zh.js +89 -3
  355. package/dist/i18n/locales/zh.js.map +1 -1
  356. package/dist/i18n/review.d.ts +8 -3
  357. package/dist/i18n/review.js +15 -1
  358. package/dist/i18n/review.js.map +1 -1
  359. package/dist/index.d.ts +27 -11
  360. package/dist/index.js +77 -4
  361. package/dist/index.js.map +1 -1
  362. package/dist/landing/landing-actions.d.ts +8 -3
  363. package/dist/landing/landing-sections.d.ts +8 -3
  364. package/dist/landing/landing-sections.js +7 -3
  365. package/dist/landing/landing-sections.js.map +1 -1
  366. package/dist/landing/public-header.d.ts +9 -4
  367. package/dist/landing/public-header.js +3 -3
  368. package/dist/landing/public-header.js.map +1 -1
  369. package/dist/landing/routing.d.ts +8 -3
  370. package/dist/lib/cn.js +3 -2
  371. package/dist/lib/cn.js.map +1 -1
  372. package/dist/rhf/fields.d.ts +8 -3
  373. package/dist/rhf/fields.js +1 -1
  374. package/dist/rhf/fields.js.map +1 -1
  375. package/dist/rhf/form.d.ts +8 -3
  376. package/dist/rhf/form.js +2 -2
  377. package/dist/rhf/form.js.map +1 -1
  378. package/dist/rhf.d.ts +8 -3
  379. package/dist/search/command-palette.d.ts +1 -1
  380. package/dist/search/command-palette.js +9 -8
  381. package/dist/search/command-palette.js.map +1 -1
  382. package/dist/settings/settings-layout.js +2 -1
  383. package/dist/settings/settings-layout.js.map +1 -1
  384. package/dist/settings/settings-section.d.ts +8 -3
  385. package/dist/settings/use-account-appearance.d.ts +92 -0
  386. package/dist/settings/use-account-appearance.js +63 -0
  387. package/dist/settings/use-account-appearance.js.map +1 -0
  388. package/dist/settings/use-settings-route.d.ts +1 -1
  389. package/dist/settings/use-settings-route.js +2 -3
  390. package/dist/settings/use-settings-route.js.map +1 -1
  391. package/dist/shell/app-shell.d.ts +12 -171
  392. package/dist/shell/app-shell.js +144 -28
  393. package/dist/shell/app-shell.js.map +1 -1
  394. package/dist/shell/auth-layout.js +1 -1
  395. package/dist/shell/auth-layout.js.map +1 -1
  396. package/dist/shell/option-switcher-menu.js +1 -1
  397. package/dist/shell/option-switcher-menu.js.map +1 -1
  398. package/dist/shell/top-bar-brand.d.ts +8 -3
  399. package/dist/shell/top-bar-brand.js +3 -1
  400. package/dist/shell/top-bar-brand.js.map +1 -1
  401. package/dist/shell/topbar-action-menu.d.ts +4 -0
  402. package/dist/shell/topbar-action-menu.js +3 -3
  403. package/dist/shell/topbar-action-menu.js.map +1 -1
  404. package/dist/shell/topbar-controls.js +4 -4
  405. package/dist/shell/topbar-controls.js.map +1 -1
  406. package/dist/shell.d.ts +9 -5
  407. package/dist/theme/contrast-tokens.d.ts +41 -0
  408. package/dist/theme/contrast-tokens.js +59 -0
  409. package/dist/theme/contrast-tokens.js.map +1 -0
  410. package/dist/theme/contrast.d.ts +110 -0
  411. package/dist/theme/contrast.js +128 -0
  412. package/dist/theme/contrast.js.map +1 -0
  413. package/dist/theme/palette-presets.d.ts +12 -1
  414. package/dist/theme/palette-presets.js +9 -4
  415. package/dist/theme/palette-presets.js.map +1 -1
  416. package/dist/theme/palette-store.js.map +1 -1
  417. package/dist/theme/text-size.d.ts +173 -0
  418. package/dist/theme/text-size.js +121 -0
  419. package/dist/theme/text-size.js.map +1 -0
  420. package/dist/wizard/stepper-nav.d.ts +8 -3
  421. package/dist/wizard/wizard-summary.js +1 -1
  422. package/dist/wizard/wizard-summary.js.map +1 -1
  423. package/dist/wizard.d.ts +8 -3
  424. package/package.json +1 -1
  425. package/src/admin/admin-action-log.tsx +6 -1
  426. package/src/admin/invitations-panel.tsx +36 -29
  427. package/src/admin/user-roster.tsx +17 -72
  428. package/src/auth/auth-errors.ts +68 -7
  429. package/src/billing/billing-banner.tsx +205 -0
  430. package/src/billing/billing-labels.ts +240 -0
  431. package/src/billing/billing-lock.ts +75 -0
  432. package/src/billing/billing-standing.ts +72 -0
  433. package/src/billing/plan-card.tsx +458 -0
  434. package/src/billing/plan-limit-notice.tsx +145 -0
  435. package/src/billing/plan-limit.ts +53 -0
  436. package/src/billing/plan-price.ts +101 -0
  437. package/src/billing/subscription-actions.tsx +76 -0
  438. package/src/billing/subscription-status-chip.tsx +95 -0
  439. package/src/chart.ts +2 -0
  440. package/src/components/account-chips.tsx +36 -10
  441. package/src/components/alert-banner.tsx +16 -7
  442. package/src/components/amount-input.tsx +5 -5
  443. package/src/components/appearance-labels.ts +47 -0
  444. package/src/components/autocomplete.tsx +2 -2
  445. package/src/components/breadcrumbs.tsx +3 -3
  446. package/src/components/bulk-action-bar.tsx +11 -14
  447. package/src/components/button-group.tsx +21 -0
  448. package/src/components/calculator.tsx +4 -3
  449. package/src/components/calendar-heatmap.tsx +5 -5
  450. package/src/components/chart-height.ts +30 -0
  451. package/src/components/chart-zoom.tsx +6 -1
  452. package/src/components/checkbox-group.tsx +2 -2
  453. package/src/components/checkbox.tsx +62 -20
  454. package/src/components/chip.tsx +139 -30
  455. package/src/components/choice-card.tsx +16 -11
  456. package/src/components/column-mapper.tsx +1 -1
  457. package/src/components/combobox-core.tsx +8 -7
  458. package/src/components/combobox.tsx +8 -8
  459. package/src/components/country-select.tsx +1 -1
  460. package/src/components/currency-select.tsx +3 -3
  461. package/src/components/data-table-filter-popover.tsx +7 -1
  462. package/src/components/data-table-pagination.tsx +60 -31
  463. package/src/components/data-table.tsx +164 -43
  464. package/src/components/date-picker.tsx +44 -10
  465. package/src/components/description-list.tsx +2 -2
  466. package/src/components/dialog-frame.tsx +2 -1
  467. package/src/components/disclosure.tsx +3 -2
  468. package/src/components/dropdown.tsx +10 -2
  469. package/src/components/facing-pair.ts +12 -5
  470. package/src/components/field-parts.tsx +190 -7
  471. package/src/components/field-sync.tsx +17 -3
  472. package/src/components/field.tsx +2 -2
  473. package/src/components/floating-panel.tsx +7 -4
  474. package/src/components/focus-ring.ts +12 -0
  475. package/src/components/form-actions.tsx +55 -18
  476. package/src/components/full-bleed-dialog.tsx +18 -4
  477. package/src/components/image-grid.tsx +1 -1
  478. package/src/components/inline-edit-field.tsx +26 -4
  479. package/src/components/legal-page.tsx +28 -2
  480. package/src/components/legal.tsx +19 -1
  481. package/src/components/lightbox.tsx +24 -2
  482. package/src/components/line-items.tsx +7 -1
  483. package/src/components/list.tsx +36 -10
  484. package/src/components/measured-grid.tsx +49 -24
  485. package/src/components/menu-item.tsx +2 -2
  486. package/src/components/mini-calendar.tsx +8 -4
  487. package/src/components/month-picker.tsx +15 -6
  488. package/src/components/nav-pills.tsx +1 -1
  489. package/src/components/number-input.tsx +14 -7
  490. package/src/components/numpad-sheet.tsx +2 -2
  491. package/src/components/one-time-code-input.tsx +2 -2
  492. package/src/components/page-contents.tsx +2 -2
  493. package/src/components/phone-input.tsx +9 -4
  494. package/src/components/pie-chart.tsx +19 -9
  495. package/src/components/popover.tsx +22 -5
  496. package/src/components/row-actions.tsx +450 -0
  497. package/src/components/scroll-area.tsx +1 -1
  498. package/src/components/search-field.tsx +4 -2
  499. package/src/components/series-chart-budget.ts +40 -8
  500. package/src/components/series-chart-ticks.ts +10 -0
  501. package/src/components/series-chart.tsx +123 -72
  502. package/src/components/settings-fields.tsx +89 -5
  503. package/src/components/share-card.tsx +31 -2
  504. package/src/components/sign-chip.tsx +28 -4
  505. package/src/components/signature-pad.tsx +13 -7
  506. package/src/components/slider.tsx +22 -9
  507. package/src/components/sparkline.tsx +11 -3
  508. package/src/components/stat-tile.tsx +70 -14
  509. package/src/components/status-dot.tsx +25 -2
  510. package/src/components/swipeable-row.tsx +10 -2
  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 +115 -14
  518. package/src/components/toggle-legend.tsx +53 -8
  519. package/src/components/translation-review-editor.tsx +1 -1
  520. package/src/components/translation-review.tsx +11 -5
  521. package/src/components/tree-view.tsx +3 -2
  522. package/src/components/treemap.tsx +27 -10
  523. package/src/components/ui.tsx +353 -153
  524. package/src/components/user-avatar.tsx +1 -1
  525. package/src/components/wizard-stepper.tsx +5 -1
  526. package/src/demo/demo-banner.tsx +5 -0
  527. package/src/feedback/feedback-dialog.tsx +3 -3
  528. package/src/feedback/feedback-inbox.tsx +2 -2
  529. package/src/feedback/feedback-thread.tsx +3 -3
  530. package/src/hooks/use-anchored-panel.ts +67 -5
  531. package/src/hooks/use-breakpoint.ts +132 -0
  532. package/src/hooks/use-large-text.ts +41 -0
  533. package/src/hooks/use-windowed-rows.ts +168 -12
  534. package/src/i18n/defaults.ts +8 -0
  535. package/src/i18n/german.ts +107 -0
  536. package/src/i18n/kit-labels.tsx +12 -0
  537. package/src/i18n/locales/es.ts +98 -0
  538. package/src/i18n/locales/fr.ts +102 -0
  539. package/src/i18n/locales/hu.ts +100 -0
  540. package/src/i18n/locales/it.ts +103 -0
  541. package/src/i18n/locales/zh.ts +94 -0
  542. package/src/i18n/review.ts +14 -0
  543. package/src/index.ts +76 -6
  544. package/src/landing/landing-sections.tsx +7 -3
  545. package/src/landing/public-header.tsx +4 -4
  546. package/src/lib/cn.ts +13 -2
  547. package/src/rhf/fields.tsx +1 -1
  548. package/src/rhf/form.tsx +2 -2
  549. package/src/search/command-palette.tsx +10 -9
  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/theme/contrast-tokens.ts +112 -0
  560. package/src/theme/contrast.ts +225 -0
  561. package/src/theme/palette-presets.ts +24 -4
  562. package/src/theme/palette-store.ts +6 -1
  563. package/src/theme/text-size.ts +283 -0
  564. package/src/wizard/wizard-summary.tsx +4 -2
  565. package/tokens.css +158 -8
@@ -0,0 +1,148 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import { isTextSize, resolveTextSize } from "../theme/text-size";
3
+ import type { TextSize } from "../theme/text-size";
4
+ import { isContrastMode, resolveContrastMode } from "../theme/contrast";
5
+ import type { ContrastMode } from "../theme/contrast";
6
+
7
+ /**
8
+ * The text size and contrast follow the account like the language does
9
+ * (docs/text-size-harmonization.md §2.2, §2.6, §6; settings contract §6.2) — the same
10
+ * rule as {@link useAccountLanguage}, for two values at once.
11
+ */
12
+
13
+ /** Where a value in force came from, in the order they are asked. */
14
+ export type AppearanceSource = "device" | "account" | "default";
15
+
16
+ /** The two fields of an `/auth/me` answer this reads — server-kit 0.6's `UserResponse`,
17
+ * both nullable (null = never chosen). */
18
+ export interface AccountAppearanceFields {
19
+ text_size?: string | null;
20
+ contrast?: string | null;
21
+ }
22
+
23
+ /** This device's own choices — the text-size and contrast stores' values, `null` where
24
+ * this device never chose. */
25
+ export interface DeviceAppearance {
26
+ textSize?: string | null;
27
+ contrast?: string | null;
28
+ }
29
+
30
+ /** The body of `PATCH /auth/me` for a pick: one field, as server-kit's `ProfileUpdate`
31
+ * spells it. */
32
+ export type AccountAppearancePatch = { text_size: TextSize } | { contrast: ContrastMode };
33
+
34
+ export interface ResolvedAccountAppearance {
35
+ textSize: TextSize;
36
+ /** The stored MODE — "system" included — not whether more contrast is on now. */
37
+ contrast: ContrastMode;
38
+ sources: { textSize: AppearanceSource; contrast: AppearanceSource };
39
+ }
40
+
41
+ function source(device: boolean, account: boolean): AppearanceSource {
42
+ return device ? "device" : account ? "account" : "default";
43
+ }
44
+
45
+ /**
46
+ * The values on a device (§2.6): **its own choice → the account's → the default**
47
+ * (Normal; "system" for contrast), each taken only when it is a known value. Pure.
48
+ */
49
+ export function resolveAccountAppearance(
50
+ device: DeviceAppearance,
51
+ account: AccountAppearanceFields | null | undefined,
52
+ ): ResolvedAccountAppearance {
53
+ return {
54
+ textSize: resolveTextSize(device.textSize, account?.text_size),
55
+ contrast: resolveContrastMode(device.contrast, account?.contrast),
56
+ sources: {
57
+ textSize: source(isTextSize(device.textSize), isTextSize(account?.text_size)),
58
+ contrast: source(isContrastMode(device.contrast), isContrastMode(account?.contrast)),
59
+ },
60
+ };
61
+ }
62
+
63
+ export interface UseAccountAppearanceOptions {
64
+ /** The signed-in account (`/auth/me`), or `null` / `undefined` while signed out or not
65
+ * loaded yet — then nothing is saved to it. */
66
+ account: AccountAppearanceFields | null | undefined;
67
+ /** This device's own choices: `useTextSizeStore((s) => s.size)` and
68
+ * `useContrastStore((s) => s.contrast)`. */
69
+ device: DeviceAppearance;
70
+ /** Store a device choice: `(patch) => { if (patch.textSize) setSize(patch.textSize);
71
+ * if (patch.contrast) setContrast(patch.contrast); }`. */
72
+ setDevice: (patch: { textSize?: TextSize; contrast?: ContrastMode }) => void;
73
+ /** Write the account: `PATCH /auth/me` with the patch as its body. Return its promise. */
74
+ save: (patch: AccountAppearancePatch) => unknown;
75
+ /** A demo session: a pick stays on the device, the account is never written (§6). */
76
+ isDemo?: boolean;
77
+ }
78
+
79
+ export interface AccountAppearance extends ResolvedAccountAppearance {
80
+ /** The user picked a size: it becomes the device's choice and — signed in, and not a
81
+ * demo — the account's. The promise is `save`'s; the device keeps the pick if it
82
+ * fails, and the app says so (`appearance.saveFailed`, 0.32.1). */
83
+ pickTextSize: (size: TextSize) => Promise<unknown>;
84
+ /** As {@link pickTextSize}, for the contrast mode. */
85
+ pickContrast: (mode: ContrastMode) => Promise<unknown>;
86
+ }
87
+
88
+ /**
89
+ * The account's text size and contrast on this device (§6): the order of
90
+ * {@link resolveAccountAppearance}, and the writes.
91
+ *
92
+ * - **Nothing writes the account on its own.** Rendering, signing in and adopting the
93
+ * account's values call neither `save` nor `setDevice`, so a new device adopts the
94
+ * account instead of overwriting it.
95
+ * - **A pick** writes the device's choice, then `save` — except in a demo session or
96
+ * signed out.
97
+ *
98
+ * The hook only answers. The values are put in force by the stores' apply hooks, which
99
+ * resolve the same order from the same inputs:
100
+ *
101
+ * ```tsx
102
+ * useApplyTextSize({ account: me?.text_size });
103
+ * useApplyContrast({ account: me?.contrast });
104
+ * ```
105
+ */
106
+ export function useAccountAppearance(options: UseAccountAppearanceOptions): AccountAppearance {
107
+ const { account, device, setDevice, save, isDemo = false } = options;
108
+ const deviceTextSize = device.textSize;
109
+ const deviceContrast = device.contrast;
110
+ const accountTextSize = account?.text_size;
111
+ const accountContrast = account?.contrast;
112
+ const signedIn = account !== null && account !== undefined;
113
+ // By value, so a fresh `/auth/me` object with the same two fields changes nothing.
114
+ const resolved = useMemo(
115
+ () =>
116
+ resolveAccountAppearance(
117
+ { textSize: deviceTextSize, contrast: deviceContrast },
118
+ signedIn ? { text_size: accountTextSize, contrast: accountContrast } : undefined,
119
+ ),
120
+ [deviceTextSize, deviceContrast, signedIn, accountTextSize, accountContrast],
121
+ );
122
+ const write = useCallback(
123
+ (patch: AccountAppearancePatch) => {
124
+ if (isDemo || !signedIn) return Promise.resolve();
125
+ try {
126
+ return Promise.resolve(save(patch));
127
+ } catch (error) {
128
+ return Promise.reject(error);
129
+ }
130
+ },
131
+ [save, isDemo, signedIn],
132
+ );
133
+ const pickTextSize = useCallback(
134
+ (size: TextSize) => {
135
+ setDevice({ textSize: size });
136
+ return write({ text_size: size });
137
+ },
138
+ [setDevice, write],
139
+ );
140
+ const pickContrast = useCallback(
141
+ (mode: ContrastMode) => {
142
+ setDevice({ contrast: mode });
143
+ return write({ contrast: mode });
144
+ },
145
+ [setDevice, write],
146
+ );
147
+ return { ...resolved, pickTextSize, pickContrast };
148
+ }
@@ -1,12 +1,11 @@
1
1
  import { useCallback, useEffect, useMemo } from "react";
2
2
  import { useLocation, useNavigate } from "react-router";
3
- import { useMediaQuery } from "../hooks/use-media-query";
4
- import { PHONE_QUERY } from "../components/ui";
3
+ import { usePhoneLayout } from "../hooks/use-breakpoint";
5
4
  import { normalizeSettingsBase, visibleSettingsGroups } from "./settings-catalogue";
6
5
  import type { SettingsEntry, SettingsGroup } from "./settings-catalogue";
7
6
 
8
7
  /**
9
- * Which layout the settings page is in. `"auto"` follows the screen ({@link PHONE_QUERY},
8
+ * Which layout the settings page is in. `"auto"` follows the screen (`usePhoneLayout()`,
10
9
  * as `Tabs` does); `"desktop"` / `"phone"` pin one — for a preview of both side by side,
11
10
  * where the window is one width.
12
11
  */
@@ -187,7 +186,7 @@ export function useSettingsRoute<G extends string>(options: UseSettingsRouteOpti
187
186
  const { groups, entries, basePath, defaultGroup, aliases, layout = "auto" } = options;
188
187
  const location = useLocation();
189
188
  const navigate = useNavigate();
190
- const narrow = useMediaQuery(PHONE_QUERY, false);
189
+ const narrow = usePhoneLayout();
191
190
  const phone = layout === "phone" || (layout === "auto" && narrow);
192
191
  const base = normalizeSettingsBase(basePath);
193
192
 
@@ -1,8 +1,8 @@
1
1
  import { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
2
2
  import type { ComponentPropsWithoutRef, ReactNode } from "react";
3
3
  import { createPortal } from "react-dom";
4
- import { Link, NavLink, matchPath, useLocation } from "react-router";
5
- import { ChevronRight, PanelLeftClose, PanelLeftOpen } from "lucide-react";
4
+ import { Link, NavLink, matchPath, useHref, useLocation } from "react-router";
5
+ import { ChevronRight, MoreHorizontal, PanelLeftClose, PanelLeftOpen } from "lucide-react";
6
6
  import type { LucideIcon } from "lucide-react";
7
7
  import { cn } from "../lib/cn";
8
8
  import { scrollIntoStrip, useStripFade } from "../lib/strip-fade";
@@ -10,9 +10,13 @@ import { dirOf, type Direction } from "../lib/direction";
10
10
  import { readStored, writeStored } from "../lib/safe-storage";
11
11
  import { Tooltip } from "../components/tooltip";
12
12
  import { pickLinkRenderer } from "../components/text-link";
13
+ import { DialogFrame } from "../components/dialog-frame";
14
+ import { CompactControls } from "../components/ui";
13
15
  import { useAnchoredRect } from "../hooks/use-anchored-rect";
14
16
  import { useEscapeKey } from "../hooks/use-dismiss";
15
- import { useMediaQuery } from "../hooks/use-media-query";
17
+ import { useBreakpoint } from "../hooks/use-breakpoint";
18
+ import { useTextSize } from "../theme/text-size";
19
+ import type { TextSize } from "../theme/text-size";
16
20
  import { DEFAULT_APP_SHELL_LABELS, useKitLabels, useKitLink } from "../i18n/kit-labels";
17
21
  import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
18
22
 
@@ -95,6 +99,46 @@ export type AppShellSidebarFooterItem =
95
99
  links: { to: string; label: string; external?: boolean }[];
96
100
  };
97
101
 
102
+ /**
103
+ * The words of the phone bar's "More" cell and its sheet (0.32,
104
+ * docs/text-size-harmonization.md §4 Navigation, §10.7) — the `appShellMore` namespace of
105
+ * `<UiKitProvider labels>`, overridable per shell through `moreLabels`.
106
+ */
107
+ export interface AppShellMoreLabels {
108
+ /** The cell's visible label, under its icon — one short word. */
109
+ more: string;
110
+ /** The sheet's heading, and so its accessible name. */
111
+ moreTitle: string;
112
+ }
113
+
114
+ export const DEFAULT_APP_SHELL_MORE_LABELS: AppShellMoreLabels = {
115
+ more: "More",
116
+ moreTitle: "More pages",
117
+ };
118
+
119
+ /**
120
+ * The most entries the phone bar shows before the rest go under "More", per text size
121
+ * (§10.7): every entry at Normal, four at Large, three at Extra large. A fifth of a
122
+ * 360 px phone at 150 % is 48 px of Normal-sized room, and the cells hold a word each.
123
+ */
124
+ export const DEFAULT_MOBILE_BAR_MAX: Readonly<Record<TextSize, number>> = {
125
+ normal: Number.POSITIVE_INFINITY,
126
+ large: 4,
127
+ xlarge: 3,
128
+ };
129
+
130
+ /**
131
+ * Split the bar's entries into the cells shown and the ones under "More", in the app's
132
+ * order (§10.7: the app puts its four most used first, or orders by role). One hidden
133
+ * entry is no saving — the More cell would take its place — so the bar then shows them
134
+ * all: the cell count is the same either way, and a real entry beats a detour.
135
+ */
136
+ export function splitMobileBar<T>(entries: readonly T[], max: number): { shown: T[]; hidden: T[] } {
137
+ const limit = Math.max(1, Math.floor(max));
138
+ if (entries.length <= limit + 1) return { shown: [...entries], hidden: [] };
139
+ return { shown: entries.slice(0, limit), hidden: entries.slice(limit) };
140
+ }
141
+
98
142
  /**
99
143
  * Exported and `<div>`-shaped. This is the outermost element of every consuming app, so
100
144
  * there is nothing above it to hang an id, a landmark label or a `data-tour` anchor on —
@@ -156,6 +200,22 @@ export interface AppShellProps extends ComponentPropsWithoutRef<"div"> {
156
200
  * price of pages hidden past the edge, so it is the app's call, per shell.
157
201
  */
158
202
  mobileSubNavLayout?: "wrap" | "scroll";
203
+ /**
204
+ * The most entries the phone bar shows; the rest move into a "More" cell that opens a
205
+ * sheet listing them, each group with its pages (0.32, docs/text-size-harmonization.md
206
+ * §4, §10.7). The bar takes `nav` in the app's order — after `mobileHidden` — so the
207
+ * app puts its most used entries first (or orders by role: kastlan's manager and
208
+ * accountant see different firsts).
209
+ *
210
+ * Default {@link DEFAULT_MOBILE_BAR_MAX}: unlimited at Normal, 4 at Large, 3 at Extra
211
+ * large, because the cells' words grow with the text and the phone does not. A number
212
+ * replaces the default at every size; `{ large: 5 }` replaces it at one size only.
213
+ * Never fewer than one entry; and one entry over the limit shows in full rather than
214
+ * behind a More that would take the same cell.
215
+ */
216
+ mobileBarMax?: number | Partial<Record<TextSize, number>>;
217
+ /** The More cell's words over the provider's `appShellMore`. */
218
+ moreLabels?: Partial<AppShellMoreLabels>;
159
219
  /**
160
220
  * Lay the shell out inside its PARENT'S box instead of the viewport: `h-full` rather
161
221
  * than `h-dvh`, its own scroll container at every width, a bottom bar pinned to the
@@ -201,9 +261,10 @@ function useNavHeightVar(
201
261
  // Re-run when the breakpoint flips. A ResizeObserver SKIPS an element that is not
202
262
  // being rendered, so the nav going `display:none` above `md` fires no callback and
203
263
  // would leave the last phone height published — a desktop footer would then sit 56px
204
- // off the bottom after a live resize. The query is the one the nav's own `md:hidden`
205
- // compiles to, so the two cannot disagree about where the boundary is.
206
- const isMdUp = useMediaQuery("(min-width: 768px)", false);
264
+ // off the bottom after a live resize. `useBreakpoint("md")` is the nav's own `md:hidden`
265
+ // at the text size in force (0.32), so the two cannot disagree about where the
266
+ // boundary is — at Extra large both move to 72rem.
267
+ const isMdUp = useBreakpoint("md", false);
207
268
  useEffect(() => {
208
269
  const node = ref.current;
209
270
  const shell = shellRef.current;
@@ -272,14 +333,26 @@ export function AppShell({
272
333
  subNav = "flyout",
273
334
  mobileSubNav = true,
274
335
  mobileSubNavLayout = "wrap",
336
+ mobileBarMax,
337
+ moreLabels,
275
338
  toggleGroupLabel,
276
339
  embedded: embeddedProp,
277
340
  className,
278
341
  ...rest
279
342
  }: AppShellProps) {
280
343
  const embedded = useContext(AppShellNesting) || !!embeddedProp;
281
- // The bottom bar's cells: every entry the phone does not reach another way.
282
- const barNav = nav.filter((item) => !item.mobileHidden);
344
+ const { size: textSize } = useTextSize();
345
+ const large = textSize !== "normal";
346
+ // The bottom bar's cells: every entry the phone does not reach another way, up to the
347
+ // text size's limit; the rest go under More (§10.7).
348
+ const barMax =
349
+ typeof mobileBarMax === "number"
350
+ ? mobileBarMax
351
+ : (mobileBarMax?.[textSize] ?? DEFAULT_MOBILE_BAR_MAX[textSize]);
352
+ const { shown: barNav, hidden: moreNav } = splitMobileBar(
353
+ nav.filter((item) => !item.mobileHidden),
354
+ barMax,
355
+ );
283
356
  // An embedded shell sharing the default key would overwrite the outer app's sidebar
284
357
  // preference with its own — so it persists only when given a key of its own.
285
358
  const collapseStorageKey =
@@ -310,6 +383,11 @@ export function AppShell({
310
383
  useEffect(() => {
311
384
  if (collapseStorageKey !== null) writeStored(collapseStorageKey, collapsed ? "1" : "0");
312
385
  }, [collapsed, collapseStorageKey]);
386
+ // §4: at Large and Extra large the sidebar never collapses to icons only — a glyph
387
+ // with its name in a tooltip is the very thing a reader who asked for bigger type
388
+ // cannot use. The persisted preference is left alone, so Normal gets it back, and the
389
+ // toggle goes while it could not do anything.
390
+ const iconOnly = collapsed && !large;
313
391
 
314
392
  return (
315
393
  // The caller's attributes go on the shell's own root, never on the `data-tour="nav"`
@@ -331,7 +409,10 @@ export function AppShell({
331
409
  className,
332
410
  )}
333
411
  >
334
- {topBar}
412
+ {/* §4: "the top bar keeps its essential icons" — its IconButtons stay icons at
413
+ Large (their labels the names and tooltips) and a locked one keeps its reason
414
+ in the tooltip: a bar 3rem tall has no room for words under every glyph. */}
415
+ <CompactControls>{topBar}</CompactControls>
335
416
  <div className="flex flex-1 min-h-0">
336
417
  <aside
337
418
  className={cn(
@@ -340,7 +421,7 @@ export function AppShell({
340
421
  // Embedded, the row it sits in is already the shell's height; the sticky
341
422
  // offsets below are measured against the window's top bar.
342
423
  embedded ? "md:self-stretch" : "md:sticky md:top-12 md:self-start md:h-[calc(100vh-3rem)]",
343
- collapsed ? "md:w-14" : "md:w-60",
424
+ iconOnly ? "md:w-14" : "md:w-60",
344
425
  )}
345
426
  >
346
427
  {/* The `<nav>` is flex-1 so it fills the sidebar's height (with empty
@@ -355,7 +436,7 @@ export function AppShell({
355
436
  <SidebarNavItem
356
437
  key={item.to}
357
438
  item={item}
358
- collapsed={collapsed}
439
+ collapsed={iconOnly}
359
440
  inline={subNav === "inline"}
360
441
  toggleGroupLabel={labels.toggleGroup}
361
442
  openGroup={openGroup}
@@ -365,33 +446,36 @@ export function AppShell({
365
446
  </div>
366
447
  </nav>
367
448
  {!!sidebarFooterItems?.length && (
368
- <SidebarFooterItems items={sidebarFooterItems} collapsed={collapsed} renderLink={renderLink} />
449
+ <SidebarFooterItems items={sidebarFooterItems} collapsed={iconOnly} renderLink={renderLink} />
369
450
  )}
370
- {sidebarFooter?.(collapsed)}
371
- <div className="border-t border-[var(--border)] p-2">
372
- {collapsed ? (
373
- <Tooltip label={labels.expand} side="end" portal className="block">
451
+ {sidebarFooter?.(iconOnly)}
452
+ {/* No toggle at Large: the sidebar cannot collapse there (see `iconOnly`). */}
453
+ {!large && (
454
+ <div className="border-t border-[var(--border)] p-2">
455
+ {collapsed ? (
456
+ <Tooltip label={labels.expand} side="end" portal className="block">
457
+ <button
458
+ type="button"
459
+ onClick={() => setCollapsed(false)}
460
+ aria-label={labels.expand}
461
+ className="flex w-full items-center justify-center min-h-9 rounded-md text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
462
+ >
463
+ <PanelLeftOpen className="size-4 shrink-0 rtl:-scale-x-100" />
464
+ </button>
465
+ </Tooltip>
466
+ ) : (
374
467
  <button
375
468
  type="button"
376
- onClick={() => setCollapsed(false)}
377
- aria-label={labels.expand}
378
- className="flex w-full items-center justify-center min-h-9 rounded-md text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
469
+ onClick={() => setCollapsed(true)}
470
+ aria-label={labels.collapse}
471
+ className="flex w-full items-center gap-3 min-h-9 px-3 py-2 rounded-md text-sm text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
379
472
  >
380
- <PanelLeftOpen className="size-4 shrink-0 rtl:-scale-x-100" />
473
+ <PanelLeftClose className="size-4 shrink-0 rtl:-scale-x-100" />
474
+ <span className="truncate">{labels.collapse}</span>
381
475
  </button>
382
- </Tooltip>
383
- ) : (
384
- <button
385
- type="button"
386
- onClick={() => setCollapsed(true)}
387
- aria-label={labels.collapse}
388
- className="flex w-full items-center gap-3 min-h-9 px-3 py-2 rounded-md text-sm text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]"
389
- >
390
- <PanelLeftClose className="size-4 shrink-0 rtl:-scale-x-100" />
391
- <span className="truncate">{labels.collapse}</span>
392
- </button>
393
- )}
394
- </div>
476
+ )}
477
+ </div>
478
+ )}
395
479
  </aside>
396
480
 
397
481
  {/* Content column beside the sidebar. main grows to fill so the footer
@@ -456,11 +540,12 @@ export function AppShell({
456
540
  <nav
457
541
  data-tour="nav"
458
542
  className="grid"
459
- style={{ gridTemplateColumns: `repeat(${barNav.length}, minmax(0, 1fr))` }}
543
+ style={{ gridTemplateColumns: `repeat(${barNav.length + (moreNav.length ? 1 : 0)}, minmax(0, 1fr))` }}
460
544
  >
461
545
  {barNav.map((item) => (
462
546
  <MobileNavItem key={item.to} item={item} />
463
547
  ))}
548
+ {moreNav.length > 0 && <MobileMoreCell hidden={moreNav} labels={moreLabels} />}
464
549
  </nav>
465
550
  </div>
466
551
  </div>
@@ -527,7 +612,7 @@ function SidebarFooterItems({
527
612
  <nav
528
613
  key={item.key}
529
614
  aria-label={item.label}
530
- className="flex flex-wrap justify-center gap-x-3 gap-y-0.5 text-[11px] text-[var(--text-muted)]"
615
+ className="flex flex-wrap justify-center gap-x-3 gap-y-0.5 text-caption text-[var(--text-muted)]"
531
616
  >
532
617
  {item.links.map((l) => (
533
618
  <span key={l.to}>
@@ -561,6 +646,37 @@ function SidebarFooterItems({
561
646
  );
562
647
  }
563
648
 
649
+ /**
650
+ * A bar cell's look, shared by the entries and the More cell. The active page needs a
651
+ * clear marker, not just a subtle text shade (feedback #327): brand-accent icon + label,
652
+ * bolder weight, and a top accent bar spanning the cell so it reads at a glance.
653
+ */
654
+ const mobileCellClass = (active: boolean) =>
655
+ cn(
656
+ // `h-full`: the grid row stretches every cell to the tallest one's height, so a
657
+ // label on two lines never leaves its neighbours a different shape.
658
+ "relative flex h-full w-full flex-col items-center justify-start py-2 text-caption gap-0.5 transition-colors",
659
+ "before:absolute before:content-[''] before:inset-x-4 before:top-0 before:h-0.5 before:rounded-full before:transition-colors",
660
+ active
661
+ ? "font-semibold text-[var(--brand)] before:bg-[var(--brand)]"
662
+ : "text-[var(--text-muted)] before:bg-transparent",
663
+ );
664
+
665
+ /**
666
+ * A bar cell's label (0.32, §10.7): up to TWO lines, never an ellipsis on one. At Large
667
+ * a cell is a fifth of a phone holding type a quarter bigger, and one line truncated
668
+ * "Transactions" to "Trans…" in every app (Kurvenschmiede). Two lines and then a clamp,
669
+ * centred, a long word broken (and hyphenated where the page's `lang` allows) rather
670
+ * than overflowing into the next cell. `shortLabel` still wins where the app has one.
671
+ */
672
+ function MobileCellLabel({ children }: { children: ReactNode }) {
673
+ return (
674
+ <span className="line-clamp-2 max-w-full break-words px-0.5 text-center leading-tight hyphens-auto">
675
+ {children}
676
+ </span>
677
+ );
678
+ }
679
+
564
680
  /** One cell of the phone's group bar. Marked while the group's own page OR any of its
565
681
  * pages is current — NavLink alone only knows its own `to`, which left the bar with
566
682
  * nothing marked on every sub-page (the same defect the sidebar had). */
@@ -571,31 +687,139 @@ function MobileNavItem({ item }: { item: AppShellNavItem }) {
571
687
  to={item.to}
572
688
  end={item.end ?? true}
573
689
  data-tour={item.dataTour}
574
- className={({ isActive }) =>
575
- // The active page needs a clear marker, not just a subtle text
576
- // shade (feedback #327): brand-accent icon + label, bolder weight,
577
- // and a top accent bar spanning the cell so it reads at a glance.
578
- cn(
579
- "relative flex flex-col items-center justify-center py-2 text-[11px] gap-0.5 transition-colors",
580
- "before:absolute before:content-[''] before:inset-x-4 before:top-0 before:h-0.5 before:rounded-full before:transition-colors",
581
- isActive || subActive
582
- ? "font-semibold text-[var(--brand)] before:bg-[var(--brand)]"
583
- : "text-[var(--text-muted)] before:bg-transparent",
584
- )
585
- }
690
+ className={({ isActive }) => mobileCellClass(isActive || subActive)}
586
691
  >
587
692
  <span className="relative">
588
693
  <item.icon className="size-5" />
589
694
  </span>
590
- {/* One line, always. `truncate` is the backstop for the case a
591
- `shortLabel` was not supplied (or a translation is longer than its
592
- author expected): an ellipsis in one cell is a far smaller problem
593
- than a bar whose rows are different heights. */}
594
- <span className="max-w-full truncate px-0.5">{item.shortLabel ?? item.label}</span>
695
+ <MobileCellLabel>{item.shortLabel ?? item.label}</MobileCellLabel>
595
696
  </NavLink>
596
697
  );
597
698
  }
598
699
 
700
+ /** Whether the current route is `item`'s page or one of its pages. */
701
+ function entryIsCurrent(item: AppShellNavItem, pathname: string): boolean {
702
+ return (
703
+ !!matchPath({ path: item.to, end: item.end ?? true }, pathname) ||
704
+ !!item.items?.some((sub) => matchPath({ path: sub.to, end: sub.end ?? true }, pathname))
705
+ );
706
+ }
707
+
708
+ /** A hidden entry's link, resolved as the router would write it (basename included). */
709
+ function TourAnchor({ to, dataTour }: { to: string; dataTour?: string }) {
710
+ const href = useHref(to);
711
+ return (
712
+ // eslint-disable-next-line jsx-a11y/anchor-has-content -- a tour anchor, aria-hidden and inert; the More button under it is the control (see MobileMoreCell)
713
+ <a
714
+ href={href}
715
+ data-tour={dataTour}
716
+ // Not a control: no name, no tab stop, no pointer — the More button under it
717
+ // takes the tap. It exists only to be FOUND (see MobileMoreCell).
718
+ aria-hidden
719
+ tabIndex={-1}
720
+ className="pointer-events-none absolute inset-0"
721
+ />
722
+ );
723
+ }
724
+
725
+ /**
726
+ * The bar's "More" cell (0.32, docs/text-size-harmonization.md §4, §10.7): the entries
727
+ * past `mobileBarMax`, in a sheet — each entry as a link, and under a group its pages,
728
+ * since a group the bar no longer shows has no sub-row to reach them by either. Marked
729
+ * active while the current page is one of them, as the cell it replaced would be.
730
+ *
731
+ * **Tour anchors.** A guided tour finds a nav entry by `[data-tour="nav"] a[href="/x"]`
732
+ * (keksdose's tours) or by the entry's own `dataTour`. An entry under More has no link
733
+ * in the bar, and the sidebar's copy is `display:none` on a phone, so such a step would
734
+ * spotlight nothing. So the More cell carries the hidden entries' links — one `<a>` per
735
+ * entry and per page of a hidden group, with the entry's `href` and `dataTour` — laid
736
+ * over the cell (`absolute inset-0`): both selectors resolve to a visible box, and that
737
+ * box is the More cell, which is where the reader has to go next. They are
738
+ * `aria-hidden`, out of the tab order and pointer-transparent, so neither a screen
739
+ * reader nor a tap ever meets them; the button under them is the one control.
740
+ */
741
+ function MobileMoreCell({
742
+ hidden,
743
+ labels: labelsProp,
744
+ }: {
745
+ hidden: AppShellNavItem[];
746
+ labels?: Partial<AppShellMoreLabels>;
747
+ }) {
748
+ const labels: AppShellMoreLabels = useKitLabels("appShellMore", DEFAULT_APP_SHELL_MORE_LABELS, labelsProp);
749
+ const { pathname } = useLocation();
750
+ const [open, setOpen] = useState(false);
751
+ const active = hidden.some((item) => entryIsCurrent(item, pathname));
752
+ const close = () => setOpen(false);
753
+ return (
754
+ <div data-slot="app-shell-more" className="relative min-w-0">
755
+ <button
756
+ type="button"
757
+ aria-haspopup="dialog"
758
+ aria-expanded={open}
759
+ onClick={() => setOpen(true)}
760
+ className={mobileCellClass(active)}
761
+ >
762
+ <span className="relative">
763
+ <MoreHorizontal aria-hidden className="size-5" />
764
+ </span>
765
+ <MobileCellLabel>{labels.more}</MobileCellLabel>
766
+ </button>
767
+ {hidden.flatMap((item) => [
768
+ <TourAnchor key={item.to} to={item.to} dataTour={item.dataTour} />,
769
+ ...(item.items ?? []).map((sub) => <TourAnchor key={`${item.to} ${sub.to}`} to={sub.to} />),
770
+ ])}
771
+ <DialogFrame open={open} onClose={close} title={labels.moreTitle} closeButton bodyClassName="space-y-1">
772
+ <ul className="space-y-1">
773
+ {hidden.map((item) => (
774
+ <li key={item.to}>
775
+ <NavLink
776
+ to={item.to}
777
+ end={item.end ?? true}
778
+ onClick={close}
779
+ className={({ isActive }) =>
780
+ cn(
781
+ "flex min-h-11 items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors",
782
+ isActive
783
+ ? "bg-[var(--bg-inverse)] text-[var(--text-inverse)]"
784
+ : "text-[var(--text-primary)] hover:bg-[var(--bg-hover)]",
785
+ )
786
+ }
787
+ >
788
+ <item.icon aria-hidden className="size-5 shrink-0" />
789
+ <span className="min-w-0 break-words leading-snug">{item.label}</span>
790
+ </NavLink>
791
+ {!!item.items?.length && (
792
+ <ul aria-label={item.label} className="ms-[1.3rem] mt-0.5 space-y-0.5 border-s border-[var(--border)] ps-2">
793
+ {item.items.map((sub) => (
794
+ <li key={sub.to}>
795
+ <NavLink
796
+ to={sub.to}
797
+ end={sub.end ?? true}
798
+ onClick={close}
799
+ className={({ isActive }) =>
800
+ cn(
801
+ "flex min-h-11 items-center gap-2.5 rounded-md px-2.5 py-1.5 text-sm transition-colors",
802
+ isActive
803
+ ? "bg-[var(--bg-inverse)] text-[var(--text-inverse)]"
804
+ : "text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]",
805
+ )
806
+ }
807
+ >
808
+ <sub.icon aria-hidden className="size-4 shrink-0 opacity-70" />
809
+ <span className="min-w-0 break-words leading-snug">{sub.label}</span>
810
+ </NavLink>
811
+ </li>
812
+ ))}
813
+ </ul>
814
+ )}
815
+ </li>
816
+ ))}
817
+ </ul>
818
+ </DialogFrame>
819
+ </div>
820
+ );
821
+ }
822
+
599
823
  /**
600
824
  * The upper row of the phone navigation: the pages of the group the reader is in, as
601
825
  * a sideways-scrolling row of pills. Rendered only while such a group is current —
@@ -741,7 +965,7 @@ function SidebarNavItem({
741
965
  </span>
742
966
  {/* Wraps rather than truncates: in the sidebar a label cut to "Checkbox, switch
743
967
  & sl…" hides the one word that tells two pages apart, and there is height
744
- to spare. (The phone bar keeps `truncate` — its cells must stay one row.) */}
968
+ to spare. (The phone bar clamps at two lines instead, since 0.32 §10.7.) */}
745
969
  {!collapsed && <span className="min-w-0 flex-1 break-words leading-snug">{item.label}</span>}
746
970
  {!collapsed && hasSub && <ChevronRight className="size-3.5 shrink-0 opacity-60 rtl:-scale-x-100" />}
747
971
  </NavLink>
@@ -995,7 +1219,7 @@ function SidebarFlyout({ item, children }: { item: AppShellNavItem; children: Re
995
1219
  aria-label={item.label}
996
1220
  className="animate-flyout min-w-52 overflow-hidden rounded-md border border-[var(--border)] bg-[var(--bg-surface)] shadow-lg"
997
1221
  >
998
- <div className="border-b border-[var(--border)] px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-[var(--text-muted)]">
1222
+ <div className="border-b border-[var(--border)] px-3 py-2 text-caption font-semibold uppercase tracking-wide text-[var(--text-muted)]">
999
1223
  {item.label}
1000
1224
  </div>
1001
1225
  <ul className="py-1">
@@ -67,7 +67,7 @@ const SAFE_AREA: CSSProperties = {
67
67
  };
68
68
 
69
69
  const FOOTER_CLASS =
70
- "flex flex-wrap justify-center gap-x-4 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a:hover]:underline [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-2 [&_a:focus-visible]:ring-[var(--brand)]";
70
+ "flex flex-wrap justify-center gap-x-4 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a:hover]:underline [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-[length:var(--focus-ring-width)] [&_a:focus-visible]:ring-[var(--brand)]";
71
71
 
72
72
  /**
73
73
  * The public page frame outside the app shell: sign-in, registration, email
@@ -46,7 +46,7 @@ export function OptionSwitcherMenu<T extends string>({
46
46
  {(close) => (
47
47
  <ul className="py-1">
48
48
  {heading && (
49
- <li className="px-3 py-1 text-[10px] font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
49
+ <li className="px-3 py-1 text-micro font-semibold uppercase tracking-wide text-[var(--text-placeholder)]">
50
50
  {heading}
51
51
  </li>
52
52
  )}