@easyv/charts 1.0.70 → 1.0.71

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 (63) hide show
  1. package/.babelrc +8 -8
  2. package/lib/components/AnimateData.js +2 -2
  3. package/lib/components/Axis.js +12 -12
  4. package/lib/components/Background.js +2 -2
  5. package/lib/components/Carousel.js +2 -2
  6. package/lib/components/Chart.js +2 -2
  7. package/lib/components/ConicalGradient.js +21 -21
  8. package/lib/components/Indicator.js +2 -2
  9. package/lib/components/Label.js +13 -3
  10. package/lib/components/LinearGradient.js +2 -2
  11. package/lib/css/index.module.css +41 -41
  12. package/lib/css/piechart.module.css +26 -26
  13. package/lib/hooks/useAnimateData.js +5 -5
  14. package/lib/hooks/useAxes.js +5 -5
  15. package/lib/hooks/useCarouselAxisX.js +5 -5
  16. package/lib/hooks/useExtentData.js +6 -6
  17. package/lib/hooks/useFilterData.js +5 -5
  18. package/lib/hooks/useStackData.js +5 -5
  19. package/lib/hooks/useTooltip.js +10 -10
  20. package/package.json +34 -34
  21. package/src/components/AnimateData.tsx +24 -24
  22. package/src/components/Axis.tsx +333 -333
  23. package/src/components/Background.tsx +45 -45
  24. package/src/components/Band.tsx +160 -160
  25. package/src/components/Brush.js +159 -159
  26. package/src/components/Carousel.tsx +144 -144
  27. package/src/components/Chart.js +99 -99
  28. package/src/components/ChartContainer.tsx +63 -63
  29. package/src/components/ConicalGradient.js +258 -258
  30. package/src/components/ExtentData.js +17 -17
  31. package/src/components/FilterData.js +23 -23
  32. package/src/components/Indicator.js +13 -13
  33. package/src/components/Label.js +206 -196
  34. package/src/components/Legend.js +158 -158
  35. package/src/components/Lighter.jsx +162 -162
  36. package/src/components/Line.js +128 -128
  37. package/src/components/LinearGradient.js +29 -29
  38. package/src/components/Mapping.js +71 -71
  39. package/src/components/PieChart.js +1093 -1093
  40. package/src/components/StackData.js +20 -20
  41. package/src/components/StereoBar.tsx +298 -298
  42. package/src/components/Tooltip.js +133 -133
  43. package/src/components/index.js +49 -49
  44. package/src/context/index.js +2 -2
  45. package/src/css/index.module.css +41 -41
  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 +90 -90
  52. package/src/hooks/index.js +17 -17
  53. package/src/hooks/useAnimateData.ts +62 -62
  54. package/src/hooks/useAxes.js +143 -143
  55. package/src/hooks/useCarouselAxisX.js +163 -163
  56. package/src/hooks/useExtentData.js +88 -88
  57. package/src/hooks/useFilterData.js +72 -72
  58. package/src/hooks/useStackData.js +100 -100
  59. package/src/hooks/useTooltip.ts +96 -96
  60. package/src/index.js +6 -6
  61. package/src/types/index.d.ts +59 -59
  62. package/src/utils/index.js +671 -671
  63. package/tsconfig.json +22 -22
@@ -1,1093 +1,1093 @@
1
- /**
2
- * 饼环图
3
- */
4
- import React, {
5
- memo,
6
- useMemo,
7
- useCallback,
8
- useRef,
9
- useState,
10
- useContext,
11
- useLayoutEffect,
12
- Fragment,
13
- } from 'react';
14
- import { ChartContainer, Carousel, Legend, ConicalGradient, Mapping } from '.';
15
- import { chartContext } from '../context';
16
- import {
17
- getFontStyle,
18
- sortPie,
19
- getDataWithPercent,
20
- getColorList,
21
- } from '../utils';
22
- import { pie, arc, extent, scaleLinear } from 'd3v7';
23
- import { animate, linear } from 'popmotion';
24
- import LinearGradient from './LinearGradient';
25
- import { pieLegendFormatter as legendFormatter } from '../formatter';
26
- import ringCss from '../css/piechart.module.css';
27
-
28
- const PI = Math.PI;
29
-
30
- const defaultChart = {
31
- outerRadius: 1,
32
- innerRadius: 0,
33
- cornerRadius: 0,
34
- rose: false,
35
- roseType: 'radius',
36
- baseRadius: 0,
37
- padAngle: 0,
38
- };
39
- const defaultAngle = { startAngle: 0, endAngle: 360, antiClockwise: false };
40
-
41
- const nameDy = (showValue, showPercent, mode, dir) => {
42
- if(showValue||showPercent){
43
- if(mode=="vertical"){
44
- return dir==1?"1.1em":"-2.6em"
45
- }else{
46
- return 0
47
- }
48
- }else{
49
- if(mode=="vertical"){
50
- return dir*1.1+"em"
51
- }else{
52
- return 0
53
- }
54
- }
55
- }
56
- const valueDy = (value1, mode, dir)=>{
57
- if(value1){
58
- if(mode=="vertical"){
59
- return "1.5em"
60
- }else{
61
- return 0
62
- }
63
- }else{
64
- if(mode=="vertical"){
65
- return dir==1?"1.1em":"-1.1em"
66
- }else{
67
- return 0
68
- }
69
- }
70
- }
71
-
72
- const percentDy_ = (showName, showValue, mode, dir) => {
73
- if (showValue) {
74
- return 0;
75
- }
76
- if (showName) {
77
- if (mode == 'vertical') {
78
- return '1.5em';
79
- } else {
80
- return 0;
81
- }
82
- } else {
83
- if (mode == 'vertical') {
84
- return dir*1.1+'em';
85
- } else {
86
- return 0;
87
- }
88
- }
89
- };
90
-
91
- const percentX = (showName, showValue, mode, x) => {
92
- if (showValue) {
93
- return '';
94
- }
95
- if (showName) {
96
- if (mode == 'vertical') {
97
- return x;
98
- } else {
99
- return '';
100
- }
101
- } else {
102
- return x;
103
- }
104
- };
105
-
106
- const percentDx = (showName, showValue, mode) => {
107
- if (showValue) {
108
- return '0.5em';
109
- }
110
- if (showName) {
111
- if (mode == 'vertical') {
112
- return 0;
113
- } else {
114
- return '0.5em';
115
- }
116
- } else {
117
- return 0;
118
- }
119
- };
120
-
121
- const percentDy = (showName, showValue, mode) => {
122
- if (showValue) {
123
- return 0;
124
- }
125
- if (showName) {
126
- if (mode == 'vertical') {
127
- return '1.5em';
128
- } else {
129
- return 0;
130
- }
131
- } else {
132
- return 0;
133
- }
134
- };
135
-
136
- const valueDx = (showName, mode) => {
137
- if (!showName) {
138
- return '';
139
- }
140
- if (mode == 'vertical') {
141
- return '';
142
- } else {
143
- return '0.5em';
144
- }
145
- };
146
-
147
- const getCoord = (deg, radius) => {
148
- var x = Math.cos(deg) * radius,
149
- y = Math.sin(deg) * radius;
150
- return [x, y];
151
- };
152
-
153
- const getRoseRadius = ({ innerRadius, baseRadius }) =>
154
- innerRadius + (1 - innerRadius) * baseRadius;
155
-
156
- const getAngle = ({ startAngle, endAngle, antiClockwise, ...rest }) => {
157
- if (antiClockwise)
158
- return {
159
- ...rest,
160
- startAngle: endAngle - 180,
161
- endAngle: startAngle - 180,
162
- };
163
- return { ...rest, startAngle, endAngle };
164
- };
165
-
166
- const getArc = (
167
- radius,
168
- {
169
- padAngle = 0,
170
- innerRadius = 0,
171
- outerRadius = 1,
172
- cornerRadius = 0,
173
- startAngle = 0,
174
- endAngle = 360,
175
- ...rest
176
- },
177
- series
178
- ) => ({
179
- ...rest,
180
- type: 'pie',
181
- fieldName: series.fieldName,
182
- displayName: series.displayName || rest.data.s,
183
- series: series,
184
- innerRadius: innerRadius * radius,
185
- outerRadius: outerRadius * radius,
186
- arc: arc()
187
- .innerRadius(innerRadius * radius)
188
- .outerRadius(outerRadius * radius)
189
- .cornerRadius(cornerRadius)
190
- .startAngle(startAngle)
191
- .endAngle(endAngle)
192
- .padAngle(padAngle),
193
- });
194
-
195
- const getCircleScale = ({ count, color, width, length }=tick, radius)=>{
196
- let data = [],
197
- arcs = [],
198
- centroids = [];
199
- for (let i = 0; i < count; i++) {
200
- data.push(1);
201
- }
202
- let scaleData = pie()(data);
203
- scaleData.map((data) => {
204
- let _arc = arc()
205
- .innerRadius(radius + length/2)
206
- .outerRadius(radius + length/2)
207
- .startAngle(data.startAngle)
208
- .endAngle(data.endAngle);
209
- arcs.push(_arc());
210
- centroids.push(_arc.centroid());
211
- });
212
- return (
213
- <g>
214
- {centroids.map((center, index) => {
215
- let x = center[0],
216
- y = center[1];
217
- let rate = length / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
218
- return (
219
- <path
220
- key={index}
221
- d={`M${x},${y}l${x * rate},${y * rate}`}
222
- strokeWidth={width}
223
- stroke={color}
224
- />
225
- );
226
- })}
227
- </g>
228
- );
229
- }
230
-
231
- const Component = memo(
232
- ({
233
- config: {
234
- chart: {
235
- dimension: {
236
- chartDimension: { width, height },
237
- },
238
- label,
239
- legend: { formatter = legendFormatter, ...legend },
240
- margin: { marginLeft, marginTop },
241
- },
242
- fan: {
243
- chart = defaultChart,
244
- chart: { outerRadius = defaultChart.outerRadius,padAngle },
245
- angle = defaultAngle,
246
- stroke: { show, strokeWidth, color } = { show: false },
247
- decorate,
248
- decorate2,
249
- categoryText,
250
- outerDecorate,
251
- current,
252
- } = {},
253
- order,
254
- series,
255
- animation: {
256
- on,
257
- current: { heighten=0, opacity=0, width:radiusWidthAdd=0, color:animateColor, textStyle:animateCTS, gap=0 },
258
- rotate = 0
259
- },
260
- },
261
- state: { currentIndex, trigger },
262
- onEvent,
263
- data = [],
264
- }) => {
265
- const prevIndex = useRef(null);
266
- const { precision: legendPrecision } = legend.config.percent;
267
- const {
268
- id,
269
- width: chartWidth,
270
- height: chartHeight,
271
- triggerOnRelative,
272
- onEmit,
273
- } = useContext(chartContext);
274
-
275
- const [y, setY] = useState(1);
276
- const radius = (Math.min(chartWidth, chartHeight) / 2) * outerRadius;
277
-
278
- const arcsFunc = useMemo(() => {
279
- const { startAngle = 0, endAngle = 360 } = getAngle(angle);
280
- const arcsFunc = pie()
281
- .startAngle((startAngle * PI) / 180)
282
- .endAngle((endAngle * PI) / 180)
283
- .value((d) => d.y);
284
- return arcsFunc;
285
- }, [angle]);
286
-
287
- const arcs = useMemo(() => {
288
- const _chart = Object.assign(defaultChart, chart);
289
- const {
290
- innerRadius,
291
- outerRadius,
292
- rose,
293
- cornerRadius,
294
- padAngle,
295
- roseType,
296
- } = _chart;
297
- const _padAngle = (padAngle * Math.PI) / 180;
298
-
299
- switch (order) {
300
- case '':
301
- arcsFunc.sort(null);
302
- break;
303
- case 'desc':
304
- arcsFunc.sort((a, b) => b.y - a.y);
305
- break;
306
- case 'asc':
307
- arcsFunc.sort((a, b) => a.y - b.y);
308
- break;
309
- }
310
-
311
- const arcs = arcsFunc(data);
312
- const legendDataWithPercent = getDataWithPercent(arcs, legendPrecision);
313
- const _legendDataWithPercent = sortPie(legendDataWithPercent, order);
314
-
315
- if (rose) {
316
- const domain = extent(_legendDataWithPercent, (d) => d.value);
317
- const roseRadius = getRoseRadius(_chart);
318
- const scaler = scaleLinear().domain(domain).range([roseRadius, 1]);
319
-
320
- const angle = (PI * 2) / _legendDataWithPercent.length;
321
- return _legendDataWithPercent.map(
322
- ({ startAngle, endAngle, ...arc }, index) => ({
323
- ...arc,
324
- id: id + '_linear_' + index,
325
- startAngle: roseType == 'area' ? angle * index : startAngle,
326
- endAngle: roseType == 'area' ? angle * (index + 1) : endAngle,
327
- cornerRadius,
328
- padAngle: _padAngle,
329
- innerRadius,
330
- outerRadius: scaler(arc.value),
331
- })
332
- );
333
- }
334
- return _legendDataWithPercent.map((arc, index) => ({
335
- ...arc,
336
- id: id + '_linear_' + index,
337
- cornerRadius,
338
- padAngle: _padAngle,
339
- innerRadius,
340
- outerRadius,
341
- }));
342
- }, [data, arcsFunc, chart, legendPrecision]);
343
-
344
- const _arcs = useMemo(() => {
345
- const seriesLength = series.size;
346
- if (!seriesLength) return [];
347
- const _series = [...series.values()];
348
- return arcs.map((arc, index) => getArc(radius, arc, _series[index]));
349
- }, [series, arcs, radius]);
350
- const onClick = useCallback(
351
- (e) =>
352
- onEvent({
353
- currentIndex: +e.currentTarget.dataset.index,
354
- type: 'onClick',
355
- }),
356
- [onEvent]
357
- );
358
-
359
- const onMouseEnter = useCallback(
360
- (e) =>
361
- onEvent({
362
- currentIndex: +e.currentTarget.dataset.index,
363
- type: 'onMouseEnter',
364
- }),
365
- [onEvent]
366
- );
367
-
368
- const onMouseLeave = useCallback(
369
- (e) =>
370
- onEvent({
371
- currentIndex: +e.currentTarget.dataset.index,
372
- type: 'onMouseLeave',
373
- }),
374
- [onEvent]
375
- );
376
-
377
- useLayoutEffect(() => {
378
- let animation;
379
- if (!!on) {
380
- animation = animate({
381
- from: 0,
382
- to: 1,
383
- duration: 500,
384
- ease: linear,
385
- onPlay: () => {},
386
- onUpdate: (v) => {
387
- setY(v);
388
- },
389
- onComplete: () => {
390
- const _data = arcs[+currentIndex].data;
391
- triggerOnRelative(_data);
392
- onEmit('carousel', _data);
393
- },
394
- });
395
- } else {
396
- if (currentIndex !== null && trigger === 'onClick') {
397
- const _data = arcs[+currentIndex].data;
398
- triggerOnRelative(_data);
399
- onEmit(trigger, _data);
400
- }
401
- }
402
- return () => {
403
- prevIndex.current = currentIndex;
404
- animation && animation.stop();
405
- animation = null;
406
- };
407
- }, [
408
- JSON.stringify(arcs),
409
- on,
410
- currentIndex,
411
- trigger,
412
- onEmit,
413
- triggerOnRelative,
414
- ]);
415
-
416
- const halfChartWidth = chartWidth / 2;
417
- const halfChartHeight = chartHeight / 2;
418
-
419
- const rotate_ = decorate2?-(arcs[+currentIndex].startAngle+arcs[+currentIndex].endAngle)*90/Math.PI+rotate:0;
420
- let maxRadius = 0;
421
- _arcs.map(d=>{
422
- maxRadius=Math.max(maxRadius,d.outerRadius);
423
- })
424
- let centerRadius =0.5*maxRadius + 0.5*_arcs[0].innerRadius
425
- return (
426
- outerDecorate?<>
427
- <ChartContainer //用于生成甜甜圈图,判断依据是外环装饰这个配置项(outerDecorate)
428
- width={width}
429
- height={height}
430
- marginLeft={marginLeft}
431
- marginTop={marginTop}
432
- >
433
- <g
434
- style={{
435
- transition:"transform ease-in-out 0.3s",
436
- transform:'translate(' + halfChartWidth + 'px, ' + halfChartHeight + 'px) rotate('+rotate_+"deg)"
437
- }}
438
- >
439
- {
440
- //用于生成外环装饰的刻度
441
- outerDecorate.tick.show && getCircleScale(outerDecorate.tick, maxRadius)
442
- }
443
- <circle //外环装饰
444
- cx='0'
445
- cy='0'
446
- r={maxRadius+2}
447
- fill='none'
448
- stroke={outerDecorate.color}
449
- strokeWidth={outerDecorate.width}
450
- />
451
- {
452
- _arcs.map(
453
- ({ id, value, series, arc, innerRadius, outerRadius, index: dataIndex }, index) => {
454
- const arcWidth = (outerRadius-innerRadius);
455
- const path = arc
456
- .innerRadius(centerRadius)
457
- .outerRadius(centerRadius)(value);
458
- const dashLength=Math.ceil(Math.PI*(centerRadius)*2/_arcs.length);
459
- const pie = getColorList(series.color);
460
- return (
461
- <Fragment key={index}>
462
- <path
463
- className={ringCss['inner-arc']}
464
- style={{
465
- strokeDasharray:`${dashLength},${2*dashLength}`,
466
- strokeDashoffset: dashLength,
467
- animationDelay: `${index * 2000}ms`
468
- }}
469
- data-index={dataIndex}
470
- onClick={onClick}
471
- onMouseEnter={onMouseEnter}
472
- onMouseLeave={onMouseLeave}
473
- d={path.split("L")[0]}
474
- stroke={'url(#' + id + ')'}
475
- strokeWidth={arcWidth}
476
- fill="none"
477
- />
478
- <defs>
479
- <LinearGradient
480
- id={id}
481
- colors={pie}
482
- rotate={series.color.linear.angle + 180}
483
- // gradientUnits='objectBoundingBox'
484
- />
485
- </defs>
486
- </Fragment>
487
- );
488
- }
489
- )
490
- }
491
- {label && <RingLabel config={{...label,maxRadius: maxRadius+2}} arcs={_arcs}/>}
492
- </g>
493
- </ChartContainer>
494
- <Legend {...legend} series={_arcs} formatter={formatter}/>
495
- </> : <>
496
- <ChartContainer
497
- width={width}
498
- height={height}
499
- marginLeft={marginLeft}
500
- marginTop={marginTop}
501
- >
502
- <g
503
- style={{
504
- transition:"transform ease-in-out 0.3s",
505
- transform:'translate(' + halfChartWidth + 'px, ' + halfChartHeight + 'px) rotate('+rotate_+"deg)"
506
- }}
507
- >
508
- {_arcs.map(
509
- ({ id, value, series, arc, innerRadius, outerRadius, index: dataIndex }, index) => {
510
- const current = index == currentIndex;
511
- const prev = index == prevIndex.current;
512
- const offset = current ? y : prev ? 1 - y : 0;
513
-
514
- const fillOpacity = animateColor?1:(current ? opacity / 100 : 1);
515
- const deltaHeighten= offset*heighten;
516
- const path = arc
517
- .innerRadius(innerRadius + deltaHeighten)
518
- .outerRadius(outerRadius + deltaHeighten)(value);
519
- const pie = getColorList(series.color);
520
- const currentPie = animateColor?getColorList(animateColor):getColorList(series.color);
521
- let textPath="",categoryTextStyle={};
522
- if(categoryText && categoryText.show){ //如果有类目文本,则需要计算文字路径
523
- //let offsetWidth=decorate2.radiusWidth/2 + radiusWidthAdd/2; //当前文字需生成在装饰物内,故而半径需要减小
524
- let textArc=arc.innerRadius(outerRadius+(current?gap:categoryText.gap))
525
- .outerRadius(outerRadius+(current?gap:categoryText.gap))(value);
526
- let lastA=textArc.lastIndexOf('A');
527
- textPath=textArc.slice(0, lastA > 0 ? lastA : textArc.length); //文字路径
528
- categoryTextStyle=current?animateCTS:categoryText.textStyle; //这里把textstyle拿出来
529
- }
530
-
531
- return (
532
- <Fragment key={index}>
533
- <path
534
- data-index={dataIndex}
535
- onClick={onClick}
536
- onMouseEnter={onMouseEnter}
537
- onMouseLeave={onMouseLeave}
538
- d={path}
539
- stroke={show ? color : 'none'}
540
- strokeWidth={show ? strokeWidth : '0'}
541
- fill={'url(#' + id + ')'}
542
- fillOpacity={fillOpacity}
543
- />
544
- { //装饰物2,产生于每个弧的外部
545
- decorate2 && decorate2.show &&
546
- <path
547
- data-index={dataIndex}
548
- onClick={onClick}
549
- onMouseEnter={onMouseEnter}
550
- onMouseLeave={onMouseLeave}
551
- d={arc.innerRadius(outerRadius)
552
- .outerRadius(outerRadius + decorate2.radiusWidth + (current?radiusWidthAdd:0))(value)}
553
- stroke={show ? color : 'none'}
554
- strokeWidth={show ? strokeWidth : '0'}
555
- fill={'url(#' + id + ')'}
556
- fillOpacity={decorate2.opacity/100}
557
- />
558
- }
559
- { //类目文本
560
- categoryText && categoryText.show &&<g>
561
- <path
562
- onClick={onClick}
563
- onMouseEnter={onMouseEnter}
564
- onMouseLeave={onMouseLeave}
565
- id={id + '_text_' + index}
566
- d={textPath}
567
- fill='none'
568
- stroke='none'
569
- />
570
- <text
571
- textAnchor='middle'
572
- style={{
573
- ...categoryTextStyle,
574
- fontWeight:categoryTextStyle.bold?"bold":"normal",
575
- fontStyle:categoryTextStyle.italic?"italic":"normal"
576
- }}
577
- fill={categoryTextStyle.color}
578
- >
579
- <textPath
580
- startOffset='50%'
581
- href={'#' + id + '_text_' + index}
582
- >
583
- {_arcs[index].displayName || _arcs[index].fieldName}
584
- </textPath>
585
- </text>
586
- </g>
587
- }
588
- <defs>
589
- <LinearGradient
590
- id={id}
591
- colors={current?currentPie:pie}
592
- rotate={current?(animateColor?animateColor.linear.angle + 180:series.color.linear.angle + 180):series.color.linear.angle + 180}
593
- // gradientUnits='objectBoundingBox'
594
- />
595
- </defs>
596
- </Fragment>
597
- );
598
- }
599
- )}
600
- {label && <Label config={label} arcs={_arcs} />}
601
- {current && (
602
- <g fillOpacity={y} style={{transform:"rotate("+(-rotate_)+"deg)"}}>
603
- <Current
604
- config={current}
605
- data={_arcs}
606
- currentIndex={+currentIndex}
607
- />
608
- </g>
609
- )}
610
- </g>
611
- </ChartContainer>
612
- {decorate && (
613
- <ConicalGradient
614
- width={width}
615
- height={height}
616
- centerX={halfChartWidth + marginLeft}
617
- centerY={halfChartHeight + marginTop}
618
- config={decorate}
619
- arcs={_arcs}
620
- radius={radius}
621
- />
622
- )}
623
- <Legend {...legend} series={_arcs} formatter={formatter} />
624
- </>
625
- );
626
- }
627
- );
628
-
629
- const Current = ({
630
- config: {
631
- show,
632
- gap,
633
- name: { show: showName, font: nameFont, textBreak },
634
- percent: {
635
- show: showPercent,
636
- font: percentFont,
637
- precision,
638
- translate: { x: translatePercentX, y: translatePercentY },
639
- },
640
- value: {
641
- show: showValue,
642
- font: valueFont,
643
- translate: { x: translateValueX, y: translateValueY },
644
- suffix: {
645
- show: showSuffix,
646
- fontSize,
647
- text,
648
- translate: { x: translateSuffixX, y: translateSuffixY },
649
- },
650
- },
651
- },
652
- data,
653
- currentIndex,
654
- }) => {
655
- const _data = useMemo(() => {
656
- const legendDataWithPercent = getDataWithPercent(data, precision);
657
- return sortPie(legendDataWithPercent, '');
658
- }, [data, precision]);
659
- const currentData = _data[currentIndex];
660
-
661
- if (!currentData) return null;
662
-
663
- const { displayName, fieldName, value, percent } = currentData;
664
- let nameTemp = displayName? displayName : fieldName; //类目名
665
- let nameList = [];
666
- if(textBreak){ //如果限制了首行字符,则切割组件
667
- while(nameTemp.length>textBreak){
668
- nameList.push(nameTemp.slice(0,textBreak));
669
- nameTemp = nameTemp.slice(textBreak)
670
- }
671
- }
672
- nameList.push(nameTemp)
673
- let foreignStyle={ //foreignObject标签样式,
674
- width:"100%",
675
- height:"100%",
676
- transform:"translate(-50%,-50%)",
677
- },
678
- boxStyle={ //弹性盒子样式,用于当前值的上下居中对齐等
679
- width:"100%",
680
- height:'100%',
681
- display:"flex",
682
- flexDirection:"column",
683
- justifyContent:"center",
684
- alignItems:"center"
685
- }
686
-
687
- return (
688
- show && (
689
- <>
690
- <foreignObject style={foreignStyle}>
691
- <div style={boxStyle}>
692
- { //类目名称
693
- showName && <div
694
- style={{
695
- ...getFontStyle(nameFont),
696
- margin:gap/2+"px 0",
697
- display:"flex",
698
- flexDirection:"column",
699
- alignItems:"center"
700
- }}>
701
- {
702
- nameList.map((d,i)=>{
703
- return <span key={i}>{d}</span>
704
- })
705
- }
706
- </div>
707
- }
708
- { //真实值
709
- showValue && <span
710
- style={{
711
- ...getFontStyle(valueFont),
712
- transform:"translate("+translateValueX+"px,"+translateValueY+"px)",
713
- margin:gap/2+"px 0"
714
- }}>
715
- {value}
716
- {showSuffix && text && (
717
- <span
718
- style={{
719
- transform:"translate("+translateSuffixX+"px,"+translateSuffixY+"px)",
720
- fontSize:fontSize
721
- }}
722
- >
723
- {text}
724
- </span>
725
- )}
726
- </span>
727
- }
728
- { //百分比值
729
- showPercent && <span
730
- style={{
731
- transform:"translate("+translatePercentX+"px,"+translatePercentY+"px)",
732
- ...getFontStyle(percentFont),
733
- margin:gap/2+"px 0"
734
- }}
735
- >
736
- {percent + '%'}
737
- </span>
738
- }
739
- </div>
740
- </foreignObject>
741
- </>
742
- )
743
- );
744
- };
745
-
746
- const Label = ({
747
- config: {
748
- maxRadius = 0,
749
- lineLength,
750
- lineColor,
751
- distance,
752
- mode,
753
- show,
754
- translate: { x: translateX, y: translateY },
755
- name: { show: showName, font: nameFont },
756
- value: {
757
- show: showValue,
758
- font: valueFont,
759
- suffix: {
760
- show: showSuffix,
761
- text,
762
- fontSize: suffixFontSize,
763
- translate: { x: suffixTranslateX, y: suffixTranslateY },
764
- },
765
- sameColor: valueSameColor = false,
766
- },
767
- percent: {
768
- show: showPercent,
769
- font: percentFont,
770
- precision,
771
- sameColor: percentSameColor = false,
772
- },
773
- },
774
- arcs,
775
- animation
776
- }) => {
777
- // const [labels, setLabels] = useState(null);
778
- // const [opacity, setOpacity] = useState(0);
779
-
780
- const _arcs = useMemo(
781
- () => getDataWithPercent(arcs, precision),
782
- [arcs, precision]
783
- );
784
- // useEffect(() => {
785
- // if (labels) {
786
- // const children = [...labels.children];
787
- // const bbox = children.reduce(
788
- // (prev, current) => {
789
- // const { topRight, bottomRight, bottomLeft, topLeft } = prev;
790
- // const { x, y, height } = current.getBBox();
791
-
792
- // current._y1 = y;
793
- // current._y2 = y + height;
794
- // current._delta = 0;
795
-
796
- // if (x > 0) {
797
- // if (y > 0) {
798
- // bottomRight.push(current);
799
- // } else {
800
- // topRight.push(current);
801
- // }
802
- // } else {
803
- // if (y > 0) {
804
- // bottomLeft.push(current);
805
- // } else {
806
- // topLeft.push(current);
807
- // }
808
- // }
809
- // return prev;
810
- // },
811
- // {
812
- // topRight: [],
813
- // bottomRight: [],
814
- // bottomLeft: [],
815
- // topLeft: [],
816
- // }
817
- // );
818
- // console.log('bbox: ', bbox);
819
- // }
820
- // }, [labels]);
821
-
822
- return (
823
- <g
824
- // style={{ opacity }} ref={setLabels}
825
- >
826
- {_arcs.map(
827
- (
828
- {
829
- series: {
830
- color: {
831
- type,
832
- pure,
833
- linear: { stops },
834
- },
835
- },
836
- displayName,
837
- value,
838
- percent,
839
- arc,
840
- outerRadius,
841
- index:actualIndex
842
- },
843
- index
844
- ) => {
845
-
846
- const [x, y] = arc.centroid();
847
- const midAngle = Math.atan2(y, x);
848
-
849
- const [x1, y1] = getCoord(midAngle, maxRadius?maxRadius:outerRadius);
850
-
851
- const radius = (maxRadius?maxRadius:outerRadius) + distance;
852
- const [x2, y2] = getCoord(midAngle, radius);
853
-
854
- const direction = x2 < 0 ? -1 : 1;
855
- const x3 = x2 + lineLength * direction;
856
-
857
- const _x = x3 + (translateX + 6) * direction;
858
-
859
- const _showName = showName && displayName;
860
- const _showValue = showValue && (value || showSuffix);
861
-
862
- return (
863
- show &&
864
- (_showName || showPercent || showValue) && (
865
- <g key={index}>
866
- <path
867
- className={animation? ringCss['label-line']:""}
868
- style={{ animationDelay:`${animation? ((actualIndex+1) * 2000 - 800) : 0}ms`}}
869
- d={
870
- 'M' + x1 + ', ' + y1 + 'L' +
871
- x2 + ', ' + y2 + 'L' +
872
- x3 + ', ' + y2
873
- }
874
- stroke={lineColor?lineColor:(type == 'pure' ? pure : stops[0].color)}
875
- fill='none'
876
- />
877
- <text
878
- className={animation? ringCss['label-text']:""}
879
- style={{ animationDelay:`${animation? ((actualIndex+1) * 2000 - 800) : 0}ms`}}
880
- x={_x}
881
- y={y2 + translateY}
882
- dominantBaseline='middle'
883
- textAnchor={x3 >= 0 ? 'start' : 'end'}
884
- >
885
- {_showName && (
886
- <tspan style={getFontStyle(nameFont, 'svg')}>
887
- {displayName + (showValue || showPercent ? ':' : '')}
888
- </tspan>
889
- )}
890
- {showValue && (
891
- <>
892
- <tspan
893
- x={!!(_showName && mode == 'vertical') ? _x : ''}
894
- dx={valueDx(_showName, mode)}
895
- dy={!!(_showName && mode == 'vertical') ? '1.5em' : ''}
896
- style={getFontStyle(valueFont, 'svg')}
897
- >
898
- {value}
899
- </tspan>
900
- {showSuffix && (
901
- <tspan
902
- style={{
903
- ...getFontStyle(valueFont, 'svg'),
904
- fontSize: suffixFontSize,
905
- }}
906
- dx={suffixTranslateX}
907
- dy={suffixTranslateY}
908
- >
909
- {text}
910
- </tspan>
911
- )}
912
- </>
913
- )}
914
- {showPercent && (
915
- <tspan
916
- x={percentX(_showName, _showValue, mode, _x)}
917
- dx={percentDx(_showName, _showValue, mode)}
918
- dy={
919
- percentDy(_showName, _showValue, mode) +
920
- (_showValue && showSuffix ? suffixTranslateY * -1 : "")
921
- }
922
- style={getFontStyle(percentFont, 'svg')}
923
- >
924
- {(_showValue ? '(' : '') +
925
- percent +
926
- '%' +
927
- (_showValue ? ')' : '')}
928
- </tspan>
929
- )}
930
- </text>
931
- </g>
932
- )
933
- );
934
- }
935
- )}
936
- </g>
937
- );
938
- };
939
-
940
- const RingLabel =({
941
- config: {
942
- maxRadius = 0,
943
- lineLength,
944
- lineColor,
945
- distance,
946
- mode,
947
- show,
948
- translate: { x: translateX, y: translateY },
949
- name: { show: showName, font: nameFont },
950
- value: {
951
- show: showValue,
952
- font: valueFont,
953
- suffix: {
954
- show: showSuffix,
955
- text,
956
- fontSize: suffixFontSize,
957
- translate: { x: suffixTranslateX, y: suffixTranslateY },
958
- },
959
- sameColor: valueSameColor = false,
960
- },
961
- percent: {
962
- show: showPercent,
963
- font: percentFont,
964
- precision,
965
- sameColor: percentSameColor = false,
966
- },
967
- },
968
- arcs
969
- }) => {
970
- const _arcs = useMemo(
971
- () => getDataWithPercent(arcs, precision),
972
- [arcs, precision]
973
- );
974
-
975
- return (
976
- <g>
977
- {_arcs.map(
978
- (
979
- {
980
- series: {
981
- color: {
982
- type,
983
- pure,
984
- linear: { stops },
985
- },
986
- },
987
- data:realData,
988
- displayName,
989
- value,
990
- percent,
991
- arc,
992
- outerRadius,
993
- index:actualIndex
994
- },
995
- index
996
- ) => {
997
-
998
- const [x, y] = arc.centroid();
999
-
1000
- const midAngle = Math.atan2(y, x);
1001
-
1002
- const [x1, y1] = getCoord(midAngle, maxRadius?maxRadius:outerRadius);
1003
-
1004
- const radius = (maxRadius?maxRadius:outerRadius) + distance;
1005
- const [x2, y2] = getCoord(midAngle, radius);
1006
-
1007
- const directionX = x2 < 0 ? -1 : 1;
1008
- const directionY = y2 < 0 ? -1 : 1;
1009
- const x3 = x2 + lineLength * directionX;
1010
- const _x = x3 + (translateX + 6) * directionX;
1011
-
1012
- const _showName = showName && displayName;
1013
- const _showValue = showValue && (value || showSuffix);
1014
-
1015
- return (
1016
- show &&
1017
- (_showName || showPercent || _showValue) && (
1018
- <g key={index}>
1019
- <path
1020
- className={ringCss['label-line']}
1021
- style={{ animationDelay:`${(actualIndex+1) * 2000 - 800}ms`}}
1022
- d={
1023
- 'M' + x1 + ', ' + y1 + 'L' +
1024
- x2 + ', ' + y2 + 'L' +
1025
- x3 + ', ' + y2
1026
- }
1027
- stroke={lineColor?lineColor:(type == 'pure' ? pure : stops[0].color)}
1028
- fill='none'
1029
- />
1030
- <text
1031
- className={ringCss['label-text']}
1032
- style={{ animationDelay:`${(actualIndex+1) * 2000 - 800}ms`}}
1033
- x={mode=="horizontal"?_x:x2}
1034
- y={y2 + translateY}
1035
- dominantBaseline='middle'
1036
- textAnchor={x3 >= 0 ? 'start' : 'end'}
1037
- >
1038
- {_showName && (
1039
- <tspan dy={nameDy(_showValue, showPercent, mode, directionY)} style={getFontStyle(nameFont, 'svg')}>
1040
- {displayName + (showValue || showPercent ? ':' : '')}
1041
- </tspan>
1042
- )}
1043
- {_showValue && (
1044
- <>
1045
- <tspan
1046
- x={ _showName?(mode=="horizontal"?'':x2):'' }
1047
- dx={valueDx(_showName, mode)}
1048
- dy={valueDy(_showName, mode, directionY)}
1049
- style={getFontStyle(valueFont, 'svg')}
1050
- >
1051
- {realData.y}
1052
- </tspan>
1053
- {showSuffix && (
1054
- <tspan
1055
- style={{
1056
- ...getFontStyle(valueFont, 'svg'),
1057
- fontSize: suffixFontSize,
1058
- }}
1059
- dx={suffixTranslateX}
1060
- dy={suffixTranslateY}
1061
- >
1062
- {text}
1063
- </tspan>
1064
- )}
1065
- </>
1066
- )}
1067
- {showPercent && (
1068
- <tspan
1069
- x={_showName?(_showValue?'':(mode=="vertical"?x2:'')):''}
1070
- dx={percentDx(_showName, _showValue, mode)}
1071
- dy={
1072
- percentDy_(_showName, _showValue, mode, directionY) +
1073
- (_showValue && showSuffix ? suffixTranslateY * -1 : "")
1074
- }
1075
- style={getFontStyle(percentFont, 'svg')}
1076
- >
1077
- {(_showValue ? '(' : '') +
1078
- percent +
1079
- '%' +
1080
- (_showValue ? ')' : '')}
1081
- </tspan>
1082
- )}
1083
- </text>
1084
- </g>
1085
- )
1086
- );
1087
- }
1088
- )}
1089
- </g>
1090
- );
1091
- };
1092
-
1093
- export default Mapping(Carousel(Component));
1
+ /**
2
+ * 饼环图
3
+ */
4
+ import React, {
5
+ memo,
6
+ useMemo,
7
+ useCallback,
8
+ useRef,
9
+ useState,
10
+ useContext,
11
+ useLayoutEffect,
12
+ Fragment,
13
+ } from 'react';
14
+ import { ChartContainer, Carousel, Legend, ConicalGradient, Mapping } from '.';
15
+ import { chartContext } from '../context';
16
+ import {
17
+ getFontStyle,
18
+ sortPie,
19
+ getDataWithPercent,
20
+ getColorList,
21
+ } from '../utils';
22
+ import { pie, arc, extent, scaleLinear } from 'd3v7';
23
+ import { animate, linear } from 'popmotion';
24
+ import LinearGradient from './LinearGradient';
25
+ import { pieLegendFormatter as legendFormatter } from '../formatter';
26
+ import ringCss from '../css/piechart.module.css';
27
+
28
+ const PI = Math.PI;
29
+
30
+ const defaultChart = {
31
+ outerRadius: 1,
32
+ innerRadius: 0,
33
+ cornerRadius: 0,
34
+ rose: false,
35
+ roseType: 'radius',
36
+ baseRadius: 0,
37
+ padAngle: 0,
38
+ };
39
+ const defaultAngle = { startAngle: 0, endAngle: 360, antiClockwise: false };
40
+
41
+ const nameDy = (showValue, showPercent, mode, dir) => {
42
+ if(showValue||showPercent){
43
+ if(mode=="vertical"){
44
+ return dir==1?"1.1em":"-2.6em"
45
+ }else{
46
+ return 0
47
+ }
48
+ }else{
49
+ if(mode=="vertical"){
50
+ return dir*1.1+"em"
51
+ }else{
52
+ return 0
53
+ }
54
+ }
55
+ }
56
+ const valueDy = (value1, mode, dir)=>{
57
+ if(value1){
58
+ if(mode=="vertical"){
59
+ return "1.5em"
60
+ }else{
61
+ return 0
62
+ }
63
+ }else{
64
+ if(mode=="vertical"){
65
+ return dir==1?"1.1em":"-1.1em"
66
+ }else{
67
+ return 0
68
+ }
69
+ }
70
+ }
71
+
72
+ const percentDy_ = (showName, showValue, mode, dir) => {
73
+ if (showValue) {
74
+ return 0;
75
+ }
76
+ if (showName) {
77
+ if (mode == 'vertical') {
78
+ return '1.5em';
79
+ } else {
80
+ return 0;
81
+ }
82
+ } else {
83
+ if (mode == 'vertical') {
84
+ return dir*1.1+'em';
85
+ } else {
86
+ return 0;
87
+ }
88
+ }
89
+ };
90
+
91
+ const percentX = (showName, showValue, mode, x) => {
92
+ if (showValue) {
93
+ return '';
94
+ }
95
+ if (showName) {
96
+ if (mode == 'vertical') {
97
+ return x;
98
+ } else {
99
+ return '';
100
+ }
101
+ } else {
102
+ return x;
103
+ }
104
+ };
105
+
106
+ const percentDx = (showName, showValue, mode) => {
107
+ if (showValue) {
108
+ return '0.5em';
109
+ }
110
+ if (showName) {
111
+ if (mode == 'vertical') {
112
+ return 0;
113
+ } else {
114
+ return '0.5em';
115
+ }
116
+ } else {
117
+ return 0;
118
+ }
119
+ };
120
+
121
+ const percentDy = (showName, showValue, mode) => {
122
+ if (showValue) {
123
+ return 0;
124
+ }
125
+ if (showName) {
126
+ if (mode == 'vertical') {
127
+ return '1.5em';
128
+ } else {
129
+ return 0;
130
+ }
131
+ } else {
132
+ return 0;
133
+ }
134
+ };
135
+
136
+ const valueDx = (showName, mode) => {
137
+ if (!showName) {
138
+ return '';
139
+ }
140
+ if (mode == 'vertical') {
141
+ return '';
142
+ } else {
143
+ return '0.5em';
144
+ }
145
+ };
146
+
147
+ const getCoord = (deg, radius) => {
148
+ var x = Math.cos(deg) * radius,
149
+ y = Math.sin(deg) * radius;
150
+ return [x, y];
151
+ };
152
+
153
+ const getRoseRadius = ({ innerRadius, baseRadius }) =>
154
+ innerRadius + (1 - innerRadius) * baseRadius;
155
+
156
+ const getAngle = ({ startAngle, endAngle, antiClockwise, ...rest }) => {
157
+ if (antiClockwise)
158
+ return {
159
+ ...rest,
160
+ startAngle: endAngle - 180,
161
+ endAngle: startAngle - 180,
162
+ };
163
+ return { ...rest, startAngle, endAngle };
164
+ };
165
+
166
+ const getArc = (
167
+ radius,
168
+ {
169
+ padAngle = 0,
170
+ innerRadius = 0,
171
+ outerRadius = 1,
172
+ cornerRadius = 0,
173
+ startAngle = 0,
174
+ endAngle = 360,
175
+ ...rest
176
+ },
177
+ series
178
+ ) => ({
179
+ ...rest,
180
+ type: 'pie',
181
+ fieldName: series.fieldName,
182
+ displayName: series.displayName || rest.data.s,
183
+ series: series,
184
+ innerRadius: innerRadius * radius,
185
+ outerRadius: outerRadius * radius,
186
+ arc: arc()
187
+ .innerRadius(innerRadius * radius)
188
+ .outerRadius(outerRadius * radius)
189
+ .cornerRadius(cornerRadius)
190
+ .startAngle(startAngle)
191
+ .endAngle(endAngle)
192
+ .padAngle(padAngle),
193
+ });
194
+
195
+ const getCircleScale = ({ count, color, width, length }=tick, radius)=>{
196
+ let data = [],
197
+ arcs = [],
198
+ centroids = [];
199
+ for (let i = 0; i < count; i++) {
200
+ data.push(1);
201
+ }
202
+ let scaleData = pie()(data);
203
+ scaleData.map((data) => {
204
+ let _arc = arc()
205
+ .innerRadius(radius + length/2)
206
+ .outerRadius(radius + length/2)
207
+ .startAngle(data.startAngle)
208
+ .endAngle(data.endAngle);
209
+ arcs.push(_arc());
210
+ centroids.push(_arc.centroid());
211
+ });
212
+ return (
213
+ <g>
214
+ {centroids.map((center, index) => {
215
+ let x = center[0],
216
+ y = center[1];
217
+ let rate = length / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
218
+ return (
219
+ <path
220
+ key={index}
221
+ d={`M${x},${y}l${x * rate},${y * rate}`}
222
+ strokeWidth={width}
223
+ stroke={color}
224
+ />
225
+ );
226
+ })}
227
+ </g>
228
+ );
229
+ }
230
+
231
+ const Component = memo(
232
+ ({
233
+ config: {
234
+ chart: {
235
+ dimension: {
236
+ chartDimension: { width, height },
237
+ },
238
+ label,
239
+ legend: { formatter = legendFormatter, ...legend },
240
+ margin: { marginLeft, marginTop },
241
+ },
242
+ fan: {
243
+ chart = defaultChart,
244
+ chart: { outerRadius = defaultChart.outerRadius,padAngle },
245
+ angle = defaultAngle,
246
+ stroke: { show, strokeWidth, color } = { show: false },
247
+ decorate,
248
+ decorate2,
249
+ categoryText,
250
+ outerDecorate,
251
+ current,
252
+ } = {},
253
+ order,
254
+ series,
255
+ animation: {
256
+ on,
257
+ current: { heighten=0, opacity=0, width:radiusWidthAdd=0, color:animateColor, textStyle:animateCTS, gap=0 },
258
+ rotate = 0
259
+ },
260
+ },
261
+ state: { currentIndex, trigger },
262
+ onEvent,
263
+ data = [],
264
+ }) => {
265
+ const prevIndex = useRef(null);
266
+ const { precision: legendPrecision } = legend.config.percent;
267
+ const {
268
+ id,
269
+ width: chartWidth,
270
+ height: chartHeight,
271
+ triggerOnRelative,
272
+ onEmit,
273
+ } = useContext(chartContext);
274
+
275
+ const [y, setY] = useState(1);
276
+ const radius = (Math.min(chartWidth, chartHeight) / 2) * outerRadius;
277
+
278
+ const arcsFunc = useMemo(() => {
279
+ const { startAngle = 0, endAngle = 360 } = getAngle(angle);
280
+ const arcsFunc = pie()
281
+ .startAngle((startAngle * PI) / 180)
282
+ .endAngle((endAngle * PI) / 180)
283
+ .value((d) => d.y);
284
+ return arcsFunc;
285
+ }, [angle]);
286
+
287
+ const arcs = useMemo(() => {
288
+ const _chart = Object.assign(defaultChart, chart);
289
+ const {
290
+ innerRadius,
291
+ outerRadius,
292
+ rose,
293
+ cornerRadius,
294
+ padAngle,
295
+ roseType,
296
+ } = _chart;
297
+ const _padAngle = (padAngle * Math.PI) / 180;
298
+
299
+ switch (order) {
300
+ case '':
301
+ arcsFunc.sort(null);
302
+ break;
303
+ case 'desc':
304
+ arcsFunc.sort((a, b) => b.y - a.y);
305
+ break;
306
+ case 'asc':
307
+ arcsFunc.sort((a, b) => a.y - b.y);
308
+ break;
309
+ }
310
+
311
+ const arcs = arcsFunc(data);
312
+ const legendDataWithPercent = getDataWithPercent(arcs, legendPrecision);
313
+ const _legendDataWithPercent = sortPie(legendDataWithPercent, order);
314
+
315
+ if (rose) {
316
+ const domain = extent(_legendDataWithPercent, (d) => d.value);
317
+ const roseRadius = getRoseRadius(_chart);
318
+ const scaler = scaleLinear().domain(domain).range([roseRadius, 1]);
319
+
320
+ const angle = (PI * 2) / _legendDataWithPercent.length;
321
+ return _legendDataWithPercent.map(
322
+ ({ startAngle, endAngle, ...arc }, index) => ({
323
+ ...arc,
324
+ id: id + '_linear_' + index,
325
+ startAngle: roseType == 'area' ? angle * index : startAngle,
326
+ endAngle: roseType == 'area' ? angle * (index + 1) : endAngle,
327
+ cornerRadius,
328
+ padAngle: _padAngle,
329
+ innerRadius,
330
+ outerRadius: scaler(arc.value),
331
+ })
332
+ );
333
+ }
334
+ return _legendDataWithPercent.map((arc, index) => ({
335
+ ...arc,
336
+ id: id + '_linear_' + index,
337
+ cornerRadius,
338
+ padAngle: _padAngle,
339
+ innerRadius,
340
+ outerRadius,
341
+ }));
342
+ }, [data, arcsFunc, chart, legendPrecision]);
343
+
344
+ const _arcs = useMemo(() => {
345
+ const seriesLength = series.size;
346
+ if (!seriesLength) return [];
347
+ const _series = [...series.values()];
348
+ return arcs.map((arc, index) => getArc(radius, arc, _series[index]));
349
+ }, [series, arcs, radius]);
350
+ const onClick = useCallback(
351
+ (e) =>
352
+ onEvent({
353
+ currentIndex: +e.currentTarget.dataset.index,
354
+ type: 'onClick',
355
+ }),
356
+ [onEvent]
357
+ );
358
+
359
+ const onMouseEnter = useCallback(
360
+ (e) =>
361
+ onEvent({
362
+ currentIndex: +e.currentTarget.dataset.index,
363
+ type: 'onMouseEnter',
364
+ }),
365
+ [onEvent]
366
+ );
367
+
368
+ const onMouseLeave = useCallback(
369
+ (e) =>
370
+ onEvent({
371
+ currentIndex: +e.currentTarget.dataset.index,
372
+ type: 'onMouseLeave',
373
+ }),
374
+ [onEvent]
375
+ );
376
+
377
+ useLayoutEffect(() => {
378
+ let animation;
379
+ if (!!on) {
380
+ animation = animate({
381
+ from: 0,
382
+ to: 1,
383
+ duration: 500,
384
+ ease: linear,
385
+ onPlay: () => {},
386
+ onUpdate: (v) => {
387
+ setY(v);
388
+ },
389
+ onComplete: () => {
390
+ const _data = arcs[+currentIndex].data;
391
+ triggerOnRelative(_data);
392
+ onEmit('carousel', _data);
393
+ },
394
+ });
395
+ } else {
396
+ if (currentIndex !== null && trigger === 'onClick') {
397
+ const _data = arcs[+currentIndex].data;
398
+ triggerOnRelative(_data);
399
+ onEmit(trigger, _data);
400
+ }
401
+ }
402
+ return () => {
403
+ prevIndex.current = currentIndex;
404
+ animation && animation.stop();
405
+ animation = null;
406
+ };
407
+ }, [
408
+ JSON.stringify(arcs),
409
+ on,
410
+ currentIndex,
411
+ trigger,
412
+ onEmit,
413
+ triggerOnRelative,
414
+ ]);
415
+
416
+ const halfChartWidth = chartWidth / 2;
417
+ const halfChartHeight = chartHeight / 2;
418
+
419
+ const rotate_ = decorate2?-(arcs[+currentIndex].startAngle+arcs[+currentIndex].endAngle)*90/Math.PI+rotate:0;
420
+ let maxRadius = 0;
421
+ _arcs.map(d=>{
422
+ maxRadius=Math.max(maxRadius,d.outerRadius);
423
+ })
424
+ let centerRadius =0.5*maxRadius + 0.5*_arcs[0].innerRadius
425
+ return (
426
+ outerDecorate?<>
427
+ <ChartContainer //用于生成甜甜圈图,判断依据是外环装饰这个配置项(outerDecorate)
428
+ width={width}
429
+ height={height}
430
+ marginLeft={marginLeft}
431
+ marginTop={marginTop}
432
+ >
433
+ <g
434
+ style={{
435
+ transition:"transform ease-in-out 0.3s",
436
+ transform:'translate(' + halfChartWidth + 'px, ' + halfChartHeight + 'px) rotate('+rotate_+"deg)"
437
+ }}
438
+ >
439
+ {
440
+ //用于生成外环装饰的刻度
441
+ outerDecorate.tick.show && getCircleScale(outerDecorate.tick, maxRadius)
442
+ }
443
+ <circle //外环装饰
444
+ cx='0'
445
+ cy='0'
446
+ r={maxRadius+2}
447
+ fill='none'
448
+ stroke={outerDecorate.color}
449
+ strokeWidth={outerDecorate.width}
450
+ />
451
+ {
452
+ _arcs.map(
453
+ ({ id, value, series, arc, innerRadius, outerRadius, index: dataIndex }, index) => {
454
+ const arcWidth = (outerRadius-innerRadius);
455
+ const path = arc
456
+ .innerRadius(centerRadius)
457
+ .outerRadius(centerRadius)(value);
458
+ const dashLength=Math.ceil(Math.PI*(centerRadius)*2/_arcs.length);
459
+ const pie = getColorList(series.color);
460
+ return (
461
+ <Fragment key={index}>
462
+ <path
463
+ className={ringCss['inner-arc']}
464
+ style={{
465
+ strokeDasharray:`${dashLength},${2*dashLength}`,
466
+ strokeDashoffset: dashLength,
467
+ animationDelay: `${index * 2000}ms`
468
+ }}
469
+ data-index={dataIndex}
470
+ onClick={onClick}
471
+ onMouseEnter={onMouseEnter}
472
+ onMouseLeave={onMouseLeave}
473
+ d={path.split("L")[0]}
474
+ stroke={'url(#' + id + ')'}
475
+ strokeWidth={arcWidth}
476
+ fill="none"
477
+ />
478
+ <defs>
479
+ <LinearGradient
480
+ id={id}
481
+ colors={pie}
482
+ rotate={series.color.linear.angle + 180}
483
+ // gradientUnits='objectBoundingBox'
484
+ />
485
+ </defs>
486
+ </Fragment>
487
+ );
488
+ }
489
+ )
490
+ }
491
+ {label && <RingLabel config={{...label,maxRadius: maxRadius+2}} arcs={_arcs}/>}
492
+ </g>
493
+ </ChartContainer>
494
+ <Legend {...legend} series={_arcs} formatter={formatter}/>
495
+ </> : <>
496
+ <ChartContainer
497
+ width={width}
498
+ height={height}
499
+ marginLeft={marginLeft}
500
+ marginTop={marginTop}
501
+ >
502
+ <g
503
+ style={{
504
+ transition:"transform ease-in-out 0.3s",
505
+ transform:'translate(' + halfChartWidth + 'px, ' + halfChartHeight + 'px) rotate('+rotate_+"deg)"
506
+ }}
507
+ >
508
+ {_arcs.map(
509
+ ({ id, value, series, arc, innerRadius, outerRadius, index: dataIndex }, index) => {
510
+ const current = index == currentIndex;
511
+ const prev = index == prevIndex.current;
512
+ const offset = current ? y : prev ? 1 - y : 0;
513
+
514
+ const fillOpacity = animateColor?1:(current ? opacity / 100 : 1);
515
+ const deltaHeighten= offset*heighten;
516
+ const path = arc
517
+ .innerRadius(innerRadius + deltaHeighten)
518
+ .outerRadius(outerRadius + deltaHeighten)(value);
519
+ const pie = getColorList(series.color);
520
+ const currentPie = animateColor?getColorList(animateColor):getColorList(series.color);
521
+ let textPath="",categoryTextStyle={};
522
+ if(categoryText && categoryText.show){ //如果有类目文本,则需要计算文字路径
523
+ //let offsetWidth=decorate2.radiusWidth/2 + radiusWidthAdd/2; //当前文字需生成在装饰物内,故而半径需要减小
524
+ let textArc=arc.innerRadius(outerRadius+(current?gap:categoryText.gap))
525
+ .outerRadius(outerRadius+(current?gap:categoryText.gap))(value);
526
+ let lastA=textArc.lastIndexOf('A');
527
+ textPath=textArc.slice(0, lastA > 0 ? lastA : textArc.length); //文字路径
528
+ categoryTextStyle=current?animateCTS:categoryText.textStyle; //这里把textstyle拿出来
529
+ }
530
+
531
+ return (
532
+ <Fragment key={index}>
533
+ <path
534
+ data-index={dataIndex}
535
+ onClick={onClick}
536
+ onMouseEnter={onMouseEnter}
537
+ onMouseLeave={onMouseLeave}
538
+ d={path}
539
+ stroke={show ? color : 'none'}
540
+ strokeWidth={show ? strokeWidth : '0'}
541
+ fill={'url(#' + id + ')'}
542
+ fillOpacity={fillOpacity}
543
+ />
544
+ { //装饰物2,产生于每个弧的外部
545
+ decorate2 && decorate2.show &&
546
+ <path
547
+ data-index={dataIndex}
548
+ onClick={onClick}
549
+ onMouseEnter={onMouseEnter}
550
+ onMouseLeave={onMouseLeave}
551
+ d={arc.innerRadius(outerRadius)
552
+ .outerRadius(outerRadius + decorate2.radiusWidth + (current?radiusWidthAdd:0))(value)}
553
+ stroke={show ? color : 'none'}
554
+ strokeWidth={show ? strokeWidth : '0'}
555
+ fill={'url(#' + id + ')'}
556
+ fillOpacity={decorate2.opacity/100}
557
+ />
558
+ }
559
+ { //类目文本
560
+ categoryText && categoryText.show &&<g>
561
+ <path
562
+ onClick={onClick}
563
+ onMouseEnter={onMouseEnter}
564
+ onMouseLeave={onMouseLeave}
565
+ id={id + '_text_' + index}
566
+ d={textPath}
567
+ fill='none'
568
+ stroke='none'
569
+ />
570
+ <text
571
+ textAnchor='middle'
572
+ style={{
573
+ ...categoryTextStyle,
574
+ fontWeight:categoryTextStyle.bold?"bold":"normal",
575
+ fontStyle:categoryTextStyle.italic?"italic":"normal"
576
+ }}
577
+ fill={categoryTextStyle.color}
578
+ >
579
+ <textPath
580
+ startOffset='50%'
581
+ href={'#' + id + '_text_' + index}
582
+ >
583
+ {_arcs[index].displayName || _arcs[index].fieldName}
584
+ </textPath>
585
+ </text>
586
+ </g>
587
+ }
588
+ <defs>
589
+ <LinearGradient
590
+ id={id}
591
+ colors={current?currentPie:pie}
592
+ rotate={current?(animateColor?animateColor.linear.angle + 180:series.color.linear.angle + 180):series.color.linear.angle + 180}
593
+ // gradientUnits='objectBoundingBox'
594
+ />
595
+ </defs>
596
+ </Fragment>
597
+ );
598
+ }
599
+ )}
600
+ {label && <Label config={label} arcs={_arcs} />}
601
+ {current && (
602
+ <g fillOpacity={y} style={{transform:"rotate("+(-rotate_)+"deg)"}}>
603
+ <Current
604
+ config={current}
605
+ data={_arcs}
606
+ currentIndex={+currentIndex}
607
+ />
608
+ </g>
609
+ )}
610
+ </g>
611
+ </ChartContainer>
612
+ {decorate && (
613
+ <ConicalGradient
614
+ width={width}
615
+ height={height}
616
+ centerX={halfChartWidth + marginLeft}
617
+ centerY={halfChartHeight + marginTop}
618
+ config={decorate}
619
+ arcs={_arcs}
620
+ radius={radius}
621
+ />
622
+ )}
623
+ <Legend {...legend} series={_arcs} formatter={formatter} />
624
+ </>
625
+ );
626
+ }
627
+ );
628
+
629
+ const Current = ({
630
+ config: {
631
+ show,
632
+ gap,
633
+ name: { show: showName, font: nameFont, textBreak },
634
+ percent: {
635
+ show: showPercent,
636
+ font: percentFont,
637
+ precision,
638
+ translate: { x: translatePercentX, y: translatePercentY },
639
+ },
640
+ value: {
641
+ show: showValue,
642
+ font: valueFont,
643
+ translate: { x: translateValueX, y: translateValueY },
644
+ suffix: {
645
+ show: showSuffix,
646
+ fontSize,
647
+ text,
648
+ translate: { x: translateSuffixX, y: translateSuffixY },
649
+ },
650
+ },
651
+ },
652
+ data,
653
+ currentIndex,
654
+ }) => {
655
+ const _data = useMemo(() => {
656
+ const legendDataWithPercent = getDataWithPercent(data, precision);
657
+ return sortPie(legendDataWithPercent, '');
658
+ }, [data, precision]);
659
+ const currentData = _data[currentIndex];
660
+
661
+ if (!currentData) return null;
662
+
663
+ const { displayName, fieldName, value, percent } = currentData;
664
+ let nameTemp = displayName? displayName : fieldName; //类目名
665
+ let nameList = [];
666
+ if(textBreak){ //如果限制了首行字符,则切割组件
667
+ while(nameTemp.length>textBreak){
668
+ nameList.push(nameTemp.slice(0,textBreak));
669
+ nameTemp = nameTemp.slice(textBreak)
670
+ }
671
+ }
672
+ nameList.push(nameTemp)
673
+ let foreignStyle={ //foreignObject标签样式,
674
+ width:"100%",
675
+ height:"100%",
676
+ transform:"translate(-50%,-50%)",
677
+ },
678
+ boxStyle={ //弹性盒子样式,用于当前值的上下居中对齐等
679
+ width:"100%",
680
+ height:'100%',
681
+ display:"flex",
682
+ flexDirection:"column",
683
+ justifyContent:"center",
684
+ alignItems:"center"
685
+ }
686
+
687
+ return (
688
+ show && (
689
+ <>
690
+ <foreignObject style={foreignStyle}>
691
+ <div style={boxStyle}>
692
+ { //类目名称
693
+ showName && <div
694
+ style={{
695
+ ...getFontStyle(nameFont),
696
+ margin:gap/2+"px 0",
697
+ display:"flex",
698
+ flexDirection:"column",
699
+ alignItems:"center"
700
+ }}>
701
+ {
702
+ nameList.map((d,i)=>{
703
+ return <span key={i}>{d}</span>
704
+ })
705
+ }
706
+ </div>
707
+ }
708
+ { //真实值
709
+ showValue && <span
710
+ style={{
711
+ ...getFontStyle(valueFont),
712
+ transform:"translate("+translateValueX+"px,"+translateValueY+"px)",
713
+ margin:gap/2+"px 0"
714
+ }}>
715
+ {value}
716
+ {showSuffix && text && (
717
+ <span
718
+ style={{
719
+ transform:"translate("+translateSuffixX+"px,"+translateSuffixY+"px)",
720
+ fontSize:fontSize
721
+ }}
722
+ >
723
+ {text}
724
+ </span>
725
+ )}
726
+ </span>
727
+ }
728
+ { //百分比值
729
+ showPercent && <span
730
+ style={{
731
+ transform:"translate("+translatePercentX+"px,"+translatePercentY+"px)",
732
+ ...getFontStyle(percentFont),
733
+ margin:gap/2+"px 0"
734
+ }}
735
+ >
736
+ {percent + '%'}
737
+ </span>
738
+ }
739
+ </div>
740
+ </foreignObject>
741
+ </>
742
+ )
743
+ );
744
+ };
745
+
746
+ const Label = ({
747
+ config: {
748
+ maxRadius = 0,
749
+ lineLength,
750
+ lineColor,
751
+ distance,
752
+ mode,
753
+ show,
754
+ translate: { x: translateX, y: translateY },
755
+ name: { show: showName, font: nameFont },
756
+ value: {
757
+ show: showValue,
758
+ font: valueFont,
759
+ suffix: {
760
+ show: showSuffix,
761
+ text,
762
+ fontSize: suffixFontSize,
763
+ translate: { x: suffixTranslateX, y: suffixTranslateY },
764
+ },
765
+ sameColor: valueSameColor = false,
766
+ },
767
+ percent: {
768
+ show: showPercent,
769
+ font: percentFont,
770
+ precision,
771
+ sameColor: percentSameColor = false,
772
+ },
773
+ },
774
+ arcs,
775
+ animation
776
+ }) => {
777
+ // const [labels, setLabels] = useState(null);
778
+ // const [opacity, setOpacity] = useState(0);
779
+
780
+ const _arcs = useMemo(
781
+ () => getDataWithPercent(arcs, precision),
782
+ [arcs, precision]
783
+ );
784
+ // useEffect(() => {
785
+ // if (labels) {
786
+ // const children = [...labels.children];
787
+ // const bbox = children.reduce(
788
+ // (prev, current) => {
789
+ // const { topRight, bottomRight, bottomLeft, topLeft } = prev;
790
+ // const { x, y, height } = current.getBBox();
791
+
792
+ // current._y1 = y;
793
+ // current._y2 = y + height;
794
+ // current._delta = 0;
795
+
796
+ // if (x > 0) {
797
+ // if (y > 0) {
798
+ // bottomRight.push(current);
799
+ // } else {
800
+ // topRight.push(current);
801
+ // }
802
+ // } else {
803
+ // if (y > 0) {
804
+ // bottomLeft.push(current);
805
+ // } else {
806
+ // topLeft.push(current);
807
+ // }
808
+ // }
809
+ // return prev;
810
+ // },
811
+ // {
812
+ // topRight: [],
813
+ // bottomRight: [],
814
+ // bottomLeft: [],
815
+ // topLeft: [],
816
+ // }
817
+ // );
818
+ // console.log('bbox: ', bbox);
819
+ // }
820
+ // }, [labels]);
821
+
822
+ return (
823
+ <g
824
+ // style={{ opacity }} ref={setLabels}
825
+ >
826
+ {_arcs.map(
827
+ (
828
+ {
829
+ series: {
830
+ color: {
831
+ type,
832
+ pure,
833
+ linear: { stops },
834
+ },
835
+ },
836
+ displayName,
837
+ value,
838
+ percent,
839
+ arc,
840
+ outerRadius,
841
+ index:actualIndex
842
+ },
843
+ index
844
+ ) => {
845
+
846
+ const [x, y] = arc.centroid();
847
+ const midAngle = Math.atan2(y, x);
848
+
849
+ const [x1, y1] = getCoord(midAngle, maxRadius?maxRadius:outerRadius);
850
+
851
+ const radius = (maxRadius?maxRadius:outerRadius) + distance;
852
+ const [x2, y2] = getCoord(midAngle, radius);
853
+
854
+ const direction = x2 < 0 ? -1 : 1;
855
+ const x3 = x2 + lineLength * direction;
856
+
857
+ const _x = x3 + (translateX + 6) * direction;
858
+
859
+ const _showName = showName && displayName;
860
+ const _showValue = showValue && (value || showSuffix);
861
+
862
+ return (
863
+ show &&
864
+ (_showName || showPercent || showValue) && (
865
+ <g key={index}>
866
+ <path
867
+ className={animation? ringCss['label-line']:""}
868
+ style={{ animationDelay:`${animation? ((actualIndex+1) * 2000 - 800) : 0}ms`}}
869
+ d={
870
+ 'M' + x1 + ', ' + y1 + 'L' +
871
+ x2 + ', ' + y2 + 'L' +
872
+ x3 + ', ' + y2
873
+ }
874
+ stroke={lineColor?lineColor:(type == 'pure' ? pure : stops[0].color)}
875
+ fill='none'
876
+ />
877
+ <text
878
+ className={animation? ringCss['label-text']:""}
879
+ style={{ animationDelay:`${animation? ((actualIndex+1) * 2000 - 800) : 0}ms`}}
880
+ x={_x}
881
+ y={y2 + translateY}
882
+ dominantBaseline='middle'
883
+ textAnchor={x3 >= 0 ? 'start' : 'end'}
884
+ >
885
+ {_showName && (
886
+ <tspan style={getFontStyle(nameFont, 'svg')}>
887
+ {displayName + (showValue || showPercent ? ':' : '')}
888
+ </tspan>
889
+ )}
890
+ {showValue && (
891
+ <>
892
+ <tspan
893
+ x={!!(_showName && mode == 'vertical') ? _x : ''}
894
+ dx={valueDx(_showName, mode)}
895
+ dy={!!(_showName && mode == 'vertical') ? '1.5em' : ''}
896
+ style={getFontStyle(valueFont, 'svg')}
897
+ >
898
+ {value}
899
+ </tspan>
900
+ {showSuffix && (
901
+ <tspan
902
+ style={{
903
+ ...getFontStyle(valueFont, 'svg'),
904
+ fontSize: suffixFontSize,
905
+ }}
906
+ dx={suffixTranslateX}
907
+ dy={suffixTranslateY}
908
+ >
909
+ {text}
910
+ </tspan>
911
+ )}
912
+ </>
913
+ )}
914
+ {showPercent && (
915
+ <tspan
916
+ x={percentX(_showName, _showValue, mode, _x)}
917
+ dx={percentDx(_showName, _showValue, mode)}
918
+ dy={
919
+ percentDy(_showName, _showValue, mode) +
920
+ (_showValue && showSuffix ? suffixTranslateY * -1 : "")
921
+ }
922
+ style={getFontStyle(percentFont, 'svg')}
923
+ >
924
+ {(_showValue ? '(' : '') +
925
+ percent +
926
+ '%' +
927
+ (_showValue ? ')' : '')}
928
+ </tspan>
929
+ )}
930
+ </text>
931
+ </g>
932
+ )
933
+ );
934
+ }
935
+ )}
936
+ </g>
937
+ );
938
+ };
939
+
940
+ const RingLabel =({
941
+ config: {
942
+ maxRadius = 0,
943
+ lineLength,
944
+ lineColor,
945
+ distance,
946
+ mode,
947
+ show,
948
+ translate: { x: translateX, y: translateY },
949
+ name: { show: showName, font: nameFont },
950
+ value: {
951
+ show: showValue,
952
+ font: valueFont,
953
+ suffix: {
954
+ show: showSuffix,
955
+ text,
956
+ fontSize: suffixFontSize,
957
+ translate: { x: suffixTranslateX, y: suffixTranslateY },
958
+ },
959
+ sameColor: valueSameColor = false,
960
+ },
961
+ percent: {
962
+ show: showPercent,
963
+ font: percentFont,
964
+ precision,
965
+ sameColor: percentSameColor = false,
966
+ },
967
+ },
968
+ arcs
969
+ }) => {
970
+ const _arcs = useMemo(
971
+ () => getDataWithPercent(arcs, precision),
972
+ [arcs, precision]
973
+ );
974
+
975
+ return (
976
+ <g>
977
+ {_arcs.map(
978
+ (
979
+ {
980
+ series: {
981
+ color: {
982
+ type,
983
+ pure,
984
+ linear: { stops },
985
+ },
986
+ },
987
+ data:realData,
988
+ displayName,
989
+ value,
990
+ percent,
991
+ arc,
992
+ outerRadius,
993
+ index:actualIndex
994
+ },
995
+ index
996
+ ) => {
997
+
998
+ const [x, y] = arc.centroid();
999
+
1000
+ const midAngle = Math.atan2(y, x);
1001
+
1002
+ const [x1, y1] = getCoord(midAngle, maxRadius?maxRadius:outerRadius);
1003
+
1004
+ const radius = (maxRadius?maxRadius:outerRadius) + distance;
1005
+ const [x2, y2] = getCoord(midAngle, radius);
1006
+
1007
+ const directionX = x2 < 0 ? -1 : 1;
1008
+ const directionY = y2 < 0 ? -1 : 1;
1009
+ const x3 = x2 + lineLength * directionX;
1010
+ const _x = x3 + (translateX + 6) * directionX;
1011
+
1012
+ const _showName = showName && displayName;
1013
+ const _showValue = showValue && (value || showSuffix);
1014
+
1015
+ return (
1016
+ show &&
1017
+ (_showName || showPercent || _showValue) && (
1018
+ <g key={index}>
1019
+ <path
1020
+ className={ringCss['label-line']}
1021
+ style={{ animationDelay:`${(actualIndex+1) * 2000 - 800}ms`}}
1022
+ d={
1023
+ 'M' + x1 + ', ' + y1 + 'L' +
1024
+ x2 + ', ' + y2 + 'L' +
1025
+ x3 + ', ' + y2
1026
+ }
1027
+ stroke={lineColor?lineColor:(type == 'pure' ? pure : stops[0].color)}
1028
+ fill='none'
1029
+ />
1030
+ <text
1031
+ className={ringCss['label-text']}
1032
+ style={{ animationDelay:`${(actualIndex+1) * 2000 - 800}ms`}}
1033
+ x={mode=="horizontal"?_x:x2}
1034
+ y={y2 + translateY}
1035
+ dominantBaseline='middle'
1036
+ textAnchor={x3 >= 0 ? 'start' : 'end'}
1037
+ >
1038
+ {_showName && (
1039
+ <tspan dy={nameDy(_showValue, showPercent, mode, directionY)} style={getFontStyle(nameFont, 'svg')}>
1040
+ {displayName + (showValue || showPercent ? ':' : '')}
1041
+ </tspan>
1042
+ )}
1043
+ {_showValue && (
1044
+ <>
1045
+ <tspan
1046
+ x={ _showName?(mode=="horizontal"?'':x2):'' }
1047
+ dx={valueDx(_showName, mode)}
1048
+ dy={valueDy(_showName, mode, directionY)}
1049
+ style={getFontStyle(valueFont, 'svg')}
1050
+ >
1051
+ {realData.y}
1052
+ </tspan>
1053
+ {showSuffix && (
1054
+ <tspan
1055
+ style={{
1056
+ ...getFontStyle(valueFont, 'svg'),
1057
+ fontSize: suffixFontSize,
1058
+ }}
1059
+ dx={suffixTranslateX}
1060
+ dy={suffixTranslateY}
1061
+ >
1062
+ {text}
1063
+ </tspan>
1064
+ )}
1065
+ </>
1066
+ )}
1067
+ {showPercent && (
1068
+ <tspan
1069
+ x={_showName?(_showValue?'':(mode=="vertical"?x2:'')):''}
1070
+ dx={percentDx(_showName, _showValue, mode)}
1071
+ dy={
1072
+ percentDy_(_showName, _showValue, mode, directionY) +
1073
+ (_showValue && showSuffix ? suffixTranslateY * -1 : "")
1074
+ }
1075
+ style={getFontStyle(percentFont, 'svg')}
1076
+ >
1077
+ {(_showValue ? '(' : '') +
1078
+ percent +
1079
+ '%' +
1080
+ (_showValue ? ')' : '')}
1081
+ </tspan>
1082
+ )}
1083
+ </text>
1084
+ </g>
1085
+ )
1086
+ );
1087
+ }
1088
+ )}
1089
+ </g>
1090
+ );
1091
+ };
1092
+
1093
+ export default Mapping(Carousel(Component));