@eifi1/ui-kit 0.11.0 → 0.13.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 (397) hide show
  1. package/README.md +59 -34
  2. package/dist/chart.d.ts +3 -1
  3. package/dist/chart.js +2 -0
  4. package/dist/chart.js.map +1 -1
  5. package/dist/components/account-settings-labels.d.ts +105 -0
  6. package/dist/components/account-settings-labels.js +81 -0
  7. package/dist/components/account-settings-labels.js.map +1 -0
  8. package/dist/components/account-settings.d.ts +36 -52
  9. package/dist/components/account-settings.js +44 -8
  10. package/dist/components/account-settings.js.map +1 -1
  11. package/dist/components/amount-input.d.ts +40 -4
  12. package/dist/components/amount-input.js +50 -12
  13. package/dist/components/amount-input.js.map +1 -1
  14. package/dist/components/authed-image.d.ts +68 -0
  15. package/dist/components/authed-image.js +117 -0
  16. package/dist/components/authed-image.js.map +1 -0
  17. package/dist/components/breadcrumbs.d.ts +2 -1
  18. package/dist/components/breadcrumbs.js +5 -2
  19. package/dist/components/breadcrumbs.js.map +1 -1
  20. package/dist/components/bulk-action-bar.d.ts +10 -2
  21. package/dist/components/bulk-action-bar.js.map +1 -1
  22. package/dist/components/button-group.d.ts +51 -2
  23. package/dist/components/button-group.js +5 -1
  24. package/dist/components/button-group.js.map +1 -1
  25. package/dist/components/calculator.d.ts +15 -4
  26. package/dist/components/calendar-heatmap.d.ts +41 -3
  27. package/dist/components/calendar-heatmap.js +48 -7
  28. package/dist/components/calendar-heatmap.js.map +1 -1
  29. package/dist/components/chart.js +14 -8
  30. package/dist/components/chart.js.map +1 -1
  31. package/dist/components/chip.d.ts +2 -1
  32. package/dist/components/chip.js +26 -18
  33. package/dist/components/chip.js.map +1 -1
  34. package/dist/components/choice-card.d.ts +35 -3
  35. package/dist/components/choice-card.js +83 -25
  36. package/dist/components/choice-card.js.map +1 -1
  37. package/dist/components/combobox-core.d.ts +3 -1
  38. package/dist/components/combobox-core.js +18 -5
  39. package/dist/components/combobox-core.js.map +1 -1
  40. package/dist/components/combobox.d.ts +2 -2
  41. package/dist/components/combobox.js +31 -9
  42. package/dist/components/combobox.js.map +1 -1
  43. package/dist/components/copy-button.d.ts +51 -70
  44. package/dist/components/copy-button.js +1 -1
  45. package/dist/components/copy-button.js.map +1 -1
  46. package/dist/components/data-table-cells.d.ts +63 -0
  47. package/dist/components/data-table-cells.js +64 -0
  48. package/dist/components/data-table-cells.js.map +1 -0
  49. package/dist/components/data-table-filter-popover.d.ts +1 -1
  50. package/dist/components/data-table-filter-popover.js +8 -1
  51. package/dist/components/data-table-filter-popover.js.map +1 -1
  52. package/dist/components/data-table-filters.d.ts +1 -1
  53. package/dist/components/data-table-filters.js +31 -1
  54. package/dist/components/data-table-filters.js.map +1 -1
  55. package/dist/components/data-table-labels.d.ts +13 -0
  56. package/dist/components/data-table-labels.js +8 -1
  57. package/dist/components/data-table-labels.js.map +1 -1
  58. package/dist/components/data-table.d.ts +1 -1
  59. package/dist/components/data-table.js +125 -27
  60. package/dist/components/data-table.js.map +1 -1
  61. package/dist/components/date-picker.d.ts +3 -1
  62. package/dist/components/date-picker.js +15 -2
  63. package/dist/components/date-picker.js.map +1 -1
  64. package/dist/components/description-list.d.ts +23 -3
  65. package/dist/components/description-list.js +23 -3
  66. package/dist/components/description-list.js.map +1 -1
  67. package/dist/components/dialog-frame.d.ts +1 -0
  68. package/dist/components/entity-combobox.d.ts +1 -1
  69. package/dist/components/entity-combobox.js +40 -20
  70. package/dist/components/entity-combobox.js.map +1 -1
  71. package/dist/components/error-boundary.d.ts +281 -0
  72. package/dist/components/error-boundary.js +302 -0
  73. package/dist/components/error-boundary.js.map +1 -0
  74. package/dist/components/field-sync.js +90 -83
  75. package/dist/components/field-sync.js.map +1 -1
  76. package/dist/components/field.d.ts +30 -4
  77. package/dist/components/field.js +40 -17
  78. package/dist/components/field.js.map +1 -1
  79. package/dist/components/file-button.d.ts +50 -208
  80. package/dist/components/file-dropzone.d.ts +49 -2
  81. package/dist/components/floating-panel.d.ts +13 -4
  82. package/dist/components/floating-panel.js +7 -3
  83. package/dist/components/floating-panel.js.map +1 -1
  84. package/dist/components/form-actions.d.ts +53 -0
  85. package/dist/components/form-actions.js +98 -0
  86. package/dist/components/form-actions.js.map +1 -0
  87. package/dist/components/full-bleed-dialog.js +4 -0
  88. package/dist/components/full-bleed-dialog.js.map +1 -1
  89. package/dist/components/hover-menu.js +64 -56
  90. package/dist/components/hover-menu.js.map +1 -1
  91. package/dist/components/image-grid.d.ts +67 -0
  92. package/dist/components/image-grid.js +112 -0
  93. package/dist/components/image-grid.js.map +1 -0
  94. package/dist/components/lightbox.d.ts +104 -0
  95. package/dist/components/lightbox.js +229 -0
  96. package/dist/components/lightbox.js.map +1 -0
  97. package/dist/components/line-items.d.ts +114 -0
  98. package/dist/components/line-items.js +238 -0
  99. package/dist/components/line-items.js.map +1 -0
  100. package/dist/components/list.d.ts +37 -4
  101. package/dist/components/list.js +35 -14
  102. package/dist/components/list.js.map +1 -1
  103. package/dist/components/loading-state.d.ts +24 -0
  104. package/dist/components/loading-state.js +32 -0
  105. package/dist/components/loading-state.js.map +1 -0
  106. package/dist/components/menu-item.d.ts +2 -1
  107. package/dist/components/menu-item.js +5 -1
  108. package/dist/components/menu-item.js.map +1 -1
  109. package/dist/components/mini-calendar.d.ts +13 -1
  110. package/dist/components/mini-calendar.js +46 -2
  111. package/dist/components/mini-calendar.js.map +1 -1
  112. package/dist/components/modal.d.ts +20 -1
  113. package/dist/components/modal.js +26 -2
  114. package/dist/components/modal.js.map +1 -1
  115. package/dist/components/month-picker.d.ts +21 -1
  116. package/dist/components/month-picker.js +139 -26
  117. package/dist/components/month-picker.js.map +1 -1
  118. package/dist/components/multi-entity-combobox.d.ts +1 -1
  119. package/dist/components/multi-entity-combobox.js +42 -20
  120. package/dist/components/multi-entity-combobox.js.map +1 -1
  121. package/dist/components/nav-pills.d.ts +2 -1
  122. package/dist/components/nav-pills.js +5 -1
  123. package/dist/components/nav-pills.js.map +1 -1
  124. package/dist/components/number-field.d.ts +24 -5
  125. package/dist/components/number-field.js +5 -7
  126. package/dist/components/number-field.js.map +1 -1
  127. package/dist/components/number-input.d.ts +25 -5
  128. package/dist/components/number-input.js +7 -1
  129. package/dist/components/number-input.js.map +1 -1
  130. package/dist/components/numpad-sheet.d.ts +20 -5
  131. package/dist/components/numpad-sheet.js +3 -2
  132. package/dist/components/numpad-sheet.js.map +1 -1
  133. package/dist/components/page-header.d.ts +35 -6
  134. package/dist/components/page-header.js +7 -1
  135. package/dist/components/page-header.js.map +1 -1
  136. package/dist/components/passkeys-setting.d.ts +67 -0
  137. package/dist/components/passkeys-setting.js +214 -0
  138. package/dist/components/passkeys-setting.js.map +1 -0
  139. package/dist/components/picker-sheet.js +5 -1
  140. package/dist/components/picker-sheet.js.map +1 -1
  141. package/dist/components/pie-chart-labels.d.ts +20 -0
  142. package/dist/components/pie-chart-labels.js +12 -0
  143. package/dist/components/pie-chart-labels.js.map +1 -0
  144. package/dist/components/pie-chart.d.ts +101 -0
  145. package/dist/components/pie-chart.js +283 -0
  146. package/dist/components/pie-chart.js.map +1 -0
  147. package/dist/components/popover.js +3 -0
  148. package/dist/components/popover.js.map +1 -1
  149. package/dist/components/progress-bar.d.ts +44 -3
  150. package/dist/components/progress-bar.js +58 -22
  151. package/dist/components/progress-bar.js.map +1 -1
  152. package/dist/components/qr-code.d.ts +34 -0
  153. package/dist/components/qr-code.js +57 -0
  154. package/dist/components/qr-code.js.map +1 -0
  155. package/dist/components/search-field.d.ts +15 -3
  156. package/dist/components/search-field.js +5 -1
  157. package/dist/components/search-field.js.map +1 -1
  158. package/dist/components/settings-fields.d.ts +49 -1
  159. package/dist/components/signed-amount.d.ts +122 -0
  160. package/dist/components/signed-amount.js +152 -0
  161. package/dist/components/signed-amount.js.map +1 -0
  162. package/dist/components/skeleton.d.ts +10 -3
  163. package/dist/components/skeleton.js +12 -2
  164. package/dist/components/skeleton.js.map +1 -1
  165. package/dist/components/stat-tile.d.ts +2 -1
  166. package/dist/components/stat-tile.js +6 -3
  167. package/dist/components/stat-tile.js.map +1 -1
  168. package/dist/components/swipeable-row.js +4 -3
  169. package/dist/components/swipeable-row.js.map +1 -1
  170. package/dist/components/table.d.ts +39 -5
  171. package/dist/components/table.js +45 -11
  172. package/dist/components/table.js.map +1 -1
  173. package/dist/components/text-link.d.ts +238 -0
  174. package/dist/components/text-link.js +144 -0
  175. package/dist/components/text-link.js.map +1 -0
  176. package/dist/components/text.d.ts +26 -2
  177. package/dist/components/text.js +16 -2
  178. package/dist/components/text.js.map +1 -1
  179. package/dist/components/time-input.d.ts +49 -1
  180. package/dist/components/toggle-group.d.ts +35 -0
  181. package/dist/components/toggle-group.js +43 -14
  182. package/dist/components/toggle-group.js.map +1 -1
  183. package/dist/components/toggle-legend.d.ts +53 -3
  184. package/dist/components/toggle-legend.js +54 -1
  185. package/dist/components/toggle-legend.js.map +1 -1
  186. package/dist/components/tooltip.d.ts +51 -1
  187. package/dist/components/tooltip.js +59 -35
  188. package/dist/components/tooltip.js.map +1 -1
  189. package/dist/components/ui.d.ts +51 -779
  190. package/dist/components/ui.js +364 -34
  191. package/dist/components/ui.js.map +1 -1
  192. package/dist/components/use-table-state.d.ts +3 -52
  193. package/dist/components/use-table-state.js +128 -30
  194. package/dist/components/use-table-state.js.map +1 -1
  195. package/dist/{data-table-filters-Dh9uF_S-.d.ts → data-table-Drun8Fk9.d.ts} +305 -61
  196. package/dist/data-table.d.ts +2 -1
  197. package/dist/data-table.js +5 -1
  198. package/dist/data-table.js.map +1 -1
  199. package/dist/feedback/feedback-attachment.js +4 -6
  200. package/dist/feedback/feedback-attachment.js.map +1 -1
  201. package/dist/feedback/feedback-dialog.d.ts +26 -3
  202. package/dist/feedback/feedback-dialog.js +21 -5
  203. package/dist/feedback/feedback-dialog.js.map +1 -1
  204. package/dist/feedback/feedback-inbox.js +86 -75
  205. package/dist/feedback/feedback-inbox.js.map +1 -1
  206. package/dist/feedback/feedback-thread.d.ts +173 -0
  207. package/dist/feedback/feedback-thread.js +264 -0
  208. package/dist/feedback/feedback-thread.js.map +1 -0
  209. package/dist/feedback.d.ts +2 -1
  210. package/dist/feedback.js +1 -0
  211. package/dist/feedback.js.map +1 -1
  212. package/dist/hooks/use-authed-src.d.ts +71 -0
  213. package/dist/hooks/use-authed-src.js +63 -0
  214. package/dist/hooks/use-authed-src.js.map +1 -0
  215. package/dist/hooks/use-file-drop.d.ts +49 -2
  216. package/dist/hooks/use-hotkey.d.ts +59 -0
  217. package/dist/hooks/use-hotkey.js +105 -0
  218. package/dist/hooks/use-hotkey.js.map +1 -0
  219. package/dist/hooks/use-search-param-state.d.ts +83 -0
  220. package/dist/hooks/use-search-param-state.js +94 -0
  221. package/dist/hooks/use-search-param-state.js.map +1 -0
  222. package/dist/i18n/defaults.d.ts +15 -4
  223. package/dist/i18n/defaults.js +31 -1
  224. package/dist/i18n/defaults.js.map +1 -1
  225. package/dist/i18n/kit-labels.d.ts +46 -312
  226. package/dist/i18n/kit-labels.js +20 -5
  227. package/dist/i18n/kit-labels.js.map +1 -1
  228. package/dist/i18n/locales/de-CH-informal.d.ts +15 -4
  229. package/dist/i18n/locales/de-CH.d.ts +15 -4
  230. package/dist/i18n/locales/de-informal.d.ts +15 -4
  231. package/dist/i18n/locales/de.d.ts +15 -4
  232. package/dist/i18n/locales/de.js +164 -3
  233. package/dist/i18n/locales/de.js.map +1 -1
  234. package/dist/i18n/locales/es.d.ts +15 -4
  235. package/dist/i18n/locales/es.js +161 -3
  236. package/dist/i18n/locales/es.js.map +1 -1
  237. package/dist/i18n/locales/fr.d.ts +15 -4
  238. package/dist/i18n/locales/fr.js +161 -3
  239. package/dist/i18n/locales/fr.js.map +1 -1
  240. package/dist/i18n/locales/hu.d.ts +15 -4
  241. package/dist/i18n/locales/hu.js +161 -3
  242. package/dist/i18n/locales/hu.js.map +1 -1
  243. package/dist/i18n/locales/it.d.ts +15 -4
  244. package/dist/i18n/locales/it.js +161 -3
  245. package/dist/i18n/locales/it.js.map +1 -1
  246. package/dist/i18n/locales/zh.d.ts +15 -4
  247. package/dist/i18n/locales/zh.js +161 -3
  248. package/dist/i18n/locales/zh.js.map +1 -1
  249. package/dist/index.d.ts +39 -19
  250. package/dist/index.js +40 -1
  251. package/dist/index.js.map +1 -1
  252. package/dist/kit-labels-Mz8zZHIF.d.ts +1744 -0
  253. package/dist/lib/format.d.ts +162 -0
  254. package/dist/lib/format.js +151 -0
  255. package/dist/lib/format.js.map +1 -0
  256. package/dist/lib/qr-encode.d.ts +44 -0
  257. package/dist/lib/qr-encode.js +338 -0
  258. package/dist/lib/qr-encode.js.map +1 -0
  259. package/dist/rhf/fields.d.ts +283 -0
  260. package/dist/rhf/fields.js +631 -0
  261. package/dist/rhf/fields.js.map +1 -0
  262. package/dist/rhf/form.d.ts +49 -1
  263. package/dist/rhf/line-items.d.ts +31 -0
  264. package/dist/rhf/line-items.js +59 -0
  265. package/dist/rhf/line-items.js.map +1 -0
  266. package/dist/rhf.d.ts +61 -1
  267. package/dist/rhf.js +2 -0
  268. package/dist/rhf.js.map +1 -1
  269. package/dist/search/command-palette.js +20 -14
  270. package/dist/search/command-palette.js.map +1 -1
  271. package/dist/shell/app-shell.d.ts +99 -2
  272. package/dist/shell/app-shell.js +120 -57
  273. package/dist/shell/app-shell.js.map +1 -1
  274. package/dist/shell/auth-layout.d.ts +74 -0
  275. package/dist/shell/auth-layout.js +97 -0
  276. package/dist/shell/auth-layout.js.map +1 -0
  277. package/dist/shell/top-bar-brand.d.ts +81 -0
  278. package/dist/shell/top-bar-brand.js +47 -0
  279. package/dist/shell/top-bar-brand.js.map +1 -0
  280. package/dist/shell/topbar-action-menu.d.ts +21 -4
  281. package/dist/shell/topbar-action-menu.js +18 -2
  282. package/dist/shell/topbar-action-menu.js.map +1 -1
  283. package/dist/shell.d.ts +56 -1
  284. package/dist/shell.js +2 -0
  285. package/dist/shell.js.map +1 -1
  286. package/dist/tour/tour.js +10 -4
  287. package/dist/tour/tour.js.map +1 -1
  288. package/dist/wizard/stepper-nav.d.ts +48 -1
  289. package/dist/wizard/use-wizard.js +4 -2
  290. package/dist/wizard/use-wizard.js.map +1 -1
  291. package/dist/wizard/wizard-context.d.ts +24 -1
  292. package/dist/wizard/wizard-context.js +19 -2
  293. package/dist/wizard/wizard-context.js.map +1 -1
  294. package/dist/wizard/wizard-step.d.ts +30 -5
  295. package/dist/wizard/wizard-step.js +34 -3
  296. package/dist/wizard/wizard-step.js.map +1 -1
  297. package/dist/wizard.d.ts +50 -3
  298. package/package.json +6 -6
  299. package/src/chart.ts +4 -0
  300. package/src/components/account-settings-labels.ts +190 -0
  301. package/src/components/account-settings.tsx +110 -63
  302. package/src/components/amount-input.tsx +109 -10
  303. package/src/components/authed-image.tsx +190 -0
  304. package/src/components/breadcrumbs.tsx +8 -2
  305. package/src/components/bulk-action-bar.tsx +16 -3
  306. package/src/components/button-group.tsx +7 -2
  307. package/src/components/calendar-heatmap.tsx +105 -10
  308. package/src/components/chart.tsx +19 -8
  309. package/src/components/chip.tsx +14 -4
  310. package/src/components/choice-card.tsx +136 -25
  311. package/src/components/combobox-core.tsx +33 -9
  312. package/src/components/combobox.tsx +33 -9
  313. package/src/components/copy-button.tsx +3 -2
  314. package/src/components/data-table-cells.tsx +119 -0
  315. package/src/components/data-table-filter-popover.tsx +13 -5
  316. package/src/components/data-table-filters.ts +77 -0
  317. package/src/components/data-table-labels.ts +23 -0
  318. package/src/components/data-table.tsx +230 -13
  319. package/src/components/date-picker.tsx +19 -3
  320. package/src/components/description-list.tsx +52 -3
  321. package/src/components/entity-combobox.tsx +25 -7
  322. package/src/components/error-boundary.tsx +640 -0
  323. package/src/components/field-sync.tsx +48 -40
  324. package/src/components/field.tsx +56 -5
  325. package/src/components/floating-panel.tsx +20 -7
  326. package/src/components/form-actions.tsx +191 -0
  327. package/src/components/full-bleed-dialog.tsx +4 -0
  328. package/src/components/hover-menu.tsx +26 -17
  329. package/src/components/image-grid.tsx +181 -0
  330. package/src/components/lightbox.tsx +374 -0
  331. package/src/components/line-items.tsx +385 -0
  332. package/src/components/list.tsx +82 -19
  333. package/src/components/loading-state.tsx +47 -0
  334. package/src/components/menu-item.tsx +7 -2
  335. package/src/components/mini-calendar.tsx +72 -3
  336. package/src/components/modal.tsx +65 -3
  337. package/src/components/month-picker.tsx +181 -26
  338. package/src/components/multi-entity-combobox.tsx +27 -7
  339. package/src/components/nav-pills.tsx +8 -1
  340. package/src/components/number-field.tsx +15 -10
  341. package/src/components/number-input.tsx +17 -1
  342. package/src/components/numpad-sheet.tsx +6 -1
  343. package/src/components/page-header.tsx +42 -5
  344. package/src/components/passkeys-setting.tsx +331 -0
  345. package/src/components/picker-sheet.tsx +10 -1
  346. package/src/components/pie-chart-labels.ts +32 -0
  347. package/src/components/pie-chart.tsx +479 -0
  348. package/src/components/popover.tsx +3 -0
  349. package/src/components/progress-bar.tsx +129 -19
  350. package/src/components/qr-code.tsx +83 -0
  351. package/src/components/search-field.tsx +20 -4
  352. package/src/components/signed-amount.tsx +295 -0
  353. package/src/components/skeleton.tsx +21 -3
  354. package/src/components/stat-tile.tsx +11 -3
  355. package/src/components/swipeable-row.tsx +7 -7
  356. package/src/components/table.tsx +102 -13
  357. package/src/components/text-link.tsx +356 -0
  358. package/src/components/text.tsx +46 -2
  359. package/src/components/toggle-group.tsx +93 -20
  360. package/src/components/toggle-legend.tsx +108 -2
  361. package/src/components/tooltip.tsx +79 -8
  362. package/src/components/ui.tsx +707 -49
  363. package/src/components/use-table-state.ts +265 -33
  364. package/src/data-table.ts +10 -0
  365. package/src/feedback/feedback-attachment.tsx +10 -7
  366. package/src/feedback/feedback-dialog.tsx +49 -7
  367. package/src/feedback/feedback-inbox.tsx +7 -0
  368. package/src/feedback/feedback-thread.tsx +510 -0
  369. package/src/feedback.ts +1 -0
  370. package/src/hooks/use-authed-src.ts +161 -0
  371. package/src/hooks/use-hotkey.ts +172 -0
  372. package/src/hooks/use-search-param-state.ts +187 -0
  373. package/src/i18n/defaults.ts +30 -0
  374. package/src/i18n/kit-labels.tsx +118 -8
  375. package/src/i18n/locales/de.ts +161 -0
  376. package/src/i18n/locales/es.ts +158 -0
  377. package/src/i18n/locales/fr.ts +161 -0
  378. package/src/i18n/locales/hu.ts +159 -0
  379. package/src/i18n/locales/it.ts +159 -0
  380. package/src/i18n/locales/zh.ts +158 -0
  381. package/src/index.ts +59 -0
  382. package/src/lib/format.ts +348 -0
  383. package/src/lib/qr-encode.ts +420 -0
  384. package/src/rhf/fields.tsx +1135 -0
  385. package/src/rhf/line-items.tsx +130 -0
  386. package/src/rhf.ts +6 -0
  387. package/src/search/command-palette.tsx +29 -19
  388. package/src/shell/app-shell.tsx +153 -3
  389. package/src/shell/auth-layout.tsx +190 -0
  390. package/src/shell/top-bar-brand.tsx +76 -0
  391. package/src/shell/topbar-action-menu.tsx +32 -4
  392. package/src/shell.ts +2 -0
  393. package/src/tour/tour.tsx +16 -5
  394. package/src/wizard/use-wizard.ts +8 -4
  395. package/src/wizard/wizard-context.tsx +41 -1
  396. package/src/wizard/wizard-step.tsx +75 -6
  397. package/tokens.css +12 -0
@@ -0,0 +1,76 @@
1
+ import type { ReactNode } from "react";
2
+ import { Link as RouterLink } from "react-router";
3
+ import { cn } from "../lib/cn";
4
+ import { useKitLink } from "../i18n/kit-labels";
5
+ import { pickLinkRenderer } from "../components/text-link";
6
+ import type { KitLinkComponent, KitLinkProps } from "../i18n/kit-labels";
7
+
8
+ const routerLink: KitLinkComponent = ({ href, ...p }) => <RouterLink to={href} {...p} />;
9
+ const plainLink: KitLinkComponent = ({ children, ...p }) => <a {...p}>{children}</a>;
10
+
11
+ /** Below which width only the logo shows. The name stays in the link's accessible
12
+ * name at every width. */
13
+ const COLLAPSE = {
14
+ sm: "sr-only sm:not-sr-only sm:truncate",
15
+ md: "sr-only md:not-sr-only md:truncate",
16
+ } as const;
17
+
18
+ export interface TopBarBrandProps extends Omit<KitLinkProps, "href" | "children"> {
19
+ /** The brand mark — an SVG, sized by the caller or `h-6 w-auto` by default. */
20
+ logo: ReactNode;
21
+ /** The product name beside the logo, and the link's accessible name. */
22
+ name: ReactNode;
23
+ /** Where the brand links to. Default `/`. */
24
+ to?: string;
25
+ /** Hide the name below `sm` (default) or `md`; `false` always shows it. It is
26
+ * visually hidden, not removed, so the link keeps its name on a phone. */
27
+ collapseBelow?: "sm" | "md" | false;
28
+ /** The app's router link for this one link. Default: the provider's
29
+ * `linkComponent`, else react-router's `Link`. */
30
+ renderLink?: KitLinkComponent;
31
+ }
32
+
33
+ /**
34
+ * The top bar's home link: logo + product name, collapsing to the logo on a phone.
35
+ * Pass it as {@link TopBar}'s `brand`.
36
+ *
37
+ * kastlan's (top-bar.tsx:66) hid nothing and truncated the name instead — on a phone
38
+ * with five actions that left "Ka…" beside the logo — and needed an `aria-label`
39
+ * because the name could be cut. Here the name is `sr-only` below the breakpoint, so
40
+ * the logo is the whole visible link there and the name is still what it is called.
41
+ */
42
+ export function TopBarBrand({
43
+ logo,
44
+ name,
45
+ to = "/",
46
+ collapseBelow = "sm",
47
+ renderLink,
48
+ className,
49
+ ...rest
50
+ }: TopBarBrandProps) {
51
+ const kitLink = useKitLink();
52
+ return (
53
+ <RenderedLink
54
+ // The kit's one link rule (`pickLinkRenderer`): an external `to` or an in-page
55
+ // `#anchor` is a plain `<a>`, never the router's link.
56
+ render={pickLinkRenderer(renderLink, kitLink ?? routerLink, to) ?? plainLink}
57
+ {...rest}
58
+ href={to}
59
+ className={cn(
60
+ // `relative` contains the sr-only name.
61
+ "relative flex min-w-0 items-center gap-2 rounded-md font-semibold text-[var(--text-primary)] hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)]",
62
+ className,
63
+ )}
64
+ >
65
+ <span aria-hidden className="flex shrink-0 items-center [&_svg]:h-6 [&_svg]:w-auto">
66
+ {logo}
67
+ </span>
68
+ <span className={collapseBelow ? COLLAPSE[collapseBelow] : "truncate"}>{name}</span>
69
+ </RenderedLink>
70
+ );
71
+ }
72
+
73
+ /** Calls the link renderer as a component, so a router link's hooks are its own. */
74
+ function RenderedLink({ render, ...props }: KitLinkProps & { render: KitLinkComponent }) {
75
+ return render(props);
76
+ }
@@ -5,6 +5,8 @@ import { cn } from "../lib/cn";
5
5
  import { HoverMenu } from "../components/hover-menu";
6
6
  import { MenuItem } from "../components/menu-item";
7
7
  import type { MenuItemTone } from "../components/menu-item";
8
+ import { StatusDot } from "../components/status-dot";
9
+ import type { UserAvatarBadge } from "../components/user-avatar";
8
10
  import { TOPBAR_TRIGGER_CLASS } from "./topbar-controls";
9
11
 
10
12
  /**
@@ -56,9 +58,15 @@ export interface TopBarMenuHeader {
56
58
  title: ReactNode;
57
59
  /** The muted second line — an email, a role. */
58
60
  subtitle?: ReactNode;
59
- /** Anything below — a role chip. A link or button here stays out of the arrow keys
60
- * and in the Tab order, like the footer's. */
61
- extra?: ReactNode;
61
+ /**
62
+ * Anything below — a role chip. A link or button here stays out of the arrow keys
63
+ * and in the Tab order, like the footer's.
64
+ *
65
+ * Pass a function to get `close`, as `footer` does — keksdose F7: its admin role
66
+ * chip links to the admin area, and a router link does not unmount the menu, so
67
+ * without `close` the panel stayed open over the page it had just navigated to.
68
+ */
69
+ extra?: ReactNode | ((close: () => void) => ReactNode);
62
70
  }
63
71
 
64
72
  /** What a control is, as far as `HoverMenu` is concerned, when it lives in the header
@@ -125,6 +133,7 @@ function keepTabInsidePanel(e: ReactKeyboardEvent<HTMLUListElement>) {
125
133
  */
126
134
  export function TopBarActionMenu({
127
135
  icon,
136
+ iconBadge,
128
137
  trigger,
129
138
  triggerClassName,
130
139
  ariaLabel,
@@ -139,6 +148,13 @@ export function TopBarActionMenu({
139
148
  }: {
140
149
  /** The trigger's face for the usual icon button. Ignored when `trigger` is given. */
141
150
  icon?: ReactNode;
151
+ /**
152
+ * A status dot in the top-end corner of `icon` — keksdose's unread-support dot on the
153
+ * feedback trigger. Like `UserAvatar`'s `badge`, its `label` joins the trigger's name
154
+ * ("Send feedback 2 unread"), so the dot is not something only the eye is told about.
155
+ * Ignored with `trigger`: give the face its own badge there.
156
+ */
157
+ iconBadge?: UserAvatarBadge | null;
142
158
  /**
143
159
  * A face of your own for the trigger — the account menu's `UserAvatar`. It is drawn
144
160
  * INSIDE the kit's button, which keeps the wiring: `aria-haspopup`, `aria-expanded`,
@@ -190,6 +206,17 @@ export function TopBarActionMenu({
190
206
  {" "}
191
207
  {trigger({ open })}
192
208
  </button>
209
+ ) : iconBadge ? (
210
+ // Named by its content, like the custom-trigger branch, so the badge label is
211
+ // part of the name; an `aria-label` would have hidden it.
212
+ <button type="button" onClick={toggle} className={cn(TOPBAR_TRIGGER_CLASS, "relative", triggerClassName)}>
213
+ <span className="sr-only">{ariaLabel}</span>{" "}
214
+ <span aria-hidden className="relative inline-flex">
215
+ {icon}
216
+ <StatusDot ring tone={iconBadge.tone ?? "danger"} className="absolute -end-0.5 -top-0.5" />
217
+ </span>{" "}
218
+ <span className="sr-only">{iconBadge.label}</span>
219
+ </button>
193
220
  ) : (
194
221
  <button
195
222
  type="button"
@@ -204,6 +231,7 @@ export function TopBarActionMenu({
204
231
  >
205
232
  {(close) => {
206
233
  const footerBody = typeof footer === "function" ? footer(close) : footer;
234
+ const headerExtra = typeof header?.extra === "function" ? header.extra(close) : header?.extra;
207
235
  const footerClass =
208
236
  "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--text-muted)] [&_a:hover]:text-[var(--text-primary)] [&_a]:rounded-sm [&_a:focus-visible]:outline-none [&_a:focus-visible]:ring-2 [&_a:focus-visible]:ring-[var(--brand)]";
209
237
  return (
@@ -215,7 +243,7 @@ export function TopBarActionMenu({
215
243
  {header.subtitle != null && (
216
244
  <div className="truncate text-xs text-[var(--text-muted)]">{header.subtitle}</div>
217
245
  )}
218
- {header.extra != null && <div className="mt-1">{header.extra}</div>}
246
+ {headerExtra != null && headerExtra !== false && <div className="mt-1">{headerExtra}</div>}
219
247
  </MenuSection>
220
248
  )}
221
249
  {heading && (
package/src/shell.ts CHANGED
@@ -8,3 +8,5 @@ export * from "./shell/app-shell";
8
8
  export * from "./shell/option-switcher-menu";
9
9
  export * from "./shell/role-switcher";
10
10
  export * from "./shell/topbar-action-menu";
11
+ export * from "./shell/top-bar-brand";
12
+ export * from "./shell/auth-layout";
package/src/tour/tour.tsx CHANGED
@@ -157,11 +157,15 @@ export function TourProvider({
157
157
  const [activeId, setActiveId] = useState<string | null>(null);
158
158
  const optsRef = useRef<StartOptions>({});
159
159
  // Live mirrors so the step-control callbacks can stay stable (identity-safe for
160
- // consumers) while still reading the current steps/index.
160
+ // consumers) while still reading the current steps/index. Synced in a layout
161
+ // effect — after the commit, before paint and before any passive effect or event
162
+ // can call them — rather than written during render.
161
163
  const stepsRef = useRef<TourStep[] | null>(null);
162
- stepsRef.current = steps;
163
164
  const indexRef = useRef(0);
164
- indexRef.current = index;
165
+ useLayoutEffect(() => {
166
+ stepsRef.current = steps;
167
+ indexRef.current = index;
168
+ }, [steps, index]);
165
169
  const active = !!steps && steps.length > 0;
166
170
  const total = steps?.length ?? 0;
167
171
 
@@ -277,6 +281,15 @@ function TourOverlay({
277
281
  const isLast = index === steps.length - 1;
278
282
  const [rect, setRect] = useState<Rect | null>(null);
279
283
  const [ready, setReady] = useState(false);
284
+ // A new step starts unlocated: reset during render when the step changes, so the
285
+ // previous step's spotlight never paints over the new one. The effect below then
286
+ // locates it.
287
+ const [locating, setLocating] = useState({ index, step });
288
+ if (locating.index !== index || locating.step !== step) {
289
+ setLocating({ index, step });
290
+ setReady(false);
291
+ setRect(null);
292
+ }
280
293
  // The overlay is portalled to <body>, out of whatever `dir` subtree the app set, so it
281
294
  // takes the direction of the element it spotlights (the document's for a centered
282
295
  // step). Arrow keys and start/end placement read it.
@@ -287,8 +300,6 @@ function TourOverlay({
287
300
  useEffect(() => {
288
301
  let cancelled = false;
289
302
  let raf = 0;
290
- setReady(false);
291
- setRect(null);
292
303
  void (async () => {
293
304
  await step.beforeStep?.();
294
305
  if (cancelled) return;
@@ -14,7 +14,7 @@
14
14
  * The committing step is the last one unless a step says `commits: true`; the
15
15
  * steps after that one are post-commit steps (see `WizardStepConfig.commits`).
16
16
  */
17
- import { useCallback, useEffect, useReducer, useRef, useState } from "react";
17
+ import { useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from "react";
18
18
  import { useSearchParams } from "react-router";
19
19
  import { useKitLabels } from "../i18n/kit-labels";
20
20
  import { DEFAULT_WIZARD_LABELS } from "./types";
@@ -163,8 +163,12 @@ export function useWizard<TData extends Record<string, unknown>>(
163
163
  missingRequiredMessage,
164
164
  onValidationFailed,
165
165
  } = options;
166
+ // The latest `steps`, for the stable callbacks below. Synced after commit (before
167
+ // paint, and before any handler can run) rather than written during render.
166
168
  const stepsRef = useRef<WizardStepConfig[]>(steps);
167
- stepsRef.current = steps;
169
+ useLayoutEffect(() => {
170
+ stepsRef.current = steps;
171
+ }, [steps]);
168
172
  // A hook, not a component, but it produces two sentences of its own — the
169
173
  // "fill in the required fields" toast and the fallback submit error — and both
170
174
  // reach the user. So it reads the provider like any component does; only the two
@@ -239,8 +243,8 @@ export function useWizard<TData extends Record<string, unknown>>(
239
243
  // it back.
240
244
  const clearStepUrl = useStepUrl(urlParam, state.currentStepIndex);
241
245
 
242
- // `steps`, not `stepsRef.current`: during render the two are the same array, and
243
- // reading a ref in render is what the hooks linter (rightly) objects to.
246
+ // `steps`, not `stepsRef.current`: reading a ref in render is what the hooks
247
+ // linter (rightly) objects to, and the ref only catches up after the commit.
244
248
  const currentStep = steps[state.currentStepIndex];
245
249
  const isFirstStep = state.currentStepIndex === 0;
246
250
  const isLastStep = state.currentStepIndex === steps.length - 1;
@@ -1,4 +1,4 @@
1
- import { createContext, useContext } from "react";
1
+ import { createContext, useContext, useEffect, useRef } from "react";
2
2
  import type { ValidateResult } from "./types";
3
3
 
4
4
  export interface WizardContextValue {
@@ -36,3 +36,43 @@ export function useWizardContext(): WizardContextValue {
36
36
  export function useOptionalWizardContext(): WizardContextValue | null {
37
37
  return useContext(WizardContext);
38
38
  }
39
+
40
+ /**
41
+ * Registers a plain validator for the mounted step — the counterpart to
42
+ * `useRhfWizardStep` (in `@eifi1/ui-kit/rhf`) for a step that gates on its own state
43
+ * (a hand-managed list of lines, a sum that has to balance) rather than a form.
44
+ *
45
+ * ```tsx
46
+ * useWizardStepValidate(() => ({ ok: lines.length >= 2, errors: { lines: t("…") } }));
47
+ * ```
48
+ *
49
+ * Next (and Finish) run it with the step's other validators; a `false` or a result
50
+ * with `ok: false` keeps the wizard where it is. `validate` is read through a ref, so
51
+ * an inline closure over the latest state registers exactly once. Throws outside a
52
+ * `<StepperNav>`, like {@link useWizardContext}: a validator with nothing to run it is
53
+ * a bug to hear about.
54
+ */
55
+ export function useWizardStepValidate(
56
+ validate: () => ValidateResult | Promise<ValidateResult>,
57
+ ): void {
58
+ const { registerStepValidate } = useWizardContext();
59
+ const ref = useRef(validate);
60
+ useEffect(() => {
61
+ ref.current = validate;
62
+ });
63
+ useEffect(() => registerStepValidate(() => ref.current()), [registerStepValidate]);
64
+ }
65
+
66
+ /**
67
+ * Disables the wizard's Next and Skip while `blocked` holds (tenant shares that do not
68
+ * add up to 100 %). Unlike {@link useWizardStepValidate}, which answers a click, this
69
+ * greys the button out before one. The gate is lifted again when the step unmounts, so
70
+ * the next step starts unblocked.
71
+ */
72
+ export function useWizardNextGate(blocked: boolean): void {
73
+ const { setNextBlocked } = useWizardContext();
74
+ useEffect(() => {
75
+ setNextBlocked(blocked);
76
+ return () => setNextBlocked(false);
77
+ }, [blocked, setNextBlocked]);
78
+ }
@@ -1,14 +1,83 @@
1
1
  import type { ReactNode } from "react";
2
2
  import { cn } from "../lib/cn";
3
3
 
4
+ export interface WizardStepProps {
5
+ children: ReactNode;
6
+ className?: string;
7
+ /**
8
+ * The step's heading ("Choose a unit"), drawn in the one style every step shares.
9
+ * kastlan's steps each hand-wrote `<h3 className="text-lg font-semibold">` (about
10
+ * sixteen of them: lease-picker-step, lease-unit-step, …), and a hand-written
11
+ * heading is one that drifts — a size here, a colour there. Omitted: no heading.
12
+ */
13
+ title?: ReactNode;
14
+ /** The line under the title: what the step asks for, or why. Rendered only with a
15
+ * `title` or on its own when there is none. */
16
+ description?: ReactNode;
17
+ /**
18
+ * Controls for the step as a whole, at the end of the header row beside the title —
19
+ * kastlan 43: the buildings step's "Add building" had nowhere to go but UNDER the
20
+ * list it adds to, where it falls below the fold once there are three buildings.
21
+ * A header action is where every other kit surface (`PageHeader`, `Card`) puts it.
22
+ *
23
+ * Wraps under the title on a narrow screen rather than squeezing it. Renders the
24
+ * header on its own if there is no `title` either.
25
+ */
26
+ actions?: ReactNode;
27
+ /** The heading's level. Default 3: the page has its `h1` and the wizard's own
28
+ * chrome sits at `h2` (the review step, the cancel dialog), so a step is under it. */
29
+ headingLevel?: 2 | 3 | 4;
30
+ }
31
+
32
+ const HEADING = { 2: "h2", 3: "h3", 4: "h4" } as const;
33
+
4
34
  /** The vertical rhythm one wizard step's content sits on. A component rather
5
- * than a class string so every step in every app spaces its fields the same. */
35
+ * than a class string so every step in every app spaces its fields the same —
36
+ * and, with `title` / `description`, heads them the same. */
6
37
  export function WizardStep({
7
38
  children,
8
39
  className,
9
- }: {
10
- children: ReactNode;
11
- className?: string;
12
- }) {
13
- return <div className={cn("space-y-6", className)}>{children}</div>;
40
+ title,
41
+ description,
42
+ headingLevel = 3,
43
+ actions,
44
+ }: WizardStepProps) {
45
+ const hasTitle = title !== undefined && title !== null && title !== false && title !== "";
46
+ const hasDescription =
47
+ description !== undefined && description !== null && description !== false && description !== "";
48
+ const hasActions = actions !== undefined && actions !== null && actions !== false && actions !== "";
49
+ const Heading = HEADING[headingLevel];
50
+ const heading = (
51
+ <>
52
+ {hasTitle && <Heading className="text-lg font-semibold text-[var(--text-primary)]">{title}</Heading>}
53
+ {hasDescription && <p className="text-sm text-[var(--text-muted)]">{description}</p>}
54
+ </>
55
+ );
56
+ return (
57
+ <div className={cn("space-y-6", className)}>
58
+ {hasActions ? (
59
+ // One row: the text takes the room, the actions keep their width at the end
60
+ // and wrap below it when there is not enough — `basis-64` is the width the
61
+ // title asks for before that happens (a zero basis would never wrap and
62
+ // squeeze the title to a word a line instead). `items-start` so a two-line
63
+ // description does not pull the button down to its middle; `ms-auto` keeps
64
+ // the actions at the end with no title, and when they wrap.
65
+ <div data-slot="wizard-step-header" className="flex flex-wrap items-start justify-between gap-x-4 gap-y-2">
66
+ {(hasTitle || hasDescription) && <div className="min-w-0 grow basis-64 space-y-1">{heading}</div>}
67
+ <div data-slot="wizard-step-actions" className="ms-auto flex shrink-0 flex-wrap items-center gap-2">
68
+ {actions}
69
+ </div>
70
+ </div>
71
+ ) : (
72
+ // Without actions the header is exactly what it was, so a step that passes
73
+ // none renders as before.
74
+ (hasTitle || hasDescription) && (
75
+ <div data-slot="wizard-step-header" className="space-y-1">
76
+ {heading}
77
+ </div>
78
+ )
79
+ )}
80
+ {children}
81
+ </div>
82
+ );
14
83
  }
package/tokens.css CHANGED
@@ -233,6 +233,18 @@
233
233
  --success-bg: rgba(6, 78, 59, 0.25);
234
234
  }
235
235
 
236
+ /* ── Media ─────────────────────────────────────────────────────────────────
237
+ Controls drawn ON a photo or a live camera picture (the camera shutter), whose
238
+ background is the picture, not the page — so they must NOT follow the theme: a
239
+ dark-mode inverse pair would put a dark ring on a dark viewfinder. The same in both
240
+ themes, on purpose. */
241
+ :root,
242
+ .dark {
243
+ --media-ink: #ffffff;
244
+ --media-scrim: rgb(0 0 0 / 0.35);
245
+ --media-scrim-hover: rgb(0 0 0 / 0.5);
246
+ }
247
+
236
248
  /* ── Toasts ─────────────────────────────────────────────────────────────────
237
249
  The kit `<Toaster>` (components/toast.tsx) paints sonner with the AlertBanner
238
250
  tones. These are ALIASES, not new colours, and they exist for one reason: sonner