Skip to content

Commit 1892938

Browse files
authored
feat: support composing charts (#1)
1 parent a9fe536 commit 1892938

File tree

4 files changed

+79
-42
lines changed

4 files changed

+79
-42
lines changed

src/charts.tsx

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,93 @@ import {
77
} from 'echarts/charts';
88
import type { ComposeOption } from 'echarts/core';
99
import { GridComponent, type GridComponentOption } from 'echarts/components';
10-
import { ChartContext, defaultSetOptionOpt, echarts, useInitialChartContext, useRegister } from './shared.js';
10+
import type { ComponentOption } from 'echarts/types/src/util/types.js';
11+
import {
12+
ChartContext,
13+
defaultSetOptionOpt,
14+
echarts,
15+
useInitialChartContext,
16+
useRegister,
17+
type EChartExt,
18+
type Simplify,
19+
} from './shared.js';
1120

1221
function assignForwardedRef<T>(ref: React.ForwardedRef<T>, value: T | null) {
1322
if (!ref) return;
1423
if (typeof ref === 'function') ref(value);
1524
else ref.current = value;
1625
}
1726

18-
type LineChartOption = echarts.EChartsCoreOption &
19-
ComposeOption<LineSeriesOption | GridComponentOption | PieSeriesOption>;
20-
21-
interface LineChartProps extends LineChartOption {
27+
interface ChartBaseProps<T extends readonly ChartComponentType<any>[]> {
2228
className?: string;
2329
style?: React.CSSProperties;
2430
containerProps?: React.HTMLAttributes<HTMLDivElement>;
31+
compose?: [...T];
32+
children?: React.ReactNode;
2533
}
2634

27-
export const LineChart = forwardRef(
28-
(
29-
{ className, style, containerProps, children, ...props }: React.PropsWithChildren<LineChartProps>,
30-
ref: React.ForwardedRef<echarts.ECharts>
31-
) => {
32-
const containerRef = useRef<HTMLDivElement>(null);
33-
const chartRef = useRef<echarts.ECharts | null>(null);
34-
const ctx = useInitialChartContext();
35+
type InferChartComponentOption<
36+
T extends readonly ChartComponentType<any>[],
37+
U = T[number]
38+
> = U extends ChartComponentType<infer P> ? P : never;
39+
40+
interface ChartComponentType<T extends ComponentOption> {
41+
<U extends readonly ChartComponentType<any>[] = []>(
42+
props: ChartBaseProps<U> &
43+
echarts.EChartsCoreOption &
44+
Simplify<ComposeOption<T | InferChartComponentOption<U>>> & { ref?: React.Ref<echarts.ECharts> }
45+
): React.JSX.Element;
46+
47+
ext: EChartExt;
48+
}
3549

36-
useRegister((echarts) => {
37-
// TODO: Make different charts composable.
38-
echarts.use([EChartLineChart, GridComponent, EChartPieChart]);
39-
});
50+
function defineChart<T extends ComponentOption>(ext: EChartExt) {
51+
const ChartComponent = forwardRef(
52+
(
53+
{ className, style, containerProps, compose, children, ...props }: ChartBaseProps<any>,
54+
ref: React.ForwardedRef<echarts.ECharts>
55+
) => {
56+
const containerRef = useRef<HTMLDivElement>(null);
57+
const chartRef = useRef<echarts.ECharts | null>(null);
58+
const ctx = useInitialChartContext();
4059

41-
useLayoutEffect(() => {
42-
const chart = (chartRef.current = echarts.init(containerRef.current, null));
43-
assignForwardedRef(ref, chart);
44-
return () => {
45-
chart.dispose();
46-
assignForwardedRef(ref, null);
47-
};
48-
}, []);
60+
useRegister((echarts) => {
61+
echarts.use([ext, compose?.map((comp) => comp.ext).flat() || []].flat());
62+
});
63+
64+
useLayoutEffect(() => {
65+
const chart = (chartRef.current = echarts.init(containerRef.current, null));
66+
assignForwardedRef(ref, chart);
67+
return () => {
68+
chart.dispose();
69+
assignForwardedRef(ref, null);
70+
};
71+
}, []);
72+
73+
useLayoutEffect(() => {
74+
const chart = chartRef.current;
75+
if (!chart) return;
76+
for (const opt of ctx.options) chart.setOption(opt, defaultSetOptionOpt);
77+
chart.setOption(props, defaultSetOptionOpt);
78+
chart.setOption(chart.getOption(), { notMerge: true }); // used for toolbox.restore snapshot
79+
ctx.options.length = 0;
80+
});
81+
82+
return (
83+
<ChartContext.Provider value={ctx}>
84+
<div className={className} style={style} {...containerProps} ref={containerRef} />
85+
{children}
86+
</ChartContext.Provider>
87+
);
88+
}
89+
) as unknown as ChartComponentType<T>;
90+
ChartComponent.ext = ext;
91+
return ChartComponent;
92+
}
4993

50-
useLayoutEffect(() => {
51-
const chart = chartRef.current;
52-
if (!chart) return;
53-
for (const opt of ctx.options) chart.setOption(opt, defaultSetOptionOpt);
54-
chart.setOption(props, defaultSetOptionOpt);
55-
chart.setOption(chart.getOption(), { notMerge: true }); // used for toolbox.restore snapshot
56-
ctx.options.length = 0;
57-
});
94+
export const LineChart = /*#__PURE__*/ defineChart<LineSeriesOption | GridComponentOption>([
95+
EChartLineChart,
96+
GridComponent,
97+
]);
5898

59-
return (
60-
<ChartContext.Provider value={ctx}>
61-
<div className={className} style={style} {...containerProps} ref={containerRef} />
62-
{children}
63-
</ChartContext.Provider>
64-
);
65-
}
66-
) as (props: React.PropsWithChildren<LineChartProps> & { ref?: React.Ref<echarts.ECharts> }) => React.JSX.Element;
99+
export const PieChart = /*#__PURE__*/ defineChart<PieSeriesOption>([EChartPieChart]);

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { LineChart } from './charts.js';
1+
export { LineChart, PieChart } from './charts.js';
22
export {
33
Dataset,
44
DataZoom,

src/shared.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@ export function useRegister(fn: (echarts: EChartsCore) => void) {
3939
}
4040

4141
export type EChartExt = Parameters<(typeof echarts)['use']>[0];
42+
43+
export type Simplify<T> = { [K in keyof T]: T[K] } & {};

stories/line.stories.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
MarkArea,
1111
MarkLine,
1212
MarkPoint,
13+
PieChart,
1314
Polar,
1415
Title,
1516
Toolbox,
@@ -2205,6 +2206,7 @@ export function DatasetLink() {
22052206
return (
22062207
<LineChart
22072208
ref={chartRef}
2209+
compose={[PieChart]}
22082210
style={{ width: 480, height: 360 }}
22092211
useUTC
22102212
xAxis={{ type: 'category' }}

0 commit comments

Comments
 (0)