Skip to content

Commit 42c7978

Browse files
committed
feat(platform): support 'canvas' chart
1 parent 7d36f78 commit 42c7978

File tree

2 files changed

+66
-47
lines changed

2 files changed

+66
-47
lines changed

packages/platform/src/app/components/chart/Chart.tsx

+50-31
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,22 @@ import type { AppTheme } from '../../utils/types';
22

33
import * as echarts from 'echarts';
44
import { cloneDeep, merge } from 'lodash';
5-
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
5+
import { useEffect, useRef, useState } from 'react';
66

7-
import { useAsync, useResize, useStorage } from '@react-devui/hooks';
7+
import { useAsync, useResize } from '@react-devui/hooks';
88
import { getClassName } from '@react-devui/utils';
99

10-
import { STORAGE_KEY } from '../../config/storage';
1110
import chartTheme from './theme.json';
1211

13-
echarts.registerTheme('light', chartTheme.light);
14-
echarts.registerTheme('dark', merge(cloneDeep(chartTheme.light), chartTheme.dark));
15-
16-
export interface AppChartProps<O extends echarts.EChartsOption> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
17-
aOption: O | null;
12+
export interface AppChartProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
13+
aRenderer?: 'canvas' | 'svg';
14+
onInit: (instance: echarts.ECharts) => void;
1815
}
1916

20-
function Chart<O extends echarts.EChartsOption>(props: AppChartProps<O>, ref: React.ForwardedRef<echarts.ECharts>): JSX.Element | null {
17+
export function AppChart(props: AppChartProps): JSX.Element | null {
2118
const {
22-
aOption,
19+
aRenderer = 'canvas',
20+
onInit,
2321

2422
...restProps
2523
} = props;
@@ -35,41 +33,62 @@ function Chart<O extends echarts.EChartsOption>(props: AppChartProps<O>, ref: Re
3533

3634
const async = useAsync();
3735

38-
const themeStorage = useStorage<AppTheme>(...STORAGE_KEY.theme);
39-
40-
const [instance, setInstance] = useState<echarts.ECharts | null>(null);
36+
const [theme, setTheme] = useState<AppTheme | null>(null);
4137

4238
useEffect(() => {
43-
const instance = containerRef.current ? echarts.init(containerRef.current, themeStorage.value, { renderer: 'svg' }) : null;
44-
setInstance(instance);
39+
for (const theme of ['light', 'dark'] as const) {
40+
if (document.body.className.includes(theme)) {
41+
setTheme(theme);
42+
break;
43+
}
44+
}
45+
46+
const observer = new MutationObserver(() => {
47+
setTheme(document.body.className.includes('dark') ? 'dark' : 'light');
48+
});
49+
observer.observe(document.body, { attributeFilter: ['class'] });
50+
4551
return () => {
46-
instance?.dispose();
52+
observer.disconnect();
4753
};
48-
}, [themeStorage.value]);
54+
}, []);
4955

5056
useEffect(() => {
51-
if (instance && aOption) {
52-
instance.setOption(aOption);
57+
if (containerRef.current && theme) {
58+
const instance = echarts.init(
59+
containerRef.current,
60+
JSON.parse(
61+
JSON.stringify(theme === 'light' ? chartTheme.light : merge(cloneDeep(chartTheme.light), chartTheme.dark)).replace(
62+
/var\((.+?)\)/g,
63+
(match, p1) => {
64+
return getComputedStyle(document.body).getPropertyValue(p1);
65+
}
66+
)
67+
),
68+
{ renderer: aRenderer }
69+
);
70+
onInit(instance);
71+
return () => {
72+
instance.dispose();
73+
};
5374
}
54-
}, [aOption, instance]);
75+
// eslint-disable-next-line react-hooks/exhaustive-deps
76+
}, [aRenderer, theme]);
5577

5678
useResize(elRef, () => {
57-
if (instance) {
58-
dataRef.current.clearTid?.();
59-
dataRef.current.clearTid = async.setTimeout(() => {
60-
dataRef.current.clearTid = undefined;
61-
instance.resize({ animation: { duration: 200 } });
62-
}, 100);
63-
}
79+
dataRef.current.clearTid?.();
80+
dataRef.current.clearTid = async.setTimeout(() => {
81+
dataRef.current.clearTid = undefined;
82+
if (containerRef.current) {
83+
const instance = echarts.getInstanceByDom(containerRef.current);
84+
instance?.resize({ animation: { duration: 200 } });
85+
}
86+
}, 100);
6487
});
6588

66-
useImperativeHandle<echarts.ECharts | null, echarts.ECharts | null>(ref, () => instance, [instance]);
67-
6889
return (
6990
<div {...restProps} ref={elRef} className={getClassName(restProps.className, 'app-chart')}>
7091
<div ref={containerRef} className="app-chart__container"></div>
7192
</div>
7293
);
7394
}
74-
75-
export const AppChart = React.forwardRef(Chart);

packages/platform/src/app/routes/dashboard/echarts/ECharts.tsx

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { useState } from 'react';
21
import { useTranslation } from 'react-i18next';
32

4-
import { useMount } from '@react-devui/hooks';
53
import { DCard } from '@react-devui/ui';
64

75
import { AppChart, AppRouteHeader } from '../../../components';
@@ -11,13 +9,8 @@ import { barOptions, lineOptions, nightingaleOptions, pieOptions, scatterOptions
119
import styles from './ECharts.module.scss';
1210

1311
const ECharts = AppRoute(() => {
14-
const [options, setOptions] = useState<echarts.EChartsOption[]>([]);
1512
const { t } = useTranslation();
1613

17-
useMount(() => {
18-
setOptions([lineOptions, stackedLineOptions, barOptions, stackedBarOptions, pieOptions, nightingaleOptions, scatterOptions]);
19-
});
20-
2114
return (
2215
<>
2316
<AppRouteHeader>
@@ -31,15 +24,22 @@ const ECharts = AppRoute(() => {
3124
</AppRouteHeader>
3225
<div className={styles['app-echarts']}>
3326
<div className="row" style={{ gap: 'var(--bs-gutter-x) 0' }}>
34-
{options.map((option, index) => (
35-
<div key={index} className="col-12 col-xxl-6">
36-
<DCard>
37-
<DCard.Content>
38-
<AppChart style={{ height: 320 }} aOption={option} />
39-
</DCard.Content>
40-
</DCard>
41-
</div>
42-
))}
27+
{[lineOptions, stackedLineOptions, barOptions, stackedBarOptions, pieOptions, nightingaleOptions, scatterOptions].map(
28+
(option, index) => (
29+
<div key={index} className="col-12 col-xxl-6">
30+
<DCard>
31+
<DCard.Content>
32+
<AppChart
33+
style={{ height: 320 }}
34+
onInit={(instance) => {
35+
instance.setOption(option);
36+
}}
37+
/>
38+
</DCard.Content>
39+
</DCard>
40+
</div>
41+
)
42+
)}
4343
</div>
4444
</div>
4545
</>

0 commit comments

Comments
 (0)