@lfrprazeres/ui 0.1.0 → 0.3.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/README.md +46 -6
- package/dist/charts/chart-data-table.d.ts +30 -0
- package/dist/charts/chart-data-table.d.ts.map +1 -0
- package/dist/charts/chart-data-table.js +34 -0
- package/dist/charts/chart-data-table.js.map +1 -0
- package/dist/charts/chart-frame.d.ts +33 -0
- package/dist/charts/chart-frame.d.ts.map +1 -0
- package/dist/charts/chart-frame.js +35 -0
- package/dist/charts/chart-frame.js.map +1 -0
- package/dist/charts/chart-legend-list.d.ts +20 -0
- package/dist/charts/chart-legend-list.d.ts.map +1 -0
- package/dist/charts/chart-legend-list.js +38 -0
- package/dist/charts/chart-legend-list.js.map +1 -0
- package/dist/charts/chart-ramp.d.ts +7 -0
- package/dist/charts/chart-ramp.d.ts.map +1 -0
- package/dist/charts/chart-ramp.js +16 -0
- package/dist/charts/chart-ramp.js.map +1 -0
- package/dist/charts/donut-chart.d.ts +16 -0
- package/dist/charts/donut-chart.d.ts.map +1 -0
- package/dist/charts/donut-chart.js +78 -0
- package/dist/charts/donut-chart.js.map +1 -0
- package/dist/charts/index.d.ts +10 -0
- package/dist/charts/index.js +9 -0
- package/dist/charts/line-chart.d.ts +38 -0
- package/dist/charts/line-chart.d.ts.map +1 -0
- package/dist/charts/line-chart.js +89 -0
- package/dist/charts/line-chart.js.map +1 -0
- package/dist/charts/types.d.ts +35 -0
- package/dist/charts/types.d.ts.map +1 -0
- package/dist/charts/use-cartesian-chart.d.ts +35 -0
- package/dist/charts/use-cartesian-chart.d.ts.map +1 -0
- package/dist/charts/use-cartesian-chart.js +48 -0
- package/dist/charts/use-cartesian-chart.js.map +1 -0
- package/dist/components/chip.d.ts +7 -4
- package/dist/components/chip.d.ts.map +1 -1
- package/dist/components/chip.js +15 -11
- package/dist/components/chip.js.map +1 -1
- package/dist/components/file-dropzone.d.ts +24 -0
- package/dist/components/file-dropzone.d.ts.map +1 -0
- package/dist/components/file-dropzone.js +70 -0
- package/dist/components/file-dropzone.js.map +1 -0
- package/dist/components/index.d.ts +4 -2
- package/dist/components/index.js +3 -1
- package/dist/components/marquee.d.ts +7 -1
- package/dist/components/marquee.d.ts.map +1 -1
- package/dist/components/marquee.js +33 -9
- package/dist/components/marquee.js.map +1 -1
- package/dist/components/search-combobox-option.js +24 -0
- package/dist/components/search-combobox-option.js.map +1 -0
- package/dist/components/search-combobox.d.ts +35 -0
- package/dist/components/search-combobox.d.ts.map +1 -0
- package/dist/components/search-combobox.js +128 -0
- package/dist/components/search-combobox.js.map +1 -0
- package/dist/components/theme-toggle.d.ts +13 -2
- package/dist/components/theme-toggle.d.ts.map +1 -1
- package/dist/components/theme-toggle.js +6 -3
- package/dist/components/theme-toggle.js.map +1 -1
- package/dist/elements/avatar.d.ts +1 -1
- package/dist/elements/badge.d.ts +1 -1
- package/dist/elements/button.d.ts +1 -1
- package/dist/elements/chart.d.ts +47 -0
- package/dist/elements/chart.d.ts.map +1 -0
- package/dist/elements/chart.js +151 -0
- package/dist/elements/chart.js.map +1 -0
- package/dist/elements/checkbox.d.ts +1 -1
- package/dist/elements/dialog.d.ts +1 -1
- package/dist/elements/dropdown-menu.d.ts +1 -1
- package/dist/elements/index.d.ts +4 -1
- package/dist/elements/index.js +4 -1
- package/dist/elements/label.d.ts +1 -1
- package/dist/elements/popover.d.ts +1 -1
- package/dist/elements/select.d.ts +1 -1
- package/dist/elements/separator.d.ts +1 -1
- package/dist/elements/switch.d.ts +1 -1
- package/dist/elements/table.d.ts +13 -0
- package/dist/elements/table.d.ts.map +1 -0
- package/dist/elements/table.js +68 -0
- package/dist/elements/table.js.map +1 -0
- package/dist/elements/toggle-group.d.ts +12 -0
- package/dist/elements/toggle-group.d.ts.map +1 -0
- package/dist/elements/toggle-group.js +50 -0
- package/dist/elements/toggle-group.js.map +1 -0
- package/dist/elements/toggle.d.ts +12 -0
- package/dist/elements/toggle.d.ts.map +1 -0
- package/dist/elements/toggle.js +38 -0
- package/dist/elements/toggle.js.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -1
- package/dist/tokens/palettes/base.css +3 -3
- package/dist/tokens/palettes/cyberpunk.css +4 -4
- package/dist/tokens/palettes/gold.css +2 -2
- package/dist/tokens/palettes/minimal.css +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -24,6 +24,13 @@ pnpm add @lfrprazeres/ui
|
|
|
24
24
|
|
|
25
25
|
`react`, `react-dom` and `tailwindcss` are peer dependencies.
|
|
26
26
|
|
|
27
|
+
`recharts` is an **optional** peer. Install it only if you import from
|
|
28
|
+
`@lfrprazeres/ui/charts`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add recharts
|
|
32
|
+
```
|
|
33
|
+
|
|
27
34
|
## Setup
|
|
28
35
|
|
|
29
36
|
Import the stylesheet once, at your CSS entry point:
|
|
@@ -61,15 +68,43 @@ import { Button } from "@lfrprazeres/ui/elements";
|
|
|
61
68
|
import { StatTile } from "@lfrprazeres/ui/components";
|
|
62
69
|
```
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
Charts are the exception: they ship only from their own entry point, never from
|
|
72
|
+
the root barrel.
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { DonutChart } from "@lfrprazeres/ui/charts"; // needs recharts
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Both resolve to the same bytes. `Button` measures 9.58 kB either way, and the
|
|
79
|
+
whole surface is 98 kB.
|
|
65
80
|
|
|
66
81
|
## What ships
|
|
67
82
|
|
|
68
|
-
**Elements** (
|
|
69
|
-
Input, Label, Popover, Select, Separator, Skeleton, Switch, Textarea,
|
|
83
|
+
**Elements** (19): Avatar, Badge, Button, Card, Checkbox, Dialog, DropdownMenu,
|
|
84
|
+
Input, Label, Popover, Select, Separator, Skeleton, Switch, Table, Textarea,
|
|
85
|
+
Toaster, Toggle, ToggleGroup.
|
|
86
|
+
|
|
87
|
+
**Components** (9): Chip, FileDropzone, LanguageSwitcher, Marquee, MessageBubble,
|
|
88
|
+
SearchCombobox, StatTile, StreamingDots, ThemeToggle.
|
|
89
|
+
|
|
90
|
+
**Charts**, from `@lfrprazeres/ui/charts`: `Chart` (the recharts primitives),
|
|
91
|
+
`DonutChart`, `LineChart`, plus `ChartFrame`, `ChartDataTable` and
|
|
92
|
+
`ChartLegendList` for building your own. More chart types are coming; the rest
|
|
93
|
+
of the recharts catalogue is a later release.
|
|
94
|
+
|
|
95
|
+
### Why charts have their own entry point
|
|
70
96
|
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
`recharts` is far heavier than everything else here combined, so charts ship
|
|
98
|
+
from `@lfrprazeres/ui/charts` and nowhere else.
|
|
99
|
+
|
|
100
|
+
Tree-shaking alone was not enough. Bundlers did drop recharts from the output,
|
|
101
|
+
which is why `Button` never grew, but importing the root barrel in a runtime
|
|
102
|
+
that does not bundle — plain Node, an un-bundled SSR path, a script — loaded 246
|
|
103
|
+
recharts modules to get a `Button`. Now it loads none, and `pnpm check:charts`
|
|
104
|
+
walks the built module graph on every release to assert recharts is unreachable
|
|
105
|
+
from `.`, `/elements` and `/components`, and reachable from `/charts`.
|
|
106
|
+
|
|
107
|
+
Because it is an optional peer, nothing installs it unless you ask for it.
|
|
73
108
|
|
|
74
109
|
## Tiers
|
|
75
110
|
|
|
@@ -82,6 +117,10 @@ StreamingDots, ThemeToggle.
|
|
|
82
117
|
The library never ships features. If two projects need the same one, extract the
|
|
83
118
|
reusable mechanism as a component and keep a thin feature on top of it in each.
|
|
84
119
|
|
|
120
|
+
`charts` is not a fourth tier. It is a packaging boundary: `Chart` is an element
|
|
121
|
+
and the charts built on it are components, grouped behind one entry point
|
|
122
|
+
because they share a dependency heavy enough to be worth opting into.
|
|
123
|
+
|
|
85
124
|
## Palettes
|
|
86
125
|
|
|
87
126
|
A palette is a preset that redefines the semantic tier. Components never know
|
|
@@ -137,12 +176,13 @@ the import comes back and needs re-inverting.
|
|
|
137
176
|
| `pnpm build` | Builds the package with tsdown |
|
|
138
177
|
| `pnpm check:rsc` | Fails if a `"use client"` directive was lost in the build |
|
|
139
178
|
| `pnpm check:package` | publint plus are-the-types-wrong |
|
|
179
|
+
| `pnpm check:charts` | Fails if recharts is reachable from a non-chart entry point |
|
|
140
180
|
| `pnpm check:size` | size-limit budgets |
|
|
141
181
|
|
|
142
182
|
## Notes on the build
|
|
143
183
|
|
|
144
184
|
Built unbundled, preserving module structure. That is load-bearing rather than a
|
|
145
|
-
preference: bundling hoists or strips the `"use client"` directives
|
|
185
|
+
preference: bundling hoists or strips the `"use client"` directives twenty-two
|
|
146
186
|
modules depend on, and React Server Components then break with no error at all.
|
|
147
187
|
`pnpm check:rsc` compares source against the build and fails if any went
|
|
148
188
|
missing.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/charts/chart-data-table.d.ts
|
|
2
|
+
interface ChartDataTableRow {
|
|
3
|
+
/** One cell per column after the first. */
|
|
4
|
+
cells: string[];
|
|
5
|
+
/** Row header, typically the series or slice name. */
|
|
6
|
+
header: string;
|
|
7
|
+
}
|
|
8
|
+
interface ChartDataTableProps {
|
|
9
|
+
/** Describes the chart. Reuse the chart's accessible name. */
|
|
10
|
+
caption: string;
|
|
11
|
+
/** Column headers, including the leading row-header column. */
|
|
12
|
+
columns: string[];
|
|
13
|
+
rows: ChartDataTableRow[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The text alternative every chart renders.
|
|
17
|
+
*
|
|
18
|
+
* An SVG chart is an image to assistive technology no matter how much ARIA it
|
|
19
|
+
* carries, so the numbers have to exist somewhere reachable. This is that
|
|
20
|
+
* somewhere: a real table, visually hidden, carrying exactly the data the chart
|
|
21
|
+
* plots.
|
|
22
|
+
*
|
|
23
|
+
* It must be a sibling of the element carrying `role="img"`, never a child.
|
|
24
|
+
* `role="img"` removes its descendants from the accessibility tree, so a table
|
|
25
|
+
* nested inside one is invisible to the readers it exists for.
|
|
26
|
+
*/
|
|
27
|
+
declare function ChartDataTable({ caption, columns, rows }: ChartDataTableProps): import("react").JSX.Element;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { ChartDataTable, ChartDataTableProps, ChartDataTableRow };
|
|
30
|
+
//# sourceMappingURL=chart-data-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-data-table.d.ts","names":[],"sources":["../../src/charts/chart-data-table.tsx"],"mappings":";UAAiB;;EAEf;;EAEA;;UAGe;;EAEf;;EAEA;EACA,MAAM;;;;;;;;;;;;;;iBAeQ,iBACd,SACA,SACA,QACC,sCAAmB,IAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/charts/chart-data-table.tsx
|
|
3
|
+
/**
|
|
4
|
+
* The text alternative every chart renders.
|
|
5
|
+
*
|
|
6
|
+
* An SVG chart is an image to assistive technology no matter how much ARIA it
|
|
7
|
+
* carries, so the numbers have to exist somewhere reachable. This is that
|
|
8
|
+
* somewhere: a real table, visually hidden, carrying exactly the data the chart
|
|
9
|
+
* plots.
|
|
10
|
+
*
|
|
11
|
+
* It must be a sibling of the element carrying `role="img"`, never a child.
|
|
12
|
+
* `role="img"` removes its descendants from the accessibility tree, so a table
|
|
13
|
+
* nested inside one is invisible to the readers it exists for.
|
|
14
|
+
*/
|
|
15
|
+
function ChartDataTable({ caption, columns, rows }) {
|
|
16
|
+
return /* @__PURE__ */ jsxs("table", {
|
|
17
|
+
className: "sr-only",
|
|
18
|
+
children: [
|
|
19
|
+
/* @__PURE__ */ jsx("caption", { children: caption }),
|
|
20
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx("th", {
|
|
21
|
+
scope: "col",
|
|
22
|
+
children: column
|
|
23
|
+
}, column)) }) }),
|
|
24
|
+
/* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
|
|
25
|
+
scope: "row",
|
|
26
|
+
children: row.header
|
|
27
|
+
}), row.cells.map((cell, index) => /* @__PURE__ */ jsx("td", { children: cell }, columns[index + 1] ?? String(index)))] }, row.header)) })
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ChartDataTable };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=chart-data-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-data-table.js","names":[],"sources":["../../src/charts/chart-data-table.tsx"],"sourcesContent":["export interface ChartDataTableRow {\n /** One cell per column after the first. */\n cells: string[];\n /** Row header, typically the series or slice name. */\n header: string;\n}\n\nexport interface ChartDataTableProps {\n /** Describes the chart. Reuse the chart's accessible name. */\n caption: string;\n /** Column headers, including the leading row-header column. */\n columns: string[];\n rows: ChartDataTableRow[];\n}\n\n/**\n * The text alternative every chart renders.\n *\n * An SVG chart is an image to assistive technology no matter how much ARIA it\n * carries, so the numbers have to exist somewhere reachable. This is that\n * somewhere: a real table, visually hidden, carrying exactly the data the chart\n * plots.\n *\n * It must be a sibling of the element carrying `role=\"img\"`, never a child.\n * `role=\"img\"` removes its descendants from the accessibility tree, so a table\n * nested inside one is invisible to the readers it exists for.\n */\nexport function ChartDataTable({\n caption,\n columns,\n rows,\n}: ChartDataTableProps) {\n return (\n <table className=\"sr-only\">\n <caption>{caption}</caption>\n <thead>\n <tr>\n {columns.map((column) => (\n <th key={column} scope=\"col\">\n {column}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.header}>\n <th scope=\"row\">{row.header}</th>\n {row.cells.map((cell, index) => (\n <td key={columns[index + 1] ?? String(index)}>{cell}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AA2BA,SAAgB,eAAe,EAC7B,SACA,SACA,QACsB;CACtB,OACE,qBAAC,SAAD;EAAO,WAAU;EAAjB,UAAA;GACE,oBAAC,WAAD,EAAA,UAAU,QAAiB,CAAA;GAC3B,oBAAC,SAAD,EAAA,UACE,oBAAC,MAAD,EAAA,UACG,QAAQ,KAAK,WACZ,oBAAC,MAAD;IAAiB,OAAM;IACpB,UAAA;GACC,GAFK,MAEL,CACL,EACC,CAAA,EACC,CAAA;GACP,oBAAC,SAAD,EAAA,UACG,KAAK,KAAK,QACT,qBAAC,MAAD,EAAA,UAAA,CACE,oBAAC,MAAD;IAAI,OAAM;IAAO,UAAA,IAAI;GAAW,CAAA,GAC/B,IAAI,MAAM,KAAK,MAAM,UACpB,oBAAC,MAAD,EAAA,UAA+C,KAAS,GAA/C,QAAQ,QAAQ,MAAM,OAAO,KAAK,CAAa,CACzD,CACC,EAAA,GALK,IAAI,MAKT,CACL,EACI,CAAA;EACF;;AAEX"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/charts/chart-frame.d.ts
|
|
3
|
+
interface ChartFrameProps {
|
|
4
|
+
/** The chart itself. Rendered inside the image role. */
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Rendered outside the image role so assistive tech can reach it. */
|
|
8
|
+
dataTable?: ReactNode;
|
|
9
|
+
emptyLabel?: string;
|
|
10
|
+
/**
|
|
11
|
+
* True when the chart itself is keyboard operable. recharts gives cartesian
|
|
12
|
+
* charts a tab stop and `role="application"`, and wrapping a focusable,
|
|
13
|
+
* interactive thing in `role="img"` both hides it from assistive tech and
|
|
14
|
+
* lies about what it is.
|
|
15
|
+
*/
|
|
16
|
+
interactive?: boolean;
|
|
17
|
+
isEmpty?: boolean;
|
|
18
|
+
label: string;
|
|
19
|
+
legend?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The shell every chart composes: accessible name, text alternative, legend
|
|
23
|
+
* and a consistent empty state.
|
|
24
|
+
*
|
|
25
|
+
* The nesting is load-bearing. `role="img"` collapses everything beneath it
|
|
26
|
+
* into a single node for assistive technology, which is the right treatment for
|
|
27
|
+
* the SVG and the wrong treatment for the data table. So the table sits beside
|
|
28
|
+
* the image, not inside it.
|
|
29
|
+
*/
|
|
30
|
+
declare function ChartFrame({ children, className, dataTable, emptyLabel, interactive, isEmpty, label, legend }: ChartFrameProps): import("react").JSX.Element;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { ChartFrame, ChartFrameProps };
|
|
33
|
+
//# sourceMappingURL=chart-frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-frame.d.ts","names":[],"sources":["../../src/charts/chart-frame.tsx"],"mappings":";;UAGiB;;EAEf,UAAU;EACV;;EAEA,YAAY;EACZ;;;;;;;EAOA;EACA;EACA;EACA,SAAS;;;;;;;;;;;iBAYK,aACd,UACA,WACA,WACA,YACA,aACA,SACA,OACA,UACC,kCAAe,IAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/charts/chart-frame.tsx
|
|
4
|
+
/**
|
|
5
|
+
* The shell every chart composes: accessible name, text alternative, legend
|
|
6
|
+
* and a consistent empty state.
|
|
7
|
+
*
|
|
8
|
+
* The nesting is load-bearing. `role="img"` collapses everything beneath it
|
|
9
|
+
* into a single node for assistive technology, which is the right treatment for
|
|
10
|
+
* the SVG and the wrong treatment for the data table. So the table sits beside
|
|
11
|
+
* the image, not inside it.
|
|
12
|
+
*/
|
|
13
|
+
function ChartFrame({ children, className, dataTable, emptyLabel = "No data to display", interactive = false, isEmpty = false, label, legend }) {
|
|
14
|
+
if (isEmpty) return /* @__PURE__ */ jsx("p", {
|
|
15
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
16
|
+
children: emptyLabel
|
|
17
|
+
});
|
|
18
|
+
return /* @__PURE__ */ jsxs("figure", {
|
|
19
|
+
"aria-label": label,
|
|
20
|
+
className: cn("space-y-3", className),
|
|
21
|
+
children: [
|
|
22
|
+
interactive ? children : /* @__PURE__ */ jsx("div", {
|
|
23
|
+
"aria-label": label,
|
|
24
|
+
role: "img",
|
|
25
|
+
children
|
|
26
|
+
}),
|
|
27
|
+
dataTable,
|
|
28
|
+
legend
|
|
29
|
+
]
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ChartFrame };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=chart-frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-frame.js","names":[],"sources":["../../src/charts/chart-frame.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface ChartFrameProps {\n /** The chart itself. Rendered inside the image role. */\n children: ReactNode;\n className?: string;\n /** Rendered outside the image role so assistive tech can reach it. */\n dataTable?: ReactNode;\n emptyLabel?: string;\n /**\n * True when the chart itself is keyboard operable. recharts gives cartesian\n * charts a tab stop and `role=\"application\"`, and wrapping a focusable,\n * interactive thing in `role=\"img\"` both hides it from assistive tech and\n * lies about what it is.\n */\n interactive?: boolean;\n isEmpty?: boolean;\n label: string;\n legend?: ReactNode;\n}\n\n/**\n * The shell every chart composes: accessible name, text alternative, legend\n * and a consistent empty state.\n *\n * The nesting is load-bearing. `role=\"img\"` collapses everything beneath it\n * into a single node for assistive technology, which is the right treatment for\n * the SVG and the wrong treatment for the data table. So the table sits beside\n * the image, not inside it.\n */\nexport function ChartFrame({\n children,\n className,\n dataTable,\n emptyLabel = \"No data to display\",\n interactive = false,\n isEmpty = false,\n label,\n legend,\n}: ChartFrameProps) {\n if (isEmpty) {\n return (\n <p className={cn(\"text-muted-foreground text-sm\", className)}>\n {emptyLabel}\n </p>\n );\n }\n\n return (\n <figure aria-label={label} className={cn(\"space-y-3\", className)}>\n {interactive ? (\n children\n ) : (\n <div aria-label={label} role=\"img\">\n {children}\n </div>\n )}\n {dataTable}\n {legend}\n </figure>\n );\n}\n"],"mappings":";;;;;;;;;;;;AA+BA,SAAgB,WAAW,EACzB,UACA,WACA,WACA,aAAa,sBACb,cAAc,OACd,UAAU,OACV,OACA,UACkB;CAClB,IAAI,SACF,OACE,oBAAC,KAAD;EAAG,WAAW,GAAG,iCAAiC,SAAS;EACxD,UAAA;CACA,CAAA;CAIP,OACE,qBAAC,UAAD;EAAQ,cAAY;EAAO,WAAW,GAAG,aAAa,SAAS;EAA/D,UAAA;GACG,cACC,WAEA,oBAAC,OAAD;IAAK,cAAY;IAAO,MAAK;IAC1B;GACE,CAAA;GAEN;GACA;EACK;;AAEZ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/charts/chart-legend-list.d.ts
|
|
2
|
+
interface ChartLegendItem {
|
|
3
|
+
color: string;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Percentage or other secondary figure, right-aligned. */
|
|
6
|
+
share?: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
}
|
|
9
|
+
interface ChartLegendListProps {
|
|
10
|
+
className?: string;
|
|
11
|
+
items: ChartLegendItem[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The shared legend. Every entry names its series in text, so colour is never
|
|
15
|
+
* the only thing carrying the meaning.
|
|
16
|
+
*/
|
|
17
|
+
declare function ChartLegendList({ className, items }: ChartLegendListProps): import("react").JSX.Element;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { ChartLegendItem, ChartLegendList, ChartLegendListProps };
|
|
20
|
+
//# sourceMappingURL=chart-legend-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-legend-list.d.ts","names":[],"sources":["../../src/charts/chart-legend-list.tsx"],"mappings":";UAEiB;EACf;EACA;;EAEA;EACA;;UAGe;EACf;EACA,OAAO;;;;;;iBAOO,kBAAkB,WAAW,SAAS,uCAAoB,IAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/charts/chart-legend-list.tsx
|
|
4
|
+
/**
|
|
5
|
+
* The shared legend. Every entry names its series in text, so colour is never
|
|
6
|
+
* the only thing carrying the meaning.
|
|
7
|
+
*/
|
|
8
|
+
function ChartLegendList({ className, items }) {
|
|
9
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
10
|
+
className: cn("space-y-1.5", className),
|
|
11
|
+
children: items.map((item) => /* @__PURE__ */ jsxs("li", {
|
|
12
|
+
className: "flex items-center gap-2 text-sm",
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsx("span", {
|
|
15
|
+
"aria-hidden": "true",
|
|
16
|
+
className: "size-2.5 shrink-0 rounded-[2px]",
|
|
17
|
+
style: { backgroundColor: item.color }
|
|
18
|
+
}),
|
|
19
|
+
/* @__PURE__ */ jsx("span", {
|
|
20
|
+
className: "flex-1 truncate text-foreground",
|
|
21
|
+
children: item.label
|
|
22
|
+
}),
|
|
23
|
+
item.value ? /* @__PURE__ */ jsx("span", {
|
|
24
|
+
className: "text-muted-foreground tabular-nums",
|
|
25
|
+
children: item.value
|
|
26
|
+
}) : null,
|
|
27
|
+
item.share ? /* @__PURE__ */ jsx("span", {
|
|
28
|
+
className: "w-12 text-right text-muted-foreground tabular-nums",
|
|
29
|
+
children: item.share
|
|
30
|
+
}) : null
|
|
31
|
+
]
|
|
32
|
+
}, item.label))
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { ChartLegendList };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=chart-legend-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-legend-list.js","names":[],"sources":["../../src/charts/chart-legend-list.tsx"],"sourcesContent":["import { cn } from \"@/lib/cn\";\n\nexport interface ChartLegendItem {\n color: string;\n label: string;\n /** Percentage or other secondary figure, right-aligned. */\n share?: string;\n value?: string;\n}\n\nexport interface ChartLegendListProps {\n className?: string;\n items: ChartLegendItem[];\n}\n\n/**\n * The shared legend. Every entry names its series in text, so colour is never\n * the only thing carrying the meaning.\n */\nexport function ChartLegendList({ className, items }: ChartLegendListProps) {\n return (\n <ul className={cn(\"space-y-1.5\", className)}>\n {items.map((item) => (\n <li className=\"flex items-center gap-2 text-sm\" key={item.label}>\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-[2px]\"\n style={{ backgroundColor: item.color }}\n />\n <span className=\"flex-1 truncate text-foreground\">{item.label}</span>\n {item.value ? (\n <span className=\"text-muted-foreground tabular-nums\">\n {item.value}\n </span>\n ) : null}\n {item.share ? (\n <span className=\"w-12 text-right text-muted-foreground tabular-nums\">\n {item.share}\n </span>\n ) : null}\n </li>\n ))}\n </ul>\n );\n}\n"],"mappings":";;;;;;;AAmBA,SAAgB,gBAAgB,EAAE,WAAW,SAA+B;CAC1E,OACE,oBAAC,MAAD;EAAI,WAAW,GAAG,eAAe,SAAS;EACvC,UAAA,MAAM,KAAK,SACV,qBAAC,MAAD;GAAI,WAAU;GAAd,UAAA;IACE,oBAAC,QAAD;KACE,eAAY;KACZ,WAAU;KACV,OAAO,EAAE,iBAAiB,KAAK,MAAM;IACtC,CAAA;IACD,oBAAC,QAAD;KAAM,WAAU;KAAmC,UAAA,KAAK;IAAY,CAAA;IACnE,KAAK,QACJ,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA,KAAK;IACF,CAAA,IACJ;IACH,KAAK,QACJ,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA,KAAK;IACF,CAAA,IACJ;GACF;EAjBiD,GAAA,KAAK,KAiBtD,CACL;CACC,CAAA;AAER"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/charts/chart-ramp.d.ts
|
|
2
|
+
declare const CHART_RAMP: readonly ["var(--color-chart-1)", "var(--color-chart-2)", "var(--color-chart-3)", "var(--color-chart-4)", "var(--color-chart-5)"];
|
|
3
|
+
/** Picks a ramp colour by position, or honours an explicit override. */
|
|
4
|
+
declare function rampColor(index: number, override?: string): string;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { CHART_RAMP, rampColor };
|
|
7
|
+
//# sourceMappingURL=chart-ramp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-ramp.d.ts","names":[],"sources":["../../src/charts/chart-ramp.ts"],"mappings":";cAIa;;iBASG,UAAU,eAAe"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/charts/chart-ramp.ts
|
|
2
|
+
const CHART_RAMP = [
|
|
3
|
+
"var(--color-chart-1)",
|
|
4
|
+
"var(--color-chart-2)",
|
|
5
|
+
"var(--color-chart-3)",
|
|
6
|
+
"var(--color-chart-4)",
|
|
7
|
+
"var(--color-chart-5)"
|
|
8
|
+
];
|
|
9
|
+
/** Picks a ramp colour by position, or honours an explicit override. */
|
|
10
|
+
function rampColor(index, override) {
|
|
11
|
+
return override ?? CHART_RAMP[index % CHART_RAMP.length] ?? CHART_RAMP[0];
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { CHART_RAMP, rampColor };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=chart-ramp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-ramp.js","names":[],"sources":["../../src/charts/chart-ramp.ts"],"sourcesContent":["/*\n * The five chart roles are part of the semantic tier, so a palette retunes the\n * whole ramp and no chart ever learns a literal colour.\n */\nexport const CHART_RAMP = [\n \"var(--color-chart-1)\",\n \"var(--color-chart-2)\",\n \"var(--color-chart-3)\",\n \"var(--color-chart-4)\",\n \"var(--color-chart-5)\",\n] as const;\n\n/** Picks a ramp colour by position, or honours an explicit override. */\nexport function rampColor(index: number, override?: string): string {\n // The modulo keeps this in range; the literal only satisfies the checker.\n return override ?? CHART_RAMP[index % CHART_RAMP.length] ?? CHART_RAMP[0];\n}\n"],"mappings":";AAIA,MAAa,aAAa;CACxB;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAgB,UAAU,OAAe,UAA2B;CAElE,OAAO,YAAY,WAAW,QAAQ,WAAW,WAAW,WAAW;AACzE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ChartBaseProps, ChartSlice } from "./types.js";
|
|
2
|
+
//#region src/charts/donut-chart.d.ts
|
|
3
|
+
interface DonutChartProps extends ChartBaseProps {
|
|
4
|
+
data: ChartSlice[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A proportional breakdown.
|
|
8
|
+
*
|
|
9
|
+
* Pie charts get no accessibility layer from recharts, so the data table that
|
|
10
|
+
* `ChartFrame` renders is the only text alternative. That is why it is not
|
|
11
|
+
* optional here.
|
|
12
|
+
*/
|
|
13
|
+
declare function DonutChart({ className, data, emptyLabel, formatValue, label, legend }: DonutChartProps): import("react").JSX.Element;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { DonutChart, DonutChartProps };
|
|
16
|
+
//# sourceMappingURL=donut-chart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"donut-chart.d.ts","names":[],"sources":["../../src/charts/donut-chart.tsx"],"mappings":";;UAgBiB,wBAAwB;EACvC,MAAM;;;;;;;;;iBAiBQ,aACd,WACA,MACA,YACA,aACA,OACA,UACC,kCAAe,IAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../elements/chart.js";
|
|
3
|
+
import { ChartDataTable } from "./chart-data-table.js";
|
|
4
|
+
import { ChartFrame } from "./chart-frame.js";
|
|
5
|
+
import { ChartLegendList } from "./chart-legend-list.js";
|
|
6
|
+
import { rampColor } from "./chart-ramp.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useMemo } from "react";
|
|
9
|
+
import { Cell, Pie, PieChart } from "recharts";
|
|
10
|
+
//#region src/charts/donut-chart.tsx
|
|
11
|
+
function share(value, total) {
|
|
12
|
+
if (total === 0) return "0%";
|
|
13
|
+
return `${(value / total * 100).toFixed(1)}%`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A proportional breakdown.
|
|
17
|
+
*
|
|
18
|
+
* Pie charts get no accessibility layer from recharts, so the data table that
|
|
19
|
+
* `ChartFrame` renders is the only text alternative. That is why it is not
|
|
20
|
+
* optional here.
|
|
21
|
+
*/
|
|
22
|
+
function DonutChart({ className, data, emptyLabel, formatValue, label, legend = true }) {
|
|
23
|
+
const total = useMemo(() => data.reduce((sum, slice) => sum + slice.value, 0), [data]);
|
|
24
|
+
const config = useMemo(() => Object.fromEntries(data.map((slice, index) => [slice.name, {
|
|
25
|
+
color: rampColor(index, slice.color),
|
|
26
|
+
label: slice.label ?? slice.name
|
|
27
|
+
}])), [data]);
|
|
28
|
+
const format = formatValue ?? ((value) => value.toLocaleString());
|
|
29
|
+
return /* @__PURE__ */ jsx(ChartFrame, {
|
|
30
|
+
className,
|
|
31
|
+
dataTable: /* @__PURE__ */ jsx(ChartDataTable, {
|
|
32
|
+
caption: label,
|
|
33
|
+
columns: [
|
|
34
|
+
"Category",
|
|
35
|
+
"Value",
|
|
36
|
+
"Share"
|
|
37
|
+
],
|
|
38
|
+
rows: data.map((slice) => ({
|
|
39
|
+
cells: [format(slice.value), share(slice.value, total)],
|
|
40
|
+
header: slice.label ?? slice.name
|
|
41
|
+
}))
|
|
42
|
+
}),
|
|
43
|
+
emptyLabel,
|
|
44
|
+
isEmpty: data.length === 0,
|
|
45
|
+
label,
|
|
46
|
+
legend: legend ? /* @__PURE__ */ jsx(ChartLegendList, { items: data.map((slice, index) => ({
|
|
47
|
+
color: rampColor(index, slice.color),
|
|
48
|
+
label: slice.label ?? slice.name,
|
|
49
|
+
share: share(slice.value, total),
|
|
50
|
+
value: format(slice.value)
|
|
51
|
+
})) }) : null,
|
|
52
|
+
children: /* @__PURE__ */ jsx(ChartContainer, {
|
|
53
|
+
className: "mx-auto aspect-square max-h-56",
|
|
54
|
+
config,
|
|
55
|
+
initialDimension: {
|
|
56
|
+
height: 224,
|
|
57
|
+
width: 224
|
|
58
|
+
},
|
|
59
|
+
children: /* @__PURE__ */ jsxs(PieChart, { children: [/* @__PURE__ */ jsx(ChartTooltip, { content: /* @__PURE__ */ jsx(ChartTooltipContent, {
|
|
60
|
+
hideLabel: true,
|
|
61
|
+
nameKey: "name"
|
|
62
|
+
}) }), /* @__PURE__ */ jsx(Pie, {
|
|
63
|
+
data,
|
|
64
|
+
dataKey: "value",
|
|
65
|
+
innerRadius: "55%",
|
|
66
|
+
nameKey: "name",
|
|
67
|
+
outerRadius: "85%",
|
|
68
|
+
paddingAngle: 2,
|
|
69
|
+
strokeWidth: 0,
|
|
70
|
+
children: data.map((slice, index) => /* @__PURE__ */ jsx(Cell, { fill: rampColor(index, slice.color) }, slice.name))
|
|
71
|
+
})] })
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { DonutChart };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=donut-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"donut-chart.js","names":[],"sources":["../../src/charts/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { Cell, Pie, PieChart } from \"recharts\";\nimport { ChartDataTable } from \"@/charts/chart-data-table\";\nimport { ChartFrame } from \"@/charts/chart-frame\";\nimport { ChartLegendList } from \"@/charts/chart-legend-list\";\nimport { rampColor } from \"@/charts/chart-ramp\";\nimport type { ChartBaseProps, ChartSlice } from \"@/charts/types\";\nimport {\n type ChartConfig,\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n} from \"@/elements/chart\";\n\nexport interface DonutChartProps extends ChartBaseProps {\n data: ChartSlice[];\n}\n\nfunction share(value: number, total: number): string {\n if (total === 0) {\n return \"0%\";\n }\n return `${((value / total) * 100).toFixed(1)}%`;\n}\n\n/**\n * A proportional breakdown.\n *\n * Pie charts get no accessibility layer from recharts, so the data table that\n * `ChartFrame` renders is the only text alternative. That is why it is not\n * optional here.\n */\nexport function DonutChart({\n className,\n data,\n emptyLabel,\n formatValue,\n label,\n legend = true,\n}: DonutChartProps) {\n const total = useMemo(\n () => data.reduce((sum, slice) => sum + slice.value, 0),\n [data]\n );\n\n const config = useMemo<ChartConfig>(\n () =>\n Object.fromEntries(\n data.map((slice, index) => [\n slice.name,\n {\n color: rampColor(index, slice.color),\n label: slice.label ?? slice.name,\n },\n ])\n ),\n [data]\n );\n\n const format = formatValue ?? ((value: number) => value.toLocaleString());\n\n return (\n <ChartFrame\n className={className}\n dataTable={\n <ChartDataTable\n caption={label}\n columns={[\"Category\", \"Value\", \"Share\"]}\n rows={data.map((slice) => ({\n cells: [format(slice.value), share(slice.value, total)],\n header: slice.label ?? slice.name,\n }))}\n />\n }\n emptyLabel={emptyLabel}\n isEmpty={data.length === 0}\n label={label}\n legend={\n legend ? (\n <ChartLegendList\n items={data.map((slice, index) => ({\n color: rampColor(index, slice.color),\n label: slice.label ?? slice.name,\n share: share(slice.value, total),\n value: format(slice.value),\n }))}\n />\n ) : null\n }\n >\n <ChartContainer\n className=\"mx-auto aspect-square max-h-56\"\n config={config}\n initialDimension={{ height: 224, width: 224 }}\n >\n <PieChart>\n <ChartTooltip\n content={<ChartTooltipContent hideLabel nameKey=\"name\" />}\n />\n <Pie\n data={data}\n dataKey=\"value\"\n innerRadius=\"55%\"\n nameKey=\"name\"\n outerRadius=\"85%\"\n paddingAngle={2}\n strokeWidth={0}\n >\n {data.map((slice, index) => (\n <Cell fill={rampColor(index, slice.color)} key={slice.name} />\n ))}\n </Pie>\n </PieChart>\n </ChartContainer>\n </ChartFrame>\n );\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAS,MAAM,OAAe,OAAuB;CACnD,IAAI,UAAU,GACZ,OAAO;CAET,OAAO,IAAK,QAAQ,QAAS,IAAA,CAAK,QAAQ,CAAC,EAAE;AAC/C;;;;;;;;AASA,SAAgB,WAAW,EACzB,WACA,MACA,YACA,aACA,OACA,SAAS,QACS;CAClB,MAAM,QAAQ,cACN,KAAK,QAAQ,KAAK,UAAU,MAAM,MAAM,OAAO,CAAC,GACtD,CAAC,IAAI,CACP;CAEA,MAAM,SAAS,cAEX,OAAO,YACL,KAAK,KAAK,OAAO,UAAU,CACzB,MAAM,MACN;EACE,OAAO,UAAU,OAAO,MAAM,KAAK;EACnC,OAAO,MAAM,SAAS,MAAM;CAC9B,CACF,CAAC,CACH,GACF,CAAC,IAAI,CACP;CAEA,MAAM,SAAS,iBAAiB,UAAkB,MAAM,eAAe;CAEvE,OACE,oBAAC,YAAD;EACa;EACX,WACE,oBAAC,gBAAD;GACE,SAAS;GACT,SAAS;IAAC;IAAY;IAAS;GAAO;GACtC,MAAM,KAAK,KAAK,WAAW;IACzB,OAAO,CAAC,OAAO,MAAM,KAAK,GAAG,MAAM,MAAM,OAAO,KAAK,CAAC;IACtD,QAAQ,MAAM,SAAS,MAAM;GAC/B,EAAE;EACH,CAAA;EAES;EACZ,SAAS,KAAK,WAAW;EAClB;EACP,QACE,SACE,oBAAC,iBAAD,EACE,OAAO,KAAK,KAAK,OAAO,WAAW;GACjC,OAAO,UAAU,OAAO,MAAM,KAAK;GACnC,OAAO,MAAM,SAAS,MAAM;GAC5B,OAAO,MAAM,MAAM,OAAO,KAAK;GAC/B,OAAO,OAAO,MAAM,KAAK;EAC3B,EAAE,EACH,CAAA,IACC;EAGN,UAAA,oBAAC,gBAAD;GACE,WAAU;GACF;GACR,kBAAkB;IAAE,QAAQ;IAAK,OAAO;GAAI;GAE5C,UAAA,qBAAC,UAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EACE,SAAS,oBAAC,qBAAD;IAAqB,WAAA;IAAU,SAAQ;GAAQ,CAAA,EACzD,CAAA,GACD,oBAAC,KAAD;IACQ;IACN,SAAQ;IACR,aAAY;IACZ,SAAQ;IACR,aAAY;IACZ,cAAc;IACd,aAAa;IAEZ,UAAA,KAAK,KAAK,OAAO,UAChB,oBAAC,MAAD,EAAM,MAAM,UAAU,OAAO,MAAM,KAAK,EAAqB,GAAb,MAAM,IAAO,CAC9D;GACE,CAAA,CACG,EAAA,CAAA;EACI,CAAA;CACN,CAAA;AAEhB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent } from "../elements/chart.js";
|
|
2
|
+
import { ChartDataTable, ChartDataTableProps, ChartDataTableRow } from "./chart-data-table.js";
|
|
3
|
+
import { ChartFrame, ChartFrameProps } from "./chart-frame.js";
|
|
4
|
+
import { ChartLegendItem, ChartLegendList, ChartLegendListProps } from "./chart-legend-list.js";
|
|
5
|
+
import { CHART_RAMP, rampColor } from "./chart-ramp.js";
|
|
6
|
+
import { ChartBaseProps, ChartRow, ChartSeries, ChartSlice } from "./types.js";
|
|
7
|
+
import { DonutChart, DonutChartProps } from "./donut-chart.js";
|
|
8
|
+
import { LineChart, LineChartProps, LineChartValueAxis } from "./line-chart.js";
|
|
9
|
+
import { CartesianChartInput, useCartesianChart } from "./use-cartesian-chart.js";
|
|
10
|
+
export { CHART_RAMP, CartesianChartInput, ChartBaseProps, ChartConfig, ChartContainer, ChartDataTable, ChartDataTableProps, ChartDataTableRow, ChartFrame, ChartFrameProps, ChartLegend, ChartLegendContent, ChartLegendItem, ChartLegendList, ChartLegendListProps, ChartRow, ChartSeries, ChartSlice, ChartStyle, ChartTooltip, ChartTooltipContent, DonutChart, DonutChartProps, LineChart, LineChartProps, LineChartValueAxis, rampColor, useCartesianChart };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent } from "../elements/chart.js";
|
|
2
|
+
import { ChartDataTable } from "./chart-data-table.js";
|
|
3
|
+
import { ChartFrame } from "./chart-frame.js";
|
|
4
|
+
import { ChartLegendList } from "./chart-legend-list.js";
|
|
5
|
+
import { CHART_RAMP, rampColor } from "./chart-ramp.js";
|
|
6
|
+
import { DonutChart } from "./donut-chart.js";
|
|
7
|
+
import { useCartesianChart } from "./use-cartesian-chart.js";
|
|
8
|
+
import { LineChart } from "./line-chart.js";
|
|
9
|
+
export { CHART_RAMP, ChartContainer, ChartDataTable, ChartFrame, ChartLegend, ChartLegendContent, ChartLegendList, ChartStyle, ChartTooltip, ChartTooltipContent, DonutChart, LineChart, rampColor, useCartesianChart };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ChartTooltip } from "../elements/chart.js";
|
|
2
|
+
import { ChartBaseProps, ChartRow, ChartSeries } from "./types.js";
|
|
3
|
+
import { ComponentProps } from "react";
|
|
4
|
+
//#region src/charts/line-chart.d.ts
|
|
5
|
+
interface LineChartValueAxis {
|
|
6
|
+
/** Scale to the data range instead of anchoring at zero. */
|
|
7
|
+
fit?: boolean;
|
|
8
|
+
orientation?: "left" | "right";
|
|
9
|
+
width?: number;
|
|
10
|
+
}
|
|
11
|
+
interface LineChartProps extends ChartBaseProps {
|
|
12
|
+
data: ChartRow[];
|
|
13
|
+
/** Draws points as well as the line. */
|
|
14
|
+
dots?: boolean;
|
|
15
|
+
/** Formats the category axis ticks, e.g. a timestamp into a date. */
|
|
16
|
+
formatX?: (value: string | number) => string;
|
|
17
|
+
/** Draw the background grid. */
|
|
18
|
+
grid?: boolean;
|
|
19
|
+
series: ChartSeries[];
|
|
20
|
+
/** Replaces the default tooltip content entirely. */
|
|
21
|
+
tooltip?: ComponentProps<typeof ChartTooltip>["content"];
|
|
22
|
+
/** Value-axis options, or false to hide it. */
|
|
23
|
+
valueAxis?: false | LineChartValueAxis;
|
|
24
|
+
/** Key on each row holding the category or time value. */
|
|
25
|
+
xKey: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A trend over an ordered axis.
|
|
29
|
+
*
|
|
30
|
+
* recharts turns its accessibility layer on by default from v3, so the chart is
|
|
31
|
+
* a single tab stop and arrow keys move between points. Do not add per-point
|
|
32
|
+
* tab stops: that forces keyboard users through every value to get past the
|
|
33
|
+
* chart.
|
|
34
|
+
*/
|
|
35
|
+
declare function LineChart({ className, data, dots, emptyLabel, formatValue, formatX, grid, label, legend, series, tooltip, valueAxis, xKey }: LineChartProps): import("react").JSX.Element;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { LineChart, LineChartProps, LineChartValueAxis };
|
|
38
|
+
//# sourceMappingURL=line-chart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-chart.d.ts","names":[],"sources":["../../src/charts/line-chart.tsx"],"mappings":";;;;UAqBiB;;EAEf;EACA;EACA;;UAGe,uBAAuB;EACtC,MAAM;;EAEN;;EAEA,WAAW;;EAEX;EACA,QAAQ;;EAER,UAAU,sBAAsB;;EAEhC,oBAAoB;;EAEpB;;;;;;;;;;iBAWc,YACd,WACA,MACA,MACA,YACA,aACA,SACA,MACA,OACA,QACA,QACA,SACA,WACA,QACC,iCAAc,IAAA"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../elements/chart.js";
|
|
3
|
+
import { ChartDataTable } from "./chart-data-table.js";
|
|
4
|
+
import { ChartFrame } from "./chart-frame.js";
|
|
5
|
+
import { ChartLegendList } from "./chart-legend-list.js";
|
|
6
|
+
import { useCartesianChart } from "./use-cartesian-chart.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
|
9
|
+
//#region src/charts/line-chart.tsx
|
|
10
|
+
/**
|
|
11
|
+
* A trend over an ordered axis.
|
|
12
|
+
*
|
|
13
|
+
* recharts turns its accessibility layer on by default from v3, so the chart is
|
|
14
|
+
* a single tab stop and arrow keys move between points. Do not add per-point
|
|
15
|
+
* tab stops: that forces keyboard users through every value to get past the
|
|
16
|
+
* chart.
|
|
17
|
+
*/
|
|
18
|
+
function LineChart$1({ className, data, dots = false, emptyLabel, formatValue, formatX, grid = true, label, legend = true, series, tooltip, valueAxis, xKey }) {
|
|
19
|
+
const axis = valueAxis === false ? null : valueAxis ?? {};
|
|
20
|
+
const { colors, columns, config, legendItems, rows } = useCartesianChart({
|
|
21
|
+
data,
|
|
22
|
+
formatValue,
|
|
23
|
+
series,
|
|
24
|
+
xKey
|
|
25
|
+
});
|
|
26
|
+
return /* @__PURE__ */ jsx(ChartFrame, {
|
|
27
|
+
className,
|
|
28
|
+
dataTable: /* @__PURE__ */ jsx(ChartDataTable, {
|
|
29
|
+
caption: label,
|
|
30
|
+
columns,
|
|
31
|
+
rows
|
|
32
|
+
}),
|
|
33
|
+
emptyLabel,
|
|
34
|
+
interactive: true,
|
|
35
|
+
isEmpty: data.length === 0 || series.length === 0,
|
|
36
|
+
label,
|
|
37
|
+
legend: legend ? /* @__PURE__ */ jsx(ChartLegendList, { items: legendItems }) : null,
|
|
38
|
+
children: /* @__PURE__ */ jsx(ChartContainer, {
|
|
39
|
+
className: "aspect-video w-full",
|
|
40
|
+
config,
|
|
41
|
+
initialDimension: {
|
|
42
|
+
height: 240,
|
|
43
|
+
width: 420
|
|
44
|
+
},
|
|
45
|
+
children: /* @__PURE__ */ jsxs(LineChart, {
|
|
46
|
+
data,
|
|
47
|
+
margin: {
|
|
48
|
+
left: 4,
|
|
49
|
+
right: 8,
|
|
50
|
+
top: 8
|
|
51
|
+
},
|
|
52
|
+
children: [
|
|
53
|
+
grid ? /* @__PURE__ */ jsx(CartesianGrid, {
|
|
54
|
+
strokeDasharray: "3 3",
|
|
55
|
+
vertical: false
|
|
56
|
+
}) : null,
|
|
57
|
+
/* @__PURE__ */ jsx(XAxis, {
|
|
58
|
+
axisLine: false,
|
|
59
|
+
dataKey: xKey,
|
|
60
|
+
tickFormatter: formatX,
|
|
61
|
+
tickLine: false,
|
|
62
|
+
tickMargin: 8
|
|
63
|
+
}),
|
|
64
|
+
axis ? /* @__PURE__ */ jsx(YAxis, {
|
|
65
|
+
axisLine: false,
|
|
66
|
+
domain: axis.fit ? ["dataMin", "dataMax"] : void 0,
|
|
67
|
+
orientation: axis.orientation ?? "left",
|
|
68
|
+
tickFormatter: formatValue,
|
|
69
|
+
tickLine: false,
|
|
70
|
+
tickMargin: 8,
|
|
71
|
+
width: axis.width ?? 48
|
|
72
|
+
}) : null,
|
|
73
|
+
/* @__PURE__ */ jsx(ChartTooltip, { content: tooltip ?? /* @__PURE__ */ jsx(ChartTooltipContent, {}) }),
|
|
74
|
+
series.map((entry, index) => /* @__PURE__ */ jsx(Line, {
|
|
75
|
+
dataKey: entry.key,
|
|
76
|
+
dot: dots,
|
|
77
|
+
stroke: colors[index],
|
|
78
|
+
strokeWidth: 2,
|
|
79
|
+
type: "monotone"
|
|
80
|
+
}, entry.key))
|
|
81
|
+
]
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { LineChart$1 as LineChart };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=line-chart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-chart.js","names":["LineChart","RechartsLine"],"sources":["../../src/charts/line-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport {\n CartesianGrid,\n Line,\n LineChart as RechartsLine,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { ChartDataTable } from \"@/charts/chart-data-table\";\nimport { ChartFrame } from \"@/charts/chart-frame\";\nimport { ChartLegendList } from \"@/charts/chart-legend-list\";\nimport type { ChartBaseProps, ChartRow, ChartSeries } from \"@/charts/types\";\nimport { useCartesianChart } from \"@/charts/use-cartesian-chart\";\nimport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n} from \"@/elements/chart\";\n\nexport interface LineChartValueAxis {\n /** Scale to the data range instead of anchoring at zero. */\n fit?: boolean;\n orientation?: \"left\" | \"right\";\n width?: number;\n}\n\nexport interface LineChartProps extends ChartBaseProps {\n data: ChartRow[];\n /** Draws points as well as the line. */\n dots?: boolean;\n /** Formats the category axis ticks, e.g. a timestamp into a date. */\n formatX?: (value: string | number) => string;\n /** Draw the background grid. */\n grid?: boolean;\n series: ChartSeries[];\n /** Replaces the default tooltip content entirely. */\n tooltip?: ComponentProps<typeof ChartTooltip>[\"content\"];\n /** Value-axis options, or false to hide it. */\n valueAxis?: false | LineChartValueAxis;\n /** Key on each row holding the category or time value. */\n xKey: string;\n}\n\n/**\n * A trend over an ordered axis.\n *\n * recharts turns its accessibility layer on by default from v3, so the chart is\n * a single tab stop and arrow keys move between points. Do not add per-point\n * tab stops: that forces keyboard users through every value to get past the\n * chart.\n */\nexport function LineChart({\n className,\n data,\n dots = false,\n emptyLabel,\n formatValue,\n formatX,\n grid = true,\n label,\n legend = true,\n series,\n tooltip,\n valueAxis,\n xKey,\n}: LineChartProps) {\n const axis = valueAxis === false ? null : (valueAxis ?? {});\n const { colors, columns, config, legendItems, rows } = useCartesianChart({\n data,\n formatValue,\n series,\n xKey,\n });\n\n return (\n <ChartFrame\n className={className}\n dataTable={\n <ChartDataTable caption={label} columns={columns} rows={rows} />\n }\n emptyLabel={emptyLabel}\n interactive\n isEmpty={data.length === 0 || series.length === 0}\n label={label}\n legend={legend ? <ChartLegendList items={legendItems} /> : null}\n >\n <ChartContainer\n className=\"aspect-video w-full\"\n config={config}\n initialDimension={{ height: 240, width: 420 }}\n >\n <RechartsLine data={data} margin={{ left: 4, right: 8, top: 8 }}>\n {grid ? (\n <CartesianGrid strokeDasharray=\"3 3\" vertical={false} />\n ) : null}\n <XAxis\n axisLine={false}\n dataKey={xKey}\n tickFormatter={formatX}\n tickLine={false}\n tickMargin={8}\n />\n {axis ? (\n <YAxis\n axisLine={false}\n domain={axis.fit ? [\"dataMin\", \"dataMax\"] : undefined}\n orientation={axis.orientation ?? \"left\"}\n tickFormatter={formatValue}\n tickLine={false}\n tickMargin={8}\n width={axis.width ?? 48}\n />\n ) : null}\n <ChartTooltip content={tooltip ?? <ChartTooltipContent />} />\n {series.map((entry, index) => (\n <Line\n dataKey={entry.key}\n dot={dots}\n key={entry.key}\n stroke={colors[index]}\n strokeWidth={2}\n type=\"monotone\"\n />\n ))}\n </RechartsLine>\n </ChartContainer>\n </ChartFrame>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqDA,SAAgBA,YAAU,EACxB,WACA,MACA,OAAO,OACP,YACA,aACA,SACA,OAAO,MACP,OACA,SAAS,MACT,QACA,SACA,WACA,QACiB;CACjB,MAAM,OAAO,cAAc,QAAQ,OAAQ,aAAa,CAAC;CACzD,MAAM,EAAE,QAAQ,SAAS,QAAQ,aAAa,SAAS,kBAAkB;EACvE;EACA;EACA;EACA;CACF,CAAC;CAED,OACE,oBAAC,YAAD;EACa;EACX,WACE,oBAAC,gBAAD;GAAgB,SAAS;GAAgB;GAAe;EAAO,CAAA;EAErD;EACZ,aAAA;EACA,SAAS,KAAK,WAAW,KAAK,OAAO,WAAW;EACzC;EACP,QAAQ,SAAS,oBAAC,iBAAD,EAAiB,OAAO,YAAc,CAAA,IAAI;EAE3D,UAAA,oBAAC,gBAAD;GACE,WAAU;GACF;GACR,kBAAkB;IAAE,QAAQ;IAAK,OAAO;GAAI;GAE5C,UAAA,qBAACC,WAAD;IAAoB;IAAM,QAAQ;KAAE,MAAM;KAAG,OAAO;KAAG,KAAK;IAAE;IAA9D,UAAA;KACG,OACC,oBAAC,eAAD;MAAe,iBAAgB;MAAM,UAAU;KAAQ,CAAA,IACrD;KACJ,oBAAC,OAAD;MACE,UAAU;MACV,SAAS;MACT,eAAe;MACf,UAAU;MACV,YAAY;KACb,CAAA;KACA,OACC,oBAAC,OAAD;MACE,UAAU;MACV,QAAQ,KAAK,MAAM,CAAC,WAAW,SAAS,IAAI,KAAA;MAC5C,aAAa,KAAK,eAAe;MACjC,eAAe;MACf,UAAU;MACV,YAAY;MACZ,OAAO,KAAK,SAAS;KACtB,CAAA,IACC;KACJ,oBAAC,cAAD,EAAc,SAAS,WAAW,oBAAC,qBAAD,CAAsB,CAAA,EAAI,CAAA;KAC3D,OAAO,KAAK,OAAO,UAClB,oBAAC,MAAD;MACE,SAAS,MAAM;MACf,KAAK;MAEL,QAAQ,OAAO;MACf,aAAa;MACb,MAAK;KACN,GAJM,MAAM,GAIZ,CACF;IACW;;EACA,CAAA;CACN,CAAA;AAEhB"}
|