@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.
Files changed (93) hide show
  1. package/README.md +46 -6
  2. package/dist/charts/chart-data-table.d.ts +30 -0
  3. package/dist/charts/chart-data-table.d.ts.map +1 -0
  4. package/dist/charts/chart-data-table.js +34 -0
  5. package/dist/charts/chart-data-table.js.map +1 -0
  6. package/dist/charts/chart-frame.d.ts +33 -0
  7. package/dist/charts/chart-frame.d.ts.map +1 -0
  8. package/dist/charts/chart-frame.js +35 -0
  9. package/dist/charts/chart-frame.js.map +1 -0
  10. package/dist/charts/chart-legend-list.d.ts +20 -0
  11. package/dist/charts/chart-legend-list.d.ts.map +1 -0
  12. package/dist/charts/chart-legend-list.js +38 -0
  13. package/dist/charts/chart-legend-list.js.map +1 -0
  14. package/dist/charts/chart-ramp.d.ts +7 -0
  15. package/dist/charts/chart-ramp.d.ts.map +1 -0
  16. package/dist/charts/chart-ramp.js +16 -0
  17. package/dist/charts/chart-ramp.js.map +1 -0
  18. package/dist/charts/donut-chart.d.ts +16 -0
  19. package/dist/charts/donut-chart.d.ts.map +1 -0
  20. package/dist/charts/donut-chart.js +78 -0
  21. package/dist/charts/donut-chart.js.map +1 -0
  22. package/dist/charts/index.d.ts +10 -0
  23. package/dist/charts/index.js +9 -0
  24. package/dist/charts/line-chart.d.ts +38 -0
  25. package/dist/charts/line-chart.d.ts.map +1 -0
  26. package/dist/charts/line-chart.js +89 -0
  27. package/dist/charts/line-chart.js.map +1 -0
  28. package/dist/charts/types.d.ts +35 -0
  29. package/dist/charts/types.d.ts.map +1 -0
  30. package/dist/charts/use-cartesian-chart.d.ts +35 -0
  31. package/dist/charts/use-cartesian-chart.d.ts.map +1 -0
  32. package/dist/charts/use-cartesian-chart.js +48 -0
  33. package/dist/charts/use-cartesian-chart.js.map +1 -0
  34. package/dist/components/chip.d.ts +7 -4
  35. package/dist/components/chip.d.ts.map +1 -1
  36. package/dist/components/chip.js +15 -11
  37. package/dist/components/chip.js.map +1 -1
  38. package/dist/components/file-dropzone.d.ts +24 -0
  39. package/dist/components/file-dropzone.d.ts.map +1 -0
  40. package/dist/components/file-dropzone.js +70 -0
  41. package/dist/components/file-dropzone.js.map +1 -0
  42. package/dist/components/index.d.ts +4 -2
  43. package/dist/components/index.js +3 -1
  44. package/dist/components/marquee.d.ts +7 -1
  45. package/dist/components/marquee.d.ts.map +1 -1
  46. package/dist/components/marquee.js +33 -9
  47. package/dist/components/marquee.js.map +1 -1
  48. package/dist/components/search-combobox-option.js +24 -0
  49. package/dist/components/search-combobox-option.js.map +1 -0
  50. package/dist/components/search-combobox.d.ts +35 -0
  51. package/dist/components/search-combobox.d.ts.map +1 -0
  52. package/dist/components/search-combobox.js +128 -0
  53. package/dist/components/search-combobox.js.map +1 -0
  54. package/dist/components/theme-toggle.d.ts +13 -2
  55. package/dist/components/theme-toggle.d.ts.map +1 -1
  56. package/dist/components/theme-toggle.js +6 -3
  57. package/dist/components/theme-toggle.js.map +1 -1
  58. package/dist/elements/avatar.d.ts +1 -1
  59. package/dist/elements/badge.d.ts +1 -1
  60. package/dist/elements/button.d.ts +1 -1
  61. package/dist/elements/chart.d.ts +47 -0
  62. package/dist/elements/chart.d.ts.map +1 -0
  63. package/dist/elements/chart.js +151 -0
  64. package/dist/elements/chart.js.map +1 -0
  65. package/dist/elements/checkbox.d.ts +1 -1
  66. package/dist/elements/dialog.d.ts +1 -1
  67. package/dist/elements/dropdown-menu.d.ts +1 -1
  68. package/dist/elements/index.d.ts +4 -1
  69. package/dist/elements/index.js +4 -1
  70. package/dist/elements/label.d.ts +1 -1
  71. package/dist/elements/popover.d.ts +1 -1
  72. package/dist/elements/select.d.ts +1 -1
  73. package/dist/elements/separator.d.ts +1 -1
  74. package/dist/elements/switch.d.ts +1 -1
  75. package/dist/elements/table.d.ts +13 -0
  76. package/dist/elements/table.d.ts.map +1 -0
  77. package/dist/elements/table.js +68 -0
  78. package/dist/elements/table.js.map +1 -0
  79. package/dist/elements/toggle-group.d.ts +12 -0
  80. package/dist/elements/toggle-group.d.ts.map +1 -0
  81. package/dist/elements/toggle-group.js +50 -0
  82. package/dist/elements/toggle-group.js.map +1 -0
  83. package/dist/elements/toggle.d.ts +12 -0
  84. package/dist/elements/toggle.d.ts.map +1 -0
  85. package/dist/elements/toggle.js +38 -0
  86. package/dist/elements/toggle.js.map +1 -0
  87. package/dist/index.d.ts +7 -2
  88. package/dist/index.js +6 -1
  89. package/dist/tokens/palettes/base.css +3 -3
  90. package/dist/tokens/palettes/cyberpunk.css +4 -4
  91. package/dist/tokens/palettes/gold.css +2 -2
  92. package/dist/tokens/palettes/minimal.css +1 -1
  93. 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
- Both resolve to the same bytes. `Button` measures 9.59 kB either way.
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** (16): Avatar, Badge, Button, Card, Checkbox, Dialog, DropdownMenu,
69
- Input, Label, Popover, Select, Separator, Skeleton, Switch, Textarea, Toaster.
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
- **Components** (7): Chip, LanguageSwitcher, Marquee, MessageBubble, StatTile,
72
- StreamingDots, ThemeToggle.
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 thirteen
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"}