react-use-echarts 0.0.11 → 1.0.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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![GitHub pull requests](https://img.shields.io/github/issues-pr/chensid/react-use-echarts)](https://github.com/chensid/react-use-echarts/pulls)
8
8
  [![GitHub license](https://img.shields.io/github/license/chensid/react-use-echarts.svg)](https://github.com/chensid/react-use-echarts/blob/main/LICENSE.txt)
9
9
 
10
- A powerful React hooks library for Apache ECharts, making it easy to use ECharts in your React applications with minimal boilerplate.
10
+ A React hooks library for Apache ECharts with full TypeScript support. Simple, lightweight, and gets out of your way.
11
11
 
12
12
  ## ✨ Features
13
13
 
@@ -18,6 +18,16 @@ A powerful React hooks library for Apache ECharts, making it easy to use ECharts
18
18
  - ⚡ **Auto-updating** - Automatically updates chart when data or options change
19
19
  - 📱 **Responsive** - Handles container resizing automatically with ResizeObserver
20
20
  - 🎯 **Event handling** - Easy to use event system with flexible configuration
21
+ - 🎭 **Built-in themes** - Includes light, dark, and macarons themes out of the box
22
+ - 🔗 **Chart linkage** - Connect multiple charts for synchronized interactions
23
+ - 🦥 **Lazy initialization** - Only initialize charts when they enter the viewport
24
+
25
+ ## 📋 Requirements
26
+
27
+ - React 19.x
28
+ - ECharts 6.x
29
+
30
+ > **Note**: This library is designed for client-side rendering (CSR) only. Server-side rendering (SSR) is not supported as ECharts requires DOM access.
21
31
 
22
32
  ## 📦 Installation
23
33
 
@@ -35,11 +45,13 @@ pnpm add react-use-echarts echarts
35
45
  ## 🔨 Usage
36
46
 
37
47
  ```tsx
38
- import React from 'react';
48
+ import { useRef } from 'react';
39
49
  import { useEcharts } from 'react-use-echarts';
40
50
  import type { EChartsOption } from 'echarts';
41
51
 
42
52
  function MyChart() {
53
+ const chartRef = useRef<HTMLDivElement>(null);
54
+
43
55
  const options: EChartsOption = {
44
56
  title: {
45
57
  text: 'Basic Line Chart Example'
@@ -58,7 +70,7 @@ function MyChart() {
58
70
  }]
59
71
  };
60
72
 
61
- const { chartRef } = useEcharts({
73
+ useEcharts(chartRef, {
62
74
  option: options
63
75
  });
64
76
 
@@ -66,43 +78,543 @@ function MyChart() {
66
78
  }
67
79
  ```
68
80
 
81
+ ## 🚀 Advanced Usage
82
+
83
+ ### Event Handling
84
+
85
+ ```tsx
86
+ import { useRef } from 'react';
87
+ import { useEcharts } from 'react-use-echarts';
88
+ import type { EChartsOption } from 'echarts';
89
+
90
+ function InteractiveChart() {
91
+ const chartRef = useRef<HTMLDivElement>(null);
92
+
93
+ const options: EChartsOption = {
94
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
95
+ yAxis: { type: 'value' },
96
+ series: [{ data: [120, 200, 150], type: 'bar' }]
97
+ };
98
+
99
+ useEcharts(chartRef, {
100
+ option: options,
101
+ onEvents: {
102
+ click: {
103
+ handler: (params) => {
104
+ console.log('Clicked:', params);
105
+ }
106
+ },
107
+ mouseover: {
108
+ handler: (params) => {
109
+ console.log('Hover:', params);
110
+ },
111
+ query: 'series' // Only trigger on series elements
112
+ }
113
+ }
114
+ });
115
+
116
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
117
+ }
118
+ ```
119
+
120
+ ### Loading State
121
+
122
+ ```tsx
123
+ import { useState, useEffect, useRef } from 'react';
124
+ import { useEcharts } from 'react-use-echarts';
125
+
126
+ function ChartWithLoading() {
127
+ const chartRef = useRef<HTMLDivElement>(null);
128
+ const [loading, setLoading] = useState(true);
129
+ const [data, setData] = useState<number[]>([]);
130
+
131
+ // Simulate data fetching
132
+ useEffect(() => {
133
+ setTimeout(() => {
134
+ setData([820, 932, 901, 934, 1290, 1330, 1320]);
135
+ setLoading(false);
136
+ }, 2000);
137
+ }, []);
138
+
139
+ useEcharts(chartRef, {
140
+ option: {
141
+ xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] },
142
+ yAxis: { type: 'value' },
143
+ series: [{ data, type: 'line' }]
144
+ },
145
+ showLoading: loading
146
+ });
147
+
148
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
149
+ }
150
+ ```
151
+
152
+ ### Dynamic Updates
153
+
154
+ ```tsx
155
+ import { useState, useRef } from 'react';
156
+ import { useEcharts } from 'react-use-echarts';
157
+
158
+ function DynamicChart() {
159
+ const chartRef = useRef<HTMLDivElement>(null);
160
+ const [data, setData] = useState([120, 200, 150, 80, 70, 110, 130]);
161
+
162
+ const { setOption } = useEcharts(chartRef, {
163
+ option: {
164
+ xAxis: { type: 'category', data: ['A', 'B', 'C', 'D', 'E', 'F', 'G'] },
165
+ yAxis: { type: 'value' },
166
+ series: [{ data, type: 'bar' }]
167
+ }
168
+ });
169
+
170
+ const updateData = () => {
171
+ const newData = data.map(() => Math.floor(Math.random() * 200));
172
+ setData(newData);
173
+ setOption({
174
+ series: [{ data: newData }]
175
+ });
176
+ };
177
+
178
+ return (
179
+ <div>
180
+ <button onClick={updateData}>Update Data</button>
181
+ <div ref={chartRef} style={{ width: '100%', height: '400px' }} />
182
+ </div>
183
+ );
184
+ }
185
+ ```
186
+
187
+ ### Built-in Themes
188
+
189
+ The library includes three built-in themes: `light`, `dark`, and `macarons`.
190
+
191
+ ```tsx
192
+ import { useRef } from 'react';
193
+ import { useEcharts } from 'react-use-echarts';
194
+
195
+ function ThemedChart() {
196
+ const chartRef = useRef<HTMLDivElement>(null);
197
+
198
+ useEcharts(chartRef, {
199
+ option: {
200
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
201
+ yAxis: { type: 'value' },
202
+ series: [{ data: [120, 200, 150], type: 'bar' }]
203
+ },
204
+ theme: 'dark' // 'light' | 'dark' | 'macarons'
205
+ });
206
+
207
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
208
+ }
209
+ ```
210
+
211
+ ### Custom Theme
212
+
213
+ ```tsx
214
+ import { useRef } from 'react';
215
+ import { useEcharts } from 'react-use-echarts';
216
+
217
+ function CustomThemedChart() {
218
+ const chartRef = useRef<HTMLDivElement>(null);
219
+
220
+ const customTheme = {
221
+ color: ['#fc8452', '#9a60b4', '#ea7ccc'],
222
+ backgroundColor: '#1e1e1e'
223
+ };
224
+
225
+ useEcharts(chartRef, {
226
+ option: {
227
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
228
+ yAxis: { type: 'value' },
229
+ series: [{ data: [120, 200, 150], type: 'bar' }]
230
+ },
231
+ theme: customTheme
232
+ });
233
+
234
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
235
+ }
236
+ ```
237
+
238
+ ### Chart Linkage
239
+
240
+ Connect multiple charts to synchronize their interactions (e.g., tooltip, highlight).
241
+
242
+ ```tsx
243
+ import { useRef } from 'react';
244
+ import { useEcharts } from 'react-use-echarts';
245
+
246
+ function LinkedCharts() {
247
+ const chartRef1 = useRef<HTMLDivElement>(null);
248
+ const chartRef2 = useRef<HTMLDivElement>(null);
249
+
250
+ const xAxisData = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
251
+
252
+ useEcharts(chartRef1, {
253
+ option: {
254
+ xAxis: { type: 'category', data: xAxisData },
255
+ yAxis: { type: 'value' },
256
+ tooltip: { trigger: 'axis' },
257
+ series: [{ data: [120, 200, 150, 80, 70, 110, 130], type: 'line' }]
258
+ },
259
+ group: 'my-chart-group'
260
+ });
261
+
262
+ useEcharts(chartRef2, {
263
+ option: {
264
+ xAxis: { type: 'category', data: xAxisData },
265
+ yAxis: { type: 'value' },
266
+ tooltip: { trigger: 'axis' },
267
+ series: [{ data: [220, 180, 191, 234, 290, 330, 310], type: 'bar' }]
268
+ },
269
+ group: 'my-chart-group'
270
+ });
271
+
272
+ return (
273
+ <div>
274
+ <div ref={chartRef1} style={{ width: '100%', height: '300px' }} />
275
+ <div ref={chartRef2} style={{ width: '100%', height: '300px' }} />
276
+ </div>
277
+ );
278
+ }
279
+ ```
280
+
281
+ ### Lazy Initialization
282
+
283
+ Only initialize charts when they enter the viewport, great for pages with many charts.
284
+
285
+ ```tsx
286
+ import { useRef } from 'react';
287
+ import { useEcharts } from 'react-use-echarts';
288
+
289
+ function LazyChart() {
290
+ const chartRef = useRef<HTMLDivElement>(null);
291
+
292
+ useEcharts(chartRef, {
293
+ option: {
294
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
295
+ yAxis: { type: 'value' },
296
+ series: [{ data: [120, 200, 150], type: 'bar' }]
297
+ },
298
+ lazyInit: true // Chart will only initialize when scrolled into view
299
+ });
300
+
301
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
302
+ }
303
+
304
+ // With custom IntersectionObserver options
305
+ function LazyChartWithOptions() {
306
+ const chartRef = useRef<HTMLDivElement>(null);
307
+
308
+ useEcharts(chartRef, {
309
+ option: {
310
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
311
+ yAxis: { type: 'value' },
312
+ series: [{ data: [120, 200, 150], type: 'bar' }]
313
+ },
314
+ lazyInit: {
315
+ rootMargin: '100px', // Pre-load when 100px away from viewport
316
+ threshold: 0.1
317
+ }
318
+ });
319
+
320
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
321
+ }
322
+ ```
323
+
324
+ ### SVG Renderer
325
+
326
+ Use SVG renderer instead of Canvas for better accessibility and print quality.
327
+
328
+ ```tsx
329
+ import { useRef } from 'react';
330
+ import { useEcharts } from 'react-use-echarts';
331
+
332
+ function SVGChart() {
333
+ const chartRef = useRef<HTMLDivElement>(null);
334
+
335
+ useEcharts(chartRef, {
336
+ option: {
337
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
338
+ yAxis: { type: 'value' },
339
+ series: [{ data: [120, 200, 150], type: 'bar' }]
340
+ },
341
+ renderer: 'svg'
342
+ });
343
+
344
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
345
+ }
346
+ ```
347
+
348
+ ### Accessing ECharts Instance
349
+
350
+ ```tsx
351
+ import { useRef } from 'react';
352
+ import { useEcharts } from 'react-use-echarts';
353
+
354
+ function ChartWithInstance() {
355
+ const chartRef = useRef<HTMLDivElement>(null);
356
+
357
+ const { getInstance } = useEcharts(chartRef, {
358
+ option: {
359
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
360
+ yAxis: { type: 'value' },
361
+ series: [{ data: [120, 200, 150], type: 'bar' }]
362
+ }
363
+ });
364
+
365
+ const exportImage = () => {
366
+ const instance = getInstance();
367
+ if (instance) {
368
+ const url = instance.getDataURL({
369
+ type: 'png',
370
+ pixelRatio: 2,
371
+ backgroundColor: '#fff'
372
+ });
373
+ // Download or use the image URL
374
+ const link = document.createElement('a');
375
+ link.download = 'chart.png';
376
+ link.href = url;
377
+ link.click();
378
+ }
379
+ };
380
+
381
+ return (
382
+ <div>
383
+ <button onClick={exportImage}>Export as Image</button>
384
+ <div ref={chartRef} style={{ width: '100%', height: '400px' }} />
385
+ </div>
386
+ );
387
+ }
388
+ ```
389
+
390
+ ### Manual Resize
391
+
392
+ ```tsx
393
+ import { useRef } from 'react';
394
+ import { useEcharts } from 'react-use-echarts';
395
+
396
+ function ResizableChart() {
397
+ const chartRef = useRef<HTMLDivElement>(null);
398
+
399
+ const { resize } = useEcharts(chartRef, {
400
+ option: {
401
+ xAxis: { type: 'category', data: ['A', 'B', 'C'] },
402
+ yAxis: { type: 'value' },
403
+ series: [{ data: [120, 200, 150], type: 'bar' }]
404
+ }
405
+ });
406
+
407
+ // Manually trigger resize after some container size change
408
+ const handleContainerResize = () => {
409
+ resize();
410
+ };
411
+
412
+ return (
413
+ <div>
414
+ <button onClick={handleContainerResize}>Trigger Resize</button>
415
+ <div ref={chartRef} style={{ width: '100%', height: '400px' }} />
416
+ </div>
417
+ );
418
+ }
419
+ ```
420
+
69
421
  ## 📖 API
70
422
 
71
423
  ### useEcharts
72
424
 
73
425
  The main hook for using ECharts in React components.
74
426
 
427
+ #### Parameters
428
+
75
429
  ```tsx
430
+ import { useRef } from 'react';
76
431
  import type { EChartsOption } from 'echarts';
77
432
  import type { UseEchartsOptions } from 'react-use-echarts';
433
+ import { useEcharts } from 'react-use-echarts';
78
434
 
79
- const { chartRef, setOption, getInstance } = useEcharts({
80
- option: EChartsOption; // ECharts options configuration (required)
81
- theme?: string | object; // ECharts theme name or configuration
82
- notMerge?: boolean; // Whether to not merge with previous options
83
- lazyUpdate?: boolean; // Whether to update chart lazily
84
- showLoading?: boolean; // Whether to display loading animation
85
- loadingOption?: object; // Loading animation configuration
86
- onEvents?: { // Event handlers
87
- [eventName: string]: {
88
- handler: (params: any) => void;
89
- query?: string | object;
90
- context?: object;
91
- }
92
- }
93
- });
435
+ const chartRef = useRef<HTMLDivElement>(null);
436
+
437
+ const someEchartsOption: EChartsOption = {
438
+ xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
439
+ yAxis: { type: 'value' },
440
+ series: [{ type: 'line', data: [120, 200, 150] }],
441
+ };
442
+
443
+ const options: UseEchartsOptions = {
444
+ option: someEchartsOption, // Required: ECharts options
445
+ theme: 'dark', // Optional: 'light' | 'dark' | 'macarons' | custom object | null
446
+ renderer: 'canvas', // Optional: 'canvas' | 'svg' (default: 'canvas')
447
+ lazyInit: false, // Optional: boolean | IntersectionObserverInit
448
+ group: 'my-group', // Optional: Group ID for chart linkage
449
+ setOptionOpts: { notMerge: false }, // Optional: Default setOption options
450
+ showLoading: false, // Optional: Show loading state
451
+ loadingOption: { text: 'Loading…' }, // Optional: Loading options
452
+ onEvents: {
453
+ click: {
454
+ handler: (params) => {
455
+ console.log(params);
456
+ },
457
+ query: 'series', // Optional: Event query
458
+ },
459
+ },
460
+ };
461
+
462
+ const { setOption, getInstance, resize } = useEcharts(chartRef, options);
94
463
  ```
95
464
 
465
+ #### Options
466
+
467
+ | Property | Type | Default | Description |
468
+ |----------|------|---------|-------------|
469
+ | `option` | `EChartsOption` | **required** | ECharts configuration options |
470
+ | `theme` | `'light' \| 'dark' \| 'macarons' \| object \| null` | `null` | Theme name or custom theme object |
471
+ | `renderer` | `'canvas' \| 'svg'` | `'canvas'` | Renderer type |
472
+ | `lazyInit` | `boolean \| IntersectionObserverInit` | `false` | Lazy initialization config |
473
+ | `group` | `string` | - | Group ID for chart linkage |
474
+ | `setOptionOpts` | `SetOptionOpts` | - | Default options for setOption |
475
+ | `showLoading` | `boolean` | `false` | Show loading state |
476
+ | `loadingOption` | `object` | - | Loading configuration |
477
+ | `onEvents` | `EChartsEvents` | - | Event handlers |
478
+
96
479
  #### Returns
97
480
 
98
- - `chartRef`: Ref object to attach to the chart container
99
- - `setOption`: Function to update chart options
100
- - `getInstance`: Function to get the ECharts instance (available after component mounts)
481
+ ```tsx
482
+ {
483
+ setOption: (option: EChartsOption, opts?: SetOptionOpts) => void;
484
+ getInstance: () => ECharts | undefined;
485
+ resize: () => void;
486
+ }
487
+ ```
488
+
489
+ - **`setOption`**: Update chart options dynamically
490
+ - **`getInstance`**: Get the ECharts instance (returns `undefined` before initialization)
491
+ - **`resize`**: Manually trigger chart resize
492
+
493
+ ### Theme Utilities
494
+
495
+ ```tsx
496
+ import {
497
+ registerBuiltinThemes,
498
+ getBuiltinTheme,
499
+ isBuiltinTheme,
500
+ registerCustomTheme,
501
+ getAvailableThemes,
502
+ } from 'react-use-echarts';
503
+
504
+ // Get all available built-in theme names
505
+ const themes = getAvailableThemes(); // ['light', 'dark', 'macarons']
506
+
507
+ // Check if a theme name is built-in
508
+ isBuiltinTheme('dark'); // true
509
+ isBuiltinTheme('custom'); // false
510
+
511
+ // Get built-in theme configuration
512
+ const darkTheme = getBuiltinTheme('dark');
513
+
514
+ // Register a custom theme globally
515
+ registerCustomTheme('my-theme', { color: ['#ff0000', '#00ff00'] });
516
+ ```
517
+
518
+ ### useLazyInit
519
+
520
+ A standalone hook for lazy initialization using IntersectionObserver.
521
+
522
+ ```tsx
523
+ import { useRef } from 'react';
524
+ import { useLazyInit } from 'react-use-echarts';
525
+
526
+ function MyComponent() {
527
+ const elementRef = useRef<HTMLDivElement>(null);
528
+
529
+ // Returns true when element enters viewport
530
+ const isInView = useLazyInit(elementRef, {
531
+ rootMargin: '50px',
532
+ threshold: 0.1
533
+ });
534
+
535
+ return (
536
+ <div ref={elementRef}>
537
+ {isInView ? <ExpensiveComponent /> : <Placeholder />}
538
+ </div>
539
+ );
540
+ }
541
+ ```
101
542
 
102
543
  ## 🤝 Contributing
103
544
 
104
545
  We welcome all contributions. Please read our [contributing guidelines](CONTRIBUTING.md) first. You can submit any ideas as [pull requests](https://github.com/chensid/react-use-echarts/pulls) or as [GitHub issues](https://github.com/chensid/react-use-echarts/issues).
105
546
 
547
+ ## 🔄 Migration Guide
548
+
549
+ ### From v0.0.11 to v1.0
550
+
551
+ #### Breaking Change: External Ref Management
552
+
553
+ The `useEcharts` hook no longer returns a `chartRef`. Instead, you now create and manage the ref externally:
554
+
555
+ **Before (v0.0.11):**
556
+ ```tsx
557
+ import { useEcharts } from 'react-use-echarts';
558
+
559
+ function MyChart() {
560
+ const { chartRef, setOption, getInstance } = useEcharts({
561
+ option: { /* ... */ }
562
+ });
563
+
564
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
565
+ }
566
+ ```
567
+
568
+ **After (v1.0):**
569
+ ```tsx
570
+ import { useRef } from 'react';
571
+ import { useEcharts } from 'react-use-echarts';
572
+
573
+ function MyChart() {
574
+ const chartRef = useRef<HTMLDivElement>(null);
575
+
576
+ const { setOption, getInstance, resize } = useEcharts(chartRef, {
577
+ option: { /* ... */ }
578
+ });
579
+
580
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
581
+ }
582
+ ```
583
+
584
+ #### New Features in v1.0
585
+
586
+ - **Built-in themes**: Use `theme: 'light' | 'dark' | 'macarons'` or pass a custom theme object
587
+ - **Chart linkage**: Connect charts using the `group` option
588
+ - **Lazy initialization**: Use `lazyInit: true` or custom `IntersectionObserverInit` options
589
+ - **SVG renderer**: Use `renderer: 'svg'` for better accessibility and print quality
590
+ - **Manual resize**: New `resize()` function returned from the hook
591
+
592
+ #### Important Notes for Custom Themes
593
+
594
+ When using custom theme objects, **memoize them** to avoid unnecessary chart recreation:
595
+
596
+ ```tsx
597
+ import { useRef, useMemo } from 'react';
598
+ import { useEcharts } from 'react-use-echarts';
599
+
600
+ function MyChart() {
601
+ const chartRef = useRef<HTMLDivElement>(null);
602
+
603
+ // ✅ Good: Memoized theme object
604
+ const customTheme = useMemo(() => ({
605
+ color: ['#fc8452', '#9a60b4', '#ea7ccc'],
606
+ backgroundColor: '#1e1e1e'
607
+ }), []);
608
+
609
+ useEcharts(chartRef, {
610
+ option: { /* ... */ },
611
+ theme: customTheme
612
+ });
613
+
614
+ return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
615
+ }
616
+ ```
617
+
106
618
  ## 📝 Changelog
107
619
 
108
620
  Detailed changes for each release are documented in the [release notes](https://github.com/chensid/react-use-echarts/releases).
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { UseEchartsOptions, UseEchartsReturn } from '../types';
2
+ /**
3
+ * React hook for Apache ECharts integration (v1.0)
4
+ * Apache ECharts React Hook (v1.0)
5
+ * @param ref React ref to the chart container element
6
+ * @param options Configuration options
7
+ * @returns Chart control methods
8
+ */
9
+ declare function useEcharts(ref: React.RefObject<HTMLDivElement | null>, options: UseEchartsOptions): UseEchartsReturn;
10
+ export default useEcharts;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Hook for lazy initialization using IntersectionObserver
3
+ * 使用 IntersectionObserver 的懒加载 Hook
4
+ * @param elementRef Element reference to observe
5
+ * @param options IntersectionObserver options or false to disable lazy init
6
+ * @returns Whether the element is in viewport (or true if lazy init is disabled)
7
+ */
8
+ export declare function useLazyInit(elementRef: React.RefObject<Element | null>, options?: boolean | IntersectionObserverInit): boolean;