@wizeworks/silicaui-charts 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WizeWorks
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @wizeworks/silicaui-charts
2
+
3
+ Silica UI charts — [Apache ECharts](https://echarts.apache.org/) wrapped in a
4
+ single `<Chart>` component that **auto-themes to your Silica design tokens**.
5
+ Full ECharts API, zero manual palette wiring.
6
+
7
+ [![Website](https://img.shields.io/badge/website-silicaui.com-8b5cf6?style=flat-square)](https://silicaui.com)
8
+ [![npm version](https://img.shields.io/npm/v/@wizeworks/silicaui-charts.svg?style=flat-square)](https://www.npmjs.com/package/@wizeworks/silicaui-charts)
9
+ [![npm downloads](https://img.shields.io/npm/dm/@wizeworks/silicaui-charts.svg?style=flat-square)](https://www.npmjs.com/package/@wizeworks/silicaui-charts)
10
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@wizeworks/silicaui-charts?style=flat-square)](https://bundlephobia.com/package/@wizeworks/silicaui-charts)
11
+ [![license](https://img.shields.io/npm/l/@wizeworks/silicaui-charts.svg?style=flat-square)](https://github.com/silicaui/silicaui/blob/main/LICENSE)
12
+ [![CI](https://img.shields.io/github/actions/workflow/status/silicaui/silicaui/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/silicaui/silicaui/actions/workflows/ci.yml)
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pnpm add @wizeworks/silicaui-charts @wizeworks/silicaui-react
18
+ pnpm add -D @wizeworks/silicaui tailwindcss
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```tsx
24
+ import { Chart } from "@wizeworks/silicaui-charts";
25
+
26
+ <Chart
27
+ style={{ height: 320 }}
28
+ option={{
29
+ xAxis: { type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri"] },
30
+ yAxis: { type: "value" },
31
+ series: [{ type: "bar", data: [120, 200, 150, 80, 70] }],
32
+ }}
33
+ />
34
+ ```
35
+
36
+ The chart re-reads Silica's color tokens — and re-inits, since ECharts themes
37
+ are fixed at `init` — whenever the ambient theme changes (a `data-theme` flip
38
+ or an OS light/dark switch), so it tracks the rest of your UI automatically.
39
+ It also resizes with its container via a `ResizeObserver`. Give the container
40
+ a height; ECharts can't measure a zero-height box.
41
+
42
+ ## `<Chart>` props
43
+
44
+ | Prop | Type | Default |
45
+ | --- | --- | --- |
46
+ | `option` | `EChartsOption` — the full ECharts option (series, axes, tooltip, legend, …) | — |
47
+ | `notMerge` | `boolean` — replace the whole option instead of merging on update | `true` |
48
+ | `loading` | `boolean` — toggle ECharts' built-in loading spinner | `false` |
49
+ | `renderer` | `"canvas" \| "svg"` | `"canvas"` |
50
+ | `onInit` | `(chart: EChartsInstance) => void` — called after each (re-)init | — |
51
+
52
+ Also ships `<Sparkline>` for compact inline trend charts, and re-exports
53
+ `EChartsOption` so you can type `option` without a direct `echarts` import.
54
+
55
+ ## Links
56
+
57
+ - [silicaui.com](https://silicaui.com) — website & docs
58
+
59
+ - [GitHub repo](https://github.com/silicaui/silicaui)
60
+ - [Issues](https://github.com/silicaui/silicaui/issues)
61
+ - [`@wizeworks/silicaui-react`](https://www.npmjs.com/package/@wizeworks/silicaui-react) — the component layer this package extends
@@ -0,0 +1,75 @@
1
+ import * as React from 'react';
2
+ import * as echarts from 'echarts';
3
+ import { EChartsOption } from 'echarts';
4
+ export { EChartsOption } from 'echarts';
5
+
6
+ /** The live ECharts instance type (whatever `echarts.init` returns). */
7
+ type EChartsInstance = ReturnType<typeof echarts.init>;
8
+ interface ChartProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onClick"> {
9
+ /** The ECharts option (series, axes, tooltip, legend, …) — full ECharts API. */
10
+ option: EChartsOption;
11
+ /** Replace the whole option instead of merging on update. Default `true`. */
12
+ notMerge?: boolean;
13
+ /** Toggle ECharts' built-in loading spinner. */
14
+ loading?: boolean;
15
+ /** Renderer backend. Default `"canvas"`. */
16
+ renderer?: "canvas" | "svg";
17
+ /** Called with the instance right after it's created (and after each re-init). */
18
+ onInit?: (chart: EChartsInstance) => void;
19
+ }
20
+ /**
21
+ * A thin, fully-featured wrapper over Apache ECharts that auto-themes to the
22
+ * active Silica tokens.
23
+ *
24
+ * ECharts owns all rendering; Silica supplies the palette. The chart re-reads
25
+ * the tokens (and re-inits, since ECharts themes are fixed at `init`) whenever
26
+ * the ambient theme changes — a `data-theme` flip on `<html>` or an OS
27
+ * light/dark switch — so charts track the rest of the UI automatically. It also
28
+ * resizes with its container via a `ResizeObserver`.
29
+ *
30
+ * Give the container a height (defaults to 20rem); ECharts cannot measure a
31
+ * zero-height box.
32
+ */
33
+ declare const Chart: React.ForwardRefExoticComponent<ChartProps & React.RefAttributes<HTMLDivElement>>;
34
+
35
+ interface SparklineProps extends Omit<ChartProps, "option"> {
36
+ /** The series values. */
37
+ data: number[];
38
+ /** `"line"` (default) or `"bar"`. */
39
+ type?: "line" | "bar";
40
+ /** Fill under a line sparkline. */
41
+ area?: boolean;
42
+ /** Series color. Defaults to the Silica primary via the theme palette. */
43
+ color?: string;
44
+ /** Show a tooltip on hover. Default `false` — sparklines are glanceable. */
45
+ tooltip?: boolean;
46
+ /** Optional category labels (used only in the tooltip). */
47
+ labels?: (string | number)[];
48
+ }
49
+ /**
50
+ * A compact, axis-less trend line/bar for inline metrics (KPI cards, table
51
+ * cells). It builds the ECharts option for you and renders through {@link Chart},
52
+ * so it inherits Silica theming and container-resize behavior.
53
+ */
54
+ declare const Sparkline: React.ForwardRefExoticComponent<SparklineProps & React.RefAttributes<HTMLDivElement>>;
55
+
56
+ /**
57
+ * Builds an Apache ECharts theme from the live Silica design tokens.
58
+ *
59
+ * ECharts bakes its theme in at `init()` time and reads plain color strings, not
60
+ * CSS variables — so we resolve the current `--color-*` custom properties off a
61
+ * live element with `getComputedStyle` and hand ECharts concrete values. Because
62
+ * we read from a real element, a prefixed / nested-theme subtree (a client site
63
+ * embedded in the platform shell) yields that subtree's palette, and the
64
+ * `<Chart>` wrapper re-runs this whenever the ambient theme flips light↔dark.
65
+ *
66
+ * The resolved values may be `oklch(…)` / `color-mix(…)` strings; modern canvas
67
+ * and SVG renderers accept those directly.
68
+ */
69
+ /**
70
+ * @param el - element to read tokens from; defaults to `document.documentElement`.
71
+ * @returns an ECharts theme object (safe to pass to `echarts.registerTheme`).
72
+ */
73
+ declare function buildSilicaEChartsTheme(el?: HTMLElement | null): Record<string, unknown>;
74
+
75
+ export { Chart, type ChartProps, type EChartsInstance, Sparkline, type SparklineProps, buildSilicaEChartsTheme };
package/dist/index.js ADDED
@@ -0,0 +1,187 @@
1
+ import * as React from 'react';
2
+ import * as echarts from 'echarts';
3
+ import { useSilicaClass, cx } from '@wizeworks/silicaui-react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/chart.tsx
7
+
8
+ // src/theme.ts
9
+ function readVar(cs, name, fallback) {
10
+ const v = cs.getPropertyValue(name).trim();
11
+ return v || fallback;
12
+ }
13
+ function buildSilicaEChartsTheme(el) {
14
+ const root = el ?? (typeof document !== "undefined" ? document.documentElement : null);
15
+ if (!root || typeof getComputedStyle === "undefined") {
16
+ return { backgroundColor: "transparent" };
17
+ }
18
+ const cs = getComputedStyle(root);
19
+ const base100 = readVar(cs, "--color-base-100", "#ffffff");
20
+ const base300 = readVar(cs, "--color-base-300", "#e5e7eb");
21
+ const content = readVar(cs, "--color-base-content", "#1f2937");
22
+ const palette = [
23
+ "--color-primary",
24
+ "--color-secondary",
25
+ "--color-accent",
26
+ "--color-info",
27
+ "--color-success",
28
+ "--color-warning",
29
+ "--color-error"
30
+ ].map((n) => readVar(cs, n, "")).filter(Boolean);
31
+ const faint = (pct) => `color-mix(in oklab, ${content} ${pct}%, transparent)`;
32
+ const label = faint(70);
33
+ const axisLine = faint(22);
34
+ const splitLine = faint(9);
35
+ const axis = {
36
+ axisLine: { show: true, lineStyle: { color: axisLine } },
37
+ axisTick: { show: true, lineStyle: { color: axisLine } },
38
+ axisLabel: { color: label },
39
+ splitLine: { show: true, lineStyle: { color: splitLine } },
40
+ splitArea: { show: false }
41
+ };
42
+ return {
43
+ color: palette.length ? palette : void 0,
44
+ backgroundColor: "transparent",
45
+ textStyle: { color: content },
46
+ title: { textStyle: { color: content }, subtextStyle: { color: label } },
47
+ legend: { textStyle: { color: label } },
48
+ tooltip: {
49
+ backgroundColor: base100,
50
+ borderColor: base300,
51
+ borderWidth: 1,
52
+ textStyle: { color: content },
53
+ axisPointer: {
54
+ lineStyle: { color: axisLine },
55
+ crossStyle: { color: axisLine },
56
+ shadowStyle: { color: faint(6) }
57
+ }
58
+ },
59
+ grid: { borderColor: base300, containLabel: true },
60
+ categoryAxis: axis,
61
+ valueAxis: axis,
62
+ logAxis: axis,
63
+ timeAxis: axis,
64
+ line: {
65
+ symbol: "circle",
66
+ symbolSize: 7,
67
+ lineStyle: { width: 2 },
68
+ smooth: false
69
+ },
70
+ bar: { itemStyle: { borderRadius: [4, 4, 0, 0] } },
71
+ pie: {
72
+ itemStyle: { borderColor: base100, borderWidth: 2 },
73
+ label: { color: content }
74
+ },
75
+ scatter: { symbolSize: 10 }
76
+ };
77
+ }
78
+ var Chart = React.forwardRef(function Chart2({
79
+ option,
80
+ notMerge = true,
81
+ loading = false,
82
+ renderer = "canvas",
83
+ onInit,
84
+ className,
85
+ style,
86
+ ...rest
87
+ }, forwardedRef) {
88
+ const sc = useSilicaClass();
89
+ const containerRef = React.useRef(null);
90
+ const chartRef = React.useRef(null);
91
+ const onInitRef = React.useRef(onInit);
92
+ onInitRef.current = onInit;
93
+ const themeName = `silica-${React.useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
94
+ const [themeTick, setThemeTick] = React.useState(0);
95
+ const setRefs = React.useCallback(
96
+ (node) => {
97
+ containerRef.current = node;
98
+ if (typeof forwardedRef === "function") forwardedRef(node);
99
+ else if (forwardedRef) forwardedRef.current = node;
100
+ },
101
+ [forwardedRef]
102
+ );
103
+ React.useEffect(() => {
104
+ if (typeof window === "undefined") return;
105
+ const bump = () => setThemeTick((t) => t + 1);
106
+ const html = document.documentElement;
107
+ const mo = new MutationObserver(bump);
108
+ mo.observe(html, {
109
+ attributes: true,
110
+ attributeFilter: ["data-theme", "class", "style"]
111
+ });
112
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
113
+ mq.addEventListener("change", bump);
114
+ return () => {
115
+ mo.disconnect();
116
+ mq.removeEventListener("change", bump);
117
+ };
118
+ }, []);
119
+ React.useEffect(() => {
120
+ const el = containerRef.current;
121
+ if (!el) return;
122
+ echarts.registerTheme(themeName, buildSilicaEChartsTheme(el));
123
+ const chart = echarts.init(el, themeName, { renderer });
124
+ chartRef.current = chart;
125
+ onInitRef.current?.(chart);
126
+ const ro = new ResizeObserver(() => chart.resize());
127
+ ro.observe(el);
128
+ return () => {
129
+ ro.disconnect();
130
+ chart.dispose();
131
+ chartRef.current = null;
132
+ };
133
+ }, [themeName, renderer, themeTick]);
134
+ React.useEffect(() => {
135
+ chartRef.current?.setOption(option, notMerge);
136
+ }, [option, notMerge, themeTick]);
137
+ React.useEffect(() => {
138
+ const chart = chartRef.current;
139
+ if (!chart) return;
140
+ if (loading) chart.showLoading();
141
+ else chart.hideLoading();
142
+ }, [loading, themeTick]);
143
+ return /* @__PURE__ */ jsx(
144
+ "div",
145
+ {
146
+ ref: setRefs,
147
+ className: cx(sc("chart"), className),
148
+ style: { width: "100%", height: "20rem", ...style },
149
+ ...rest
150
+ }
151
+ );
152
+ });
153
+ var Sparkline = React.forwardRef(
154
+ function Sparkline2({ data, type = "line", area = false, color, tooltip = false, labels, style, ...rest }, ref) {
155
+ const option = React.useMemo(
156
+ () => ({
157
+ grid: { left: 1, right: 1, top: 3, bottom: 3 },
158
+ xAxis: {
159
+ type: "category",
160
+ show: false,
161
+ boundaryGap: type === "bar",
162
+ data: labels ?? data.map((_, i) => i)
163
+ },
164
+ yAxis: { type: "value", show: false, scale: true },
165
+ tooltip: tooltip ? { trigger: "axis", axisPointer: { type: "line" } } : void 0,
166
+ series: [
167
+ {
168
+ type,
169
+ data,
170
+ symbol: "none",
171
+ smooth: type === "line",
172
+ lineStyle: { width: 2 },
173
+ areaStyle: area && type === "line" ? { opacity: 0.18 } : void 0,
174
+ itemStyle: color ? { color } : void 0,
175
+ barCategoryGap: type === "bar" ? "30%" : void 0
176
+ }
177
+ ]
178
+ }),
179
+ [data, type, area, color, tooltip, labels]
180
+ );
181
+ return /* @__PURE__ */ jsx(Chart, { ref, option, style: { height: "3rem", ...style }, ...rest });
182
+ }
183
+ );
184
+
185
+ export { Chart, Sparkline, buildSilicaEChartsTheme };
186
+ //# sourceMappingURL=index.js.map
187
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theme.ts","../src/chart.tsx","../src/sparkline.tsx"],"names":["Chart","React2","Sparkline","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Builds an Apache ECharts theme from the live Silica design tokens.\n *\n * ECharts bakes its theme in at `init()` time and reads plain color strings, not\n * CSS variables — so we resolve the current `--color-*` custom properties off a\n * live element with `getComputedStyle` and hand ECharts concrete values. Because\n * we read from a real element, a prefixed / nested-theme subtree (a client site\n * embedded in the platform shell) yields that subtree's palette, and the\n * `<Chart>` wrapper re-runs this whenever the ambient theme flips light↔dark.\n *\n * The resolved values may be `oklch(…)` / `color-mix(…)` strings; modern canvas\n * and SVG renderers accept those directly.\n */\n\nfunction readVar(cs: CSSStyleDeclaration, name: string, fallback: string): string {\n const v = cs.getPropertyValue(name).trim();\n return v || fallback;\n}\n\n/**\n * @param el - element to read tokens from; defaults to `document.documentElement`.\n * @returns an ECharts theme object (safe to pass to `echarts.registerTheme`).\n */\nexport function buildSilicaEChartsTheme(\n el?: HTMLElement | null,\n): Record<string, unknown> {\n const root =\n el ?? (typeof document !== \"undefined\" ? document.documentElement : null);\n\n // No DOM (SSR): a neutral, transparent theme so nothing crashes.\n if (!root || typeof getComputedStyle === \"undefined\") {\n return { backgroundColor: \"transparent\" };\n }\n\n const cs = getComputedStyle(root);\n const base100 = readVar(cs, \"--color-base-100\", \"#ffffff\");\n const base300 = readVar(cs, \"--color-base-300\", \"#e5e7eb\");\n const content = readVar(cs, \"--color-base-content\", \"#1f2937\");\n\n const palette = [\n \"--color-primary\",\n \"--color-secondary\",\n \"--color-accent\",\n \"--color-info\",\n \"--color-success\",\n \"--color-warning\",\n \"--color-error\",\n ]\n .map((n) => readVar(cs, n, \"\"))\n .filter(Boolean);\n\n // Translucent washes of the text color for axes/gridlines — oklab, never oklch\n // (oklch rotates hue toward 0° when mixing with transparent → tinted grey).\n const faint = (pct: number) =>\n `color-mix(in oklab, ${content} ${pct}%, transparent)`;\n const label = faint(70);\n const axisLine = faint(22);\n const splitLine = faint(9);\n\n const axis = {\n axisLine: { show: true, lineStyle: { color: axisLine } },\n axisTick: { show: true, lineStyle: { color: axisLine } },\n axisLabel: { color: label },\n splitLine: { show: true, lineStyle: { color: splitLine } },\n splitArea: { show: false },\n };\n\n return {\n color: palette.length ? palette : undefined,\n backgroundColor: \"transparent\",\n textStyle: { color: content },\n title: { textStyle: { color: content }, subtextStyle: { color: label } },\n legend: { textStyle: { color: label } },\n tooltip: {\n backgroundColor: base100,\n borderColor: base300,\n borderWidth: 1,\n textStyle: { color: content },\n axisPointer: {\n lineStyle: { color: axisLine },\n crossStyle: { color: axisLine },\n shadowStyle: { color: faint(6) },\n },\n },\n grid: { borderColor: base300, containLabel: true },\n categoryAxis: axis,\n valueAxis: axis,\n logAxis: axis,\n timeAxis: axis,\n line: {\n symbol: \"circle\",\n symbolSize: 7,\n lineStyle: { width: 2 },\n smooth: false,\n },\n bar: { itemStyle: { borderRadius: [4, 4, 0, 0] } },\n pie: {\n itemStyle: { borderColor: base100, borderWidth: 2 },\n label: { color: content },\n },\n scatter: { symbolSize: 10 },\n };\n}\n","import * as React from \"react\";\nimport * as echarts from \"echarts\";\nimport type { EChartsOption } from \"echarts\";\nimport { cx, useSilicaClass } from \"@wizeworks/silicaui-react\";\nimport { buildSilicaEChartsTheme } from \"./theme\";\n\n/** The live ECharts instance type (whatever `echarts.init` returns). */\nexport type EChartsInstance = ReturnType<typeof echarts.init>;\n\nexport interface ChartProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onClick\"> {\n /** The ECharts option (series, axes, tooltip, legend, …) — full ECharts API. */\n option: EChartsOption;\n /** Replace the whole option instead of merging on update. Default `true`. */\n notMerge?: boolean;\n /** Toggle ECharts' built-in loading spinner. */\n loading?: boolean;\n /** Renderer backend. Default `\"canvas\"`. */\n renderer?: \"canvas\" | \"svg\";\n /** Called with the instance right after it's created (and after each re-init). */\n onInit?: (chart: EChartsInstance) => void;\n}\n\n/**\n * A thin, fully-featured wrapper over Apache ECharts that auto-themes to the\n * active Silica tokens.\n *\n * ECharts owns all rendering; Silica supplies the palette. The chart re-reads\n * the tokens (and re-inits, since ECharts themes are fixed at `init`) whenever\n * the ambient theme changes — a `data-theme` flip on `<html>` or an OS\n * light/dark switch — so charts track the rest of the UI automatically. It also\n * resizes with its container via a `ResizeObserver`.\n *\n * Give the container a height (defaults to 20rem); ECharts cannot measure a\n * zero-height box.\n */\nexport const Chart = React.forwardRef<HTMLDivElement, ChartProps>(function Chart(\n {\n option,\n notMerge = true,\n loading = false,\n renderer = \"canvas\",\n onInit,\n className,\n style,\n ...rest\n },\n forwardedRef,\n) {\n const sc = useSilicaClass();\n const containerRef = React.useRef<HTMLDivElement | null>(null);\n const chartRef = React.useRef<EChartsInstance | null>(null);\n const onInitRef = React.useRef(onInit);\n onInitRef.current = onInit;\n\n // A DOM-safe, stable theme name unique to this instance so concurrent charts\n // don't clobber one another's registered theme.\n const themeName = `silica-${React.useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n\n // Bumped whenever the ambient Silica theme changes → forces a re-init below.\n const [themeTick, setThemeTick] = React.useState(0);\n\n const setRefs = React.useCallback(\n (node: HTMLDivElement | null) => {\n containerRef.current = node;\n if (typeof forwardedRef === \"function\") forwardedRef(node);\n else if (forwardedRef) forwardedRef.current = node;\n },\n [forwardedRef],\n );\n\n // Watch for theme changes: attribute flips on <html> + OS scheme changes.\n React.useEffect(() => {\n if (typeof window === \"undefined\") return;\n const bump = () => setThemeTick((t) => t + 1);\n const html = document.documentElement;\n const mo = new MutationObserver(bump);\n mo.observe(html, {\n attributes: true,\n attributeFilter: [\"data-theme\", \"class\", \"style\"],\n });\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n mq.addEventListener(\"change\", bump);\n return () => {\n mo.disconnect();\n mq.removeEventListener(\"change\", bump);\n };\n }, []);\n\n // Create / recreate the chart instance (re-runs on theme change).\n React.useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n echarts.registerTheme(themeName, buildSilicaEChartsTheme(el));\n const chart = echarts.init(el, themeName, { renderer });\n chartRef.current = chart;\n onInitRef.current?.(chart);\n const ro = new ResizeObserver(() => chart.resize());\n ro.observe(el);\n return () => {\n ro.disconnect();\n chart.dispose();\n chartRef.current = null;\n };\n }, [themeName, renderer, themeTick]);\n\n // Push option updates.\n React.useEffect(() => {\n chartRef.current?.setOption(option, notMerge);\n }, [option, notMerge, themeTick]);\n\n // Loading overlay.\n React.useEffect(() => {\n const chart = chartRef.current;\n if (!chart) return;\n if (loading) chart.showLoading();\n else chart.hideLoading();\n }, [loading, themeTick]);\n\n return (\n <div\n ref={setRefs}\n className={cx(sc(\"chart\"), className)}\n style={{ width: \"100%\", height: \"20rem\", ...style }}\n {...rest}\n />\n );\n});\n","import * as React from \"react\";\nimport type { EChartsOption } from \"echarts\";\nimport { Chart } from \"./chart\";\nimport type { ChartProps } from \"./chart\";\n\nexport interface SparklineProps extends Omit<ChartProps, \"option\"> {\n /** The series values. */\n data: number[];\n /** `\"line\"` (default) or `\"bar\"`. */\n type?: \"line\" | \"bar\";\n /** Fill under a line sparkline. */\n area?: boolean;\n /** Series color. Defaults to the Silica primary via the theme palette. */\n color?: string;\n /** Show a tooltip on hover. Default `false` — sparklines are glanceable. */\n tooltip?: boolean;\n /** Optional category labels (used only in the tooltip). */\n labels?: (string | number)[];\n}\n\n/**\n * A compact, axis-less trend line/bar for inline metrics (KPI cards, table\n * cells). It builds the ECharts option for you and renders through {@link Chart},\n * so it inherits Silica theming and container-resize behavior.\n */\nexport const Sparkline = React.forwardRef<HTMLDivElement, SparklineProps>(\n function Sparkline(\n { data, type = \"line\", area = false, color, tooltip = false, labels, style, ...rest },\n ref,\n ) {\n const option = React.useMemo<EChartsOption>(\n () => ({\n grid: { left: 1, right: 1, top: 3, bottom: 3 },\n xAxis: {\n type: \"category\",\n show: false,\n boundaryGap: type === \"bar\",\n data: labels ?? data.map((_, i) => i),\n },\n yAxis: { type: \"value\", show: false, scale: true },\n tooltip: tooltip\n ? { trigger: \"axis\", axisPointer: { type: \"line\" } }\n : undefined,\n series: [\n {\n type,\n data,\n symbol: \"none\",\n smooth: type === \"line\",\n lineStyle: { width: 2 },\n areaStyle: area && type === \"line\" ? { opacity: 0.18 } : undefined,\n itemStyle: color ? { color } : undefined,\n barCategoryGap: type === \"bar\" ? \"30%\" : undefined,\n } as never,\n ],\n }),\n [data, type, area, color, tooltip, labels],\n );\n\n return (\n <Chart ref={ref} option={option} style={{ height: \"3rem\", ...style }} {...rest} />\n );\n },\n);\n"]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@wizeworks/silicaui-charts",
3
+ "version": "0.3.0",
4
+ "description": "Silica UI charts — Apache ECharts wrapped and auto-themed to Silica's design tokens.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "WizeWorks",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/silicaui/silicaui.git",
11
+ "directory": "packages/silicaui-charts"
12
+ },
13
+ "homepage": "https://silicaui.com",
14
+ "bugs": "https://github.com/silicaui/silicaui/issues",
15
+ "sideEffects": false,
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ }
21
+ },
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "peerDependencies": {
28
+ "react": ">=18",
29
+ "react-dom": ">=18",
30
+ "@wizeworks/silicaui-react": ">=0.0.0"
31
+ },
32
+ "dependencies": {
33
+ "echarts": "^5.5.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/react": "^19.0.0",
37
+ "@types/react-dom": "^19.0.0",
38
+ "react": "^19.0.0",
39
+ "react-dom": "^19.0.0",
40
+ "tsup": "^8.3.0",
41
+ "typescript": "^5.6.0",
42
+ "@wizeworks/silicaui-react": "0.3.0"
43
+ },
44
+ "keywords": [
45
+ "react",
46
+ "charts",
47
+ "echarts",
48
+ "design-system",
49
+ "silica"
50
+ ],
51
+ "scripts": {
52
+ "build": "tsup",
53
+ "dev": "tsup --watch",
54
+ "typecheck": "tsc --noEmit"
55
+ }
56
+ }