{
94 | return new Promise((resolve) => {
95 | // create temporary echart instance
96 | this.echarts.init(this.ele, this.props.theme, this.props.opts);
97 | const echartsInstance = this.getEchartsInstance();
98 |
99 | echartsInstance.on('finished', () => {
100 | // get final width and height
101 | const width = this.ele.clientWidth;
102 | const height = this.ele.clientHeight;
103 |
104 | // dispose temporary echart instance
105 | this.echarts.dispose(this.ele);
106 |
107 | // recreate echart instance
108 | // we use final width and height only if not originally provided as opts
109 | const opts = {
110 | width,
111 | height,
112 | ...this.props.opts,
113 | };
114 | resolve(this.echarts.init(this.ele, this.props.theme, opts));
115 | });
116 | });
117 | }
118 |
119 | /**
120 | * return the existing echart object
121 | */
122 | public getEchartsInstance(): ECharts {
123 | return this.echarts.getInstanceByDom(this.ele);
124 | }
125 |
126 | /**
127 | * dispose echarts and clear size-sensor
128 | */
129 | private dispose() {
130 | if (this.ele) {
131 | try {
132 | clear(this.ele);
133 | } catch (e) {
134 | console.warn(e);
135 | }
136 | // dispose echarts instance
137 | this.echarts.dispose(this.ele);
138 | }
139 | }
140 |
141 | /**
142 | * render a new echarts instance
143 | */
144 | private async renderNewEcharts() {
145 | const { onEvents, onChartReady, autoResize = true } = this.props;
146 |
147 | // 1. init echarts instance
148 | await this.initEchartsInstance();
149 |
150 | // 2. update echarts instance
151 | const echartsInstance = this.updateEChartsOption();
152 |
153 | // 3. bind events
154 | this.bindEvents(echartsInstance, onEvents || {});
155 |
156 | // 4. on chart ready
157 | if (isFunction(onChartReady)) onChartReady(echartsInstance);
158 |
159 | // 5. on resize
160 | if (this.ele && autoResize) {
161 | bind(this.ele, () => {
162 | this.resize();
163 | });
164 | }
165 | }
166 |
167 | // bind the events
168 | private bindEvents(instance, events: EChartsReactProps['onEvents']) {
169 | function _bindEvent(eventName: string, func: Function) {
170 | // ignore the event config which not satisfy
171 | if (isString(eventName) && isFunction(func)) {
172 | // binding event
173 | instance.on(eventName, (param) => {
174 | func(param, instance);
175 | });
176 | }
177 | }
178 |
179 | // loop and bind
180 | for (const eventName in events) {
181 | if (Object.prototype.hasOwnProperty.call(events, eventName)) {
182 | _bindEvent(eventName, events[eventName]);
183 | }
184 | }
185 | }
186 |
187 | // off the events
188 | private offEvents(instance, events: EChartsReactProps['onEvents']) {
189 | if (!events) return;
190 | // loop and off
191 | for (const eventName in events) {
192 | if (isString(eventName)) {
193 | instance.off(eventName);
194 | }
195 | }
196 | }
197 |
198 | /**
199 | * render the echarts
200 | */
201 | private updateEChartsOption(): EChartsInstance {
202 | const {
203 | option,
204 | notMerge = false,
205 | replaceMerge = null,
206 | lazyUpdate = false,
207 | showLoading,
208 | loadingOption = null,
209 | } = this.props;
210 | // 1. get or initial the echarts object
211 | const echartInstance = this.getEchartsInstance();
212 | // 2. set the echarts option
213 | echartInstance.setOption(option, { notMerge, replaceMerge, lazyUpdate });
214 | // 3. set loading mask
215 | if (showLoading) echartInstance.showLoading(loadingOption);
216 | else echartInstance.hideLoading();
217 |
218 | return echartInstance;
219 | }
220 |
221 | /**
222 | * resize wrapper
223 | */
224 | private resize() {
225 | // 1. get the echarts object
226 | const echartsInstance = this.getEchartsInstance();
227 |
228 | // 2. call echarts instance resize if not the initial resize
229 | // resize should not happen on first render as it will cancel initial echarts animations
230 | if (!this.isInitialResize) {
231 | try {
232 | echartsInstance.resize({
233 | width: 'auto',
234 | height: 'auto',
235 | });
236 | } catch (e) {
237 | console.warn(e);
238 | }
239 | }
240 |
241 | // 3. update variable for future calls
242 | this.isInitialResize = false;
243 | }
244 |
245 | render(): JSX.Element {
246 | const { style, className = '' } = this.props;
247 | // default height = 300
248 | const newStyle = { height: 300, ...style };
249 |
250 | return (
251 | {
253 | this.ele = e;
254 | }}
255 | style={newStyle}
256 | className={`echarts-for-react ${className}`}
257 | />
258 | );
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/src/helper/is-equal.ts:
--------------------------------------------------------------------------------
1 | import isEqual from 'fast-deep-equal';
2 |
3 | export { isEqual };
4 |
--------------------------------------------------------------------------------
/src/helper/is-function.ts:
--------------------------------------------------------------------------------
1 | export function isFunction(v: any): boolean {
2 | return typeof v === 'function';
3 | }
4 |
--------------------------------------------------------------------------------
/src/helper/is-string.ts:
--------------------------------------------------------------------------------
1 | export function isString(v: any): boolean {
2 | return typeof v === 'string';
3 | }
4 |
--------------------------------------------------------------------------------
/src/helper/pick.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 保留 object 中的部分内容
3 | * @param obj
4 | * @param keys
5 | */
6 | export function pick(obj: Record, keys: string[]): Record {
7 | const r = {};
8 | keys.forEach((key) => {
9 | r[key] = obj[key];
10 | });
11 | return r;
12 | }
13 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as echarts from 'echarts';
2 | import { EChartsReactProps, EChartsOption, EChartsInstance } from './types';
3 | import EChartsReactCore from './core';
4 |
5 | export type { EChartsReactProps, EChartsOption, EChartsInstance };
6 |
7 | // export the Component the echarts Object.
8 | export default class EChartsReact extends EChartsReactCore {
9 | constructor(props: EChartsReactProps) {
10 | super(props);
11 |
12 | // 初始化为 echarts 整个包
13 | this.echarts = echarts;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type { CSSProperties } from 'react';
2 | import type { EChartsType } from 'echarts';
3 |
4 | /**
5 | * Solve the type conflict caused by multiple type files
6 | */
7 | export type EChartsOption = any;
8 |
9 | export type EChartsInstance = EChartsType;
10 |
11 | export type Opts = {
12 | readonly devicePixelRatio?: number;
13 | readonly renderer?: 'canvas' | 'svg';
14 | readonly width?: number | null | undefined | 'auto';
15 | readonly height?: number | null | undefined | 'auto';
16 | readonly locale?: string;
17 | };
18 |
19 | export type EChartsReactProps = {
20 | /**
21 | * echarts library entry, use it for import necessary.
22 | */
23 | readonly echarts?: any;
24 | /**
25 | * `className` for container
26 | */
27 | readonly className?: string;
28 | /**
29 | * `style` for container
30 | */
31 | readonly style?: CSSProperties;
32 | /**
33 | * echarts option
34 | */
35 | readonly option: EChartsOption;
36 | /**
37 | * echarts theme config, can be:
38 | * 1. theme name string
39 | * 2. theme object
40 | */
41 | readonly theme?: string | Record;
42 | /**
43 | * notMerge config for echarts, default is `false`
44 | */
45 | readonly notMerge?: boolean;
46 | /**
47 | * replaceMerge config for echarts, default is `null`
48 | */
49 | readonly replaceMerge?: string | string[];
50 | /**
51 | * lazyUpdate config for echarts, default is `false`
52 | */
53 | readonly lazyUpdate?: boolean;
54 | /**
55 | * showLoading config for echarts, default is `false`
56 | */
57 | readonly showLoading?: boolean;
58 | /**
59 | * loadingOption config for echarts, default is `null`
60 | */
61 | readonly loadingOption?: any;
62 | /**
63 | * echarts opts config, default is `{}`
64 | */
65 | readonly opts?: Opts;
66 | /**
67 | * when after chart render, do the callback with echarts instance
68 | */
69 | readonly onChartReady?: (instance: EChartsInstance) => void;
70 | /**
71 | * bind events, default is `{}`
72 | */
73 | readonly onEvents?: Record;
74 | /**
75 | * should update echarts options
76 | */
77 | readonly shouldSetOption?: (prevProps: EChartsReactProps, props: EChartsReactProps) => boolean;
78 |
79 | /**
80 | * should trigger resize when window resize
81 | */
82 | readonly autoResize?: boolean;
83 | };
84 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "module": "commonjs",
5 | "sourceMap": true,
6 | "inlineSources": true,
7 | "target": "es5",
8 | "outDir": "lib",
9 | "declaration": true,
10 | "importHelpers": true,
11 | "moduleResolution": "node",
12 | "allowSyntheticDefaultImports": true,
13 | "esModuleInterop": true,
14 | "resolveJsonModule": true,
15 | "skipLibCheck": true,
16 | "lib": ["esnext", "dom"],
17 | "types": ["jest", "react", "node"]
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------