@easyv/charts 1.7.36 → 1.7.37

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.
Files changed (60) hide show
  1. package/.babelrc +8 -8
  2. package/CHANGELOG.md +18 -18
  3. package/commitlint.config.js +1 -1
  4. package/lib/components/Background.js +2 -2
  5. package/lib/components/Band.js +2 -2
  6. package/lib/components/Brush.js +2 -2
  7. package/lib/components/Chart.js +2 -2
  8. package/lib/components/ChartContainer.js +2 -2
  9. package/lib/components/ConicalGradient.js +21 -21
  10. package/lib/components/ExtentData.js +2 -2
  11. package/lib/components/Indicator.js +2 -2
  12. package/lib/components/Label.js +2 -2
  13. package/lib/components/Legend.js +38 -6
  14. package/lib/components/Lighter.js +2 -2
  15. package/lib/components/Line.js +2 -2
  16. package/lib/components/LinearGradient.js +2 -2
  17. package/lib/components/StereoBar.js +2 -2
  18. package/lib/css/index.module.css +39 -42
  19. package/lib/css/piechart.module.css +26 -26
  20. package/lib/formatter/legend.js +55 -50
  21. package/lib/hooks/useAnimateData.js +6 -6
  22. package/lib/hooks/useFilterData.js +5 -5
  23. package/lib/hooks/useStackData.js +5 -5
  24. package/lib/hooks/useTooltip.js +11 -11
  25. package/package.json +54 -54
  26. package/src/components/Background.tsx +61 -61
  27. package/src/components/Band.tsx +334 -334
  28. package/src/components/Brush.js +159 -159
  29. package/src/components/Chart.js +157 -157
  30. package/src/components/ChartContainer.tsx +71 -71
  31. package/src/components/ConicalGradient.js +258 -258
  32. package/src/components/Control.jsx +242 -242
  33. package/src/components/ExtentData.js +18 -18
  34. package/src/components/Indicator.js +61 -61
  35. package/src/components/Label.js +262 -262
  36. package/src/components/Legend.js +289 -267
  37. package/src/components/Lighter.jsx +173 -173
  38. package/src/components/Line.js +153 -153
  39. package/src/components/LinearGradient.js +29 -29
  40. package/src/components/PieTooltip.jsx +160 -160
  41. package/src/components/SplitText.tsx +70 -70
  42. package/src/components/StereoBar.tsx +307 -307
  43. package/src/components/index.js +61 -61
  44. package/src/context/index.js +2 -2
  45. package/src/css/index.module.css +39 -42
  46. package/src/css/piechart.module.css +26 -26
  47. package/src/element/ConicGradient.jsx +55 -55
  48. package/src/element/Line.tsx +33 -33
  49. package/src/element/index.ts +3 -3
  50. package/src/formatter/index.js +1 -1
  51. package/src/formatter/legend.js +122 -115
  52. package/src/hooks/index.js +20 -20
  53. package/src/hooks/useAnimateData.ts +68 -68
  54. package/src/hooks/useFilterData.js +77 -77
  55. package/src/hooks/useStackData.js +140 -140
  56. package/src/hooks/useTooltip.ts +103 -103
  57. package/src/index.js +6 -6
  58. package/src/types/index.d.ts +68 -68
  59. package/src/utils/index.js +812 -812
  60. package/tsconfig.json +23 -23
@@ -1,267 +1,289 @@
1
- /**
2
- * 图例
3
- */
4
- import React, { memo, useCallback, useState, useEffect, useRef } from 'react';
5
- import { getIcon, sortPie } from '../utils';
6
- import TextOverflow from './TextOverflow';
7
-
8
- const defaultFont = {
9
- fontStyle: 'normal',
10
- fontWeight: 'normal',
11
- };
12
-
13
- export default memo(
14
- ({
15
- series,
16
- height,
17
- config,
18
- config: {
19
- show,
20
- order = '',
21
- interactive,
22
- LegendType,//类型
23
- maxWidth,
24
- textOverflow,
25
- speed,
26
- layout: {
27
- alignment = 'right center',
28
- gridTemplateColumns,
29
- gridGap: { gridColumnGap, gridRowGap },
30
- translate: { x, y },
31
- },
32
- loop = {},
33
- font: { italic, bold, ...font } = defaultFont,
34
- unselect: { opacity = 1 } = {},
35
- },
36
- filterData,
37
- formatter,
38
- judge
39
- }) => {
40
- if (!show) return null;
41
-
42
- const ref_container = useRef(null); // 滚动容器
43
- const ref_scrollTop = useRef(0); // 当前滚动距离
44
-
45
- const [scrollStep, setScrollStep] = useState(0); // 行高度
46
-
47
- // 初始化行高
48
- useEffect(() => {
49
- if (ref_container.current) {
50
- const rowHeight = ref_container.current.querySelector('li').clientHeight + gridRowGap;
51
- setScrollStep(rowHeight);
52
- }
53
- }, [gridRowGap]);
54
-
55
- // 启动自动滚动定时器
56
- useEffect(() => {
57
- if (!loop.show) return;
58
-
59
- ref_scrollTop.current = 0;
60
-
61
- const timer = setInterval(() => {
62
- handleAutoScroll();
63
- }, loop.interval * 1000); // 每隔3秒滚动一次
64
-
65
- // 清除定时器
66
- return () => clearInterval(timer);
67
- }, [scrollStep, loop.show, loop.interval])
68
-
69
- const handleAutoScroll = () => {
70
- const table = ref_container.current;
71
- if (!table) return;
72
-
73
- // 如果已经滚动到了底部,则返回顶部
74
- if (ref_scrollTop.current + table.clientHeight >= table.scrollHeight) {
75
- ref_scrollTop.current = 0;
76
- } else {
77
- // 否则,滚动一行的高度
78
- ref_scrollTop.current += scrollStep;
79
- }
80
-
81
- table.scrollTo({ top: ref_scrollTop.current, behavior: 'smooth' });
82
- };
83
-
84
- const _series = sortPie(series, order);
85
- const [_alignment, position] = alignment.split(' ');
86
- const length = _series.length;
87
-
88
- const onClick = useCallback(
89
- (e) => {
90
- const { dataset } = e.currentTarget;
91
- const { name } = dataset;
92
- filterData && interactive && filterData(name);
93
- },
94
- [interactive, filterData]
95
- );
96
-
97
- if (judge == 0) {
98
- _series.forEach((d) => {
99
- d.percent=0
100
- })
101
- }
102
-
103
- return (
104
- LegendType=="FixedWidth"?
105
- <div
106
- className='__easyv-legend-wrapper'
107
- style={{
108
- position: 'absolute',
109
- display: 'flex',
110
- ...getPosition(position, _alignment, x, y),
111
- height: loop.show ? height : 'auto',
112
- overflowY: loop.show ? 'scroll' : 'auto',
113
- pointerEvents:"none"//鼠标穿透
114
- }}
115
- ref={ref_container}
116
- >
117
- <ul
118
- style={{
119
- display: 'grid',
120
- gridGap: gridRowGap + 'px ' + gridColumnGap + 'px',
121
- gridTemplateColumns:
122
- 'repeat(' + Math.min(gridTemplateColumns, length) + ', 1fr)',
123
- }}
124
- >
125
- {_series.map((series, index) => {
126
- const { type, name, displayName, icon, selected } = series;
127
- const _icon = getIcon(type, icon, series?.config?.line?.type);
128
- return (
129
- <li
130
- key={index}
131
- onClick={onClick}
132
- data-name={name}
133
- style={{
134
- display: 'flex',
135
- opacity: selected === false ? opacity / 100 : 1,
136
- alignItems: 'center',
137
- cursor: "pointer",
138
- gap: _icon.gap,
139
- }}
140
- >
141
- {formatter ? (
142
- formatter(series, config)
143
- ) : (
144
- <>
145
- <span style={_icon} />
146
- <TextOverflow type={textOverflow} value={displayName || name} style={{
147
- ...font,
148
- width:maxWidth,
149
- fontStyle: italic ? 'italic' : 'normal',
150
- fontWeight: bold ? 'bold' : 'normal',
151
- }} speed={speed}></TextOverflow>
152
-
153
- </>
154
- )}
155
- </li>
156
- );
157
- })}
158
- </ul>
159
- </div>:<div
160
- className='__easyv-legend-wrapper'
161
- style={{
162
- display:'flex',
163
- flexWrap:"wrap",
164
- alignContent:alignment.split(" ")[0]=="center"&&(alignment.split(" ")[1]=="left"||alignment.split(" ")[1]=="right")?alignment.split(" ")[1]=="left"?"flex-start":"flex-end":alignment.split(" ")[0]=="left"?"flex-start":alignment.split(" ")[0]=="center"?"center":"flex-end",
165
- flexDirection: "column",
166
- position: 'absolute',
167
- ...getPosition(position, _alignment, x, y),
168
- height: loop.show ? height : 'auto',
169
- overflowY: loop.show ? 'scroll' : 'auto',
170
- width:"100%",
171
- pointerEvents:"none"
172
- }}
173
- ref={ref_container}
174
- >
175
-
176
- {[...Array(Math.ceil(series.length/gridTemplateColumns))].map((_,indexs)=>(
177
- <ul
178
- key={indexs}
179
- style={{
180
- display: 'flex',
181
- width: 'fit-content',
182
- justifyContent:alignment.split(" ")[0]=="left"?"flex-start":alignment.split(" ")[0]=="center"?"center":"flex-end",
183
- marginBottom:"0px",
184
- gap:`${gridRowGap}px ${gridColumnGap}px`,
185
- marginBottom:gridRowGap+"px"
186
- }}
187
- >
188
- {_series.map((series, index) => {
189
- if(Math.floor(index/gridTemplateColumns)==indexs){
190
- const { type, name, displayName, icon, selected } = series;
191
- const _icon = getIcon(type, icon, series?.config?.line?.type);
192
- return (
193
- <li
194
- key={index}
195
- onClick={onClick}
196
- data-name={name}
197
- style={{
198
- display: 'flex',
199
- opacity: selected === false ? opacity / 100 : 1,
200
- alignItems: 'center',
201
- cursor: "pointer",
202
- gap: _icon.gap,
203
- }}
204
- >
205
- {formatter ? (
206
- formatter(series, config)
207
- ) : (
208
- <>
209
- <span style={_icon} />
210
- <TextOverflow ShowType={LegendType} type="ellipsis" value={displayName || name} style={{
211
- ...font,
212
- fontStyle: italic ? 'italic' : 'normal',
213
- fontWeight: bold ? 'bold' : 'normal',
214
- minWidth:getCanvasTextWidth(displayName.substring(0,5) || name.substring(0,5),font.letterSpacing,`${font.fontSize}px ${font.fontFamily}` )
215
- }} speed={speed}></TextOverflow>
216
-
217
- </>
218
- )}
219
- </li>
220
- );
221
- }
222
- })}
223
- </ul>
224
- ))
225
- }
226
- </div>
227
- );
228
- }
229
- );
230
-
231
- const getPosition = (position, alignment, x = 0, y = 0) => {
232
- switch (position) {
233
- case 'top':
234
- return {
235
- left: alignment == 'left' ? 5 : alignment == 'center' ? '50%' : '',
236
- right: alignment == 'right' ? 10 : '',
237
- top: 5,
238
- transform: `translate3d(calc(${alignment == 'center' ? '-50%' : '0px'} + ${x}px), ${y}px, 0px)`
239
- };
240
- case 'right':
241
- return {
242
- top: '50%',
243
- right: 10,
244
- transform: `translate3d(${x}px, calc(-50% + ${y}px), 0px)`
245
- };
246
- case 'left':
247
- return {
248
- top: '50%',
249
- left: 5,
250
- transform: `translate3d(${x}px, calc(-50% + ${y}px), 0px)`
251
- };
252
- default: // bottom
253
- return {
254
- left: alignment == 'left' ? 5 : alignment == 'center' ? '50%' : '',
255
- right: alignment == 'right' ? 10 : '',
256
- bottom: 5,
257
- transform: `translate3d(calc(${alignment == 'center' ? '-50%' : '0px'} + ${x}px), ${y}px, 0px)`
258
- };
259
- }
260
- };
261
- const getCanvasTextWidth=(text,letterSpacing, font = '16px Arial')=>{
262
- const canvas = document.createElement('canvas');
263
- const ctx = canvas.getContext('2d');
264
- ctx.font = font;
265
- return ctx.measureText(text).width+(text.length)*letterSpacing+"px";
266
- // return ctx.measureText(text).width+(text.length-1)*letterSpacing+"px";//-1有bug
267
- }
1
+ /**
2
+ * 图例
3
+ */
4
+ import React, { memo, useCallback, useState, useEffect, useRef } from 'react';
5
+ import { getIcon, sortPie } from '../utils';
6
+ import TextOverflow from './TextOverflow';
7
+
8
+ const defaultFont = {
9
+ fontStyle: 'normal',
10
+ fontWeight: 'normal',
11
+ };
12
+
13
+ export default memo(
14
+ ({
15
+ series,
16
+ height,
17
+ config,
18
+ config: {
19
+ show,
20
+ order = '',
21
+ interactive,
22
+ LegendType,//类型
23
+ maxWidth,
24
+ textOverflow,
25
+ speed,
26
+ layout: {
27
+ alignment = 'right center',
28
+ gridTemplateColumns,
29
+ gridGap: { gridColumnGap, gridRowGap },
30
+ translate: { x, y },
31
+ },
32
+ loop = {},
33
+ font: { italic, bold, ...font } = defaultFont,
34
+ unselect: { opacity = 1 } = {},
35
+ },
36
+ filterData,
37
+ formatter,
38
+ judge
39
+ }) => {
40
+ if (!show) return null;
41
+
42
+ const ref_container = useRef(null); // 滚动容器
43
+ const ref_scrollTop = useRef(0); // 当前滚动距离
44
+
45
+ const [scrollStep, setScrollStep] = useState(0); // 行高度
46
+
47
+ // 初始化行高
48
+ useEffect(() => {
49
+ if (ref_container.current) {
50
+ const rowHeight = ref_container.current.querySelector('li').clientHeight + gridRowGap;
51
+ setScrollStep(rowHeight);
52
+ }
53
+ }, [gridRowGap]);
54
+
55
+ // 启动自动滚动定时器
56
+ useEffect(() => {
57
+ if (!loop.show) return;
58
+
59
+ ref_scrollTop.current = 0;
60
+
61
+ const timer = setInterval(() => {
62
+ handleAutoScroll();
63
+ }, loop.interval * 1000); // 每隔3秒滚动一次
64
+
65
+ // 清除定时器
66
+ return () => clearInterval(timer);
67
+ }, [scrollStep, loop.show, loop.interval])
68
+
69
+ const handleAutoScroll = () => {
70
+ const table = ref_container.current;
71
+ if (!table) return;
72
+
73
+ // 如果已经滚动到了底部,则返回顶部
74
+ if (ref_scrollTop.current + table.clientHeight >= table.scrollHeight) {
75
+ ref_scrollTop.current = 0;
76
+ } else {
77
+ // 否则,滚动一行的高度
78
+ ref_scrollTop.current += scrollStep;
79
+ }
80
+
81
+ table.scrollTo({ top: ref_scrollTop.current, behavior: 'smooth' });
82
+ };
83
+
84
+ const _series = sortPie(series, order);
85
+ const [_alignment, position] = alignment.split(' ');
86
+ const length = _series.length;
87
+
88
+ const onClick = useCallback(
89
+ (e) => {
90
+ const { dataset } = e.currentTarget;
91
+ const { name } = dataset;
92
+ filterData && interactive && filterData(name);
93
+ },
94
+ [interactive, filterData]
95
+ );
96
+
97
+ if (judge == 0) {
98
+ _series.forEach((d) => {
99
+ d.percent=0
100
+ })
101
+ }
102
+
103
+ // 计算最大宽度
104
+ const valueFont = config.value?.font || {};
105
+ const percentFont = config.percent?.font || {};
106
+ const valueSuffix = config.value?.suffix?.text || '';
107
+ const valueSplitConfig = config.value?.splitConfig || {};
108
+ const getValueStr = (item) => {
109
+ let valueStr = (item.data?.y ?? item.value ?? '') + (valueSuffix || '');
110
+ if (valueSplitConfig.show && valueSplitConfig.separator) {
111
+ valueStr = valueStr.toString().replace(/\B(?=(\d{3})+(?!\d))/g, valueSplitConfig.separator);
112
+ }
113
+ return valueStr;
114
+ };
115
+ const valueMaxWidth = Math.max(
116
+ ..._series.map(item => parseFloat(getCanvasTextWidth(getValueStr(item), valueFont.letterSpacing || 0, `${valueFont.fontSize || 12}px ${valueFont.fontFamily || 'Arial'}`)))
117
+ );
118
+ const percentMaxWidth = Math.max(
119
+ ..._series.map(item => {
120
+ const percentStr = (item.percent !== undefined ? item.percent : item.data?.percent) + '%';
121
+ return parseFloat(getCanvasTextWidth(percentStr, percentFont.letterSpacing || 0, `${percentFont.fontSize || 12}px ${percentFont.fontFamily || 'Arial'}`));
122
+ })
123
+ );
124
+ const nameMaxWidth = config.name?.maxWidth || 80;
125
+
126
+ return (
127
+ LegendType=="FixedWidth"?
128
+ <div
129
+ className='__easyv-legend-wrapper'
130
+ style={{
131
+ position: 'absolute',
132
+ display: 'flex',
133
+ ...getPosition(position, _alignment, x, y),
134
+ height: loop.show ? height : 'auto',
135
+ overflowY: loop.show ? 'scroll' : 'auto',
136
+ pointerEvents:"none"//鼠标穿透
137
+ }}
138
+ ref={ref_container}
139
+ >
140
+ <ul
141
+ style={{
142
+ display: 'grid',
143
+ gridGap: gridRowGap + 'px ' + gridColumnGap + 'px',
144
+ gridTemplateColumns: `${nameMaxWidth}px ${valueMaxWidth}px ${percentMaxWidth}px`,
145
+ }}
146
+ >
147
+ {_series.map((series, index) => {
148
+ const { type, name, displayName, icon, selected } = series;
149
+ const _icon = getIcon(type, icon, series?.config?.line?.type);
150
+ return (
151
+ <li
152
+ key={index}
153
+ onClick={onClick}
154
+ data-name={name}
155
+ style={{
156
+ display: 'flex',
157
+ opacity: selected === false ? opacity / 100 : 1,
158
+ alignItems: 'center',
159
+ cursor: "pointer",
160
+ gap: _icon.gap,
161
+ }}
162
+ >
163
+ {formatter ? (
164
+ formatter(series, { ...config, valueMaxWidth, percentMaxWidth, nameMaxWidth })
165
+ ) : (
166
+ <>
167
+ <span style={_icon} />
168
+ <TextOverflow type={textOverflow} value={displayName || name} style={{
169
+ ...font,
170
+ width:maxWidth,
171
+ fontStyle: italic ? 'italic' : 'normal',
172
+ fontWeight: bold ? 'bold' : 'normal',
173
+ }} speed={speed}></TextOverflow>
174
+
175
+ </>
176
+ )}
177
+ </li>
178
+ );
179
+ })}
180
+ </ul>
181
+ </div>:<div
182
+ className='__easyv-legend-wrapper'
183
+ style={{
184
+ display:'flex',
185
+ flexWrap:"wrap",
186
+ alignContent:alignment.split(" ")[0]=="center"&&(alignment.split(" ")[1]=="left"||alignment.split(" ")[1]=="right")?alignment.split(" ")[1]=="left"?"flex-start":"flex-end":alignment.split(" ")[0]=="left"?"flex-start":alignment.split(" ")[0]=="center"?"center":"flex-end",
187
+ flexDirection: "column",
188
+ position: 'absolute',
189
+ ...getPosition(position, _alignment, x, y),
190
+ height: loop.show ? height : 'auto',
191
+ overflowY: loop.show ? 'scroll' : 'auto',
192
+ width:"100%",
193
+ pointerEvents:"none"
194
+ }}
195
+ ref={ref_container}
196
+ >
197
+
198
+ {[...Array(Math.ceil(series.length/gridTemplateColumns))].map((_,indexs)=>(
199
+ <ul
200
+ key={indexs}
201
+ style={{
202
+ display: 'flex',
203
+ width: 'fit-content',
204
+ justifyContent:alignment.split(" ")[0]=="left"?"flex-start":alignment.split(" ")[0]=="center"?"center":"flex-end",
205
+ marginBottom:"0px",
206
+ gap:`${gridRowGap}px ${gridColumnGap}px`,
207
+ marginBottom:gridRowGap+"px"
208
+ }}
209
+ >
210
+ {_series.map((series, index) => {
211
+ if(Math.floor(index/gridTemplateColumns)==indexs){
212
+ const { type, name, displayName, icon, selected } = series;
213
+ const _icon = getIcon(type, icon, series?.config?.line?.type);
214
+ return (
215
+ <li
216
+ key={index}
217
+ onClick={onClick}
218
+ data-name={name}
219
+ style={{
220
+ display: 'flex',
221
+ opacity: selected === false ? opacity / 100 : 1,
222
+ alignItems: 'center',
223
+ cursor: "pointer",
224
+ gap: _icon.gap,
225
+ }}
226
+ >
227
+ {formatter ? (
228
+ formatter(series, { ...config, valueMaxWidth, percentMaxWidth, nameMaxWidth })
229
+ ) : (
230
+ <>
231
+ <span style={_icon} />
232
+ <TextOverflow ShowType={LegendType} type="ellipsis" value={displayName || name} style={{
233
+ ...font,
234
+ fontStyle: italic ? 'italic' : 'normal',
235
+ fontWeight: bold ? 'bold' : 'normal',
236
+ minWidth:getCanvasTextWidth(displayName.substring(0,5) || name.substring(0,5),font.letterSpacing,`${font.fontSize}px ${font.fontFamily}` )
237
+ }} speed={speed}></TextOverflow>
238
+
239
+ </>
240
+ )}
241
+ </li>
242
+ );
243
+ }
244
+ })}
245
+ </ul>
246
+ ))
247
+ }
248
+ </div>
249
+ );
250
+ }
251
+ );
252
+
253
+ const getPosition = (position, alignment, x = 0, y = 0) => {
254
+ switch (position) {
255
+ case 'top':
256
+ return {
257
+ left: alignment == 'left' ? 5 : alignment == 'center' ? '50%' : '',
258
+ right: alignment == 'right' ? 10 : '',
259
+ top: 5,
260
+ transform: `translate3d(calc(${alignment == 'center' ? '-50%' : '0px'} + ${x}px), ${y}px, 0px)`
261
+ };
262
+ case 'right':
263
+ return {
264
+ top: '50%',
265
+ right: 10,
266
+ transform: `translate3d(${x}px, calc(-50% + ${y}px), 0px)`
267
+ };
268
+ case 'left':
269
+ return {
270
+ top: '50%',
271
+ left: 5,
272
+ transform: `translate3d(${x}px, calc(-50% + ${y}px), 0px)`
273
+ };
274
+ default: // bottom
275
+ return {
276
+ left: alignment == 'left' ? 5 : alignment == 'center' ? '50%' : '',
277
+ right: alignment == 'right' ? 10 : '',
278
+ bottom: 5,
279
+ transform: `translate3d(calc(${alignment == 'center' ? '-50%' : '0px'} + ${x}px), ${y}px, 0px)`
280
+ };
281
+ }
282
+ };
283
+ const getCanvasTextWidth=(text,letterSpacing, font = '16px Arial')=>{
284
+ const canvas = document.createElement('canvas');
285
+ const ctx = canvas.getContext('2d');
286
+ ctx.font = font;
287
+ return ctx.measureText(text).width+(text.length)*letterSpacing+"px";
288
+ // return ctx.measureText(text).width+(text.length-1)*letterSpacing+"px";//-1有bug
289
+ }