Skip to content

Commit

Permalink
feat(platform): support 'canvas' chart
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Jul 5, 2023
1 parent 7d36f78 commit 42c7978
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 47 deletions.
81 changes: 50 additions & 31 deletions packages/platform/src/app/components/chart/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,22 @@ import type { AppTheme } from '../../utils/types';

import * as echarts from 'echarts';
import { cloneDeep, merge } from 'lodash';
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';

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

import { STORAGE_KEY } from '../../config/storage';
import chartTheme from './theme.json';

echarts.registerTheme('light', chartTheme.light);
echarts.registerTheme('dark', merge(cloneDeep(chartTheme.light), chartTheme.dark));

export interface AppChartProps<O extends echarts.EChartsOption> extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
aOption: O | null;
export interface AppChartProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
aRenderer?: 'canvas' | 'svg';
onInit: (instance: echarts.ECharts) => void;
}

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

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

const async = useAsync();

const themeStorage = useStorage<AppTheme>(...STORAGE_KEY.theme);

const [instance, setInstance] = useState<echarts.ECharts | null>(null);
const [theme, setTheme] = useState<AppTheme | null>(null);

useEffect(() => {
const instance = containerRef.current ? echarts.init(containerRef.current, themeStorage.value, { renderer: 'svg' }) : null;
setInstance(instance);
for (const theme of ['light', 'dark'] as const) {
if (document.body.className.includes(theme)) {
setTheme(theme);
break;
}
}

const observer = new MutationObserver(() => {
setTheme(document.body.className.includes('dark') ? 'dark' : 'light');
});
observer.observe(document.body, { attributeFilter: ['class'] });

return () => {
instance?.dispose();
observer.disconnect();
};
}, [themeStorage.value]);
}, []);

useEffect(() => {
if (instance && aOption) {
instance.setOption(aOption);
if (containerRef.current && theme) {
const instance = echarts.init(
containerRef.current,
JSON.parse(
JSON.stringify(theme === 'light' ? chartTheme.light : merge(cloneDeep(chartTheme.light), chartTheme.dark)).replace(
/var\((.+?)\)/g,
(match, p1) => {
return getComputedStyle(document.body).getPropertyValue(p1);
}
)
),
{ renderer: aRenderer }
);
onInit(instance);
return () => {
instance.dispose();
};
}
}, [aOption, instance]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [aRenderer, theme]);

useResize(elRef, () => {
if (instance) {
dataRef.current.clearTid?.();
dataRef.current.clearTid = async.setTimeout(() => {
dataRef.current.clearTid = undefined;
instance.resize({ animation: { duration: 200 } });
}, 100);
}
dataRef.current.clearTid?.();
dataRef.current.clearTid = async.setTimeout(() => {
dataRef.current.clearTid = undefined;
if (containerRef.current) {
const instance = echarts.getInstanceByDom(containerRef.current);
instance?.resize({ animation: { duration: 200 } });
}
}, 100);
});

useImperativeHandle<echarts.ECharts | null, echarts.ECharts | null>(ref, () => instance, [instance]);

return (
<div {...restProps} ref={elRef} className={getClassName(restProps.className, 'app-chart')}>
<div ref={containerRef} className="app-chart__container"></div>
</div>
);
}

export const AppChart = React.forwardRef(Chart);
32 changes: 16 additions & 16 deletions packages/platform/src/app/routes/dashboard/echarts/ECharts.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

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

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

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

useMount(() => {
setOptions([lineOptions, stackedLineOptions, barOptions, stackedBarOptions, pieOptions, nightingaleOptions, scatterOptions]);
});

return (
<>
<AppRouteHeader>
Expand All @@ -31,15 +24,22 @@ const ECharts = AppRoute(() => {
</AppRouteHeader>
<div className={styles['app-echarts']}>
<div className="row" style={{ gap: 'var(--bs-gutter-x) 0' }}>
{options.map((option, index) => (
<div key={index} className="col-12 col-xxl-6">
<DCard>
<DCard.Content>
<AppChart style={{ height: 320 }} aOption={option} />
</DCard.Content>
</DCard>
</div>
))}
{[lineOptions, stackedLineOptions, barOptions, stackedBarOptions, pieOptions, nightingaleOptions, scatterOptions].map(
(option, index) => (
<div key={index} className="col-12 col-xxl-6">
<DCard>
<DCard.Content>
<AppChart
style={{ height: 320 }}
onInit={(instance) => {
instance.setOption(option);
}}
/>
</DCard.Content>
</DCard>
</div>
)
)}
</div>
</div>
</>
Expand Down

0 comments on commit 42c7978

Please sign in to comment.