@eifi1/ui-kit 0.11.0 → 0.12.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 (342) hide show
  1. package/README.md +57 -32
  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 +24 -4
  12. package/dist/components/amount-input.js +5 -2
  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/button-group.d.ts +51 -2
  21. package/dist/components/button-group.js +5 -1
  22. package/dist/components/button-group.js.map +1 -1
  23. package/dist/components/calculator.d.ts +15 -4
  24. package/dist/components/calendar-heatmap.d.ts +41 -3
  25. package/dist/components/calendar-heatmap.js +48 -7
  26. package/dist/components/calendar-heatmap.js.map +1 -1
  27. package/dist/components/chip.d.ts +2 -1
  28. package/dist/components/chip.js +6 -3
  29. package/dist/components/chip.js.map +1 -1
  30. package/dist/components/choice-card.d.ts +35 -3
  31. package/dist/components/choice-card.js +83 -25
  32. package/dist/components/choice-card.js.map +1 -1
  33. package/dist/components/combobox-core.js +2 -0
  34. package/dist/components/combobox-core.js.map +1 -1
  35. package/dist/components/combobox.d.ts +2 -2
  36. package/dist/components/combobox.js +31 -9
  37. package/dist/components/combobox.js.map +1 -1
  38. package/dist/components/copy-button.d.ts +51 -70
  39. package/dist/components/copy-button.js +1 -1
  40. package/dist/components/copy-button.js.map +1 -1
  41. package/dist/components/data-table-cells.d.ts +63 -0
  42. package/dist/components/data-table-cells.js +64 -0
  43. package/dist/components/data-table-cells.js.map +1 -0
  44. package/dist/components/data-table-filter-popover.d.ts +1 -1
  45. package/dist/components/data-table-filters.d.ts +1 -1
  46. package/dist/components/data-table-filters.js +31 -1
  47. package/dist/components/data-table-filters.js.map +1 -1
  48. package/dist/components/data-table-labels.d.ts +13 -0
  49. package/dist/components/data-table-labels.js +8 -1
  50. package/dist/components/data-table-labels.js.map +1 -1
  51. package/dist/components/data-table.d.ts +1 -1
  52. package/dist/components/data-table.js +102 -12
  53. package/dist/components/data-table.js.map +1 -1
  54. package/dist/components/date-picker.d.ts +3 -1
  55. package/dist/components/date-picker.js +15 -2
  56. package/dist/components/date-picker.js.map +1 -1
  57. package/dist/components/description-list.d.ts +23 -3
  58. package/dist/components/description-list.js +23 -3
  59. package/dist/components/description-list.js.map +1 -1
  60. package/dist/components/dialog-frame.d.ts +1 -0
  61. package/dist/components/entity-combobox.d.ts +1 -1
  62. package/dist/components/entity-combobox.js +18 -3
  63. package/dist/components/entity-combobox.js.map +1 -1
  64. package/dist/components/error-boundary.d.ts +92 -0
  65. package/dist/components/error-boundary.js +111 -0
  66. package/dist/components/error-boundary.js.map +1 -0
  67. package/dist/components/field.d.ts +30 -4
  68. package/dist/components/field.js +40 -17
  69. package/dist/components/field.js.map +1 -1
  70. package/dist/components/file-button.d.ts +50 -208
  71. package/dist/components/file-dropzone.d.ts +49 -2
  72. package/dist/components/floating-panel.d.ts +13 -4
  73. package/dist/components/floating-panel.js +7 -3
  74. package/dist/components/floating-panel.js.map +1 -1
  75. package/dist/components/form-actions.d.ts +53 -0
  76. package/dist/components/form-actions.js +98 -0
  77. package/dist/components/form-actions.js.map +1 -0
  78. package/dist/components/image-grid.d.ts +67 -0
  79. package/dist/components/image-grid.js +112 -0
  80. package/dist/components/image-grid.js.map +1 -0
  81. package/dist/components/lightbox.d.ts +104 -0
  82. package/dist/components/lightbox.js +229 -0
  83. package/dist/components/lightbox.js.map +1 -0
  84. package/dist/components/line-items.d.ts +114 -0
  85. package/dist/components/line-items.js +238 -0
  86. package/dist/components/line-items.js.map +1 -0
  87. package/dist/components/list.d.ts +23 -2
  88. package/dist/components/list.js +32 -13
  89. package/dist/components/list.js.map +1 -1
  90. package/dist/components/loading-state.d.ts +24 -0
  91. package/dist/components/loading-state.js +32 -0
  92. package/dist/components/loading-state.js.map +1 -0
  93. package/dist/components/menu-item.d.ts +2 -1
  94. package/dist/components/menu-item.js +5 -1
  95. package/dist/components/menu-item.js.map +1 -1
  96. package/dist/components/mini-calendar.d.ts +13 -1
  97. package/dist/components/mini-calendar.js +46 -2
  98. package/dist/components/mini-calendar.js.map +1 -1
  99. package/dist/components/modal.d.ts +20 -1
  100. package/dist/components/modal.js +26 -2
  101. package/dist/components/modal.js.map +1 -1
  102. package/dist/components/month-picker.d.ts +21 -1
  103. package/dist/components/month-picker.js +139 -26
  104. package/dist/components/month-picker.js.map +1 -1
  105. package/dist/components/multi-entity-combobox.d.ts +1 -1
  106. package/dist/components/multi-entity-combobox.js +19 -3
  107. package/dist/components/multi-entity-combobox.js.map +1 -1
  108. package/dist/components/nav-pills.d.ts +2 -1
  109. package/dist/components/nav-pills.js +5 -1
  110. package/dist/components/nav-pills.js.map +1 -1
  111. package/dist/components/number-field.d.ts +24 -5
  112. package/dist/components/number-field.js +5 -7
  113. package/dist/components/number-field.js.map +1 -1
  114. package/dist/components/number-input.d.ts +25 -5
  115. package/dist/components/number-input.js +7 -1
  116. package/dist/components/number-input.js.map +1 -1
  117. package/dist/components/numpad-sheet.d.ts +15 -4
  118. package/dist/components/passkeys-setting.d.ts +67 -0
  119. package/dist/components/passkeys-setting.js +214 -0
  120. package/dist/components/passkeys-setting.js.map +1 -0
  121. package/dist/components/picker-sheet.js +2 -1
  122. package/dist/components/picker-sheet.js.map +1 -1
  123. package/dist/components/pie-chart-labels.d.ts +20 -0
  124. package/dist/components/pie-chart-labels.js +12 -0
  125. package/dist/components/pie-chart-labels.js.map +1 -0
  126. package/dist/components/pie-chart.d.ts +101 -0
  127. package/dist/components/pie-chart.js +283 -0
  128. package/dist/components/pie-chart.js.map +1 -0
  129. package/dist/components/progress-bar.d.ts +44 -3
  130. package/dist/components/progress-bar.js +58 -22
  131. package/dist/components/progress-bar.js.map +1 -1
  132. package/dist/components/qr-code.d.ts +34 -0
  133. package/dist/components/qr-code.js +57 -0
  134. package/dist/components/qr-code.js.map +1 -0
  135. package/dist/components/search-field.d.ts +15 -3
  136. package/dist/components/search-field.js +5 -1
  137. package/dist/components/search-field.js.map +1 -1
  138. package/dist/components/settings-fields.d.ts +49 -1
  139. package/dist/components/signed-amount.d.ts +121 -0
  140. package/dist/components/signed-amount.js +152 -0
  141. package/dist/components/signed-amount.js.map +1 -0
  142. package/dist/components/skeleton.d.ts +10 -3
  143. package/dist/components/skeleton.js +12 -2
  144. package/dist/components/skeleton.js.map +1 -1
  145. package/dist/components/stat-tile.d.ts +2 -1
  146. package/dist/components/stat-tile.js +5 -2
  147. package/dist/components/stat-tile.js.map +1 -1
  148. package/dist/components/table.d.ts +39 -5
  149. package/dist/components/table.js +45 -11
  150. package/dist/components/table.js.map +1 -1
  151. package/dist/components/text-link.d.ts +176 -0
  152. package/dist/components/text-link.js +109 -0
  153. package/dist/components/text-link.js.map +1 -0
  154. package/dist/components/time-input.d.ts +49 -1
  155. package/dist/components/toggle-group.d.ts +13 -0
  156. package/dist/components/toggle-group.js +35 -6
  157. package/dist/components/toggle-group.js.map +1 -1
  158. package/dist/components/toggle-legend.d.ts +36 -3
  159. package/dist/components/toggle-legend.js +49 -0
  160. package/dist/components/toggle-legend.js.map +1 -1
  161. package/dist/components/ui.d.ts +51 -779
  162. package/dist/components/ui.js +215 -23
  163. package/dist/components/ui.js.map +1 -1
  164. package/dist/components/use-table-state.d.ts +3 -52
  165. package/dist/components/use-table-state.js +128 -30
  166. package/dist/components/use-table-state.js.map +1 -1
  167. package/dist/{data-table-filters-Dh9uF_S-.d.ts → data-table-Drun8Fk9.d.ts} +305 -61
  168. package/dist/data-table.d.ts +2 -1
  169. package/dist/data-table.js +5 -1
  170. package/dist/data-table.js.map +1 -1
  171. package/dist/feedback/feedback-dialog.d.ts +26 -3
  172. package/dist/feedback/feedback-dialog.js +16 -2
  173. package/dist/feedback/feedback-dialog.js.map +1 -1
  174. package/dist/feedback/feedback-thread.d.ts +133 -0
  175. package/dist/feedback/feedback-thread.js +237 -0
  176. package/dist/feedback/feedback-thread.js.map +1 -0
  177. package/dist/feedback.d.ts +2 -1
  178. package/dist/feedback.js +1 -0
  179. package/dist/feedback.js.map +1 -1
  180. package/dist/hooks/use-authed-src.d.ts +71 -0
  181. package/dist/hooks/use-authed-src.js +63 -0
  182. package/dist/hooks/use-authed-src.js.map +1 -0
  183. package/dist/hooks/use-file-drop.d.ts +49 -2
  184. package/dist/hooks/use-hotkey.d.ts +59 -0
  185. package/dist/hooks/use-hotkey.js +105 -0
  186. package/dist/hooks/use-hotkey.js.map +1 -0
  187. package/dist/hooks/use-search-param-state.d.ts +83 -0
  188. package/dist/hooks/use-search-param-state.js +94 -0
  189. package/dist/hooks/use-search-param-state.js.map +1 -0
  190. package/dist/i18n/defaults.d.ts +15 -4
  191. package/dist/i18n/defaults.js +31 -1
  192. package/dist/i18n/defaults.js.map +1 -1
  193. package/dist/i18n/kit-labels.d.ts +46 -312
  194. package/dist/i18n/kit-labels.js +20 -5
  195. package/dist/i18n/kit-labels.js.map +1 -1
  196. package/dist/i18n/locales/de-CH-informal.d.ts +15 -4
  197. package/dist/i18n/locales/de-CH.d.ts +15 -4
  198. package/dist/i18n/locales/de-informal.d.ts +15 -4
  199. package/dist/i18n/locales/de.d.ts +15 -4
  200. package/dist/i18n/locales/de.js +156 -3
  201. package/dist/i18n/locales/de.js.map +1 -1
  202. package/dist/i18n/locales/es.d.ts +15 -4
  203. package/dist/i18n/locales/es.js +153 -3
  204. package/dist/i18n/locales/es.js.map +1 -1
  205. package/dist/i18n/locales/fr.d.ts +15 -4
  206. package/dist/i18n/locales/fr.js +153 -3
  207. package/dist/i18n/locales/fr.js.map +1 -1
  208. package/dist/i18n/locales/hu.d.ts +15 -4
  209. package/dist/i18n/locales/hu.js +153 -3
  210. package/dist/i18n/locales/hu.js.map +1 -1
  211. package/dist/i18n/locales/it.d.ts +15 -4
  212. package/dist/i18n/locales/it.js +153 -3
  213. package/dist/i18n/locales/it.js.map +1 -1
  214. package/dist/i18n/locales/zh.d.ts +15 -4
  215. package/dist/i18n/locales/zh.js +153 -3
  216. package/dist/i18n/locales/zh.js.map +1 -1
  217. package/dist/index.d.ts +37 -17
  218. package/dist/index.js +40 -1
  219. package/dist/index.js.map +1 -1
  220. package/dist/kit-labels-v3biUF1L.d.ts +1645 -0
  221. package/dist/lib/format.d.ts +144 -0
  222. package/dist/lib/format.js +149 -0
  223. package/dist/lib/format.js.map +1 -0
  224. package/dist/lib/qr-encode.d.ts +44 -0
  225. package/dist/lib/qr-encode.js +338 -0
  226. package/dist/lib/qr-encode.js.map +1 -0
  227. package/dist/rhf/fields.d.ts +241 -0
  228. package/dist/rhf/fields.js +601 -0
  229. package/dist/rhf/fields.js.map +1 -0
  230. package/dist/rhf/form.d.ts +49 -1
  231. package/dist/rhf/line-items.d.ts +31 -0
  232. package/dist/rhf/line-items.js +59 -0
  233. package/dist/rhf/line-items.js.map +1 -0
  234. package/dist/rhf.d.ts +61 -1
  235. package/dist/rhf.js +2 -0
  236. package/dist/rhf.js.map +1 -1
  237. package/dist/shell/app-shell.d.ts +99 -2
  238. package/dist/shell/app-shell.js +58 -3
  239. package/dist/shell/app-shell.js.map +1 -1
  240. package/dist/shell/auth-layout.d.ts +74 -0
  241. package/dist/shell/auth-layout.js +97 -0
  242. package/dist/shell/auth-layout.js.map +1 -0
  243. package/dist/shell/top-bar-brand.d.ts +81 -0
  244. package/dist/shell/top-bar-brand.js +47 -0
  245. package/dist/shell/top-bar-brand.js.map +1 -0
  246. package/dist/shell/topbar-action-menu.d.ts +12 -1
  247. package/dist/shell/topbar-action-menu.js +16 -1
  248. package/dist/shell/topbar-action-menu.js.map +1 -1
  249. package/dist/shell.d.ts +56 -1
  250. package/dist/shell.js +2 -0
  251. package/dist/shell.js.map +1 -1
  252. package/dist/wizard/stepper-nav.d.ts +48 -1
  253. package/dist/wizard/wizard-context.d.ts +24 -1
  254. package/dist/wizard/wizard-context.js +19 -2
  255. package/dist/wizard/wizard-context.js.map +1 -1
  256. package/dist/wizard/wizard-step.d.ts +20 -5
  257. package/dist/wizard/wizard-step.js +16 -3
  258. package/dist/wizard/wizard-step.js.map +1 -1
  259. package/dist/wizard.d.ts +50 -3
  260. package/package.json +1 -1
  261. package/src/chart.ts +4 -0
  262. package/src/components/account-settings-labels.ts +190 -0
  263. package/src/components/account-settings.tsx +110 -63
  264. package/src/components/amount-input.tsx +13 -1
  265. package/src/components/authed-image.tsx +190 -0
  266. package/src/components/breadcrumbs.tsx +8 -2
  267. package/src/components/button-group.tsx +7 -2
  268. package/src/components/calendar-heatmap.tsx +105 -10
  269. package/src/components/chip.tsx +8 -4
  270. package/src/components/choice-card.tsx +136 -25
  271. package/src/components/combobox-core.tsx +5 -0
  272. package/src/components/combobox.tsx +31 -9
  273. package/src/components/copy-button.tsx +3 -2
  274. package/src/components/data-table-cells.tsx +119 -0
  275. package/src/components/data-table-filters.ts +77 -0
  276. package/src/components/data-table-labels.ts +23 -0
  277. package/src/components/data-table.tsx +224 -13
  278. package/src/components/date-picker.tsx +19 -3
  279. package/src/components/description-list.tsx +52 -3
  280. package/src/components/entity-combobox.tsx +18 -3
  281. package/src/components/error-boundary.tsx +216 -0
  282. package/src/components/field.tsx +56 -5
  283. package/src/components/floating-panel.tsx +20 -7
  284. package/src/components/form-actions.tsx +191 -0
  285. package/src/components/image-grid.tsx +181 -0
  286. package/src/components/lightbox.tsx +374 -0
  287. package/src/components/line-items.tsx +385 -0
  288. package/src/components/list.tsx +65 -16
  289. package/src/components/loading-state.tsx +47 -0
  290. package/src/components/menu-item.tsx +7 -2
  291. package/src/components/mini-calendar.tsx +72 -3
  292. package/src/components/modal.tsx +61 -3
  293. package/src/components/month-picker.tsx +181 -26
  294. package/src/components/multi-entity-combobox.tsx +19 -3
  295. package/src/components/nav-pills.tsx +8 -1
  296. package/src/components/number-field.tsx +15 -10
  297. package/src/components/number-input.tsx +16 -1
  298. package/src/components/passkeys-setting.tsx +331 -0
  299. package/src/components/picker-sheet.tsx +7 -1
  300. package/src/components/pie-chart-labels.ts +32 -0
  301. package/src/components/pie-chart.tsx +479 -0
  302. package/src/components/progress-bar.tsx +129 -19
  303. package/src/components/qr-code.tsx +83 -0
  304. package/src/components/search-field.tsx +20 -4
  305. package/src/components/signed-amount.tsx +286 -0
  306. package/src/components/skeleton.tsx +21 -3
  307. package/src/components/stat-tile.tsx +8 -2
  308. package/src/components/table.tsx +102 -13
  309. package/src/components/text-link.tsx +248 -0
  310. package/src/components/toggle-group.tsx +58 -10
  311. package/src/components/toggle-legend.tsx +87 -2
  312. package/src/components/ui.tsx +426 -36
  313. package/src/components/use-table-state.ts +265 -33
  314. package/src/data-table.ts +10 -0
  315. package/src/feedback/feedback-dialog.tsx +42 -3
  316. package/src/feedback/feedback-thread.tsx +434 -0
  317. package/src/feedback.ts +1 -0
  318. package/src/hooks/use-authed-src.ts +161 -0
  319. package/src/hooks/use-hotkey.ts +172 -0
  320. package/src/hooks/use-search-param-state.ts +187 -0
  321. package/src/i18n/defaults.ts +30 -0
  322. package/src/i18n/kit-labels.tsx +102 -8
  323. package/src/i18n/locales/de.ts +153 -0
  324. package/src/i18n/locales/es.ts +150 -0
  325. package/src/i18n/locales/fr.ts +153 -0
  326. package/src/i18n/locales/hu.ts +151 -0
  327. package/src/i18n/locales/it.ts +151 -0
  328. package/src/i18n/locales/zh.ts +150 -0
  329. package/src/index.ts +59 -0
  330. package/src/lib/format.ts +325 -0
  331. package/src/lib/qr-encode.ts +420 -0
  332. package/src/rhf/fields.tsx +1054 -0
  333. package/src/rhf/line-items.tsx +130 -0
  334. package/src/rhf.ts +6 -0
  335. package/src/shell/app-shell.tsx +147 -2
  336. package/src/shell/auth-layout.tsx +190 -0
  337. package/src/shell/top-bar-brand.tsx +76 -0
  338. package/src/shell/topbar-action-menu.tsx +21 -0
  339. package/src/shell.ts +2 -0
  340. package/src/wizard/wizard-context.tsx +41 -1
  341. package/src/wizard/wizard-step.tsx +43 -6
  342. package/tokens.css +12 -0
@@ -1,7 +1,11 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { MenuItemTone } from '../components/menu-item.js';
4
+ import { UserAvatarBadge } from '../components/user-avatar.js';
4
5
  import 'lucide-react';
6
+ import '../components/status-dot.js';
7
+ import '../components/progress-bar.js';
8
+ import '../components/chip.js';
5
9
 
6
10
  /**
7
11
  * One row of a {@link TopBarActionMenu}. An `action` (default) runs `onSelect`, a
@@ -67,9 +71,16 @@ interface TopBarMenuHeader {
67
71
  * custom `trigger` (the avatar), a `header` (name + role / email), the rows, and a
68
72
  * free-content `footer` (the legal links).
69
73
  */
70
- declare function TopBarActionMenu({ icon, trigger, triggerClassName, ariaLabel, heading, header, entries, children, footer, footerLabel, align, panelClassName, }: {
74
+ declare function TopBarActionMenu({ icon, iconBadge, trigger, triggerClassName, ariaLabel, heading, header, entries, children, footer, footerLabel, align, panelClassName, }: {
71
75
  /** The trigger's face for the usual icon button. Ignored when `trigger` is given. */
72
76
  icon?: ReactNode;
77
+ /**
78
+ * A status dot in the top-end corner of `icon` — keksdose's unread-support dot on the
79
+ * feedback trigger. Like `UserAvatar`'s `badge`, its `label` joins the trigger's name
80
+ * ("Send feedback 2 unread"), so the dot is not something only the eye is told about.
81
+ * Ignored with `trigger`: give the face its own badge there.
82
+ */
83
+ iconBadge?: UserAvatarBadge | null;
73
84
  /**
74
85
  * A face of your own for the trigger — the account menu's `UserAvatar`. It is drawn
75
86
  * INSIDE the kit's button, which keeps the wiring: `aria-haspopup`, `aria-expanded`,
@@ -5,6 +5,7 @@ import { Link } from "react-router";
5
5
  import { cn } from "../lib/cn.js";
6
6
  import { HoverMenu } from "../components/hover-menu.js";
7
7
  import { MenuItem } from "../components/menu-item.js";
8
+ import { StatusDot } from "../components/status-dot.js";
8
9
  import { TOPBAR_TRIGGER_CLASS } from "./topbar-controls.js";
9
10
  const SECTION_CONTROLS = "a[href]:not([role]),button:not([role])";
10
11
  const TABBABLE = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]";
@@ -32,6 +33,7 @@ function keepTabInsidePanel(e) {
32
33
  }
33
34
  function TopBarActionMenu({
34
35
  icon,
36
+ iconBadge,
35
37
  trigger,
36
38
  triggerClassName,
37
39
  ariaLabel,
@@ -53,7 +55,20 @@ function TopBarActionMenu({
53
55
  /* @__PURE__ */ jsx("span", { className: "sr-only", children: ariaLabel }),
54
56
  " ",
55
57
  trigger({ open })
56
- ] }) : /* @__PURE__ */ jsx(
58
+ ] }) : iconBadge ? (
59
+ // Named by its content, like the custom-trigger branch, so the badge label is
60
+ // part of the name; an `aria-label` would have hidden it.
61
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: toggle, className: cn(TOPBAR_TRIGGER_CLASS, "relative", triggerClassName), children: [
62
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: ariaLabel }),
63
+ " ",
64
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: "relative inline-flex", children: [
65
+ icon,
66
+ /* @__PURE__ */ jsx(StatusDot, { ring: true, tone: iconBadge.tone ?? "danger", className: "absolute -end-0.5 -top-0.5" })
67
+ ] }),
68
+ " ",
69
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: iconBadge.label })
70
+ ] })
71
+ ) : /* @__PURE__ */ jsx(
57
72
  "button",
58
73
  {
59
74
  type: "button",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/shell/topbar-action-menu.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from \"react\";\nimport type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from \"react\";\nimport { Link } from \"react-router\";\nimport { cn } from \"../lib/cn\";\nimport { HoverMenu } from \"../components/hover-menu\";\nimport { MenuItem } from \"../components/menu-item\";\nimport type { MenuItemTone } from \"../components/menu-item\";\nimport { TOPBAR_TRIGGER_CLASS } from \"./topbar-controls\";\n\n/**\n * One row of a {@link TopBarActionMenu}. An `action` (default) runs `onSelect`, a\n * `link` navigates via react-router, and a `divider` draws a rule. Non-divider\n * rows close the menu automatically when chosen. `kind` may be omitted for\n * actions, keeping the common case terse.\n *\n * The rows are {@link MenuItem}s, so they take its options: `tone: \"danger\"` for the\n * log-out row, `checked` (+ `checkable`) for a choice, `current` for the link to the\n * page already open, `disabled`.\n */\nexport type TopBarMenuEntry =\n | {\n kind?: \"action\";\n key: string;\n icon?: ReactNode;\n label: ReactNode;\n trailing?: ReactNode;\n onSelect: () => void;\n tone?: MenuItemTone;\n /** Makes the row a choice (`menuitemradio`, or `menuitemcheckbox` with\n * `checkable: \"checkbox\"`) and says whether it is chosen. */\n checked?: boolean;\n checkable?: \"radio\" | \"checkbox\";\n disabled?: boolean;\n }\n | {\n kind: \"link\";\n key: string;\n icon?: ReactNode;\n label: ReactNode;\n trailing?: ReactNode;\n to: string;\n tone?: MenuItemTone;\n /** The link to the page already open: `aria-current=\"page\"` and a check mark. */\n current?: boolean;\n disabled?: boolean;\n }\n | { kind: \"divider\"; key: string };\n\n/**\n * The non-interactive identity block at the top of an account menu — kastlan's name +\n * role, keksdose's display name + email + role chip. It is text, not a `menuitem`: the\n * arrow keys pass over it.\n */\nexport interface TopBarMenuHeader {\n /** The first line — the user's name. */\n title: ReactNode;\n /** The muted second line — an email, a role. */\n subtitle?: ReactNode;\n /** Anything below — a role chip. A link or button here stays out of the arrow keys\n * and in the Tab order, like the footer's. */\n extra?: ReactNode;\n}\n\n/** What a control is, as far as `HoverMenu` is concerned, when it lives in the header\n * or the footer: not a menu row. */\nconst SECTION_CONTROLS = \"a[href]:not([role]),button:not([role])\";\nconst TABBABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]';\n\n/**\n * A header or footer inside the menu panel.\n *\n * `HoverMenu` promotes EVERY role-less button and link in its panel to a `menuitem` out\n * of the Tab order, and turns every list into `role=\"none\"` — right for the rows, wrong\n * for a footer of legal links, which would join the arrow-key cycle and drop out of Tab.\n * So before it looks (a child's layout effect runs before its parent's), the controls\n * in here get their own role spelled out, which `HoverMenu` leaves alone, and any list\n * keeps its list semantics.\n */\nfunction MenuSection({ className, children }: { className: string; children: ReactNode }) {\n const ref = useRef<HTMLLIElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n for (const c of el.querySelectorAll<HTMLElement>(SECTION_CONTROLS)) {\n c.setAttribute(\"role\", c.tagName === \"A\" ? \"link\" : \"button\");\n }\n for (const l of el.querySelectorAll<HTMLElement>(\"ul:not([role]),ol:not([role])\")) l.setAttribute(\"role\", \"list\");\n for (const l of el.querySelectorAll<HTMLElement>(\"li:not([role])\")) l.setAttribute(\"role\", \"listitem\");\n });\n return (\n <li ref={ref} role=\"none\" className={className}>\n {children}\n </li>\n );\n}\n\n/**\n * `HoverMenu` closes on any Tab inside the panel — right when Tab leaves it, wrong when\n * Tab is on its way from a row to a footer link, or from one footer link to the next:\n * the panel would unmount under the focus. So a Tab whose next stop is still inside the\n * panel stops here and the browser moves focus; one that leaves reaches `HoverMenu`,\n * which closes.\n */\nfunction keepTabInsidePanel(e: ReactKeyboardEvent<HTMLUListElement>) {\n if (e.key !== \"Tab\") return;\n const from = e.target as HTMLElement;\n const stops = Array.from(e.currentTarget.querySelectorAll<HTMLElement>(TABBABLE)).filter(\n (el) => el.getAttribute(\"tabindex\") !== \"-1\",\n );\n const way = e.shiftKey ? Node.DOCUMENT_POSITION_PRECEDING : Node.DOCUMENT_POSITION_FOLLOWING;\n if (stops.some((el) => el !== from && from.compareDocumentPosition(el) & way)) e.stopPropagation();\n}\n\n/**\n * A top-bar icon button that opens a {@link HoverMenu} of actions and/or links,\n * with an optional uppercase heading and divider rules. Generic glue over the\n * shared top-bar chrome — e.g. the feedback launcher or the guided-tours menu —\n * so those call sites describe their rows as data instead of rebuilding the\n * trigger + list markup. For a checkmarked value-switcher use\n * {@link OptionSwitcherMenu} instead.\n *\n * It is also the ACCOUNT menu kastlan and keksdose each hand-build on `HoverMenu`: a\n * custom `trigger` (the avatar), a `header` (name + role / email), the rows, and a\n * free-content `footer` (the legal links).\n */\nexport function TopBarActionMenu({\n icon,\n trigger,\n triggerClassName,\n ariaLabel,\n heading,\n header,\n entries,\n children,\n footer,\n footerLabel,\n align,\n panelClassName,\n}: {\n /** The trigger's face for the usual icon button. Ignored when `trigger` is given. */\n icon?: ReactNode;\n /**\n * A face of your own for the trigger — the account menu's `UserAvatar`. It is drawn\n * INSIDE the kit's button, which keeps the wiring: `aria-haspopup`, `aria-expanded`,\n * the toggle, the keyboard. `open` is there for a chevron that turns.\n *\n * The button is named by `ariaLabel` followed by any readable text in the face — a\n * `UserAvatar` `badge` label reaches a screen reader that way (\"Account menu 3\n * unread\"), where an `aria-label` on the button would have hidden it. A\n * `UserAvatar`'s initials are `aria-hidden`, so the person's name is only in the\n * name if `ariaLabel` carries it (\"Marcel Eifert, account menu\").\n */\n trigger?: (state: { open: boolean }) => ReactNode;\n /** Extra classes for the trigger button — `rounded-full` round an avatar. */\n triggerClassName?: string;\n /** Names the trigger and the menu. */\n ariaLabel: string;\n heading?: string;\n /** A non-interactive identity block above the rows. */\n header?: TopBarMenuHeader;\n entries: TopBarMenuEntry[];\n /**\n * Rows the entry data cannot describe (a `Disclosure` sub-list, an app's own\n * section), after `entries`. Render `<li>`s holding `MenuItem`s or plain controls;\n * `close` shuts the menu.\n */\n children?: (close: () => void) => ReactNode;\n /**\n * Free content below the rows, behind a rule — the legal links. Its links and\n * buttons are NOT menu rows: the arrow keys skip them and Tab reaches them. Pass a\n * function to get `close` for the links' `onClick`.\n */\n footer?: ReactNode | ((close: () => void) => ReactNode);\n /** Wraps the footer in a `<nav>` with this name (\"Legal\"). Without it, a plain block. */\n footerLabel?: string;\n /** Which edge of the trigger the panel lines up with (see `HoverMenu`). Default `end`. */\n align?: \"start\" | \"end\" | \"left\" | \"right\";\n panelClassName?: string;\n}) {\n return (\n <HoverMenu\n ariaLabel={ariaLabel}\n align={align}\n trigger={({ open, toggle }) =>\n trigger ? (\n <button type=\"button\" onClick={toggle} className={cn(TOPBAR_TRIGGER_CLASS, \"relative\", triggerClassName)}>\n <span className=\"sr-only\">{ariaLabel}</span>\n {/* The space keeps the name from running into the face's text (\"Account\n menu3 unread\"); as a flex item it takes no room. */}\n {\" \"}\n {trigger({ open })}\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={ariaLabel}\n className={cn(TOPBAR_TRIGGER_CLASS, triggerClassName)}\n >\n {icon}\n </button>\n )\n }\n >\n {(close) => {\n const footerBody = typeof footer === \"function\" ? footer(close) : footer;\n const footerClass =\n \"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)]\";\n return (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- a Tab bubbling up from a row or footer link, see keepTabInsidePanel\n <ul className={cn(\"py-1\", panelClassName)} onKeyDown={keepTabInsidePanel}>\n {header && (\n <MenuSection className=\"mb-1 border-b border-[var(--border)] px-3 py-2\">\n <div className=\"truncate text-sm font-medium text-[var(--text-primary)]\">{header.title}</div>\n {header.subtitle != null && (\n <div className=\"truncate text-xs text-[var(--text-muted)]\">{header.subtitle}</div>\n )}\n {header.extra != null && <div className=\"mt-1\">{header.extra}</div>}\n </MenuSection>\n )}\n {heading && (\n <li className=\"px-3 py-1 text-[10px] font-semibold uppercase tracking-wide text-[var(--text-placeholder)]\">\n {heading}\n </li>\n )}\n {entries.map((entry) => {\n if (entry.kind === \"divider\") {\n return <li key={entry.key} className=\"my-1 border-t border-[var(--border)]\" />;\n }\n return (\n <li key={entry.key}>\n {entry.kind === \"link\" ? (\n <MenuItem\n href={entry.to}\n renderLink={({ href, ...p }) => <Link to={href} {...p} />}\n onClick={close}\n leading={entry.icon}\n trailing={entry.trailing}\n tone={entry.tone}\n current={entry.current}\n disabled={entry.disabled}\n >\n {entry.label}\n </MenuItem>\n ) : (\n <MenuItem\n onClick={() => {\n entry.onSelect();\n close();\n }}\n leading={entry.icon}\n trailing={entry.trailing}\n tone={entry.tone}\n checked={entry.checked}\n checkable={entry.checkable}\n disabled={entry.disabled}\n >\n {entry.label}\n </MenuItem>\n )}\n </li>\n );\n })}\n {children?.(close)}\n {footerBody != null && footerBody !== false && (\n <MenuSection className=\"mt-1 border-t border-[var(--border)] px-3 py-2\">\n {footerLabel ? (\n <nav aria-label={footerLabel} className={footerClass}>\n {footerBody}\n </nav>\n ) : (\n <div className={footerClass}>{footerBody}</div>\n )}\n </MenuSection>\n )}\n </ul>\n );\n }}\n </HoverMenu>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/shell/topbar-action-menu.tsx"],"sourcesContent":["import { useLayoutEffect, useRef } from \"react\";\nimport type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from \"react\";\nimport { Link } from \"react-router\";\nimport { cn } from \"../lib/cn\";\nimport { HoverMenu } from \"../components/hover-menu\";\nimport { MenuItem } from \"../components/menu-item\";\nimport type { MenuItemTone } from \"../components/menu-item\";\nimport { StatusDot } from \"../components/status-dot\";\nimport type { UserAvatarBadge } from \"../components/user-avatar\";\nimport { TOPBAR_TRIGGER_CLASS } from \"./topbar-controls\";\n\n/**\n * One row of a {@link TopBarActionMenu}. An `action` (default) runs `onSelect`, a\n * `link` navigates via react-router, and a `divider` draws a rule. Non-divider\n * rows close the menu automatically when chosen. `kind` may be omitted for\n * actions, keeping the common case terse.\n *\n * The rows are {@link MenuItem}s, so they take its options: `tone: \"danger\"` for the\n * log-out row, `checked` (+ `checkable`) for a choice, `current` for the link to the\n * page already open, `disabled`.\n */\nexport type TopBarMenuEntry =\n | {\n kind?: \"action\";\n key: string;\n icon?: ReactNode;\n label: ReactNode;\n trailing?: ReactNode;\n onSelect: () => void;\n tone?: MenuItemTone;\n /** Makes the row a choice (`menuitemradio`, or `menuitemcheckbox` with\n * `checkable: \"checkbox\"`) and says whether it is chosen. */\n checked?: boolean;\n checkable?: \"radio\" | \"checkbox\";\n disabled?: boolean;\n }\n | {\n kind: \"link\";\n key: string;\n icon?: ReactNode;\n label: ReactNode;\n trailing?: ReactNode;\n to: string;\n tone?: MenuItemTone;\n /** The link to the page already open: `aria-current=\"page\"` and a check mark. */\n current?: boolean;\n disabled?: boolean;\n }\n | { kind: \"divider\"; key: string };\n\n/**\n * The non-interactive identity block at the top of an account menu — kastlan's name +\n * role, keksdose's display name + email + role chip. It is text, not a `menuitem`: the\n * arrow keys pass over it.\n */\nexport interface TopBarMenuHeader {\n /** The first line — the user's name. */\n title: ReactNode;\n /** The muted second line — an email, a role. */\n subtitle?: ReactNode;\n /** Anything below — a role chip. A link or button here stays out of the arrow keys\n * and in the Tab order, like the footer's. */\n extra?: ReactNode;\n}\n\n/** What a control is, as far as `HoverMenu` is concerned, when it lives in the header\n * or the footer: not a menu row. */\nconst SECTION_CONTROLS = \"a[href]:not([role]),button:not([role])\";\nconst TABBABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]';\n\n/**\n * A header or footer inside the menu panel.\n *\n * `HoverMenu` promotes EVERY role-less button and link in its panel to a `menuitem` out\n * of the Tab order, and turns every list into `role=\"none\"` — right for the rows, wrong\n * for a footer of legal links, which would join the arrow-key cycle and drop out of Tab.\n * So before it looks (a child's layout effect runs before its parent's), the controls\n * in here get their own role spelled out, which `HoverMenu` leaves alone, and any list\n * keeps its list semantics.\n */\nfunction MenuSection({ className, children }: { className: string; children: ReactNode }) {\n const ref = useRef<HTMLLIElement>(null);\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n for (const c of el.querySelectorAll<HTMLElement>(SECTION_CONTROLS)) {\n c.setAttribute(\"role\", c.tagName === \"A\" ? \"link\" : \"button\");\n }\n for (const l of el.querySelectorAll<HTMLElement>(\"ul:not([role]),ol:not([role])\")) l.setAttribute(\"role\", \"list\");\n for (const l of el.querySelectorAll<HTMLElement>(\"li:not([role])\")) l.setAttribute(\"role\", \"listitem\");\n });\n return (\n <li ref={ref} role=\"none\" className={className}>\n {children}\n </li>\n );\n}\n\n/**\n * `HoverMenu` closes on any Tab inside the panel — right when Tab leaves it, wrong when\n * Tab is on its way from a row to a footer link, or from one footer link to the next:\n * the panel would unmount under the focus. So a Tab whose next stop is still inside the\n * panel stops here and the browser moves focus; one that leaves reaches `HoverMenu`,\n * which closes.\n */\nfunction keepTabInsidePanel(e: ReactKeyboardEvent<HTMLUListElement>) {\n if (e.key !== \"Tab\") return;\n const from = e.target as HTMLElement;\n const stops = Array.from(e.currentTarget.querySelectorAll<HTMLElement>(TABBABLE)).filter(\n (el) => el.getAttribute(\"tabindex\") !== \"-1\",\n );\n const way = e.shiftKey ? Node.DOCUMENT_POSITION_PRECEDING : Node.DOCUMENT_POSITION_FOLLOWING;\n if (stops.some((el) => el !== from && from.compareDocumentPosition(el) & way)) e.stopPropagation();\n}\n\n/**\n * A top-bar icon button that opens a {@link HoverMenu} of actions and/or links,\n * with an optional uppercase heading and divider rules. Generic glue over the\n * shared top-bar chrome — e.g. the feedback launcher or the guided-tours menu —\n * so those call sites describe their rows as data instead of rebuilding the\n * trigger + list markup. For a checkmarked value-switcher use\n * {@link OptionSwitcherMenu} instead.\n *\n * It is also the ACCOUNT menu kastlan and keksdose each hand-build on `HoverMenu`: a\n * custom `trigger` (the avatar), a `header` (name + role / email), the rows, and a\n * free-content `footer` (the legal links).\n */\nexport function TopBarActionMenu({\n icon,\n iconBadge,\n trigger,\n triggerClassName,\n ariaLabel,\n heading,\n header,\n entries,\n children,\n footer,\n footerLabel,\n align,\n panelClassName,\n}: {\n /** The trigger's face for the usual icon button. Ignored when `trigger` is given. */\n icon?: ReactNode;\n /**\n * A status dot in the top-end corner of `icon` — keksdose's unread-support dot on the\n * feedback trigger. Like `UserAvatar`'s `badge`, its `label` joins the trigger's name\n * (\"Send feedback 2 unread\"), so the dot is not something only the eye is told about.\n * Ignored with `trigger`: give the face its own badge there.\n */\n iconBadge?: UserAvatarBadge | null;\n /**\n * A face of your own for the trigger — the account menu's `UserAvatar`. It is drawn\n * INSIDE the kit's button, which keeps the wiring: `aria-haspopup`, `aria-expanded`,\n * the toggle, the keyboard. `open` is there for a chevron that turns.\n *\n * The button is named by `ariaLabel` followed by any readable text in the face — a\n * `UserAvatar` `badge` label reaches a screen reader that way (\"Account menu 3\n * unread\"), where an `aria-label` on the button would have hidden it. A\n * `UserAvatar`'s initials are `aria-hidden`, so the person's name is only in the\n * name if `ariaLabel` carries it (\"Marcel Eifert, account menu\").\n */\n trigger?: (state: { open: boolean }) => ReactNode;\n /** Extra classes for the trigger button — `rounded-full` round an avatar. */\n triggerClassName?: string;\n /** Names the trigger and the menu. */\n ariaLabel: string;\n heading?: string;\n /** A non-interactive identity block above the rows. */\n header?: TopBarMenuHeader;\n entries: TopBarMenuEntry[];\n /**\n * Rows the entry data cannot describe (a `Disclosure` sub-list, an app's own\n * section), after `entries`. Render `<li>`s holding `MenuItem`s or plain controls;\n * `close` shuts the menu.\n */\n children?: (close: () => void) => ReactNode;\n /**\n * Free content below the rows, behind a rule — the legal links. Its links and\n * buttons are NOT menu rows: the arrow keys skip them and Tab reaches them. Pass a\n * function to get `close` for the links' `onClick`.\n */\n footer?: ReactNode | ((close: () => void) => ReactNode);\n /** Wraps the footer in a `<nav>` with this name (\"Legal\"). Without it, a plain block. */\n footerLabel?: string;\n /** Which edge of the trigger the panel lines up with (see `HoverMenu`). Default `end`. */\n align?: \"start\" | \"end\" | \"left\" | \"right\";\n panelClassName?: string;\n}) {\n return (\n <HoverMenu\n ariaLabel={ariaLabel}\n align={align}\n trigger={({ open, toggle }) =>\n trigger ? (\n <button type=\"button\" onClick={toggle} className={cn(TOPBAR_TRIGGER_CLASS, \"relative\", triggerClassName)}>\n <span className=\"sr-only\">{ariaLabel}</span>\n {/* The space keeps the name from running into the face's text (\"Account\n menu3 unread\"); as a flex item it takes no room. */}\n {\" \"}\n {trigger({ open })}\n </button>\n ) : iconBadge ? (\n // Named by its content, like the custom-trigger branch, so the badge label is\n // part of the name; an `aria-label` would have hidden it.\n <button type=\"button\" onClick={toggle} className={cn(TOPBAR_TRIGGER_CLASS, \"relative\", triggerClassName)}>\n <span className=\"sr-only\">{ariaLabel}</span>{\" \"}\n <span aria-hidden className=\"relative inline-flex\">\n {icon}\n <StatusDot ring tone={iconBadge.tone ?? \"danger\"} className=\"absolute -end-0.5 -top-0.5\" />\n </span>{\" \"}\n <span className=\"sr-only\">{iconBadge.label}</span>\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={toggle}\n aria-label={ariaLabel}\n className={cn(TOPBAR_TRIGGER_CLASS, triggerClassName)}\n >\n {icon}\n </button>\n )\n }\n >\n {(close) => {\n const footerBody = typeof footer === \"function\" ? footer(close) : footer;\n const footerClass =\n \"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)]\";\n return (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- a Tab bubbling up from a row or footer link, see keepTabInsidePanel\n <ul className={cn(\"py-1\", panelClassName)} onKeyDown={keepTabInsidePanel}>\n {header && (\n <MenuSection className=\"mb-1 border-b border-[var(--border)] px-3 py-2\">\n <div className=\"truncate text-sm font-medium text-[var(--text-primary)]\">{header.title}</div>\n {header.subtitle != null && (\n <div className=\"truncate text-xs text-[var(--text-muted)]\">{header.subtitle}</div>\n )}\n {header.extra != null && <div className=\"mt-1\">{header.extra}</div>}\n </MenuSection>\n )}\n {heading && (\n <li className=\"px-3 py-1 text-[10px] font-semibold uppercase tracking-wide text-[var(--text-placeholder)]\">\n {heading}\n </li>\n )}\n {entries.map((entry) => {\n if (entry.kind === \"divider\") {\n return <li key={entry.key} className=\"my-1 border-t border-[var(--border)]\" />;\n }\n return (\n <li key={entry.key}>\n {entry.kind === \"link\" ? (\n <MenuItem\n href={entry.to}\n renderLink={({ href, ...p }) => <Link to={href} {...p} />}\n onClick={close}\n leading={entry.icon}\n trailing={entry.trailing}\n tone={entry.tone}\n current={entry.current}\n disabled={entry.disabled}\n >\n {entry.label}\n </MenuItem>\n ) : (\n <MenuItem\n onClick={() => {\n entry.onSelect();\n close();\n }}\n leading={entry.icon}\n trailing={entry.trailing}\n tone={entry.tone}\n checked={entry.checked}\n checkable={entry.checkable}\n disabled={entry.disabled}\n >\n {entry.label}\n </MenuItem>\n )}\n </li>\n );\n })}\n {children?.(close)}\n {footerBody != null && footerBody !== false && (\n <MenuSection className=\"mt-1 border-t border-[var(--border)] px-3 py-2\">\n {footerLabel ? (\n <nav aria-label={footerLabel} className={footerClass}>\n {footerBody}\n </nav>\n ) : (\n <div className={footerClass}>{footerBody}</div>\n )}\n </MenuSection>\n )}\n </ul>\n );\n }}\n </HoverMenu>\n );\n}\n"],"mappings":"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","names":[]}
package/dist/shell.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export { LanguageMenu, LanguageOption, PaletteMenu, TOPBAR_MENU_ITEM_CLASS, TOPBAR_TRIGGER_CLASS, ThemeToggle } from './shell/topbar-controls.js';
2
2
  export { TopBar, TopBarProps } from './shell/top-bar.js';
3
- export { AppShell, AppShellNavItem, AppShellProps, AppShellSubItem } from './shell/app-shell.js';
3
+ export { AppShell, AppShellNavItem, AppShellProps, AppShellSidebarFooterItem, AppShellSubItem } from './shell/app-shell.js';
4
4
  export { OptionSwitcherMenu, OptionSwitcherOption } from './shell/option-switcher-menu.js';
5
5
  export { RoleSwitcher, RoleSwitcherProps } from './shell/role-switcher.js';
6
6
  export { TopBarActionMenu, TopBarMenuEntry, TopBarMenuHeader } from './shell/topbar-action-menu.js';
7
+ export { TopBarBrand, TopBarBrandProps } from './shell/top-bar-brand.js';
8
+ export { AuthLayout, AuthLayoutProps } from './shell/auth-layout.js';
7
9
  import 'react';
8
10
  import './theme/palette-presets.js';
9
11
  import './theme/chart-palette.js';
@@ -11,4 +13,57 @@ import './theme/theme-store.js';
11
13
  import 'zustand/middleware';
12
14
  import 'zustand';
13
15
  import 'lucide-react';
16
+ import './kit-labels-v3biUF1L.js';
17
+ import './components/data-table-labels.js';
18
+ import './components/mini-calendar.js';
19
+ import './components/calendar-heatmap.js';
20
+ import './components/popover.js';
21
+ import './components/chip.js';
22
+ import './components/field-sync.js';
23
+ import './components/tooltip.js';
24
+ import './hooks/use-anchored-rect.js';
25
+ import './lib/clipping.js';
26
+ import './wizard/types.js';
27
+ import './tour/tour.js';
28
+ import './search/command-palette.js';
29
+ import './search/global-search.js';
30
+ import './search/search-index.js';
31
+ import './components/month-picker.js';
32
+ import './components/page-contents.js';
33
+ import './components/series-chart-labels.js';
34
+ import './components/pie-chart-labels.js';
35
+ import './components/sparkline.js';
36
+ import './components/stat-tile.js';
37
+ import './components/signature-pad.js';
38
+ import './components/password-strength.js';
39
+ import './components/danger-confirm.js';
40
+ import './components/swatch-picker.js';
41
+ import './components/tile-radio.js';
42
+ import './components/icon-picker.js';
43
+ import './components/dialog-frame.js';
44
+ import './components/modal.js';
45
+ import './hooks/use-search-param-state.js';
46
+ import './components/measured-grid.js';
47
+ import './feedback/feedback-attachment.js';
48
+ import './feedback/feedback-dialog.js';
49
+ import './feedback/feedback-thread.js';
50
+ import './feedback/feedback-inbox.js';
51
+ import './components/account-settings-labels.js';
52
+ import './components/confirm-dialog.js';
53
+ import './components/floating-panel.js';
54
+ import './components/bulk-action-bar.js';
55
+ import './components/list.js';
56
+ import './components/breadcrumbs.js';
57
+ import './components/toast.js';
58
+ import './components/description-list.js';
59
+ import './components/line-items.js';
60
+ import './components/progress-bar.js';
61
+ import './components/signed-amount.js';
62
+ import './components/error-boundary.js';
63
+ import './components/authed-image.js';
64
+ import './hooks/use-authed-src.js';
65
+ import './components/image-grid.js';
66
+ import './components/lightbox.js';
14
67
  import './components/menu-item.js';
68
+ import './components/user-avatar.js';
69
+ import './components/status-dot.js';
package/dist/shell.js CHANGED
@@ -5,4 +5,6 @@ export * from "./shell/app-shell.js";
5
5
  export * from "./shell/option-switcher-menu.js";
6
6
  export * from "./shell/role-switcher.js";
7
7
  export * from "./shell/topbar-action-menu.js";
8
+ export * from "./shell/top-bar-brand.js";
9
+ export * from "./shell/auth-layout.js";
8
10
  //# sourceMappingURL=shell.js.map
package/dist/shell.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shell.ts"],"sourcesContent":["// `@eifi1/ui-kit/shell` — the composable app chrome.\n//\n// A re-slicing of the main barrel, not a new API. This entry point requires\n// `react-router`: AppShell renders NavLink and TopBarActionMenu renders Link.\nexport * from \"./shell/topbar-controls\";\nexport * from \"./shell/top-bar\";\nexport * from \"./shell/app-shell\";\nexport * from \"./shell/option-switcher-menu\";\nexport * from \"./shell/role-switcher\";\nexport * from \"./shell/topbar-action-menu\";\n"],"mappings":";AAIA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/shell.ts"],"sourcesContent":["// `@eifi1/ui-kit/shell` — the composable app chrome.\n//\n// A re-slicing of the main barrel, not a new API. This entry point requires\n// `react-router`: AppShell renders NavLink and TopBarActionMenu renders Link.\nexport * from \"./shell/topbar-controls\";\nexport * from \"./shell/top-bar\";\nexport * from \"./shell/app-shell\";\nexport * from \"./shell/option-switcher-menu\";\nexport * from \"./shell/role-switcher\";\nexport * from \"./shell/topbar-action-menu\";\nexport * from \"./shell/top-bar-brand\";\nexport * from \"./shell/auth-layout\";\n"],"mappings":";AAIA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
@@ -1,10 +1,57 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { ButtonVariant } from '../components/ui.js';
3
+ import { f as ButtonVariant } from '../kit-labels-v3biUF1L.js';
4
4
  import { UseWizardReturn, WizardLabels } from './types.js';
5
+ import '../components/data-table-labels.js';
6
+ import '../components/mini-calendar.js';
7
+ import '../components/calendar-heatmap.js';
8
+ import '../components/popover.js';
9
+ import '../components/chip.js';
10
+ import 'lucide-react';
11
+ import '../components/field-sync.js';
5
12
  import '../components/tooltip.js';
6
13
  import '../hooks/use-anchored-rect.js';
7
14
  import '../lib/clipping.js';
15
+ import '../tour/tour.js';
16
+ import '../search/command-palette.js';
17
+ import '../search/global-search.js';
18
+ import '../search/search-index.js';
19
+ import '../components/month-picker.js';
20
+ import '../components/page-contents.js';
21
+ import '../components/series-chart-labels.js';
22
+ import '../components/pie-chart-labels.js';
23
+ import '../components/sparkline.js';
24
+ import '../components/stat-tile.js';
25
+ import '../components/signature-pad.js';
26
+ import '../components/password-strength.js';
27
+ import '../components/danger-confirm.js';
28
+ import '../components/swatch-picker.js';
29
+ import '../components/tile-radio.js';
30
+ import '../components/icon-picker.js';
31
+ import '../components/dialog-frame.js';
32
+ import '../components/modal.js';
33
+ import '../hooks/use-search-param-state.js';
34
+ import '../components/measured-grid.js';
35
+ import '../feedback/feedback-attachment.js';
36
+ import '../feedback/feedback-dialog.js';
37
+ import '../feedback/feedback-thread.js';
38
+ import '../feedback/feedback-inbox.js';
39
+ import '../components/account-settings-labels.js';
40
+ import '../components/confirm-dialog.js';
41
+ import '../components/floating-panel.js';
42
+ import '../components/bulk-action-bar.js';
43
+ import '../components/list.js';
44
+ import '../components/breadcrumbs.js';
45
+ import '../components/toast.js';
46
+ import '../components/description-list.js';
47
+ import '../components/line-items.js';
48
+ import '../components/progress-bar.js';
49
+ import '../components/signed-amount.js';
50
+ import '../components/error-boundary.js';
51
+ import '../components/authed-image.js';
52
+ import '../hooks/use-authed-src.js';
53
+ import '../components/image-grid.js';
54
+ import '../components/lightbox.js';
8
55
 
9
56
  /**
10
57
  * The chrome around a wizard: the step indicator, the step content, and the
@@ -21,5 +21,28 @@ declare function useWizardContext(): WizardContextValue;
21
21
  * `@eifi1/ui-kit/rhf`, whose returned `validate` a custom nav can call itself.
22
22
  */
23
23
  declare function useOptionalWizardContext(): WizardContextValue | null;
24
+ /**
25
+ * Registers a plain validator for the mounted step — the counterpart to
26
+ * `useRhfWizardStep` (in `@eifi1/ui-kit/rhf`) for a step that gates on its own state
27
+ * (a hand-managed list of lines, a sum that has to balance) rather than a form.
28
+ *
29
+ * ```tsx
30
+ * useWizardStepValidate(() => ({ ok: lines.length >= 2, errors: { lines: t("…") } }));
31
+ * ```
32
+ *
33
+ * Next (and Finish) run it with the step's other validators; a `false` or a result
34
+ * with `ok: false` keeps the wizard where it is. `validate` is read through a ref, so
35
+ * an inline closure over the latest state registers exactly once. Throws outside a
36
+ * `<StepperNav>`, like {@link useWizardContext}: a validator with nothing to run it is
37
+ * a bug to hear about.
38
+ */
39
+ declare function useWizardStepValidate(validate: () => ValidateResult | Promise<ValidateResult>): void;
40
+ /**
41
+ * Disables the wizard's Next and Skip while `blocked` holds (tenant shares that do not
42
+ * add up to 100 %). Unlike {@link useWizardStepValidate}, which answers a click, this
43
+ * greys the button out before one. The gate is lifted again when the step unmounts, so
44
+ * the next step starts unblocked.
45
+ */
46
+ declare function useWizardNextGate(blocked: boolean): void;
24
47
 
25
- export { WizardContextProvider, type WizardContextValue, useOptionalWizardContext, useWizardContext };
48
+ export { WizardContextProvider, type WizardContextValue, useOptionalWizardContext, useWizardContext, useWizardNextGate, useWizardStepValidate };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { createContext, useContext } from "react";
2
+ import { createContext, useContext, useEffect, useRef } from "react";
3
3
  const WizardContext = createContext(null);
4
4
  const WizardContextProvider = WizardContext.Provider;
5
5
  function useWizardContext() {
@@ -12,9 +12,26 @@ function useWizardContext() {
12
12
  function useOptionalWizardContext() {
13
13
  return useContext(WizardContext);
14
14
  }
15
+ function useWizardStepValidate(validate) {
16
+ const { registerStepValidate } = useWizardContext();
17
+ const ref = useRef(validate);
18
+ useEffect(() => {
19
+ ref.current = validate;
20
+ });
21
+ useEffect(() => registerStepValidate(() => ref.current()), [registerStepValidate]);
22
+ }
23
+ function useWizardNextGate(blocked) {
24
+ const { setNextBlocked } = useWizardContext();
25
+ useEffect(() => {
26
+ setNextBlocked(blocked);
27
+ return () => setNextBlocked(false);
28
+ }, [blocked, setNextBlocked]);
29
+ }
15
30
  export {
16
31
  WizardContextProvider,
17
32
  useOptionalWizardContext,
18
- useWizardContext
33
+ useWizardContext,
34
+ useWizardNextGate,
35
+ useWizardStepValidate
19
36
  };
20
37
  //# sourceMappingURL=wizard-context.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/wizard/wizard-context.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ValidateResult } from \"./types\";\n\nexport interface WizardContextValue {\n /**\n * Register a validator for the currently-mounted step. Returns an unregister\n * fn (call it in a useEffect cleanup). `goNext` runs every registered\n * validator for the active step (plus the step config's own `validate`),\n * AND-combining the results — this replaces the per-page `useRef` +\n * `validate: () => ref.current?.()` bridge each RHF step used to wire.\n */\n registerStepValidate: (\n fn: () => ValidateResult | Promise<ValidateResult>,\n ) => () => void;\n /** Lets the mounted step disable Next/Skip (e.g. invalid tenant shares). */\n setNextBlocked: (blocked: boolean) => void;\n}\n\nconst WizardContext = createContext<WizardContextValue | null>(null);\n\nexport const WizardContextProvider = WizardContext.Provider;\n\nexport function useWizardContext(): WizardContextValue {\n const ctx = useContext(WizardContext);\n if (!ctx) {\n throw new Error(\"useWizardContext must be used within a <StepperNav>\");\n }\n return ctx;\n}\n\n/**\n * The wizard context, or `null` outside a `<StepperNav>`. For a hook that registers\n * with the wizard when there is one and works without it — `useRhfWizardStep` in\n * `@eifi1/ui-kit/rhf`, whose returned `validate` a custom nav can call itself.\n */\nexport function useOptionalWizardContext(): WizardContextValue | null {\n return useContext(WizardContext);\n}\n"],"mappings":";AAAA,SAAS,eAAe,kBAAkB;AAkB1C,MAAM,gBAAgB,cAAyC,IAAI;AAE5D,MAAM,wBAAwB,cAAc;AAE5C,SAAS,mBAAuC;AACrD,QAAM,MAAM,WAAW,aAAa;AACpC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAOO,SAAS,2BAAsD;AACpE,SAAO,WAAW,aAAa;AACjC;","names":[]}
1
+ {"version":3,"sources":["../../src/wizard/wizard-context.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useRef } from \"react\";\nimport type { ValidateResult } from \"./types\";\n\nexport interface WizardContextValue {\n /**\n * Register a validator for the currently-mounted step. Returns an unregister\n * fn (call it in a useEffect cleanup). `goNext` runs every registered\n * validator for the active step (plus the step config's own `validate`),\n * AND-combining the results — this replaces the per-page `useRef` +\n * `validate: () => ref.current?.()` bridge each RHF step used to wire.\n */\n registerStepValidate: (\n fn: () => ValidateResult | Promise<ValidateResult>,\n ) => () => void;\n /** Lets the mounted step disable Next/Skip (e.g. invalid tenant shares). */\n setNextBlocked: (blocked: boolean) => void;\n}\n\nconst WizardContext = createContext<WizardContextValue | null>(null);\n\nexport const WizardContextProvider = WizardContext.Provider;\n\nexport function useWizardContext(): WizardContextValue {\n const ctx = useContext(WizardContext);\n if (!ctx) {\n throw new Error(\"useWizardContext must be used within a <StepperNav>\");\n }\n return ctx;\n}\n\n/**\n * The wizard context, or `null` outside a `<StepperNav>`. For a hook that registers\n * with the wizard when there is one and works without it — `useRhfWizardStep` in\n * `@eifi1/ui-kit/rhf`, whose returned `validate` a custom nav can call itself.\n */\nexport function useOptionalWizardContext(): WizardContextValue | null {\n return useContext(WizardContext);\n}\n\n/**\n * Registers a plain validator for the mounted step — the counterpart to\n * `useRhfWizardStep` (in `@eifi1/ui-kit/rhf`) for a step that gates on its own state\n * (a hand-managed list of lines, a sum that has to balance) rather than a form.\n *\n * ```tsx\n * useWizardStepValidate(() => ({ ok: lines.length >= 2, errors: { lines: t(\"…\") } }));\n * ```\n *\n * Next (and Finish) run it with the step's other validators; a `false` or a result\n * with `ok: false` keeps the wizard where it is. `validate` is read through a ref, so\n * an inline closure over the latest state registers exactly once. Throws outside a\n * `<StepperNav>`, like {@link useWizardContext}: a validator with nothing to run it is\n * a bug to hear about.\n */\nexport function useWizardStepValidate(\n validate: () => ValidateResult | Promise<ValidateResult>,\n): void {\n const { registerStepValidate } = useWizardContext();\n const ref = useRef(validate);\n useEffect(() => {\n ref.current = validate;\n });\n useEffect(() => registerStepValidate(() => ref.current()), [registerStepValidate]);\n}\n\n/**\n * Disables the wizard's Next and Skip while `blocked` holds (tenant shares that do not\n * add up to 100 %). Unlike {@link useWizardStepValidate}, which answers a click, this\n * greys the button out before one. The gate is lifted again when the step unmounts, so\n * the next step starts unblocked.\n */\nexport function useWizardNextGate(blocked: boolean): void {\n const { setNextBlocked } = useWizardContext();\n useEffect(() => {\n setNextBlocked(blocked);\n return () => setNextBlocked(false);\n }, [blocked, setNextBlocked]);\n}\n"],"mappings":";AAAA,SAAS,eAAe,YAAY,WAAW,cAAc;AAkB7D,MAAM,gBAAgB,cAAyC,IAAI;AAE5D,MAAM,wBAAwB,cAAc;AAE5C,SAAS,mBAAuC;AACrD,QAAM,MAAM,WAAW,aAAa;AACpC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;AAOO,SAAS,2BAAsD;AACpE,SAAO,WAAW,aAAa;AACjC;AAiBO,SAAS,sBACd,UACM;AACN,QAAM,EAAE,qBAAqB,IAAI,iBAAiB;AAClD,QAAM,MAAM,OAAO,QAAQ;AAC3B,YAAU,MAAM;AACd,QAAI,UAAU;AAAA,EAChB,CAAC;AACD,YAAU,MAAM,qBAAqB,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAC;AACnF;AAQO,SAAS,kBAAkB,SAAwB;AACxD,QAAM,EAAE,eAAe,IAAI,iBAAiB;AAC5C,YAAU,MAAM;AACd,mBAAe,OAAO;AACtB,WAAO,MAAM,eAAe,KAAK;AAAA,EACnC,GAAG,CAAC,SAAS,cAAc,CAAC;AAC9B;","names":[]}
@@ -1,11 +1,26 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
 
4
- /** 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. */
6
- declare function WizardStep({ children, className, }: {
4
+ interface WizardStepProps {
7
5
  children: ReactNode;
8
6
  className?: string;
9
- }): react.JSX.Element;
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
+ /** The heading's level. Default 3: the page has its `h1` and the wizard's own
18
+ * chrome sits at `h2` (the review step, the cancel dialog), so a step is under it. */
19
+ headingLevel?: 2 | 3 | 4;
20
+ }
21
+ /** The vertical rhythm one wizard step's content sits on. A component rather
22
+ * than a class string so every step in every app spaces its fields the same —
23
+ * and, with `title` / `description`, heads them the same. */
24
+ declare function WizardStep({ children, className, title, description, headingLevel, }: WizardStepProps): react.JSX.Element;
10
25
 
11
- export { WizardStep };
26
+ export { WizardStep, type WizardStepProps };
@@ -1,11 +1,24 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { cn } from "../lib/cn.js";
4
+ const HEADING = { 2: "h2", 3: "h3", 4: "h4" };
4
5
  function WizardStep({
5
6
  children,
6
- className
7
+ className,
8
+ title,
9
+ description,
10
+ headingLevel = 3
7
11
  }) {
8
- return /* @__PURE__ */ jsx("div", { className: cn("space-y-6", className), children });
12
+ const hasTitle = title !== void 0 && title !== null && title !== false && title !== "";
13
+ const hasDescription = description !== void 0 && description !== null && description !== false && description !== "";
14
+ const Heading = HEADING[headingLevel];
15
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
16
+ (hasTitle || hasDescription) && /* @__PURE__ */ jsxs("div", { "data-slot": "wizard-step-header", className: "space-y-1", children: [
17
+ hasTitle && /* @__PURE__ */ jsx(Heading, { className: "text-lg font-semibold text-[var(--text-primary)]", children: title }),
18
+ hasDescription && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-muted)]", children: description })
19
+ ] }),
20
+ children
21
+ ] });
9
22
  }
10
23
  export {
11
24
  WizardStep
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/wizard/wizard-step.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/** The vertical rhythm one wizard step's content sits on. A component rather\n * than a class string so every step in every app spaces its fields the same. */\nexport function WizardStep({\n children,\n className,\n}: {\n children: ReactNode;\n className?: string;\n}) {\n return <div className={cn(\"space-y-6\", className)}>{children}</div>;\n}\n"],"mappings":";AAYS;AAXT,SAAS,UAAU;AAIZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AACF,GAGG;AACD,SAAO,oBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GAAI,UAAS;AAC/D;","names":[]}
1
+ {"version":3,"sources":["../../src/wizard/wizard-step.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\n\nexport interface WizardStepProps {\n children: ReactNode;\n className?: string;\n /**\n * The step's heading (\"Choose a unit\"), drawn in the one style every step shares.\n * kastlan's steps each hand-wrote `<h3 className=\"text-lg font-semibold\">` (about\n * sixteen of them: lease-picker-step, lease-unit-step, …), and a hand-written\n * heading is one that drifts — a size here, a colour there. Omitted: no heading.\n */\n title?: ReactNode;\n /** The line under the title: what the step asks for, or why. Rendered only with a\n * `title` or on its own when there is none. */\n description?: ReactNode;\n /** The heading's level. Default 3: the page has its `h1` and the wizard's own\n * chrome sits at `h2` (the review step, the cancel dialog), so a step is under it. */\n headingLevel?: 2 | 3 | 4;\n}\n\nconst HEADING = { 2: \"h2\", 3: \"h3\", 4: \"h4\" } as const;\n\n/** The vertical rhythm one wizard step's content sits on. A component rather\n * than a class string so every step in every app spaces its fields the same —\n * and, with `title` / `description`, heads them the same. */\nexport function WizardStep({\n children,\n className,\n title,\n description,\n headingLevel = 3,\n}: WizardStepProps) {\n const hasTitle = title !== undefined && title !== null && title !== false && title !== \"\";\n const hasDescription =\n description !== undefined && description !== null && description !== false && description !== \"\";\n const Heading = HEADING[headingLevel];\n return (\n <div className={cn(\"space-y-6\", className)}>\n {(hasTitle || hasDescription) && (\n <div data-slot=\"wizard-step-header\" className=\"space-y-1\">\n {hasTitle && (\n <Heading className=\"text-lg font-semibold text-[var(--text-primary)]\">{title}</Heading>\n )}\n {hasDescription && <p className=\"text-sm text-[var(--text-muted)]\">{description}</p>}\n </div>\n )}\n {children}\n </div>\n );\n}\n"],"mappings":";AAwCQ,SAEI,KAFJ;AAvCR,SAAS,UAAU;AAoBnB,MAAM,UAAU,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK;AAKrC,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAoB;AAClB,QAAM,WAAW,UAAU,UAAa,UAAU,QAAQ,UAAU,SAAS,UAAU;AACvF,QAAM,iBACJ,gBAAgB,UAAa,gBAAgB,QAAQ,gBAAgB,SAAS,gBAAgB;AAChG,QAAM,UAAU,QAAQ,YAAY;AACpC,SACE,qBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GACrC;AAAA,iBAAY,mBACZ,qBAAC,SAAI,aAAU,sBAAqB,WAAU,aAC3C;AAAA,kBACC,oBAAC,WAAQ,WAAU,oDAAoD,iBAAM;AAAA,MAE9E,kBAAkB,oBAAC,OAAE,WAAU,oCAAoC,uBAAY;AAAA,OAClF;AAAA,IAED;AAAA,KACH;AAEJ;","names":[]}
package/dist/wizard.d.ts CHANGED
@@ -1,12 +1,59 @@
1
1
  export { DEFAULT_WIZARD_LABELS, FieldErrors, StepStatus, SummaryItem, SummarySection, UseWizardOptions, UseWizardReturn, ValidateResult, WizardLabels, WizardStepConfig, WizardUrlSync, resolveWizardLabels } from './wizard/types.js';
2
2
  export { useWizard } from './wizard/use-wizard.js';
3
- export { WizardContextProvider, WizardContextValue, useOptionalWizardContext, useWizardContext } from './wizard/wizard-context.js';
3
+ export { WizardContextProvider, WizardContextValue, useOptionalWizardContext, useWizardContext, useWizardNextGate, useWizardStepValidate } from './wizard/wizard-context.js';
4
4
  export { RequiredFieldSpec, requiredFieldsValidator } from './wizard/validation.js';
5
- export { WizardStep } from './wizard/wizard-step.js';
5
+ export { WizardStep, WizardStepProps } from './wizard/wizard-step.js';
6
6
  export { WizardSummary } from './wizard/wizard-summary.js';
7
7
  export { StepperNav } from './wizard/stepper-nav.js';
8
8
  import 'react';
9
- import './components/ui.js';
9
+ import './kit-labels-v3biUF1L.js';
10
+ import './components/data-table-labels.js';
11
+ import './components/mini-calendar.js';
12
+ import './components/calendar-heatmap.js';
13
+ import './components/popover.js';
14
+ import './components/chip.js';
15
+ import 'lucide-react';
16
+ import './components/field-sync.js';
10
17
  import './components/tooltip.js';
11
18
  import './hooks/use-anchored-rect.js';
12
19
  import './lib/clipping.js';
20
+ import './tour/tour.js';
21
+ import './search/command-palette.js';
22
+ import './search/global-search.js';
23
+ import './search/search-index.js';
24
+ import './components/month-picker.js';
25
+ import './components/page-contents.js';
26
+ import './components/series-chart-labels.js';
27
+ import './components/pie-chart-labels.js';
28
+ import './components/sparkline.js';
29
+ import './components/stat-tile.js';
30
+ import './components/signature-pad.js';
31
+ import './components/password-strength.js';
32
+ import './components/danger-confirm.js';
33
+ import './components/swatch-picker.js';
34
+ import './components/tile-radio.js';
35
+ import './components/icon-picker.js';
36
+ import './components/dialog-frame.js';
37
+ import './components/modal.js';
38
+ import './hooks/use-search-param-state.js';
39
+ import './components/measured-grid.js';
40
+ import './feedback/feedback-attachment.js';
41
+ import './feedback/feedback-dialog.js';
42
+ import './feedback/feedback-thread.js';
43
+ import './feedback/feedback-inbox.js';
44
+ import './components/account-settings-labels.js';
45
+ import './components/confirm-dialog.js';
46
+ import './components/floating-panel.js';
47
+ import './components/bulk-action-bar.js';
48
+ import './components/list.js';
49
+ import './components/breadcrumbs.js';
50
+ import './components/toast.js';
51
+ import './components/description-list.js';
52
+ import './components/line-items.js';
53
+ import './components/progress-bar.js';
54
+ import './components/signed-amount.js';
55
+ import './components/error-boundary.js';
56
+ import './components/authed-image.js';
57
+ import './hooks/use-authed-src.js';
58
+ import './components/image-grid.js';
59
+ import './components/lightbox.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eifi1/ui-kit",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
6
6
  "_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives. Re-checked when `flag-icons` left `dependencies` and tsup started emitting a `\"use client\"` banner: neither touches this. The flag stylesheet was never imported from JS in the first place (that was the finding), and a directive prologue is a string literal every bundler lifts out before it decides whether a module is inert.",
package/src/chart.ts CHANGED
@@ -20,3 +20,7 @@ export * from "./components/chart-zoom";
20
20
  export * from "./components/toggle-legend";
21
21
  export * from "./components/facing-pair";
22
22
  export * from "./components/series-chart-labels";
23
+
24
+ // The part-to-whole chart (donut / pie) — moved here from kastlan's dashboard and reports.
25
+ export * from "./components/pie-chart";
26
+ export * from "./components/pie-chart-labels";