@agions/taroviz 1.5.0 → 1.7.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.
@@ -0,0 +1,624 @@
1
+ /**
2
+ * TaroViz 增强版主题编辑器组件
3
+ * 提供实时预览、导入导出、预设主题等高级功能
4
+ */
5
+ import React, { useState, useEffect, useCallback } from 'react';
6
+ import { ThemeOptions, getRegisteredThemes, registerTheme, switchTheme } from '../themes';
7
+ import { LineChart } from '../charts';
8
+
9
+ export interface EnhancedThemeEditorProps {
10
+ /** 当前选中的主题名称 */
11
+ selectedTheme?: string;
12
+ /** 主题变更回调 */
13
+ onThemeChange?: (theme: ThemeOptions) => void;
14
+ /** 主题保存回调 */
15
+ onThemeSave?: (theme: ThemeOptions) => void;
16
+ /** 是否禁用编辑器 */
17
+ disabled?: boolean;
18
+ /** 是否启用实时预览 */
19
+ enableLivePreview?: boolean;
20
+ /** 是否启用导入导出 */
21
+ enableImportExport?: boolean;
22
+ /** 是否启用预设主题 */
23
+ enablePresets?: boolean;
24
+ /** 编辑器样式 */
25
+ style?: React.CSSProperties;
26
+ /** 编辑器类名 */
27
+ className?: string;
28
+ }
29
+
30
+ export interface ThemeExportOptions {
31
+ format: 'json' | 'css' | 'scss';
32
+ includeVariables: boolean;
33
+ minify: boolean;
34
+ }
35
+
36
+ const PRESET_THEMES: ThemeOptions[] = [
37
+ {
38
+ name: '预设-科技蓝',
39
+ colors: ['#0050b3', '#1890ff', '#40a9ff', '#69c0ff', '#bae7ff'],
40
+ backgroundColor: '#001529',
41
+ textColor: '#ffffff',
42
+ darkMode: true,
43
+ },
44
+ {
45
+ name: '预设-活力橙',
46
+ colors: ['#d46b08', '#ff7a45', '#ffa940', '#ffc53d', '#ffe58f'],
47
+ backgroundColor: '#f5f5f5',
48
+ textColor: '#333333',
49
+ darkMode: false,
50
+ },
51
+ {
52
+ name: '预设-森林绿',
53
+ colors: ['#237804', '#52c41a', '#73d13d', '#95de64', '#b7eb8f'],
54
+ backgroundColor: '#f6ffed',
55
+ textColor: '#333333',
56
+ darkMode: false,
57
+ },
58
+ {
59
+ name: '预设-神秘紫',
60
+ colors: ['#531dab', '#722ed1', '#9254de', '#b37feb', '#d3adf7'],
61
+ backgroundColor: '#f9f0ff',
62
+ textColor: '#333333',
63
+ darkMode: false,
64
+ },
65
+ {
66
+ name: '预设-商务灰',
67
+ colors: ['#434343', '#595959', '#8c8c8c', '#bfbfbf', '#e8e8e8'],
68
+ backgroundColor: '#fafafa',
69
+ textColor: '#333333',
70
+ darkMode: false,
71
+ },
72
+ ];
73
+
74
+ const EnhancedThemeEditor: React.FC<EnhancedThemeEditorProps> = ({
75
+ selectedTheme,
76
+ onThemeChange,
77
+ onThemeSave,
78
+ disabled = false,
79
+ enableLivePreview = true,
80
+ enableImportExport = true,
81
+ enablePresets = true,
82
+ style = {},
83
+ className = '',
84
+ }) => {
85
+ const registeredThemes = getRegisteredThemes();
86
+
87
+ // 状态
88
+ const [currentTheme, setCurrentTheme] = useState<ThemeOptions>(
89
+ registeredThemes.find(t => t.name === selectedTheme) || registeredThemes[0] || {}
90
+ );
91
+ const [newThemeName, setNewThemeName] = useState<string>('');
92
+ const [isEditing, setIsEditing] = useState<boolean>(false);
93
+ const [colors, setColors] = useState<string[]>(currentTheme.colors || []);
94
+ const [backgroundColor, setBackgroundColor] = useState<string>(currentTheme.backgroundColor || '#ffffff');
95
+ const [textColor, setTextColor] = useState<string>(currentTheme.textColor || '#333333');
96
+ const [darkMode, setDarkMode] = useState<boolean>(currentTheme.darkMode || false);
97
+ const [activeTab, setActiveTab] = useState<'basic' | 'advanced' | 'presets'>('basic');
98
+
99
+ // 预览数据
100
+ const previewOption = {
101
+ title: { text: '主题预览', textStyle: { color: textColor } },
102
+ xAxis: { type: 'category' as const, data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], axisLabel: { color: textColor } },
103
+ yAxis: { type: 'value' as const, axisLabel: { color: textColor } },
104
+ series: [
105
+ { data: [120, 200, 150, 80, 70], type: 'bar' as const, itemStyle: { color: colors[0] || '#1890ff' } },
106
+ { data: [120, 200, 150, 80, 70].map((v) => ({ value: v * 1.2, itemStyle: { color: colors[1] || '#40a9ff' } })), type: 'bar' as const },
107
+ ],
108
+ backgroundColor: backgroundColor,
109
+ };
110
+
111
+ // 同步主题状态
112
+ useEffect(() => {
113
+ if (selectedTheme) {
114
+ const theme = registeredThemes.find(t => t.name === selectedTheme);
115
+ if (theme) {
116
+ setCurrentTheme(theme);
117
+ setColors(theme.colors || []);
118
+ setBackgroundColor(theme.backgroundColor || '#ffffff');
119
+ setTextColor(theme.textColor || '#333333');
120
+ setDarkMode(theme.darkMode || false);
121
+ }
122
+ }
123
+ }, [selectedTheme, registeredThemes]);
124
+
125
+ // 更新主题
126
+ const updateTheme = useCallback((updates: Partial<ThemeOptions>) => {
127
+ const updated = { ...currentTheme, ...updates };
128
+ setCurrentTheme(updated);
129
+ setColors(updated.colors || []);
130
+ setBackgroundColor(updated.backgroundColor || '#ffffff');
131
+ setTextColor(updated.textColor || '#333333');
132
+ setDarkMode(updated.darkMode || false);
133
+
134
+ if (enableLivePreview) {
135
+ switchTheme(updated);
136
+ }
137
+ onThemeChange?.(updated);
138
+ }, [currentTheme, enableLivePreview, onThemeChange]);
139
+
140
+ // 处理颜色变化
141
+ const handleColorChange = (index: number, color: string) => {
142
+ const newColors = [...colors];
143
+ newColors[index] = color;
144
+ updateTheme({ colors: newColors });
145
+ };
146
+
147
+ // 添加颜色
148
+ const handleAddColor = () => {
149
+ const newColors = [...colors, '#000000'];
150
+ updateTheme({ colors: newColors });
151
+ };
152
+
153
+ // 删除颜色
154
+ const handleRemoveColor = (index: number) => {
155
+ if (colors.length <= 1) return;
156
+ const newColors = colors.filter((_, i) => i !== index);
157
+ updateTheme({ colors: newColors });
158
+ };
159
+
160
+ // 选择预设主题
161
+ const handlePresetSelect = (preset: ThemeOptions) => {
162
+ updateTheme({
163
+ ...preset,
164
+ name: isEditing && newThemeName ? newThemeName : preset.name,
165
+ });
166
+ if (!isEditing) {
167
+ switchTheme(preset);
168
+ onThemeChange?.(preset);
169
+ }
170
+ };
171
+
172
+ // 保存主题
173
+ const handleSaveTheme = () => {
174
+ const name = isEditing && newThemeName ? newThemeName : currentTheme.name || 'custom';
175
+ const themeToSave: ThemeOptions = {
176
+ ...currentTheme,
177
+ name,
178
+ colors,
179
+ backgroundColor,
180
+ textColor,
181
+ darkMode,
182
+ };
183
+
184
+ registerTheme(name, themeToSave);
185
+ setIsEditing(false);
186
+ setNewThemeName('');
187
+
188
+ if (onThemeSave) {
189
+ onThemeSave(themeToSave);
190
+ }
191
+ };
192
+
193
+ // 导出主题
194
+ const handleExportTheme = (format: 'json' | 'css' | 'scss') => {
195
+ const theme: ThemeOptions = {
196
+ ...currentTheme,
197
+ colors,
198
+ backgroundColor,
199
+ textColor,
200
+ darkMode,
201
+ };
202
+
203
+ let content = '';
204
+ let filename = '';
205
+ let mimeType = '';
206
+
207
+ if (format === 'json') {
208
+ content = JSON.stringify(theme, null, 2);
209
+ filename = `${theme.name || 'theme'}.json`;
210
+ mimeType = 'application/json';
211
+ } else if (format === 'css') {
212
+ content = `:root {\n --theme-colors: ${colors.join(', ')};\n --theme-bg: ${backgroundColor};\n --theme-text: ${textColor};\n}`;
213
+ filename = `${theme.name || 'theme'}.css`;
214
+ mimeType = 'text/css';
215
+ } else {
216
+ content = `$theme-colors: (${colors.join(', ')});\n$theme-bg: ${backgroundColor};\n$theme-text: ${textColor};`;
217
+ filename = `${theme.name || 'theme'}.scss`;
218
+ mimeType = 'text/x-scss';
219
+ }
220
+
221
+ const blob = new Blob([content], { type: mimeType });
222
+ const url = URL.createObjectURL(blob);
223
+ const a = document.createElement('a');
224
+ a.href = url;
225
+ a.download = filename;
226
+ a.click();
227
+ URL.revokeObjectURL(url);
228
+ };
229
+
230
+ // 导入主题
231
+ const handleImportTheme = (event: React.ChangeEvent<HTMLInputElement>) => {
232
+ const file = event.target.files?.[0];
233
+ if (!file) return;
234
+
235
+ const reader = new FileReader();
236
+ reader.onload = (e) => {
237
+ try {
238
+ const content = e.target?.result as string;
239
+ if (file.name.endsWith('.json')) {
240
+ const theme = JSON.parse(content) as ThemeOptions;
241
+ updateTheme(theme);
242
+ } else {
243
+ alert('请选择 JSON 格式的主题文件');
244
+ }
245
+ } catch {
246
+ alert('文件格式错误');
247
+ }
248
+ };
249
+ reader.readAsText(file);
250
+ };
251
+
252
+ // 开始编辑新主题
253
+ const handleStartEdit = () => {
254
+ setIsEditing(true);
255
+ setNewThemeName('');
256
+ };
257
+
258
+ return (
259
+ <div
260
+ className={`taroviz-enhanced-theme-editor ${className}`}
261
+ style={{
262
+ padding: '20px',
263
+ border: '1px solid #e0e0e0',
264
+ borderRadius: '8px',
265
+ backgroundColor: '#ffffff',
266
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
267
+ ...style,
268
+ }}
269
+ >
270
+ <h3 style={{ marginBottom: '20px' }}>增强版主题编辑器</h3>
271
+
272
+ {/* 标签页 */}
273
+ <div style={{ display: 'flex', borderBottom: '1px solid #e0e0e0', marginBottom: '20px' }}>
274
+ {(['basic', 'advanced', 'presets'] as const).map(tab => (
275
+ <button
276
+ key={tab}
277
+ onClick={() => setActiveTab(tab)}
278
+ style={{
279
+ padding: '10px 20px',
280
+ border: 'none',
281
+ borderBottom: activeTab === tab ? '2px solid #1890ff' : '2px solid transparent',
282
+ backgroundColor: 'transparent',
283
+ color: activeTab === tab ? '#1890ff' : '#666',
284
+ cursor: 'pointer',
285
+ fontWeight: activeTab === tab ? 'bold' : 'normal',
286
+ }}
287
+ >
288
+ {tab === 'basic' ? '基础配置' : tab === 'advanced' ? '高级配置' : '预设主题'}
289
+ </button>
290
+ ))}
291
+ </div>
292
+
293
+ {/* 基础配置 */}
294
+ {activeTab === 'basic' && (
295
+ <>
296
+ {/* 主题选择 */}
297
+ <div style={{ marginBottom: '20px' }}>
298
+ <h4>选择主题</h4>
299
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', marginTop: '10px' }}>
300
+ {registeredThemes.map(theme => (
301
+ <button
302
+ key={theme.name}
303
+ onClick={() => {
304
+ setIsEditing(false);
305
+ updateTheme(theme);
306
+ }}
307
+ disabled={disabled}
308
+ style={{
309
+ padding: '8px 16px',
310
+ border: `2px solid ${currentTheme.name === theme.name ? '#1890ff' : '#e0e0e0'}`,
311
+ borderRadius: '4px',
312
+ backgroundColor: currentTheme.name === theme.name ? '#1890ff' : '#ffffff',
313
+ color: currentTheme.name === theme.name ? '#ffffff' : '#333',
314
+ cursor: disabled ? 'not-allowed' : 'pointer',
315
+ opacity: disabled ? 0.6 : 1,
316
+ }}
317
+ >
318
+ {theme.name}
319
+ </button>
320
+ ))}
321
+ <button
322
+ onClick={handleStartEdit}
323
+ disabled={disabled}
324
+ style={{
325
+ padding: '8px 16px',
326
+ border: '2px dashed #e0e0e0',
327
+ borderRadius: '4px',
328
+ backgroundColor: '#f5f5f5',
329
+ color: '#333',
330
+ cursor: disabled ? 'not-allowed' : 'pointer',
331
+ opacity: disabled ? 0.6 : 1,
332
+ }}
333
+ >
334
+ + 新主题
335
+ </button>
336
+ </div>
337
+ </div>
338
+
339
+ {/* 新主题编辑 */}
340
+ {isEditing && (
341
+ <div style={{ marginBottom: '20px', padding: '15px', backgroundColor: '#f9f9f9', borderRadius: '4px' }}>
342
+ <h4>新建主题</h4>
343
+ <input
344
+ type="text"
345
+ value={newThemeName}
346
+ onChange={(e) => setNewThemeName(e.target.value)}
347
+ disabled={disabled}
348
+ placeholder="输入主题名称"
349
+ style={{
350
+ padding: '8px',
351
+ border: '1px solid #e0e0e0',
352
+ borderRadius: '4px',
353
+ width: '100%',
354
+ marginTop: '10px',
355
+ opacity: disabled ? 0.6 : 1,
356
+ }}
357
+ />
358
+ </div>
359
+ )}
360
+
361
+ {/* 颜色配置 */}
362
+ <div style={{ marginBottom: '20px' }}>
363
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
364
+ <h4>主题颜色</h4>
365
+ <button onClick={handleAddColor} disabled={disabled} style={{
366
+ padding: '4px 8px',
367
+ border: '1px solid #e0e0e0',
368
+ borderRadius: '4px',
369
+ backgroundColor: '#ffffff',
370
+ cursor: disabled ? 'not-allowed' : 'pointer',
371
+ opacity: disabled ? 0.6 : 1,
372
+ }}>
373
+ + 添加颜色
374
+ </button>
375
+ </div>
376
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px' }}>
377
+ {colors.map((color, index) => (
378
+ <div key={index} style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
379
+ <input
380
+ type="color"
381
+ value={color}
382
+ onChange={(e) => handleColorChange(index, e.target.value)}
383
+ disabled={disabled}
384
+ style={{ width: '50px', height: '30px', border: 'none', cursor: 'pointer' }}
385
+ />
386
+ <input
387
+ type="text"
388
+ value={color}
389
+ onChange={(e) => handleColorChange(index, e.target.value)}
390
+ disabled={disabled}
391
+ style={{ width: '80px', padding: '4px', border: '1px solid #e0e0e0', borderRadius: '4px' }}
392
+ />
393
+ <button
394
+ onClick={() => handleRemoveColor(index)}
395
+ disabled={disabled || colors.length <= 1}
396
+ style={{
397
+ padding: '4px 8px',
398
+ border: '1px solid #ff4d4f',
399
+ borderRadius: '4px',
400
+ backgroundColor: '#ffffff',
401
+ color: '#ff4d4f',
402
+ cursor: disabled || colors.length <= 1 ? 'not-allowed' : 'pointer',
403
+ opacity: disabled || colors.length <= 1 ? 0.6 : 1,
404
+ }}
405
+ >
406
+ 删除
407
+ </button>
408
+ </div>
409
+ ))}
410
+ </div>
411
+ </div>
412
+
413
+ {/* 基础配置 */}
414
+ <div style={{ marginBottom: '20px' }}>
415
+ <h4>基础配置</h4>
416
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '15px', marginTop: '10px' }}>
417
+ <div>
418
+ <label style={{ display: 'block', marginBottom: '5px' }}>背景色:</label>
419
+ <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
420
+ <input
421
+ type="color"
422
+ value={backgroundColor}
423
+ onChange={(e) => updateTheme({ backgroundColor: e.target.value })}
424
+ disabled={disabled}
425
+ style={{ width: '50px', height: '30px', border: 'none', cursor: 'pointer' }}
426
+ />
427
+ <input
428
+ type="text"
429
+ value={backgroundColor}
430
+ onChange={(e) => updateTheme({ backgroundColor: e.target.value })}
431
+ disabled={disabled}
432
+ style={{ width: '100px', padding: '4px', border: '1px solid #e0e0e0', borderRadius: '4px' }}
433
+ />
434
+ </div>
435
+ </div>
436
+ <div>
437
+ <label style={{ display: 'block', marginBottom: '5px' }}>文本颜色:</label>
438
+ <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
439
+ <input
440
+ type="color"
441
+ value={textColor}
442
+ onChange={(e) => updateTheme({ textColor: e.target.value })}
443
+ disabled={disabled}
444
+ style={{ width: '50px', height: '30px', border: 'none', cursor: 'pointer' }}
445
+ />
446
+ <input
447
+ type="text"
448
+ value={textColor}
449
+ onChange={(e) => updateTheme({ textColor: e.target.value })}
450
+ disabled={disabled}
451
+ style={{ width: '100px', padding: '4px', border: '1px solid #e0e0e0', borderRadius: '4px' }}
452
+ />
453
+ </div>
454
+ </div>
455
+ </div>
456
+ <div style={{ marginTop: '10px' }}>
457
+ <label style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
458
+ <input
459
+ type="checkbox"
460
+ checked={darkMode}
461
+ onChange={(e) => updateTheme({ darkMode: e.target.checked })}
462
+ disabled={disabled}
463
+ />
464
+ 深色模式
465
+ </label>
466
+ </div>
467
+ </div>
468
+ </>
469
+ )}
470
+
471
+ {/* 预设主题 */}
472
+ {activeTab === 'presets' && enablePresets && (
473
+ <div>
474
+ <h4>选择预设主题</h4>
475
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '15px', marginTop: '15px' }}>
476
+ {PRESET_THEMES.map(preset => (
477
+ <div
478
+ key={preset.name}
479
+ onClick={() => handlePresetSelect(preset)}
480
+ style={{
481
+ padding: '15px',
482
+ border: '2px solid #e0e0e0',
483
+ borderRadius: '8px',
484
+ cursor: 'pointer',
485
+ backgroundColor: preset.backgroundColor,
486
+ color: preset.textColor,
487
+ transition: 'all 0.2s',
488
+ }}
489
+ >
490
+ <div style={{ display: 'flex', gap: '5px', marginBottom: '10px' }}>
491
+ {(preset.colors || []).map((color, i) => (
492
+ <div
493
+ key={i}
494
+ style={{
495
+ width: '30px',
496
+ height: '30px',
497
+ backgroundColor: color,
498
+ borderRadius: '4px',
499
+ }}
500
+ />
501
+ ))}
502
+ </div>
503
+ <div style={{ fontSize: '14px', fontWeight: 'bold' }}>{preset.name}</div>
504
+ <div style={{ fontSize: '12px', opacity: 0.7 }}>{preset.darkMode ? '深色' : '浅色'}</div>
505
+ </div>
506
+ ))}
507
+ </div>
508
+ </div>
509
+ )}
510
+
511
+ {/* 高级配置 */}
512
+ {activeTab === 'advanced' && (
513
+ <div>
514
+ <h4>导入导出</h4>
515
+ {enableImportExport && (
516
+ <div style={{ display: 'flex', gap: '10px', marginTop: '15px' }}>
517
+ <button
518
+ onClick={() => handleExportTheme('json')}
519
+ disabled={disabled}
520
+ style={{
521
+ padding: '8px 16px',
522
+ border: '1px solid #e0e0e0',
523
+ borderRadius: '4px',
524
+ backgroundColor: '#ffffff',
525
+ cursor: disabled ? 'not-allowed' : 'pointer',
526
+ opacity: disabled ? 0.6 : 1,
527
+ }}
528
+ >
529
+ 导出 JSON
530
+ </button>
531
+ <button
532
+ onClick={() => handleExportTheme('css')}
533
+ disabled={disabled}
534
+ style={{
535
+ padding: '8px 16px',
536
+ border: '1px solid #e0e0e0',
537
+ borderRadius: '4px',
538
+ backgroundColor: '#ffffff',
539
+ cursor: disabled ? 'not-allowed' : 'pointer',
540
+ opacity: disabled ? 0.6 : 1,
541
+ }}
542
+ >
543
+ 导出 CSS
544
+ </button>
545
+ <label
546
+ style={{
547
+ padding: '8px 16px',
548
+ border: '1px solid #1890ff',
549
+ borderRadius: '4px',
550
+ backgroundColor: '#1890ff',
551
+ color: '#ffffff',
552
+ cursor: 'pointer',
553
+ }}
554
+ >
555
+ 导入 JSON
556
+ <input
557
+ type="file"
558
+ accept=".json"
559
+ onChange={handleImportTheme}
560
+ style={{ display: 'none' }}
561
+ />
562
+ </label>
563
+ </div>
564
+ )}
565
+ </div>
566
+ )}
567
+
568
+ {/* 实时预览 */}
569
+ {enableLivePreview && (
570
+ <div style={{ marginTop: '20px' }}>
571
+ <h4>实时预览</h4>
572
+ <div style={{
573
+ marginTop: '10px',
574
+ border: '1px solid #e0e0e0',
575
+ borderRadius: '8px',
576
+ overflow: 'hidden',
577
+ }}>
578
+ <LineChart
579
+ option={previewOption}
580
+ width="100%"
581
+ height={200}
582
+ />
583
+ </div>
584
+ </div>
585
+ )}
586
+
587
+ {/* 保存按钮 */}
588
+ <div style={{ marginTop: '20px', display: 'flex', gap: '10px' }}>
589
+ <button
590
+ onClick={handleSaveTheme}
591
+ disabled={disabled}
592
+ style={{
593
+ padding: '10px 20px',
594
+ border: 'none',
595
+ borderRadius: '4px',
596
+ backgroundColor: '#1890ff',
597
+ color: '#ffffff',
598
+ cursor: disabled ? 'not-allowed' : 'pointer',
599
+ opacity: disabled ? 0.6 : 1,
600
+ }}
601
+ >
602
+ 保存主题
603
+ </button>
604
+ <button
605
+ onClick={() => updateTheme(currentTheme)}
606
+ disabled={disabled}
607
+ style={{
608
+ padding: '10px 20px',
609
+ border: '1px solid #e0e0e0',
610
+ borderRadius: '4px',
611
+ backgroundColor: '#ffffff',
612
+ color: '#333',
613
+ cursor: disabled ? 'not-allowed' : 'pointer',
614
+ opacity: disabled ? 0.6 : 1,
615
+ }}
616
+ >
617
+ 重置预览
618
+ </button>
619
+ </div>
620
+ </div>
621
+ );
622
+ };
623
+
624
+ export default EnhancedThemeEditor;
@@ -6,6 +6,9 @@ import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
6
6
  import { getAdapter } from '../adapters';
7
7
  import { getThemeByName } from '../themes';
8
8
  import type { EChartsOption } from 'echarts';
9
+ import { useDataZoom } from './useDataZoom';
10
+ import { useChartConnect } from './useChartConnect';
11
+ import { useChartDownload } from './useChartDownload';
9
12
 
10
13
  // ============================================================================
11
14
  // 类型定义
@@ -616,11 +619,42 @@ export function useChartTools(instance: ChartInstance | null) {
616
619
  return { getInstance, clear, repaint, dispatchAction, showTip, hideTip, zoom };
617
620
  }
618
621
 
622
+ // ============================================================================
623
+ // v1.7.0 新增 Hooks
624
+ // ============================================================================
625
+
626
+ // 数据缩放 Hook
627
+ export {
628
+ useDataZoom,
629
+ type UseDataZoomOptions,
630
+ type UseDataZoomReturn,
631
+ type DataZoomType,
632
+ type ZoomRange,
633
+ } from './useDataZoom';
634
+
635
+ // 图表联动 Hook
636
+ export {
637
+ useChartConnect,
638
+ type UseChartConnectOptions,
639
+ type UseChartConnectReturn,
640
+ type ConnectEventType,
641
+ } from './useChartConnect';
642
+
643
+ // 图表下载 Hook
644
+ export {
645
+ useChartDownload,
646
+ type UseChartDownloadOptions,
647
+ type UseChartDownloadReturn,
648
+ type DownloadFormat,
649
+ type DownloadImageOptions,
650
+ type DownloadDataOptions,
651
+ } from './useChartDownload';
652
+
619
653
  // ============================================================================
620
654
  // 导出
621
655
  // ============================================================================
622
656
 
623
- export const version = '1.4.0';
657
+ export const version = '1.7.0';
624
658
 
625
659
  // 新增数据转换 hooks
626
660
  export {
@@ -644,4 +678,8 @@ export default {
644
678
  useFullscreen,
645
679
  useExport,
646
680
  useChartTools,
681
+ // v1.7.0 新增
682
+ useDataZoom,
683
+ useChartConnect,
684
+ useChartDownload,
647
685
  };