@keboola/design 4.3.2 → 5.0.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.
- package/AGENTS.md +68 -43
- package/COMPONENT-API.md +35 -6
- package/README.md +1 -1
- package/dist/charts.cjs +268 -0
- package/dist/charts.cjs.map +1 -0
- package/dist/charts.d.cts +181 -0
- package/dist/charts.d.ts +181 -0
- package/dist/charts.js +259 -0
- package/dist/charts.js.map +1 -0
- package/dist/editors.cjs +151 -77
- package/dist/editors.cjs.map +1 -1
- package/dist/editors.js +153 -79
- package/dist/editors.js.map +1 -1
- package/dist/index.cjs +2610 -1537
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +486 -467
- package/dist/index.d.ts +486 -467
- package/dist/index.js +2337 -1273
- package/dist/index.js.map +1 -1
- package/dist/markdown-editor.cjs +155 -119
- package/dist/markdown-editor.cjs.map +1 -1
- package/dist/markdown-editor.js +155 -119
- package/dist/markdown-editor.js.map +1 -1
- package/dist/mermaid.cjs +62 -36
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.js +63 -37
- package/dist/mermaid.js.map +1 -1
- package/dist/rjsf.cjs +10294 -0
- package/dist/rjsf.cjs.map +1 -0
- package/dist/rjsf.d.cts +55 -0
- package/dist/rjsf.d.ts +55 -0
- package/dist/rjsf.js +10256 -0
- package/dist/rjsf.js.map +1 -0
- package/dist/styles.css +4 -2
- package/dist/testing.cjs +238 -111
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +6 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.js +240 -113
- package/dist/testing.js.map +1 -1
- package/package.json +51 -16
- package/src/styles.css +72 -30
package/AGENTS.md
CHANGED
|
@@ -19,16 +19,18 @@ Working **on** the package (commands, layer boundary, token surface, the FA→Lu
|
|
|
19
19
|
|
|
20
20
|
The root barrel (`@keboola/design`) is **not** the whole package. These live only at their subpath — an agent scanning the barrel (or `dist/index.d.ts`) misses them and wrongly concludes the capability is absent. The "always import from the package root" rule above holds for everything in the barrel; the rows below are the **deliberate exceptions** — import them from the subpath, not the root.
|
|
21
21
|
|
|
22
|
-
| Subpath | Exports
|
|
23
|
-
| --------------------------------- |
|
|
24
|
-
| `@keboola/design` | Components, blocks, hooks, `ThemeProvider`, forms
|
|
25
|
-
| `@keboola/design/icons` | Lucide re-export + Keboola-custom SVG icons
|
|
26
|
-
| `@keboola/design/editors` | `CodeEditor`, `SqlEditor`, `MergeEditor`
|
|
27
|
-
| `@keboola/design/
|
|
28
|
-
| `@keboola/design/
|
|
29
|
-
| `@keboola/design/
|
|
30
|
-
| `@keboola/design/
|
|
31
|
-
| `@keboola/design/
|
|
22
|
+
| Subpath | Exports | Reach for it when |
|
|
23
|
+
| --------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
|
24
|
+
| `@keboola/design` | Components, blocks, hooks, `ThemeProvider`, forms | Default — everything below the listed exceptions |
|
|
25
|
+
| `@keboola/design/icons` | Lucide re-export + Keboola-custom SVG icons | Any icon (`import { ChevronDown } from '@keboola/design/icons'`) |
|
|
26
|
+
| `@keboola/design/editors` | `CodeEditor`, `SqlEditor`, `MergeEditor` | Real code editing — pulls the ~1.3 MB CodeMirror bundle |
|
|
27
|
+
| `@keboola/design/charts` | `LineChart`, `BarChart`, `AreaChart`, `PieChart`, `ChartContainer` | Data-viz — themed recharts wrappers; **not** in the root barrel |
|
|
28
|
+
| `@keboola/design/mermaid` | `MermaidDiagram` (`{ code }`) | Render a Mermaid diagram — **not** in the root barrel |
|
|
29
|
+
| `@keboola/design/markdown-editor` | `MarkdownEditor` | Markdown editing — **not** in the root barrel |
|
|
30
|
+
| `@keboola/design/rjsf` | `RjsfSchemaEditor`, `useRootFormData` | JSON-Schema-driven config forms (RJSF) — pulls `@rjsf/*` + ajv |
|
|
31
|
+
| `@keboola/design/testing` | `renderWithTheme` | Unit tests that need a component wrapped in `ThemeProvider` |
|
|
32
|
+
| `@keboola/design/styles` | Tailwind-extending stylesheet | App entry, Tailwind consumers (**default**) |
|
|
33
|
+
| `@keboola/design/styles/compiled` | Pre-compiled CSS (~176 KB) | App entry, when you cannot run Tailwind |
|
|
32
34
|
|
|
33
35
|
### design ≠ shadcn — API deltas
|
|
34
36
|
|
|
@@ -62,16 +64,17 @@ This file answers **which** component to use; [`COMPONENT-API.md`](./COMPONENT-A
|
|
|
62
64
|
|
|
63
65
|
### Overlays
|
|
64
66
|
|
|
65
|
-
| Use | When
|
|
66
|
-
| -------------- |
|
|
67
|
-
| `Modal` | Blocking dialog
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
67
|
+
| Use | When |
|
|
68
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| `Modal` | Blocking dialog for a form or detail view (use `AlertModal` for confirmations) |
|
|
70
|
+
| `AlertModal` | Confirmation / destructive-action dialog — owns the confirm + cancel buttons and is async-aware (stays open, disables the action, and shows a spinner until `onConfirm` resolves) |
|
|
71
|
+
| `Sheet` | Slide-in panel for contextual editing without leaving the page |
|
|
72
|
+
| `Popconfirm` | Lightweight inline confirmation anchored to the trigger (delete actions) |
|
|
73
|
+
| `Popover` | Non-modal floating panel for arbitrary rich content |
|
|
74
|
+
| `HoverCard` | Read-only preview panel on hover; no interactive controls |
|
|
75
|
+
| `DropdownMenu` | Contextual list of actions or navigation items on click |
|
|
73
76
|
|
|
74
|
-
**Never** use `Popover` for destructive confirmations (use `Popconfirm`) or hover-only previews (use `HoverCard`).
|
|
77
|
+
**Never** use `Popover` for destructive confirmations (use `Popconfirm`) or hover-only previews (use `HoverCard`). **Never** hand-roll a confirmation from `Modal` + buttons — use `AlertModal`, which owns the confirm/cancel actions and the async loading state.
|
|
75
78
|
|
|
76
79
|
### Inputs
|
|
77
80
|
|
|
@@ -126,6 +129,20 @@ This file answers **which** component to use; [`COMPONENT-API.md`](./COMPONENT-A
|
|
|
126
129
|
|
|
127
130
|
**Never** reach for raw `<ul>/<li>` for entities — use `Table` for columnar data.
|
|
128
131
|
|
|
132
|
+
### Data visualization
|
|
133
|
+
|
|
134
|
+
Charts ship from the **`@keboola/design/charts`** subpath (recharts under the hood — kept out of the root barrel). Series colors come from the semantic `--color-chart-1..5` palette automatically, so charts follow both the active color scheme (light/dark) and the active brand — **never** pass a hex/rgb color. A brand reskins charts by defining `colors.chart` / `colorsDark.chart` (emitted by `ThemeProvider`); brands that leave it unset use the shared palette.
|
|
135
|
+
|
|
136
|
+
| Use | When |
|
|
137
|
+
| ---------------- | ------------------------------------------------------------------------ |
|
|
138
|
+
| `LineChart` | Trend of one or more series over an ordered category (time) |
|
|
139
|
+
| `BarChart` | Compare discrete categories; `stacked` for part-to-whole over time |
|
|
140
|
+
| `AreaChart` | Trend with volume emphasis; `stacked` for cumulative composition |
|
|
141
|
+
| `PieChart` | Share of a whole across a few categories; `innerRadius` makes a donut |
|
|
142
|
+
| `ChartContainer` | Wrap a raw recharts chart to inherit the themed axis/grid/tooltip chrome |
|
|
143
|
+
|
|
144
|
+
`data: {…}[]` rows; `xKey` + `series: [{ dataKey, name?, color? }]` (cartesian) or `nameKey` + `dataKey` (pie). Each chart is self-contained (mounts its own responsive container) — give it a `height` (default 300). Pin a series to a palette slot with `color: 1..5`; otherwise it follows series order.
|
|
145
|
+
|
|
129
146
|
### Layout
|
|
130
147
|
|
|
131
148
|
**No** `Stack` / `Flex` / `Box` component exists, by design — shadcn doesn't ship them, and adding wrappers creates a third way to do layout. Use Tailwind utility classes directly.
|
|
@@ -196,7 +213,7 @@ The `label` prop implies `orientation="horizontal"` — labels are not rendered
|
|
|
196
213
|
|
|
197
214
|
## Composition recipes
|
|
198
215
|
|
|
199
|
-
The platform ships **no** `EntityList` / `ModalForm`
|
|
216
|
+
The platform ships **no** `EntityList` / `ModalForm` wrapper — these are compositions, not components. Copy the recipe; see [`COMPONENT-API.md`](./COMPONENT-API.md) for the exact props. (Confirmations are the exception — they _are_ a shipped component: use `AlertModal`, don't hand-roll one.)
|
|
200
217
|
|
|
201
218
|
**Modal form** — `Modal` + `useAppForm`. The submit button lives in the footer and targets the form by `id`:
|
|
202
219
|
|
|
@@ -238,29 +255,23 @@ const form = useAppForm({
|
|
|
238
255
|
</Modal>;
|
|
239
256
|
```
|
|
240
257
|
|
|
241
|
-
**Confirm dialog** — `Modal
|
|
258
|
+
**Confirm dialog** — use `AlertModal`, **not** a hand-rolled `Modal`. It owns the confirm/cancel buttons and is async-aware: return a promise from `onConfirm` and it stays open, disables the action, locks Escape, and shows a spinner until it resolves, then closes. `variant` sets the status colour + icon (`info` / `warning` / `danger` / `success`); the children are the description.
|
|
242
259
|
|
|
243
260
|
```tsx
|
|
244
|
-
<
|
|
245
|
-
<
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
</Modal.Close>
|
|
255
|
-
<Button type="button" variant="danger" isLoading={isDeleting} onClick={handleConfirm}>
|
|
256
|
-
Delete bucket
|
|
257
|
-
</Button>
|
|
258
|
-
</ButtonGroup>
|
|
259
|
-
</Modal.Footer>
|
|
260
|
-
</Modal.Content>
|
|
261
|
-
</Modal>
|
|
261
|
+
<AlertModal onConfirm={() => deleteBucket(bucketId)}>
|
|
262
|
+
<AlertModal.Trigger asChild>
|
|
263
|
+
<Button variant="danger">
|
|
264
|
+
<Trash2 className="tw-size-4" /> Delete bucket
|
|
265
|
+
</Button>
|
|
266
|
+
</AlertModal.Trigger>
|
|
267
|
+
<AlertModal.Content variant="danger" title="Delete bucket?" confirmLabel="Delete bucket">
|
|
268
|
+
This action cannot be undone.
|
|
269
|
+
</AlertModal.Content>
|
|
270
|
+
</AlertModal>
|
|
262
271
|
```
|
|
263
272
|
|
|
273
|
+
Controlled (opened from elsewhere, no `Trigger`): pass `open` + `onOpenChange` to `AlertModal` (it honors `defaultOpen` too).
|
|
274
|
+
|
|
264
275
|
**Card with empty state** — branch on the collection length inside `Card.Content`:
|
|
265
276
|
|
|
266
277
|
```tsx
|
|
@@ -326,7 +337,7 @@ const form = useAppForm({
|
|
|
326
337
|
</Card>
|
|
327
338
|
```
|
|
328
339
|
|
|
329
|
-
**Alert with action** — pass the action node to `Alert`'s `actions` prop; the variant
|
|
340
|
+
**Alert with action** — pass the action node to `Alert`'s `actions` prop; the variant reads the themeable status tokens (see brand-neutrality contract):
|
|
330
341
|
|
|
331
342
|
```tsx
|
|
332
343
|
<Alert
|
|
@@ -434,13 +445,15 @@ import { ChevronDown, Keboola, Loader2 } from '@keboola/design/icons';
|
|
|
434
445
|
|
|
435
446
|
## Brand-neutrality contract (Layer 0)
|
|
436
447
|
|
|
437
|
-
Layer-0
|
|
448
|
+
Layer-0 colour usage splits into three buckets.
|
|
438
449
|
|
|
439
450
|
**Chrome — follows the brand.** Surface, structure, navigation: Card body, Modal frame, Input border, Button outline, focus rings, Sidebar, generic text. Re-skins when a customer brand is applied. **Rule:** use semantic tokens (`tw-bg-background`, `tw-text-foreground`, `tw-border-border`, `tw-bg-muted`, `tw-text-muted-foreground`, `tw-bg-accent`, `tw-bg-destructive`, …). Customers override these via `@keboola/brand-registry`.
|
|
440
451
|
|
|
441
|
-
**
|
|
452
|
+
**Status — themeable under argo, with AA-legible defaults.** The four semantic states — `success`, `warning`, `info`, and `destructive` (used for "error") — are brand-overridable tokens, not fixed scales. Alert `variant`, `HelpBlock` / field-error state, `ErrorState`, and `Stepper` status read them, so a brand recolours every status surface at once. **Rule:** consume the token as **tint + text/icon** — `tw-bg-<role>/10` for the surface, `tw-text-<role>` / `tw-border-<role>/25` for the mark. The default token values ship dark enough to be AA (≥4.5:1) as text/icon on their own tint, in light **and** dark mode — a brand overriding them should preserve that. Avoid solid fills with a white foreground (`tw-bg-<role>` + white): the token flips light in dark mode, so white-on-token fails there. A brand _can_ recolour "error" away from red — that is now allowed; the accessible-on-tint contract is what must hold, not the hue. **Legacy exception:** the themeable treatment is gated to argo (`data-[argo=true]:` / `group-data-[argo=true]/…:`). Under legacy chrome (`useArgoDesign()` → `false`) Alert and `ErrorState` keep their original fixed status scales (`blue-*`/`green-*`/`warning-*`/`error-*`), and the legacy brand retains its original status token hues — so legacy status colour is unchanged.
|
|
442
453
|
|
|
443
|
-
**
|
|
454
|
+
**Decorative — stays fixed across brands.** The non-semantic palette that encodes category rather than state: `Badge` decorative colours (`purple`, `cyan`, `teal`, `blue`, …), chart colours, decorative tag palette. **Rule:** keep using primitive scales (`purple-500`, `cyan-500`, …). These are an identity/category contract with the consumer and do not re-skin.
|
|
455
|
+
|
|
456
|
+
**Heuristic.** A `variant` / `status` prop encoding one of the four states → **status token**. A `color` prop picking an arbitrary hue for categorisation → **decorative, fixed scale**. Everything else → **chrome, semantic token**.
|
|
444
457
|
|
|
445
458
|
Layer-1 blocks (`src/blocks/`) are allowed to be Keboola-opinionated — the contract only applies to Layer-0 primitives.
|
|
446
459
|
|
|
@@ -487,6 +500,18 @@ export default defineConfig({
|
|
|
487
500
|
|
|
488
501
|
You do **not** need `test.css: true` or `server.deps.inline: [/@keboola\/design/, /react-day-picker/]`. The design system's structural CSS ships in the stylesheet (`@keboola/design/styles`), not as JS-side `.css` side-effect imports, so the barrel carries no raw `.css` import for Node to choke on. Wrap components in the brand context with `renderWithTheme` from `@keboola/design/testing`.
|
|
489
502
|
|
|
503
|
+
## Verify the rendered result
|
|
504
|
+
|
|
505
|
+
Picking the right component and passing type-check proves the code is well-formed — not that the page renders, is accessible, or re-skins under a customer brand. After you build a page with these components, run the platform verify loop and iterate to green **before** a PR — don't hand-roll a bespoke axe/screenshot harness:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
pnpm add -D @keboola/ui-gen-bench @keboola/validate-ui
|
|
509
|
+
pnpm exec playwright install chromium # once
|
|
510
|
+
pnpm exec ui-gen-bench eval --serve dist --route /tokens --brief "Tokens list with empty state"
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
`ui-gen-bench eval` boots the route in a headless browser, scores it 0–100 across runtime-health / a11y / visual-brand / brief-conformance, and exits 0 when green. For raw per-axis findings instead of one score, run `validate-ui` directly. See [`@keboola/ui-gen-bench`](https://www.npmjs.com/package/@keboola/ui-gen-bench) and [`@keboola/validate-ui`](https://www.npmjs.com/package/@keboola/validate-ui).
|
|
514
|
+
|
|
490
515
|
## Common Tailwind patterns
|
|
491
516
|
|
|
492
517
|
```
|
package/COMPONENT-API.md
CHANGED
|
@@ -364,13 +364,28 @@ _No own props — see note above for passthrough attributes._
|
|
|
364
364
|
|
|
365
365
|
Source: `src/components/ThemeProvider/ThemeProvider.tsx`
|
|
366
366
|
|
|
367
|
-
> Wrap the app root.
|
|
367
|
+
> Wrap the app root once. Resolves the active brand from `brands` by `brandId` (or `defaultBrandId` when uncontrolled); with neither set it falls back to the design-system defaults (no brand tokens injected). `scheme` sets light/dark/system (default `light`). Pair with `useTheme()` to read/flip the brand or scheme at runtime. Mounts a `TooltipProvider` around its subtree, so a `Tooltip` works without a separately-mounted provider.
|
|
368
368
|
|
|
369
|
-
| Prop
|
|
370
|
-
|
|
|
371
|
-
| `
|
|
372
|
-
| `
|
|
373
|
-
| `
|
|
369
|
+
| Prop | Type | Required | Default | Description |
|
|
370
|
+
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
371
|
+
| `brandId` | `string` | no | — | Controlled active brand id. When set, the parent owns the value and `setBrand` only notifies via `onBrandIdChange` (it does not mutate internal state). |
|
|
372
|
+
| `brands` | `readonly { id: string; name: string; theme: { colors?: { foreground?: string; background?: string; muted?: string \| undefined; mutedForeground?: string \| undefined; border?: string \| undefined; ... 22 more ...; sidebar?: { ...; } \| undefined; } \| undefined; ... 6 more ...; gradient?: { ...; }...` | no | `[]` | Brands this provider knows about; the active brand id resolves against these. |
|
|
373
|
+
| `defaultBrandId` | `string` | no | — | Initial active brand id for uncontrolled use. Defaults to the design-system sentinel (`DEFAULT_BRAND_ID`) — no brand tokens injected, so the stylesheet defaults apply. |
|
|
374
|
+
| `defaultScheme` | `ColorScheme` | no | — | Initial colour scheme for uncontrolled use. Defaults to `light` (`DEFAULT_COLOR_SCHEME`). |
|
|
375
|
+
| `onBrandIdChange` | `((brandId: string) => void)` | no | — | Called with the next id whenever `setBrand` runs — required to persist the choice in controlled mode. |
|
|
376
|
+
| `onSchemeChange` | `((theme: ColorScheme) => void)` | no | — | Called with the next scheme whenever `setScheme` runs — required to persist the choice in controlled mode. |
|
|
377
|
+
| `scheme` | `ColorScheme` | no | — | Controlled colour scheme (`light` / `dark` / `system`). When set, the parent owns the value and `setScheme` only notifies via `onSchemeChange`. `system` follows `prefers-color-scheme` and resolves reactively. |
|
|
378
|
+
|
|
379
|
+
### `BrandLogo`
|
|
380
|
+
|
|
381
|
+
Source: `src/components/BrandLogo/BrandLogo.tsx`
|
|
382
|
+
|
|
383
|
+
> Renders the active brand's logo from the nearest `<ThemeProvider>`. `variant` is `wordmark` (default) | `mark`; each falls back to the other and light↔dark. Inline SVG (a `currentColor` mark inherits text colour) or `<img>` per source. Size via `className` height. `Sidebar.Header` / `Navbar.Header` render it by default when given no children.
|
|
384
|
+
|
|
385
|
+
| Prop | Type | Required | Default | Description |
|
|
386
|
+
| ----------- | ---------------------- | -------- | ---------- | -------------------------------------------------------------------------------------- |
|
|
387
|
+
| `className` | `string` | no | — | Sets the logo height; aspect ratio is preserved (e.g. `tw-h-7`). |
|
|
388
|
+
| `variant` | `"mark" \| "wordmark"` | no | `wordmark` | `wordmark` (default, full logo) or `mark` (square icon); each falls back to the other. |
|
|
374
389
|
|
|
375
390
|
## Layout & shell
|
|
376
391
|
|
|
@@ -499,6 +514,20 @@ Source: `src/components/Progress/Progress.tsx`
|
|
|
499
514
|
| `size` | `"sm" \| "lg" \| null` | no | — | |
|
|
500
515
|
| `variant` | `"success" \| "default" \| null` | no | — | |
|
|
501
516
|
|
|
517
|
+
### `SpotlightBackdrop`
|
|
518
|
+
|
|
519
|
+
Source: `src/components/SpotlightBackdrop/SpotlightBackdrop.tsx`
|
|
520
|
+
|
|
521
|
+
> Dimmed full-viewport layer with an SVG-mask cut-out around a target rect (spotlight). Presentational — pass a `rect` from the `useTargetRect(target, enabled)` hook and compose with `Popover` for an anchored callout. Tour / coachmark orchestration (steps, next/prev, "seen") is app-owned; see the "Guided tour (recipe)" story.
|
|
522
|
+
|
|
523
|
+
| Prop | Type | Required | Default | Description |
|
|
524
|
+
| ----------- | ----------------------------------- | -------- | ------- | -------------------------------------------------------------------------------- |
|
|
525
|
+
| `rect` | `TargetRect \| null` | yes | — | Target box to cut out of the dim layer, or `null` for a full dim (no highlight). |
|
|
526
|
+
| `className` | `string` | no | — | |
|
|
527
|
+
| `onClick` | `MouseEventHandler<HTMLDivElement>` | no | — | Fired when the dim layer is clicked (e.g. to dismiss). Receives the click event. |
|
|
528
|
+
| `padding` | `number` | no | `8` | Padding around the target inside the cut-out, in px. |
|
|
529
|
+
| `radius` | `number` | no | `8` | Corner radius of the cut-out, in px. |
|
|
530
|
+
|
|
502
531
|
## Data display — layout
|
|
503
532
|
|
|
504
533
|
### `Table`
|
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ Rules of the road:
|
|
|
114
114
|
|
|
115
115
|
Worked examples: `src/components/Button/Button.stories.tsx` (click + disabled), `src/components/Combobox/Combobox.stories.tsx` (open → filter → select), `src/components/Modal/Modal.stories.tsx` (open → focus → close).
|
|
116
116
|
|
|
117
|
-
**Coverage so far:** the interactive surface is covered — Button, ButtonInline, Combobox, MultiCombobox, CreatableMultiCombobox, Select, Modal, Sheet, Popover, Popconfirm, DropdownMenu, SplitButton, Tooltip, HoverCard, InfoTooltip, Switch, Checkbox, RadioGroup, Slider, Tabs, PageHeaderTabs, Collapsible, PanelWithDetails, Toast, TextInput, NumberInput, SearchInput, SearchInputWithNavigation, PasswordInput, Textarea, AutoSaveTextarea, AlertWithAction, ListDetail, FilterToolbar
|
|
117
|
+
**Coverage so far:** the interactive surface is covered — Button, ButtonInline, Combobox, MultiCombobox, CreatableMultiCombobox, Select, Modal, Sheet, Popover, Popconfirm, DropdownMenu, SplitButton, Tooltip, HoverCard, InfoTooltip, Switch, Checkbox, RadioGroup, Slider, Tabs, PageHeaderTabs, Collapsible, PanelWithDetails, Toast, TextInput, NumberInput, SearchInput, SearchInputWithNavigation, PasswordInput, Textarea, AutoSaveTextarea, AlertWithAction, ListDetail, FilterToolbar. Add a `play()` to new interactive stories by default.
|
|
118
118
|
|
|
119
119
|
**Known gaps (not yet driven headlessly):** `Command` (cmdk input value isn't reflected on the DOM node under the test runner), `CollapsiblePanel` (collapses via CSS height, so `toBeVisible()` can't see the state change), `Calendar`/`DateInput`/`TimeInput` (day/segment targeting is brittle), `ColorInput`, `Clipboard`/`ClipboardButton` (clipboard API), and `MultiActionButton`/`Tagger` (stories use `alert()` / `onClick` side effects that block a headless run). `ModalForm` currently throws `formContext only works within a formComponent` when its modal opens (`FormBody` is outside `form.AppForm`) — a story-wiring fix, tracked separately. `Search` is deprecated and intentionally untested.
|
|
120
120
|
|
package/dist/charts.cjs
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var recharts = require('recharts');
|
|
4
|
+
var clsx = require('clsx');
|
|
5
|
+
var tailwindMerge = require('tailwind-merge');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
// src/blocks/Charts/ChartContainer.tsx
|
|
9
|
+
var twMerge = tailwindMerge.extendTailwindMerge({
|
|
10
|
+
prefix: "tw-"
|
|
11
|
+
});
|
|
12
|
+
var cn = (...classes) => twMerge(clsx.clsx(...classes));
|
|
13
|
+
var ChartContainer = ({
|
|
14
|
+
height = 300,
|
|
15
|
+
width = "100%",
|
|
16
|
+
className,
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
className: cn(
|
|
23
|
+
"tw-text-xs",
|
|
24
|
+
// Axis ticks and labels follow the muted foreground token.
|
|
25
|
+
"[&_.recharts-cartesian-axis-tick_text]:tw-fill-muted-foreground",
|
|
26
|
+
"[&_.recharts-polar-angle-axis-tick_text]:tw-fill-muted-foreground",
|
|
27
|
+
"[&_.recharts-label]:tw-fill-muted-foreground",
|
|
28
|
+
"[&_.recharts-legend-item-text]:!tw-text-muted-foreground",
|
|
29
|
+
// Grid lines, axis lines and the hover cursor follow the border token.
|
|
30
|
+
"[&_.recharts-cartesian-grid_line]:tw-stroke-border/60",
|
|
31
|
+
"[&_.recharts-cartesian-axis-line]:tw-stroke-border",
|
|
32
|
+
"[&_.recharts-cartesian-axis-tick-line]:tw-stroke-border",
|
|
33
|
+
"[&_.recharts-curve.recharts-tooltip-cursor]:tw-stroke-border",
|
|
34
|
+
"[&_.recharts-rectangle.recharts-tooltip-cursor]:tw-fill-muted/60",
|
|
35
|
+
"[&_.recharts-surface]:tw-outline-none",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
38
|
+
...props,
|
|
39
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width, height, children })
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
var ChartTooltipContent = ({
|
|
43
|
+
active,
|
|
44
|
+
label,
|
|
45
|
+
payload,
|
|
46
|
+
valueFormatter,
|
|
47
|
+
className
|
|
48
|
+
}) => {
|
|
49
|
+
if (!active || !payload?.length) return null;
|
|
50
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51
|
+
"div",
|
|
52
|
+
{
|
|
53
|
+
className: cn(
|
|
54
|
+
"tw-min-w-32 tw-rounded-md tw-border tw-border-border tw-bg-popover tw-px-2.5 tw-py-1.5 tw-text-xs tw-text-popover-foreground tw-shadow-md",
|
|
55
|
+
className
|
|
56
|
+
),
|
|
57
|
+
children: [
|
|
58
|
+
label != null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tw-mb-1 tw-font-medium", children: label }),
|
|
59
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tw-flex tw-flex-col tw-gap-1", children: payload.map((entry) => {
|
|
60
|
+
const name = String(entry.name ?? entry.dataKey ?? "");
|
|
61
|
+
const value = entry.value;
|
|
62
|
+
const hasNumber = value != null && value !== "" && Number.isFinite(Number(value));
|
|
63
|
+
const display = hasNumber ? valueFormatter ? valueFormatter(Number(value), name) : String(Number(value)) : value ?? "";
|
|
64
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
65
|
+
"div",
|
|
66
|
+
{
|
|
67
|
+
className: "tw-flex tw-items-center tw-justify-between tw-gap-3",
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tw-flex tw-items-center tw-gap-1.5", children: [
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
className: "tw-size-2 tw-shrink-0 tw-rounded-[2px]",
|
|
74
|
+
style: { backgroundColor: entry.color }
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tw-text-muted-foreground", children: name })
|
|
78
|
+
] }),
|
|
79
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tw-font-medium tw-tabular-nums", children: display })
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
String(entry.dataKey ?? name)
|
|
83
|
+
);
|
|
84
|
+
}) })
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
var ChartTooltip = ({ valueFormatter, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
90
|
+
recharts.Tooltip,
|
|
91
|
+
{
|
|
92
|
+
cursor: { strokeWidth: 1 },
|
|
93
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(ChartTooltipContent, { valueFormatter }),
|
|
94
|
+
...props
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// src/blocks/Charts/format.ts
|
|
99
|
+
var formatValue = (value, valueFormatter) => {
|
|
100
|
+
if (value == null || value === "") return "";
|
|
101
|
+
const num = Number(value);
|
|
102
|
+
if (!Number.isFinite(num)) return "";
|
|
103
|
+
return valueFormatter ? valueFormatter(num) : String(num);
|
|
104
|
+
};
|
|
105
|
+
var AXIS_PROPS = {
|
|
106
|
+
tickLine: false,
|
|
107
|
+
axisLine: false,
|
|
108
|
+
tick: { fontSize: 11 }
|
|
109
|
+
};
|
|
110
|
+
var cartesianChrome = ({
|
|
111
|
+
xKey,
|
|
112
|
+
showGrid = true,
|
|
113
|
+
showYAxis = true,
|
|
114
|
+
showLegend = false,
|
|
115
|
+
showTooltip = true,
|
|
116
|
+
valueFormatter
|
|
117
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
118
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: xKey, ...AXIS_PROPS }),
|
|
120
|
+
showYAxis && /* @__PURE__ */ jsxRuntime.jsx(
|
|
121
|
+
recharts.YAxis,
|
|
122
|
+
{
|
|
123
|
+
width: 48,
|
|
124
|
+
...AXIS_PROPS,
|
|
125
|
+
tickFormatter: valueFormatter ? (value) => formatValue(value, valueFormatter) : void 0
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
129
|
+
ChartTooltip,
|
|
130
|
+
{
|
|
131
|
+
valueFormatter: valueFormatter ? (value) => formatValue(value, valueFormatter) : void 0
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { wrapperStyle: { fontSize: 11 } })
|
|
135
|
+
] });
|
|
136
|
+
|
|
137
|
+
// src/blocks/Charts/theme.ts
|
|
138
|
+
var CHART_TOKEN_COUNT = 5;
|
|
139
|
+
var chartTokenColor = (index) => {
|
|
140
|
+
const slot = (index % CHART_TOKEN_COUNT + CHART_TOKEN_COUNT) % CHART_TOKEN_COUNT + 1;
|
|
141
|
+
return `rgb(var(--color-chart-${slot}))`;
|
|
142
|
+
};
|
|
143
|
+
var resolveSeriesColor = (index, token) => chartTokenColor(token ? token - 1 : index);
|
|
144
|
+
var LineChart = ({
|
|
145
|
+
data,
|
|
146
|
+
xKey,
|
|
147
|
+
series,
|
|
148
|
+
height,
|
|
149
|
+
width,
|
|
150
|
+
className,
|
|
151
|
+
...chrome
|
|
152
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { height, width, className, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data, margin: { top: 8, right: 8, left: 0, bottom: 0 }, children: [
|
|
153
|
+
cartesianChrome({ xKey, ...chrome }),
|
|
154
|
+
series.map((s, index) => {
|
|
155
|
+
const color = resolveSeriesColor(index, s.color);
|
|
156
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
157
|
+
recharts.Line,
|
|
158
|
+
{
|
|
159
|
+
type: "monotone",
|
|
160
|
+
dataKey: s.dataKey,
|
|
161
|
+
name: s.name ?? s.dataKey,
|
|
162
|
+
stroke: color,
|
|
163
|
+
strokeWidth: 2,
|
|
164
|
+
dot: { r: 3, fill: color, stroke: color },
|
|
165
|
+
activeDot: { r: 4 },
|
|
166
|
+
isAnimationActive: false
|
|
167
|
+
},
|
|
168
|
+
s.dataKey
|
|
169
|
+
);
|
|
170
|
+
})
|
|
171
|
+
] }) });
|
|
172
|
+
var BarChart = ({
|
|
173
|
+
data,
|
|
174
|
+
xKey,
|
|
175
|
+
series,
|
|
176
|
+
height,
|
|
177
|
+
width,
|
|
178
|
+
className,
|
|
179
|
+
stacked = false,
|
|
180
|
+
...chrome
|
|
181
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { height, width, className, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.BarChart, { data, margin: { top: 8, right: 8, left: 0, bottom: 0 }, children: [
|
|
182
|
+
cartesianChrome({ xKey, ...chrome }),
|
|
183
|
+
series.map((s, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
184
|
+
recharts.Bar,
|
|
185
|
+
{
|
|
186
|
+
dataKey: s.dataKey,
|
|
187
|
+
name: s.name ?? s.dataKey,
|
|
188
|
+
fill: resolveSeriesColor(index, s.color),
|
|
189
|
+
radius: stacked ? 0 : [4, 4, 0, 0],
|
|
190
|
+
stackId: stacked ? "stack" : void 0,
|
|
191
|
+
isAnimationActive: false
|
|
192
|
+
},
|
|
193
|
+
s.dataKey
|
|
194
|
+
))
|
|
195
|
+
] }) });
|
|
196
|
+
var AreaChart = ({
|
|
197
|
+
data,
|
|
198
|
+
xKey,
|
|
199
|
+
series,
|
|
200
|
+
height,
|
|
201
|
+
width,
|
|
202
|
+
className,
|
|
203
|
+
stacked = false,
|
|
204
|
+
...chrome
|
|
205
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { height, width, className, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.AreaChart, { data, margin: { top: 8, right: 8, left: 0, bottom: 0 }, children: [
|
|
206
|
+
cartesianChrome({ xKey, ...chrome }),
|
|
207
|
+
series.map((s, index) => {
|
|
208
|
+
const color = resolveSeriesColor(index, s.color);
|
|
209
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
210
|
+
recharts.Area,
|
|
211
|
+
{
|
|
212
|
+
type: "monotone",
|
|
213
|
+
dataKey: s.dataKey,
|
|
214
|
+
name: s.name ?? s.dataKey,
|
|
215
|
+
stroke: color,
|
|
216
|
+
fill: color,
|
|
217
|
+
fillOpacity: 0.15,
|
|
218
|
+
strokeWidth: 2,
|
|
219
|
+
stackId: stacked ? "stack" : void 0,
|
|
220
|
+
isAnimationActive: false
|
|
221
|
+
},
|
|
222
|
+
s.dataKey
|
|
223
|
+
);
|
|
224
|
+
})
|
|
225
|
+
] }) });
|
|
226
|
+
var PieChart = ({
|
|
227
|
+
data,
|
|
228
|
+
nameKey,
|
|
229
|
+
dataKey,
|
|
230
|
+
innerRadius = 0,
|
|
231
|
+
height,
|
|
232
|
+
width,
|
|
233
|
+
className,
|
|
234
|
+
showLegend = true,
|
|
235
|
+
showTooltip = true,
|
|
236
|
+
valueFormatter
|
|
237
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { height, width, className, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { children: [
|
|
238
|
+
showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
239
|
+
ChartTooltip,
|
|
240
|
+
{
|
|
241
|
+
valueFormatter: valueFormatter ? (value) => formatValue(value, valueFormatter) : void 0
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { wrapperStyle: { fontSize: 11 } }),
|
|
245
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
246
|
+
recharts.Pie,
|
|
247
|
+
{
|
|
248
|
+
data,
|
|
249
|
+
nameKey,
|
|
250
|
+
dataKey,
|
|
251
|
+
innerRadius,
|
|
252
|
+
stroke: "rgb(var(--color-background))",
|
|
253
|
+
isAnimationActive: false,
|
|
254
|
+
children: data.map((datum, index) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: chartTokenColor(index) }, String(datum[nameKey] ?? index)))
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
] }) });
|
|
258
|
+
|
|
259
|
+
exports.AreaChart = AreaChart;
|
|
260
|
+
exports.BarChart = BarChart;
|
|
261
|
+
exports.ChartContainer = ChartContainer;
|
|
262
|
+
exports.ChartTooltip = ChartTooltip;
|
|
263
|
+
exports.ChartTooltipContent = ChartTooltipContent;
|
|
264
|
+
exports.LineChart = LineChart;
|
|
265
|
+
exports.PieChart = PieChart;
|
|
266
|
+
exports.chartTokenColor = chartTokenColor;
|
|
267
|
+
//# sourceMappingURL=charts.cjs.map
|
|
268
|
+
//# sourceMappingURL=charts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/classNames.ts","../src/blocks/Charts/ChartContainer.tsx","../src/blocks/Charts/ChartTooltip.tsx","../src/blocks/Charts/format.ts","../src/blocks/Charts/cartesian.tsx","../src/blocks/Charts/theme.ts","../src/blocks/Charts/LineChart.tsx","../src/blocks/Charts/BarChart.tsx","../src/blocks/Charts/AreaChart.tsx","../src/blocks/Charts/PieChart.tsx"],"names":["extendTailwindMerge","clsx","jsx","ResponsiveContainer","jsxs","RechartsTooltip","Fragment","CartesianGrid","XAxis","YAxis","Legend","RechartsLineChart","Line","RechartsBarChart","Bar","RechartsAreaChart","Area","RechartsPieChart","Pie","Cell"],"mappings":"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","file":"charts.cjs","sourcesContent":["import { type ClassValue, clsx } from 'clsx';\nimport { extendTailwindMerge } from 'tailwind-merge';\n\nconst twMerge = extendTailwindMerge({\n prefix: 'tw-',\n});\n\nexport const cn = (...classes: ClassValue[]) => twMerge(clsx(...classes));\n","import { type ComponentProps, type ReactElement } from 'react';\nimport { ResponsiveContainer } from 'recharts';\n\nimport { cn } from '../../utils';\n\n/** A length that recharts' `<ResponsiveContainer>` accepts: px number or `\"NN%\"`. */\nexport type ChartDimension = number | `${number}%`;\n\nexport type ChartContainerProps = Omit<ComponentProps<'div'>, 'children'> & {\n /**\n * Height as a px number or percentage string. Charts have no intrinsic\n * height, so a container needs one either here or from a sized ancestor.\n * Defaults to 300.\n */\n height?: ChartDimension;\n /** Width as a px number or percentage. Defaults to `\"100%\"` (fills parent). */\n width?: ChartDimension;\n /** A single recharts chart element (`<LineChart>`, `<BarChart>`, …). */\n children: ReactElement;\n};\n\n/**\n * Responsive, brand-themed frame for a chart. Wraps the chart in recharts'\n * `<ResponsiveContainer>` and applies the semantic-token chrome (axis text,\n * grid, cursor) through CSS so every chart inside inherits it. Sizing is\n * intrinsic: give it a `height` (default 300) and it fills the parent width.\n */\nexport const ChartContainer = ({\n height = 300,\n width = '100%',\n className,\n children,\n ...props\n}: ChartContainerProps) => (\n <div\n className={cn(\n 'tw-text-xs',\n // Axis ticks and labels follow the muted foreground token.\n '[&_.recharts-cartesian-axis-tick_text]:tw-fill-muted-foreground',\n '[&_.recharts-polar-angle-axis-tick_text]:tw-fill-muted-foreground',\n '[&_.recharts-label]:tw-fill-muted-foreground',\n '[&_.recharts-legend-item-text]:!tw-text-muted-foreground',\n // Grid lines, axis lines and the hover cursor follow the border token.\n '[&_.recharts-cartesian-grid_line]:tw-stroke-border/60',\n '[&_.recharts-cartesian-axis-line]:tw-stroke-border',\n '[&_.recharts-cartesian-axis-tick-line]:tw-stroke-border',\n '[&_.recharts-curve.recharts-tooltip-cursor]:tw-stroke-border',\n '[&_.recharts-rectangle.recharts-tooltip-cursor]:tw-fill-muted/60',\n '[&_.recharts-surface]:tw-outline-none',\n className,\n )}\n {...props}\n >\n <ResponsiveContainer width={width} height={height}>\n {children}\n </ResponsiveContainer>\n </div>\n);\n","import { type ComponentProps } from 'react';\nimport { Tooltip as RechartsTooltip } from 'recharts';\n\nimport { cn } from '../../utils';\n\ntype TooltipPayloadEntry = {\n name?: string | number;\n value?: string | number;\n color?: string;\n dataKey?: string | number;\n};\n\ntype ChartTooltipContentProps = {\n active?: boolean;\n label?: string | number;\n payload?: TooltipPayloadEntry[];\n /** Format a value for display (e.g. currency, percentage). */\n valueFormatter?: (value: string | number, name: string) => string;\n className?: string;\n};\n\n/**\n * Brand-themed tooltip body. Surfaces (background, border, text) resolve from\n * semantic tokens, and each series swatch reuses the recharts-provided series\n * `color` (already a `--color-chart-*` token from this package's charts).\n */\nexport const ChartTooltipContent = ({\n active,\n label,\n payload,\n valueFormatter,\n className,\n}: ChartTooltipContentProps) => {\n if (!active || !payload?.length) return null;\n\n return (\n <div\n className={cn(\n 'tw-min-w-32 tw-rounded-md tw-border tw-border-border tw-bg-popover tw-px-2.5 tw-py-1.5 tw-text-xs tw-text-popover-foreground tw-shadow-md',\n className,\n )}\n >\n {label != null && <div className=\"tw-mb-1 tw-font-medium\">{label}</div>}\n <div className=\"tw-flex tw-flex-col tw-gap-1\">\n {payload.map((entry) => {\n const name = String(entry.name ?? entry.dataKey ?? '');\n const value = entry.value;\n // Missing / non-numeric values render blank rather than \"NaN\"/\"0\";\n // only well-formed numbers reach the formatter.\n const hasNumber = value != null && value !== '' && Number.isFinite(Number(value));\n const display = hasNumber\n ? valueFormatter\n ? valueFormatter(Number(value), name)\n : String(Number(value))\n : (value ?? '');\n return (\n <div\n key={String(entry.dataKey ?? name)}\n className=\"tw-flex tw-items-center tw-justify-between tw-gap-3\"\n >\n <span className=\"tw-flex tw-items-center tw-gap-1.5\">\n <span\n className=\"tw-size-2 tw-shrink-0 tw-rounded-[2px]\"\n style={{ backgroundColor: entry.color }}\n />\n <span className=\"tw-text-muted-foreground\">{name}</span>\n </span>\n <span className=\"tw-font-medium tw-tabular-nums\">{display}</span>\n </div>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport type ChartTooltipProps = ComponentProps<typeof RechartsTooltip> & {\n valueFormatter?: (value: string | number, name: string) => string;\n};\n\n/**\n * Drop-in recharts `<Tooltip>` pre-wired to render {@link ChartTooltipContent}.\n * Each chart mounts one by default; pass `showTooltip={false}` on the chart to omit.\n */\nexport const ChartTooltip = ({ valueFormatter, ...props }: ChartTooltipProps) => (\n <RechartsTooltip\n cursor={{ strokeWidth: 1 }}\n content={<ChartTooltipContent valueFormatter={valueFormatter} />}\n {...props}\n />\n);\n","/**\n * Apply a numeric `valueFormatter` safely. recharts hands axis ticks and\n * tooltip payload values as `string | number | null | undefined`, and partial\n * or streaming data can carry gaps. Coercing those straight through\n * `Number(value)` would render `$0` (empty string → 0) or `$NaN` (non-numeric),\n * so a missing / non-finite value is rendered as an empty string instead of\n * being fed to the formatter.\n */\nexport const formatValue = (\n value: string | number | null | undefined,\n valueFormatter?: (value: number) => string,\n): string => {\n if (value == null || value === '') return '';\n const num = Number(value);\n if (!Number.isFinite(num)) return '';\n return valueFormatter ? valueFormatter(num) : String(num);\n};\n","import { CartesianGrid, Legend, XAxis, YAxis } from 'recharts';\n\nimport { ChartTooltip } from './ChartTooltip';\nimport { formatValue } from './format';\nimport { type CartesianChartProps } from './types';\n\nconst AXIS_PROPS = {\n tickLine: false,\n axisLine: false,\n tick: { fontSize: 11 },\n} as const;\n\n/**\n * The grid + axes + tooltip + legend shared by the line, bar, and area charts.\n * Colors are applied via `ChartContainer`'s CSS chrome, not inline props, so\n * these stay brand-safe.\n */\nexport const cartesianChrome = ({\n xKey,\n showGrid = true,\n showYAxis = true,\n showLegend = false,\n showTooltip = true,\n valueFormatter,\n}: Pick<\n CartesianChartProps,\n 'xKey' | 'showGrid' | 'showYAxis' | 'showLegend' | 'showTooltip' | 'valueFormatter'\n>) => (\n <>\n {showGrid && <CartesianGrid strokeDasharray=\"3 3\" vertical={false} />}\n <XAxis dataKey={xKey} {...AXIS_PROPS} />\n {showYAxis && (\n <YAxis\n width={48}\n {...AXIS_PROPS}\n tickFormatter={\n valueFormatter\n ? (value: string | number) => formatValue(value, valueFormatter)\n : undefined\n }\n />\n )}\n {showTooltip && (\n <ChartTooltip\n valueFormatter={valueFormatter ? (value) => formatValue(value, valueFormatter) : undefined}\n />\n )}\n {showLegend && <Legend wrapperStyle={{ fontSize: 11 }} />}\n </>\n);\n","/**\n * The design system ships a five-slot categorical chart palette as CSS\n * variables (`--color-chart-1` … `--color-chart-5`). Charts resolve their\n * series colors from these tokens only — never from inline hex or a primitive\n * scale — so they follow both the active color scheme (light/dark, via\n * `data-theme` in `styles.css`) and the active brand: a brand reskins charts\n * by defining `colors.chart` (and `colorsDark.chart`) in its\n * `@keboola/brand-registry` definition, which `ThemeProvider` emits as the\n * `--color-chart-*` variables.\n *\n * The CSS variables hold raw `R G B` channels (for Tailwind's `<alpha-value>`\n * opacity modifiers), so they are wrapped in `rgb(...)` before handing them to\n * recharts as ordinary SVG `stroke` / `fill` strings.\n */\nconst CHART_TOKEN_COUNT = 5;\n\n/** Number of the semantic chart token (`1`–`5`) a series maps to. */\nexport type ChartColorToken = 1 | 2 | 3 | 4 | 5;\n\n/**\n * The CSS color for a chart token. `index` is 0-based (series position); it\n * wraps around the five-slot palette so any number of series stays on-brand.\n */\nexport const chartTokenColor = (index: number): string => {\n // Euclidean modulo so negative indices still land in 1..CHART_TOKEN_COUNT\n // (plain `%` would yield e.g. `--color-chart-0` for index -1).\n const slot = (((index % CHART_TOKEN_COUNT) + CHART_TOKEN_COUNT) % CHART_TOKEN_COUNT) + 1;\n return `rgb(var(--color-chart-${slot}))`;\n};\n\n/** Resolve an explicit token (1–5) or fall back to the series position. */\nexport const resolveSeriesColor = (index: number, token?: ChartColorToken): string =>\n chartTokenColor(token ? token - 1 : index);\n","import { Line, LineChart as RechartsLineChart } from 'recharts';\n\nimport { cartesianChrome } from './cartesian';\nimport { ChartContainer } from './ChartContainer';\nimport { resolveSeriesColor } from './theme';\nimport { type CartesianChartProps } from './types';\n\n/**\n * A themed multi-series line chart. Each series' color comes from the semantic\n * `--color-chart-*` palette (by order, or pinned via `series[].color`), which\n * follows the active color scheme and the active brand's `colors.chart` when it\n * defines one. Wrap-free — mounts its own {@link ChartContainer}.\n *\n * @example\n * <LineChart\n * data={data}\n * xKey=\"month\"\n * series={[{ dataKey: 'revenue', name: 'Revenue' }, { dataKey: 'cost' }]}\n * />\n */\nexport const LineChart = ({\n data,\n xKey,\n series,\n height,\n width,\n className,\n ...chrome\n}: CartesianChartProps) => (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsLineChart data={data} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>\n {cartesianChrome({ xKey, ...chrome })}\n {series.map((s, index) => {\n const color = resolveSeriesColor(index, s.color);\n return (\n <Line\n key={s.dataKey}\n type=\"monotone\"\n dataKey={s.dataKey}\n name={s.name ?? s.dataKey}\n stroke={color}\n strokeWidth={2}\n dot={{ r: 3, fill: color, stroke: color }}\n activeDot={{ r: 4 }}\n isAnimationActive={false}\n />\n );\n })}\n </RechartsLineChart>\n </ChartContainer>\n);\n","import { Bar, BarChart as RechartsBarChart } from 'recharts';\n\nimport { cartesianChrome } from './cartesian';\nimport { ChartContainer } from './ChartContainer';\nimport { resolveSeriesColor } from './theme';\nimport { type CartesianChartProps } from './types';\n\nexport type BarChartProps = CartesianChartProps & {\n /** Stack the series into a single bar per category. Defaults to grouped. */\n stacked?: boolean;\n};\n\n/**\n * A themed multi-series bar chart. Series colors come from the semantic\n * `--color-chart-*` palette (by order, or pinned via `series[].color`), which\n * follows the active color scheme and the active brand's `colors.chart` when it\n * defines one. Pass `stacked` to stack the series instead of grouping them.\n *\n * @example\n * <BarChart data={data} xKey=\"month\" series={[{ dataKey: 'revenue' }]} />\n */\nexport const BarChart = ({\n data,\n xKey,\n series,\n height,\n width,\n className,\n stacked = false,\n ...chrome\n}: BarChartProps) => (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsBarChart data={data} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>\n {cartesianChrome({ xKey, ...chrome })}\n {series.map((s, index) => (\n <Bar\n key={s.dataKey}\n dataKey={s.dataKey}\n name={s.name ?? s.dataKey}\n fill={resolveSeriesColor(index, s.color)}\n radius={stacked ? 0 : [4, 4, 0, 0]}\n stackId={stacked ? 'stack' : undefined}\n isAnimationActive={false}\n />\n ))}\n </RechartsBarChart>\n </ChartContainer>\n);\n","import { Area, AreaChart as RechartsAreaChart } from 'recharts';\n\nimport { cartesianChrome } from './cartesian';\nimport { ChartContainer } from './ChartContainer';\nimport { resolveSeriesColor } from './theme';\nimport { type CartesianChartProps } from './types';\n\nexport type AreaChartProps = CartesianChartProps & {\n /** Stack the series into a cumulative area. Defaults to overlaid. */\n stacked?: boolean;\n};\n\n/**\n * A themed multi-series area chart. Each series is stroked and softly filled\n * from the semantic `--color-chart-*` palette (by order, or pinned via\n * `series[].color`), which follows the active color scheme and the active\n * brand's `colors.chart` when it defines one. Pass `stacked` to stack the\n * series cumulatively.\n *\n * @example\n * <AreaChart data={data} xKey=\"month\" series={[{ dataKey: 'revenue' }]} />\n */\nexport const AreaChart = ({\n data,\n xKey,\n series,\n height,\n width,\n className,\n stacked = false,\n ...chrome\n}: AreaChartProps) => (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsAreaChart data={data} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>\n {cartesianChrome({ xKey, ...chrome })}\n {series.map((s, index) => {\n const color = resolveSeriesColor(index, s.color);\n return (\n <Area\n key={s.dataKey}\n type=\"monotone\"\n dataKey={s.dataKey}\n name={s.name ?? s.dataKey}\n stroke={color}\n fill={color}\n fillOpacity={0.15}\n strokeWidth={2}\n stackId={stacked ? 'stack' : undefined}\n isAnimationActive={false}\n />\n );\n })}\n </RechartsAreaChart>\n </ChartContainer>\n);\n","import { Cell, Legend, Pie, PieChart as RechartsPieChart } from 'recharts';\n\nimport { ChartContainer, type ChartDimension } from './ChartContainer';\nimport { ChartTooltip } from './ChartTooltip';\nimport { formatValue } from './format';\nimport { chartTokenColor } from './theme';\nimport { type ChartDatum } from './types';\n\nexport type PieChartProps = {\n /** The rows to plot — one sector per row. */\n data: ChartDatum[];\n /** Key into each datum for the sector label. */\n nameKey: string;\n /** Key into each datum for the sector value. */\n dataKey: string;\n /** Render as a donut with a hole of this inner radius (px or %). */\n innerRadius?: ChartDimension;\n /** Height as a px number or percentage. Defaults to 300. */\n height?: ChartDimension;\n /** Width as a px number or percentage. Defaults to `\"100%\"`. */\n width?: ChartDimension;\n /** Show a legend below the chart. Defaults to `true`. */\n showLegend?: boolean;\n /** Show the hover tooltip. Defaults to `true`. */\n showTooltip?: boolean;\n /** Format a sector value for the tooltip. */\n valueFormatter?: (value: number) => string;\n className?: string;\n};\n\n/**\n * A themed pie / donut chart. Each sector cycles through the semantic\n * `--color-chart-*` palette, which follows the active color scheme and the\n * active brand's `colors.chart` when it defines one. Pass `innerRadius` to make\n * a donut.\n *\n * @example\n * <PieChart data={data} nameKey=\"engine\" dataKey=\"rows\" innerRadius=\"60%\" />\n */\nexport const PieChart = ({\n data,\n nameKey,\n dataKey,\n innerRadius = 0,\n height,\n width,\n className,\n showLegend = true,\n showTooltip = true,\n valueFormatter,\n}: PieChartProps) => (\n <ChartContainer height={height} width={width} className={className}>\n <RechartsPieChart>\n {showTooltip && (\n <ChartTooltip\n valueFormatter={\n valueFormatter ? (value) => formatValue(value, valueFormatter) : undefined\n }\n />\n )}\n {showLegend && <Legend wrapperStyle={{ fontSize: 11 }} />}\n <Pie\n data={data}\n nameKey={nameKey}\n dataKey={dataKey}\n innerRadius={innerRadius}\n // Separate sectors with the brand surface color rather than recharts'\n // hardcoded white default.\n stroke=\"rgb(var(--color-background))\"\n isAnimationActive={false}\n >\n {data.map((datum, index) => (\n <Cell key={String(datum[nameKey] ?? index)} fill={chartTokenColor(index)} />\n ))}\n </Pie>\n </RechartsPieChart>\n </ChartContainer>\n);\n"]}
|