@eifi1/ui-kit 0.7.0 → 0.8.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 (233) hide show
  1. package/README.md +32 -17
  2. package/dist/chart.d.ts +3 -2
  3. package/dist/components/alert-banner.d.ts +59 -10
  4. package/dist/components/alert-banner.js +114 -8
  5. package/dist/components/alert-banner.js.map +1 -1
  6. package/dist/components/amount-input.d.ts +5 -0
  7. package/dist/components/button-group.d.ts +26 -0
  8. package/dist/components/button-group.js +49 -0
  9. package/dist/components/button-group.js.map +1 -0
  10. package/dist/components/calculator.d.ts +5 -0
  11. package/dist/components/chart-zoom.d.ts +63 -14
  12. package/dist/components/chart-zoom.js +82 -16
  13. package/dist/components/chart-zoom.js.map +1 -1
  14. package/dist/components/chip.d.ts +67 -2
  15. package/dist/components/chip.js +73 -8
  16. package/dist/components/chip.js.map +1 -1
  17. package/dist/components/confirm-dialog.d.ts +120 -0
  18. package/dist/components/confirm-dialog.js +96 -0
  19. package/dist/components/confirm-dialog.js.map +1 -0
  20. package/dist/components/copy-button.d.ts +62 -0
  21. package/dist/components/copy-button.js +95 -0
  22. package/dist/components/copy-button.js.map +1 -0
  23. package/dist/components/data-table-filter-popover.d.ts +1 -1
  24. package/dist/components/data-table-filters.d.ts +1 -1
  25. package/dist/components/data-table-pagination.d.ts +5 -1
  26. package/dist/components/data-table-pagination.js +71 -58
  27. package/dist/components/data-table-pagination.js.map +1 -1
  28. package/dist/components/data-table-sort.d.ts +28 -5
  29. package/dist/components/data-table-sort.js +11 -7
  30. package/dist/components/data-table-sort.js.map +1 -1
  31. package/dist/components/data-table.d.ts +1 -1
  32. package/dist/components/data-table.js +544 -423
  33. package/dist/components/data-table.js.map +1 -1
  34. package/dist/components/date-picker.d.ts +51 -5
  35. package/dist/components/date-picker.js +152 -40
  36. package/dist/components/date-picker.js.map +1 -1
  37. package/dist/components/description-list.d.ts +60 -0
  38. package/dist/components/description-list.js +112 -0
  39. package/dist/components/description-list.js.map +1 -0
  40. package/dist/components/disclosure.d.ts +37 -2
  41. package/dist/components/disclosure.js +14 -5
  42. package/dist/components/disclosure.js.map +1 -1
  43. package/dist/components/facing-pair.d.ts +2 -0
  44. package/dist/components/file-button.d.ts +19 -1
  45. package/dist/components/file-button.js +8 -1
  46. package/dist/components/file-button.js.map +1 -1
  47. package/dist/components/file-dropzone.d.ts +56 -9
  48. package/dist/components/file-dropzone.js +100 -70
  49. package/dist/components/file-dropzone.js.map +1 -1
  50. package/dist/components/floating-panel.d.ts +107 -0
  51. package/dist/components/floating-panel.js +206 -0
  52. package/dist/components/floating-panel.js.map +1 -0
  53. package/dist/components/full-bleed-dialog.d.ts +12 -1
  54. package/dist/components/full-bleed-dialog.js +26 -5
  55. package/dist/components/full-bleed-dialog.js.map +1 -1
  56. package/dist/components/modal.d.ts +6 -2
  57. package/dist/components/modal.js +2 -1
  58. package/dist/components/modal.js.map +1 -1
  59. package/dist/components/number-field.d.ts +5 -0
  60. package/dist/components/number-input.d.ts +5 -0
  61. package/dist/components/numpad-sheet.d.ts +5 -0
  62. package/dist/components/progress-bar.d.ts +54 -0
  63. package/dist/components/progress-bar.js +94 -0
  64. package/dist/components/progress-bar.js.map +1 -0
  65. package/dist/components/scroll-area.d.ts +53 -0
  66. package/dist/components/scroll-area.js +86 -0
  67. package/dist/components/scroll-area.js.map +1 -0
  68. package/dist/components/separator.d.ts +23 -0
  69. package/dist/components/separator.js +24 -0
  70. package/dist/components/separator.js.map +1 -0
  71. package/dist/components/series-chart-ticks.d.ts +34 -1
  72. package/dist/components/series-chart-ticks.js +83 -1
  73. package/dist/components/series-chart-ticks.js.map +1 -1
  74. package/dist/components/series-chart.d.ts +314 -19
  75. package/dist/components/series-chart.js +510 -120
  76. package/dist/components/series-chart.js.map +1 -1
  77. package/dist/components/settings-fields.d.ts +2 -0
  78. package/dist/components/skeleton.d.ts +36 -0
  79. package/dist/components/skeleton.js +35 -0
  80. package/dist/components/skeleton.js.map +1 -0
  81. package/dist/components/stat-tile.d.ts +60 -6
  82. package/dist/components/stat-tile.js +60 -26
  83. package/dist/components/stat-tile.js.map +1 -1
  84. package/dist/components/table.d.ts +83 -0
  85. package/dist/components/table.js +166 -0
  86. package/dist/components/table.js.map +1 -0
  87. package/dist/components/time-input.d.ts +2 -0
  88. package/dist/components/toggle-legend.d.ts +3 -2
  89. package/dist/components/toggle-legend.js +1 -1
  90. package/dist/components/toggle-legend.js.map +1 -1
  91. package/dist/components/tooltip.d.ts +10 -4
  92. package/dist/components/tooltip.js +23 -6
  93. package/dist/components/tooltip.js.map +1 -1
  94. package/dist/components/tree-view.d.ts +129 -0
  95. package/dist/components/tree-view.js +376 -0
  96. package/dist/components/tree-view.js.map +1 -0
  97. package/dist/components/treemap.d.ts +9 -2
  98. package/dist/components/treemap.js +2 -1
  99. package/dist/components/treemap.js.map +1 -1
  100. package/dist/components/ui.d.ts +92 -14
  101. package/dist/components/ui.js +112 -30
  102. package/dist/components/ui.js.map +1 -1
  103. package/dist/components/use-table-state.d.ts +1 -1
  104. package/dist/{data-table-filters-noy0Abvi.d.ts → data-table-filters-CF1PXqjQ.d.ts} +73 -3
  105. package/dist/data-table.d.ts +2 -2
  106. package/dist/data-table.js.map +1 -1
  107. package/dist/hooks/use-copy-to-clipboard.d.ts +28 -0
  108. package/dist/hooks/use-copy-to-clipboard.js +81 -0
  109. package/dist/hooks/use-copy-to-clipboard.js.map +1 -0
  110. package/dist/hooks/use-debounce.d.ts +43 -0
  111. package/dist/hooks/use-debounce.js +56 -0
  112. package/dist/hooks/use-debounce.js.map +1 -0
  113. package/dist/hooks/use-file-drop.d.ts +72 -0
  114. package/dist/hooks/use-file-drop.js +57 -0
  115. package/dist/hooks/use-file-drop.js.map +1 -0
  116. package/dist/i18n/defaults.d.ts +5 -0
  117. package/dist/i18n/defaults.js +7 -1
  118. package/dist/i18n/defaults.js.map +1 -1
  119. package/dist/i18n/kit-labels.d.ts +17 -0
  120. package/dist/i18n/kit-labels.js +6 -2
  121. package/dist/i18n/kit-labels.js.map +1 -1
  122. package/dist/i18n/locales/de-CH-informal.d.ts +43 -0
  123. package/dist/i18n/locales/de-CH-informal.js +8 -0
  124. package/dist/i18n/locales/de-CH-informal.js.map +1 -0
  125. package/dist/i18n/locales/de-CH.d.ts +5 -0
  126. package/dist/i18n/locales/de-CH.js +1 -14
  127. package/dist/i18n/locales/de-CH.js.map +1 -1
  128. package/dist/i18n/locales/de-informal.d.ts +64 -0
  129. package/dist/i18n/locales/de-informal.js +37 -0
  130. package/dist/i18n/locales/de-informal.js.map +1 -0
  131. package/dist/i18n/locales/de.d.ts +5 -0
  132. package/dist/i18n/locales/de.js +25 -0
  133. package/dist/i18n/locales/de.js.map +1 -1
  134. package/dist/i18n/locales/es.d.ts +5 -0
  135. package/dist/i18n/locales/es.js +25 -0
  136. package/dist/i18n/locales/es.js.map +1 -1
  137. package/dist/i18n/locales/fr.d.ts +5 -0
  138. package/dist/i18n/locales/fr.js +25 -0
  139. package/dist/i18n/locales/fr.js.map +1 -1
  140. package/dist/i18n/locales/hu.d.ts +5 -0
  141. package/dist/i18n/locales/hu.js +25 -0
  142. package/dist/i18n/locales/hu.js.map +1 -1
  143. package/dist/i18n/locales/it.d.ts +5 -0
  144. package/dist/i18n/locales/it.js +25 -0
  145. package/dist/i18n/locales/it.js.map +1 -1
  146. package/dist/i18n/locales/zh.d.ts +5 -0
  147. package/dist/i18n/locales/zh.js +25 -0
  148. package/dist/i18n/locales/zh.js.map +1 -1
  149. package/dist/i18n/swiss.d.ts +13 -0
  150. package/dist/i18n/swiss.js +19 -0
  151. package/dist/i18n/swiss.js.map +1 -0
  152. package/dist/index.d.ts +26 -11
  153. package/dist/index.js +22 -0
  154. package/dist/index.js.map +1 -1
  155. package/dist/lib/dates.d.ts +37 -1
  156. package/dist/lib/dates.js +19 -0
  157. package/dist/lib/dates.js.map +1 -1
  158. package/dist/rhf/form.d.ts +2 -0
  159. package/dist/rhf.d.ts +2 -0
  160. package/dist/search/command-palette.d.ts +15 -1
  161. package/dist/search/command-palette.js +10 -2
  162. package/dist/search/command-palette.js.map +1 -1
  163. package/dist/shell/app-shell.js +2 -2
  164. package/dist/shell/app-shell.js.map +1 -1
  165. package/dist/wizard/stepper-nav.d.ts +35 -2
  166. package/dist/wizard/stepper-nav.js +54 -22
  167. package/dist/wizard/stepper-nav.js.map +1 -1
  168. package/dist/wizard/types.d.ts +107 -5
  169. package/dist/wizard/types.js +2 -1
  170. package/dist/wizard/types.js.map +1 -1
  171. package/dist/wizard/use-wizard.js +141 -21
  172. package/dist/wizard/use-wizard.js.map +1 -1
  173. package/dist/wizard/wizard-summary.d.ts +10 -2
  174. package/dist/wizard/wizard-summary.js +3 -1
  175. package/dist/wizard/wizard-summary.js.map +1 -1
  176. package/dist/wizard.d.ts +4 -1
  177. package/dist/wizard.js.map +1 -1
  178. package/package.json +1 -1
  179. package/src/components/alert-banner.tsx +211 -20
  180. package/src/components/button-group.tsx +75 -0
  181. package/src/components/chart-zoom.tsx +158 -20
  182. package/src/components/chip.tsx +160 -9
  183. package/src/components/confirm-dialog.tsx +242 -0
  184. package/src/components/copy-button.tsx +158 -0
  185. package/src/components/data-table-pagination.tsx +19 -6
  186. package/src/components/data-table-sort.ts +49 -10
  187. package/src/components/data-table.tsx +293 -24
  188. package/src/components/date-picker.tsx +262 -57
  189. package/src/components/description-list.tsx +174 -0
  190. package/src/components/disclosure.tsx +58 -5
  191. package/src/components/file-button.tsx +22 -2
  192. package/src/components/file-dropzone.tsx +198 -100
  193. package/src/components/floating-panel.tsx +341 -0
  194. package/src/components/full-bleed-dialog.tsx +48 -5
  195. package/src/components/modal.tsx +9 -2
  196. package/src/components/progress-bar.tsx +163 -0
  197. package/src/components/scroll-area.tsx +129 -0
  198. package/src/components/separator.tsx +37 -0
  199. package/src/components/series-chart-ticks.ts +135 -0
  200. package/src/components/series-chart.tsx +934 -67
  201. package/src/components/skeleton.tsx +65 -0
  202. package/src/components/stat-tile.tsx +160 -27
  203. package/src/components/table.tsx +263 -0
  204. package/src/components/toggle-legend.tsx +4 -3
  205. package/src/components/tooltip.tsx +40 -12
  206. package/src/components/tree-view.tsx +589 -0
  207. package/src/components/treemap.tsx +10 -2
  208. package/src/components/ui.tsx +268 -42
  209. package/src/data-table.ts +2 -0
  210. package/src/hooks/use-copy-to-clipboard.ts +122 -0
  211. package/src/hooks/use-debounce.ts +105 -0
  212. package/src/hooks/use-file-drop.ts +123 -0
  213. package/src/i18n/defaults.ts +6 -0
  214. package/src/i18n/kit-labels.tsx +19 -0
  215. package/src/i18n/locales/de-CH-informal.ts +10 -0
  216. package/src/i18n/locales/de-CH.ts +1 -16
  217. package/src/i18n/locales/de-informal.ts +61 -0
  218. package/src/i18n/locales/de.ts +25 -0
  219. package/src/i18n/locales/es.ts +25 -0
  220. package/src/i18n/locales/fr.ts +25 -0
  221. package/src/i18n/locales/hu.ts +25 -0
  222. package/src/i18n/locales/it.ts +25 -0
  223. package/src/i18n/locales/zh.ts +25 -0
  224. package/src/i18n/swiss.ts +25 -0
  225. package/src/index.ts +29 -0
  226. package/src/lib/dates.ts +48 -0
  227. package/src/search/command-palette.tsx +29 -3
  228. package/src/shell/app-shell.tsx +2 -2
  229. package/src/wizard/stepper-nav.tsx +102 -28
  230. package/src/wizard/types.ts +106 -4
  231. package/src/wizard/use-wizard.ts +211 -31
  232. package/src/wizard/wizard-summary.tsx +24 -12
  233. package/src/wizard.ts +3 -2
@@ -8,10 +8,18 @@ import { SummarySection, WizardLabels } from './types.js';
8
8
  * Sections are data, not children, because "jump back to step N" is the whole
9
9
  * point of the component and a caller assembling its own cards invariably drops
10
10
  * it. Labels and values are `ReactNode`, so a value can be a badge or a link.
11
+ *
12
+ * A section with no `stepIndex` (file-level counts, a total) is a card without an
13
+ * edit button. `disabled` greys every edit button at once — while the commit runs,
14
+ * when jumping back would show a step whose edits can no longer reach the request.
11
15
  */
12
- declare function WizardSummary({ sections, onEditStep, labels, }: {
16
+ declare function WizardSummary({ sections, onEditStep, disabled, labels, }: {
13
17
  sections: SummarySection[];
14
- onEditStep: (stepIndex: number) => void;
18
+ /** Called with the section's `stepIndex`. Optional only for a summary whose
19
+ * sections name no step at all. */
20
+ onEditStep?: (stepIndex: number) => void;
21
+ /** Disable every edit button (e.g. `wizard.isSubmitting`). */
22
+ disabled?: boolean;
15
23
  labels?: Partial<WizardLabels>;
16
24
  }): react.JSX.Element;
17
25
 
@@ -7,6 +7,7 @@ import { useKitLabels } from "../i18n/kit-labels.js";
7
7
  function WizardSummary({
8
8
  sections,
9
9
  onEditStep,
10
+ disabled = false,
10
11
  labels
11
12
  }) {
12
13
  const l = useKitLabels("wizard", DEFAULT_WIZARD_LABELS, labels);
@@ -15,13 +16,14 @@ function WizardSummary({
15
16
  sections.map((section, sIdx) => /* @__PURE__ */ jsxs(Card, { children: [
16
17
  /* @__PURE__ */ jsxs(CardHeader, { className: "pb-2", children: [
17
18
  /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: section.label }),
18
- /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(
19
+ section.stepIndex !== void 0 && onEditStep && /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(
19
20
  IconButton,
20
21
  {
21
22
  size: "sm",
22
23
  variant: "ghost",
23
24
  "aria-label": l.edit,
24
25
  title: l.edit,
26
+ disabled,
25
27
  onClick: () => onEditStep(section.stepIndex),
26
28
  children: /* @__PURE__ */ jsx(Pencil, {})
27
29
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/wizard/wizard-summary.tsx"],"sourcesContent":["import { Pencil } from \"lucide-react\";\nimport { Card, CardAction, CardContent, CardHeader, CardTitle, IconButton } from \"../components/ui\";\nimport type { SummarySection, WizardLabels } from \"./types\";\nimport { DEFAULT_WIZARD_LABELS } from \"./types\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\n\n/**\n * The review step: one card per section, each row a label/value pair, each card\n * carrying an edit button back to the step it came from.\n *\n * Sections are data, not children, because \"jump back to step N\" is the whole\n * point of the component and a caller assembling its own cards invariably drops\n * it. Labels and values are `ReactNode`, so a value can be a badge or a link.\n */\nexport function WizardSummary({\n sections,\n onEditStep,\n labels,\n}: {\n sections: SummarySection[];\n onEditStep: (stepIndex: number) => void;\n labels?: Partial<WizardLabels>;\n}) {\n const l = useKitLabels(\"wizard\", DEFAULT_WIZARD_LABELS, labels);\n\n return (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">{l.reviewTitle}</h2>\n {sections.map((section, sIdx) => (\n <Card key={sIdx}>\n {/* CardHeader is a grid, and CardAction is the top-right column it\n reserves — the edit button has to go through it or it stacks under\n the title instead of sitting beside it. */}\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base\">{section.label}</CardTitle>\n <CardAction>\n <IconButton\n size=\"sm\"\n variant=\"ghost\"\n aria-label={l.edit}\n title={l.edit}\n onClick={() => onEditStep(section.stepIndex)}\n >\n <Pencil />\n </IconButton>\n </CardAction>\n </CardHeader>\n <CardContent>\n <dl className=\"grid gap-2 text-sm\">\n {section.items.map((item, iIdx) => (\n <div key={iIdx}>\n {iIdx > 0 && (\n <div className=\"mb-2 border-t border-[var(--border)]\" role=\"presentation\" />\n )}\n <div className=\"flex items-start justify-between gap-4\">\n <dt className=\"text-[var(--text-muted)]\">{item.label}</dt>\n <dd className=\"text-end font-medium text-[var(--text-primary)]\">\n {item.value}\n </dd>\n </div>\n </div>\n ))}\n </dl>\n </CardContent>\n </Card>\n ))}\n </div>\n );\n}\n"],"mappings":";AA2BM,cAMI,YANJ;AA3BN,SAAS,cAAc;AACvB,SAAS,MAAM,YAAY,aAAa,YAAY,WAAW,kBAAkB;AAEjF,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAUtB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,IAAI,aAAa,UAAU,uBAAuB,MAAM;AAE9D,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,QAAG,WAAU,oDAAoD,YAAE,aAAY;AAAA,IAC/E,SAAS,IAAI,CAAC,SAAS,SACtB,qBAAC,QAIC;AAAA,2BAAC,cAAW,WAAU,QACpB;AAAA,4BAAC,aAAU,WAAU,aAAa,kBAAQ,OAAM;AAAA,QAChD,oBAAC,cACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,cAAY,EAAE;AAAA,YACd,OAAO,EAAE;AAAA,YACT,SAAS,MAAM,WAAW,QAAQ,SAAS;AAAA,YAE3C,8BAAC,UAAO;AAAA;AAAA,QACV,GACF;AAAA,SACF;AAAA,MACA,oBAAC,eACC,8BAAC,QAAG,WAAU,sBACX,kBAAQ,MAAM,IAAI,CAAC,MAAM,SACxB,qBAAC,SACE;AAAA,eAAO,KACN,oBAAC,SAAI,WAAU,wCAAuC,MAAK,gBAAe;AAAA,QAE5E,qBAAC,SAAI,WAAU,0CACb;AAAA,8BAAC,QAAG,WAAU,4BAA4B,eAAK,OAAM;AAAA,UACrD,oBAAC,QAAG,WAAU,mDACX,eAAK,OACR;AAAA,WACF;AAAA,WATQ,IAUV,CACD,GACH,GACF;AAAA,SAlCS,IAmCX,CACD;AAAA,KACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/wizard/wizard-summary.tsx"],"sourcesContent":["import { Pencil } from \"lucide-react\";\nimport { Card, CardAction, CardContent, CardHeader, CardTitle, IconButton } from \"../components/ui\";\nimport type { SummarySection, WizardLabels } from \"./types\";\nimport { DEFAULT_WIZARD_LABELS } from \"./types\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\n\n/**\n * The review step: one card per section, each row a label/value pair, each card\n * carrying an edit button back to the step it came from.\n *\n * Sections are data, not children, because \"jump back to step N\" is the whole\n * point of the component and a caller assembling its own cards invariably drops\n * it. Labels and values are `ReactNode`, so a value can be a badge or a link.\n *\n * A section with no `stepIndex` (file-level counts, a total) is a card without an\n * edit button. `disabled` greys every edit button at once — while the commit runs,\n * when jumping back would show a step whose edits can no longer reach the request.\n */\nexport function WizardSummary({\n sections,\n onEditStep,\n disabled = false,\n labels,\n}: {\n sections: SummarySection[];\n /** Called with the section's `stepIndex`. Optional only for a summary whose\n * sections name no step at all. */\n onEditStep?: (stepIndex: number) => void;\n /** Disable every edit button (e.g. `wizard.isSubmitting`). */\n disabled?: boolean;\n labels?: Partial<WizardLabels>;\n}) {\n const l = useKitLabels(\"wizard\", DEFAULT_WIZARD_LABELS, labels);\n\n return (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold text-[var(--text-primary)]\">{l.reviewTitle}</h2>\n {sections.map((section, sIdx) => (\n <Card key={sIdx}>\n {/* CardHeader is a grid, and CardAction is the top-right column it\n reserves — the edit button has to go through it or it stacks under\n the title instead of sitting beside it. */}\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"text-base\">{section.label}</CardTitle>\n {section.stepIndex !== undefined && onEditStep && (\n <CardAction>\n <IconButton\n size=\"sm\"\n variant=\"ghost\"\n aria-label={l.edit}\n title={l.edit}\n disabled={disabled}\n onClick={() => onEditStep(section.stepIndex as number)}\n >\n <Pencil />\n </IconButton>\n </CardAction>\n )}\n </CardHeader>\n <CardContent>\n <dl className=\"grid gap-2 text-sm\">\n {section.items.map((item, iIdx) => (\n <div key={iIdx}>\n {iIdx > 0 && (\n <div className=\"mb-2 border-t border-[var(--border)]\" role=\"presentation\" />\n )}\n <div className=\"flex items-start justify-between gap-4\">\n <dt className=\"text-[var(--text-muted)]\">{item.label}</dt>\n <dd className=\"text-end font-medium text-[var(--text-primary)]\">\n {item.value}\n </dd>\n </div>\n </div>\n ))}\n </dl>\n </CardContent>\n </Card>\n ))}\n </div>\n );\n}\n"],"mappings":";AAoCM,cAMI,YANJ;AApCN,SAAS,cAAc;AACvB,SAAS,MAAM,YAAY,aAAa,YAAY,WAAW,kBAAkB;AAEjF,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AActB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAQG;AACD,QAAM,IAAI,aAAa,UAAU,uBAAuB,MAAM;AAE9D,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,QAAG,WAAU,oDAAoD,YAAE,aAAY;AAAA,IAC/E,SAAS,IAAI,CAAC,SAAS,SACtB,qBAAC,QAIC;AAAA,2BAAC,cAAW,WAAU,QACpB;AAAA,4BAAC,aAAU,WAAU,aAAa,kBAAQ,OAAM;AAAA,QAC/C,QAAQ,cAAc,UAAa,cAClC,oBAAC,cACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,cAAY,EAAE;AAAA,YACd,OAAO,EAAE;AAAA,YACT;AAAA,YACA,SAAS,MAAM,WAAW,QAAQ,SAAmB;AAAA,YAErD,8BAAC,UAAO;AAAA;AAAA,QACV,GACF;AAAA,SAEJ;AAAA,MACA,oBAAC,eACC,8BAAC,QAAG,WAAU,sBACX,kBAAQ,MAAM,IAAI,CAAC,MAAM,SACxB,qBAAC,SACE;AAAA,eAAO,KACN,oBAAC,SAAI,WAAU,wCAAuC,MAAK,gBAAe;AAAA,QAE5E,qBAAC,SAAI,WAAU,0CACb;AAAA,8BAAC,QAAG,WAAU,4BAA4B,eAAK,OAAM;AAAA,UACrD,oBAAC,QAAG,WAAU,mDACX,eAAK,OACR;AAAA,WACF;AAAA,WATQ,IAUV,CACD,GACH,GACF;AAAA,SArCS,IAsCX,CACD;AAAA,KACH;AAEJ;","names":[]}
package/dist/wizard.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { DEFAULT_WIZARD_LABELS, FieldErrors, StepStatus, SummaryItem, SummarySection, UseWizardOptions, UseWizardReturn, ValidateResult, WizardLabels, WizardStepConfig, resolveWizardLabels } from './wizard/types.js';
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
3
  export { WizardContextProvider, WizardContextValue, useWizardContext } from './wizard/wizard-context.js';
4
4
  export { RequiredFieldSpec, requiredFieldsValidator } from './wizard/validation.js';
@@ -6,3 +6,6 @@ export { WizardStep } 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';
10
+ import './components/tooltip.js';
11
+ import './hooks/use-anchored-rect.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wizard.ts"],"sourcesContent":["// `@eifi1/ui-kit/wizard` — the multi-step engine, its chrome and its review step.\n//\n// A re-slicing of the main barrel, not a new API. This entry point requires\n// `react-router`: useWizard syncs the active step to `?step=` via useSearchParams.\n//\n// NOT the same thing as `WizardStepper` in components/wizard-stepper, which is a bare\n// two-step indicator with no engine and stays on the main barrel.\nexport * from \"./wizard/types\";\nexport * from \"./wizard/use-wizard\";\nexport * from \"./wizard/wizard-context\";\nexport * from \"./wizard/validation\";\nexport * from \"./wizard/wizard-step\";\nexport * from \"./wizard/wizard-summary\";\nexport * from \"./wizard/stepper-nav\";\n"],"mappings":";AAOA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/wizard.ts"],"sourcesContent":["// `@eifi1/ui-kit/wizard` — the multi-step engine, its chrome and its review step.\n//\n// A re-slicing of the main barrel, not a new API. By default useWizard syncs the\n// active step to `?step=` via react-router's useSearchParams, so it needs a router;\n// `urlSync: false` keeps the step in memory and needs none.\n//\n// NOT the same thing as `WizardStepper` in components/wizard-stepper, which is a bare\n// two-step indicator with no engine and stays on the main barrel.\nexport * from \"./wizard/types\";\nexport * from \"./wizard/use-wizard\";\nexport * from \"./wizard/wizard-context\";\nexport * from \"./wizard/validation\";\nexport * from \"./wizard/wizard-step\";\nexport * from \"./wizard/wizard-summary\";\nexport * from \"./wizard/stepper-nav\";\n"],"mappings":";AAQA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eifi1/ui-kit",
3
- "version": "0.7.0",
3
+ "version": "0.8.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.",
@@ -1,8 +1,12 @@
1
- import type { ComponentPropsWithoutRef, ReactNode } from "react";
2
- import { AlertTriangle } from "lucide-react";
1
+ import type { ComponentPropsWithoutRef, MouseEvent, ReactNode } from "react";
2
+ import { AlertTriangle, ChevronRight, Info, X } from "lucide-react";
3
3
  import { cn } from "../lib/cn";
4
+ import { DEFAULT_COMMON_LABELS, useKitLabels } from "../i18n/kit-labels";
4
5
 
5
- export type AlertTone = "danger" | "warning" | "neutral";
6
+ /** `info` (0.8.0) is the sky family, for news that is neither good nor bad: keksdose's
7
+ * preview and beta-performance banners, which borrowed `warning` for want of it and
8
+ * so told every user of the preview build that something was wrong. */
9
+ export type AlertTone = "danger" | "warning" | "info" | "neutral";
6
10
 
7
11
  /** Border + surface per tone, WITHOUT a radius or padding, so a caller that owns
8
12
  * its own box (a Card, say) can take the tone alone. Single source for both
@@ -20,6 +24,7 @@ export type AlertTone = "danger" | "warning" | "neutral";
20
24
  const TONE_FRAME: Record<AlertTone, string> = {
21
25
  danger: "border-2 border-[var(--danger-border)] bg-[var(--danger-bg)]",
22
26
  warning: "border-2 border-[var(--warning-border)] bg-[var(--warning-bg)]",
27
+ info: "border-2 border-[var(--info-border)] bg-[var(--info-bg)]",
23
28
  neutral: "border border-[var(--border)]",
24
29
  };
25
30
 
@@ -38,38 +43,224 @@ export function alertFrameClass(tone: AlertTone): string {
38
43
  return `${box} ${TONE_FRAME[tone]}`;
39
44
  }
40
45
 
41
- const TONE_TEXT: Record<Exclude<AlertTone, "neutral">, string> = {
46
+ const TONE_TEXT: Record<AlertTone, string> = {
42
47
  danger: "text-[var(--danger)]",
43
48
  warning: "text-[var(--warning)]",
49
+ info: "text-[var(--info)]",
50
+ neutral: "text-[var(--text-secondary)]",
44
51
  };
45
52
 
46
- const TONE_ICON: Record<Exclude<AlertTone, "neutral">, string> = {
53
+ const TONE_ICON: Record<AlertTone, string> = {
47
54
  danger: "text-[var(--danger)]",
48
55
  warning: "text-[var(--warning)]",
56
+ info: "text-[var(--info)]",
57
+ neutral: "text-[var(--text-muted)]",
49
58
  };
50
59
 
51
- export interface AlertBannerProps extends ComponentPropsWithoutRef<"div"> {
52
- /** `neutral` is deliberately not offered: a callout with a warning triangle in it
53
- * and no colour is a warning that looks like a note, which is the one thing this
54
- * box must never be. The neutral frame is still available on its own through
55
- * {@link toneFrameClass}. */
56
- tone?: Exclude<AlertTone, "neutral">;
60
+ /** Which glyph a tone draws when the caller names none. The triangle belongs to the
61
+ * two tones that mean "something is wrong"; `info` and `neutral` get the ⓘ. That is
62
+ * what makes `neutral` safe to offer at all: until 0.8.0 it was withheld because a
63
+ * triangle with no colour is a warning that looks like a note — and a neutral note
64
+ * that draws no triangle is simply a note (lenkbank's "values are rounded"). */
65
+ const TONE_GLYPH: Record<AlertTone, typeof AlertTriangle> = {
66
+ danger: AlertTriangle,
67
+ warning: AlertTriangle,
68
+ info: Info,
69
+ neutral: Info,
70
+ };
71
+
72
+ interface AlertBannerBaseProps extends Omit<ComponentPropsWithoutRef<"div">, "onClick"> {
73
+ /** Default `danger`. `info` for news, `neutral` for a plain note — see
74
+ * {@link TONE_GLYPH} for why a neutral banner is now allowed. */
75
+ tone?: AlertTone;
76
+ /**
77
+ * `box` (default): the framed callout. `inline`: the tone's colour, glyph and role
78
+ * and NOTHING else — no border, no surface, no padding — so it fits inside a
79
+ * toolbar row beside the controls it is about. Lenkbank's corner-motion toolbar
80
+ * shows "Radius below the minimum" and "Solving…" there; a box would have doubled
81
+ * the row's height every time either appeared.
82
+ *
83
+ * The inline variant is also a LIVE REGION by default — `role="alert"` for
84
+ * `danger`, `role="status"` for the rest — because a message that appears in a
85
+ * toolbar in answer to what the user just did is exactly the kind a screen reader
86
+ * otherwise never hears. The box stays role-less as it always was: it is usually
87
+ * page furniture present at load, and an alert that fires on every navigation is
88
+ * noise. Either way a `role` you pass wins.
89
+ */
90
+ variant?: "box" | "inline";
91
+ /** Replaces the tone's glyph — a `<Spinner label={null} />` for "Solving…", say.
92
+ * `null` draws none. Always decorative; the text carries the message. */
93
+ icon?: ReactNode;
94
+ /** Renders an × at the end that calls this. The banner does not hide itself — the
95
+ * caller removes it (and remembers that it did), as with every other kit control
96
+ * whose state is app state. */
97
+ onDismiss?: () => void;
98
+ /** Accessible name of the ×. Default: `common.dismiss` from the
99
+ * {@link UiKitProvider}, else "Dismiss". */
100
+ dismissLabel?: string;
57
101
  children: ReactNode;
58
102
  }
59
103
 
60
- /** Static warning/danger callout box with the shared frame and an icon. */
61
- export function AlertBanner({ tone = "danger", className, children, ...rest }: AlertBannerProps) {
104
+ /**
105
+ * The whole-row modes. keksdose's budget-summary card is a banner you tap to open
106
+ * the budget; built from a `<div onClick>` it was a mouse-only target, and built as a
107
+ * banner with a link inside it was a small target inside a big one that looked like
108
+ * one. So `onClick` makes the banner itself ONE `<button>`, and `href` makes it one
109
+ * `<a>`, with a trailing chevron that says so. Mutually exclusive, as on `Chip`.
110
+ *
111
+ * With `onDismiss` as well, the × cannot go inside the button (interactive content
112
+ * may not nest), so the frame becomes a wrapper holding two siblings: the row's
113
+ * button, stretched over the whole frame, and the × on top of it. It still looks and
114
+ * clicks like one banner with an × in its corner, and the two are separate tab stops.
115
+ */
116
+ export type AlertBannerProps = AlertBannerBaseProps &
117
+ (
118
+ | {
119
+ /** Makes the whole banner one link. Mutually exclusive with `onClick`. */
120
+ href?: string;
121
+ onClick?: never;
122
+ }
123
+ | {
124
+ href?: never;
125
+ /** Makes the whole banner one button. Mutually exclusive with `href`. The
126
+ * event's element is the `<button>`, not a `<div>`. */
127
+ onClick?: (event: MouseEvent<HTMLElement>) => void;
128
+ }
129
+ );
130
+
131
+ /** Warning/danger/info callout box with the shared frame and an icon — or, with
132
+ * `variant="inline"`, the same message as a frameless line of toned text. */
133
+ export function AlertBanner({
134
+ tone = "danger",
135
+ variant = "box",
136
+ icon,
137
+ onDismiss,
138
+ dismissLabel,
139
+ href,
140
+ onClick,
141
+ className,
142
+ children,
143
+ role,
144
+ ...rest
145
+ }: AlertBannerProps) {
146
+ const common = useKitLabels("common", DEFAULT_COMMON_LABELS, { dismiss: dismissLabel });
147
+ const inline = variant === "inline";
148
+ const interactive = href !== undefined || onClick !== undefined;
149
+ const resolvedRole = role ?? (inline ? (tone === "danger" ? "alert" : "status") : undefined);
150
+
151
+ const Glyph = TONE_GLYPH[tone];
152
+ const glyph =
153
+ icon === null ? null : icon !== undefined ? (
154
+ <span aria-hidden className={cn("flex shrink-0 [&_svg]:size-4", !inline && "mt-0.5", TONE_ICON[tone])}>
155
+ {icon}
156
+ </span>
157
+ ) : (
158
+ <Glyph aria-hidden className={cn("size-4 shrink-0", !inline && "mt-0.5", TONE_ICON[tone])} />
159
+ );
160
+
161
+ // The row layout, shared by every shape below.
162
+ const row = inline ? "inline-flex items-center gap-1.5 text-sm" : "flex items-start gap-2 text-sm";
163
+ const frame = inline ? "" : alertFrameClass(tone);
164
+ const focusRing =
165
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)] focus-visible:ring-offset-1 focus-visible:ring-offset-[var(--bg-surface)]";
166
+
167
+ const content = (
168
+ <>
169
+ {glyph}
170
+ <span className={cn(!inline && "flex-1", "min-w-0 text-start")}>{children}</span>
171
+ {interactive && (
172
+ <ChevronRight
173
+ aria-hidden
174
+ className={cn("size-4 shrink-0 opacity-70 rtl:-scale-x-100", !inline && "mt-0.5")}
175
+ />
176
+ )}
177
+ </>
178
+ );
179
+
180
+ const dismiss = onDismiss ? (
181
+ <button
182
+ type="button"
183
+ aria-label={common.dismiss}
184
+ onClick={(e) => {
185
+ // A dismiss inside a whole-row banner must not also follow the row.
186
+ e.preventDefault();
187
+ e.stopPropagation();
188
+ onDismiss();
189
+ }}
190
+ className={cn(
191
+ // Negative margins so the × does not make the box taller than its text; the
192
+ // hover wash takes the tone's own hue, so it works on every tone.
193
+ "relative z-10 -my-0.5 -me-0.5 flex size-6 shrink-0 items-center justify-center rounded transition-colors hover:bg-current/10",
194
+ focusRing,
195
+ )}
196
+ >
197
+ <X aria-hidden className="size-4" />
198
+ </button>
199
+ ) : null;
200
+
201
+ if (!interactive) {
202
+ return (
203
+ <div
204
+ // Arbitrary attributes first — a `data-tour` anchor, a test id, an
205
+ // `aria-describedby` — then the layout and tone classes, which are merged
206
+ // through `cn` rather than spread so a caller's `className` refines the box
207
+ // instead of replacing it.
208
+ {...rest}
209
+ role={resolvedRole}
210
+ className={cn(row, frame, TONE_TEXT[tone], className)}
211
+ >
212
+ {content}
213
+ {dismiss}
214
+ </div>
215
+ );
216
+ }
217
+
218
+ const hover = "cursor-pointer transition-[filter] hover:brightness-[0.97] dark:hover:brightness-110";
219
+ // With a dismiss the frame is a wrapper and the row element is stretched over it;
220
+ // without one the row element IS the frame.
221
+ const split = dismiss !== null;
222
+ const actionClass = split
223
+ ? cn(
224
+ "flex min-w-0 flex-1 gap-2 text-start",
225
+ inline ? "items-center" : "items-start",
226
+ "outline-none after:absolute after:inset-0 after:rounded-md after:content-['']",
227
+ "focus-visible:after:ring-2 focus-visible:after:ring-[var(--brand)]",
228
+ )
229
+ : cn(row, !inline && "w-full", frame, TONE_TEXT[tone], hover, focusRing, className);
230
+ // The div's attributes are the row element's: an `id`, a test id or an
231
+ // `aria-describedby` belongs on the thing that takes focus. The live-region role is
232
+ // NOT carried over — a button or a link that is also a `status` is neither.
233
+ const actionRest = split ? {} : rest;
234
+
235
+ const action =
236
+ href !== undefined ? (
237
+ <a
238
+ {...(actionRest as ComponentPropsWithoutRef<"a">)}
239
+ href={href}
240
+ className={actionClass}
241
+ >
242
+ {content}
243
+ </a>
244
+ ) : (
245
+ <button
246
+ {...(actionRest as ComponentPropsWithoutRef<"button">)}
247
+ type="button"
248
+ onClick={onClick}
249
+ className={actionClass}
250
+ >
251
+ {content}
252
+ </button>
253
+ );
254
+
255
+ if (!split) return action;
62
256
  return (
63
257
  <div
64
- // Arbitrary attributes first — a `data-tour` anchor, a test id, an
65
- // `aria-describedby` — then the layout and tone classes, which are merged
66
- // through `cn` rather than spread so a caller's `className` refines the box
67
- // instead of replacing it.
68
258
  {...rest}
69
- className={cn("flex items-start gap-2 text-sm", alertFrameClass(tone), TONE_TEXT[tone], className)}
259
+ role={resolvedRole}
260
+ className={cn("relative", row, frame, TONE_TEXT[tone], hover, className)}
70
261
  >
71
- <AlertTriangle className={cn("mt-0.5 size-4 shrink-0", TONE_ICON[tone])} />
72
- <span className="flex-1">{children}</span>
262
+ {action}
263
+ {dismiss}
73
264
  </div>
74
265
  );
75
266
  }
@@ -0,0 +1,75 @@
1
+ import type { ComponentPropsWithoutRef } from "react";
2
+ import { cn } from "../lib/cn";
3
+
4
+ // Every class below is spelled out in full rather than assembled from parts: the
5
+ // consumer's Tailwind build finds classes by scanning this package's source as text,
6
+ // and a class that exists only after a template literal runs is never generated.
7
+ //
8
+ // Each rule is written twice, once for a direct child and once for a `<button>` one
9
+ // level down. The second is for a Tooltip-wrapped IconButton — the map zoom's + and −
10
+ // both carry a tooltip — where the direct child is the tooltip's wrapper span and the
11
+ // button that draws the border and the corners sits inside it.
12
+ const JOINED_COMMON = cn(
13
+ "inline-flex rounded-md border border-[var(--border)]",
14
+ // The group draws the frame; each member gives up its own border, radius and shadow.
15
+ "[&>*]:rounded-none [&>*]:border-0 [&>*]:shadow-none",
16
+ "[&>*>button]:rounded-none [&>*>button]:border-0 [&>*>button]:shadow-none",
17
+ "[&>*]:border-[var(--border)]",
18
+ // A focused member rises above its neighbours so its ring is not painted over by
19
+ // the next one's background.
20
+ "[&>*]:relative [&>*:focus-within]:z-10",
21
+ );
22
+
23
+ const JOINED: Record<"horizontal" | "vertical", string> = {
24
+ // Logical edges and corners throughout (`border-s`, `rounded-s-*`), so in RTL the
25
+ // first member sits on the right with the right-hand corners rounded — the physical
26
+ // pair would round the outside of the last button and square off the first.
27
+ horizontal: cn(
28
+ JOINED_COMMON,
29
+ "flex-row items-stretch",
30
+ "[&>*:not(:first-child)]:border-s",
31
+ "[&>:first-child]:rounded-s-md [&>:last-child]:rounded-e-md",
32
+ "[&>:first-child>button]:rounded-s-md [&>:last-child>button]:rounded-e-md",
33
+ ),
34
+ // The block axis is top-to-bottom in both reading directions, so top/bottom are
35
+ // already the logical edges here.
36
+ vertical: cn(
37
+ JOINED_COMMON,
38
+ "flex-col items-stretch",
39
+ "[&>*:not(:first-child)]:border-t",
40
+ "[&>:first-child]:rounded-t-md [&>:last-child]:rounded-b-md",
41
+ "[&>:first-child>button]:rounded-t-md [&>:last-child>button]:rounded-b-md",
42
+ ),
43
+ };
44
+
45
+ export interface ButtonGroupProps extends Omit<ComponentPropsWithoutRef<"div">, "role"> {
46
+ /** `horizontal` (default): side by side, in reading order. `vertical`: stacked —
47
+ * the map's zoom + over −. */
48
+ orientation?: "horizontal" | "vertical";
49
+ }
50
+
51
+ /**
52
+ * Adjacent `Button`s or `IconButton`s drawn as one joined control: a single frame,
53
+ * hairline dividers between the members, rounded only at the outer corners.
54
+ *
55
+ * keksdose asked for it twice — "collapse all / expand all" above its category
56
+ * tree, and the + and − of its map zoom — both hand-built with negative margins and
57
+ * physical `rounded-l` / `rounded-r`, which came out inside-out under `dir="rtl"`.
58
+ *
59
+ * `role="group"` with a name: a reader then introduces the pair as "Zoom, group"
60
+ * before reading "Zoom in", which is what tells a listener that the next button
61
+ * belongs with this one. Pass `aria-label` (or `aria-labelledby`); a group with no
62
+ * name is one more unlabelled wrapper. It is NOT a toolbar — it adds no arrow-key
63
+ * roving, and every member keeps its own tab stop, as plain adjacent buttons do.
64
+ * For one-of-many selection use `ToggleGroup`, which has the radio semantics.
65
+ */
66
+ export function ButtonGroup({ orientation = "horizontal", className, ...rest }: ButtonGroupProps) {
67
+ return (
68
+ <div
69
+ {...rest}
70
+ role="group"
71
+ data-orientation={orientation}
72
+ className={cn(JOINED[orientation], className)}
73
+ />
74
+ );
75
+ }