@easyv/charts 1.10.13 → 1.10.15

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.
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import React, { memo, useCallback, useState, useEffect, useRef } from "react";
5
5
  import { getIcon, sortPie } from "../utils";
6
+ import { parseLegendAlignment } from "../utils/legendPlacement";
6
7
  import TextOverflow from "./TextOverflow";
7
8
 
8
9
  const defaultFont = {
@@ -32,6 +33,7 @@ export default memo(
32
33
  translate: { x, y },
33
34
  },
34
35
  loop = {},
36
+ name: { layoutMode } = {},
35
37
  font: { italic, bold, ...font } = defaultFont,
36
38
  unselect: { opacity = 1 } = {},
37
39
  },
@@ -39,6 +41,11 @@ export default memo(
39
41
  formatter,
40
42
  judge,
41
43
  pieClick,
44
+ isPieChart = false,
45
+ chartWidth,
46
+ componentWidth,
47
+ marginLeft = 0,
48
+ marginRight = 0,
42
49
  }) => {
43
50
  if (!show) return null;
44
51
 
@@ -86,7 +93,18 @@ export default memo(
86
93
  };
87
94
 
88
95
  const _series = sortPie(series, order, columnsSeries);
89
- const [_alignment, position] = alignment.split(" ");
96
+ const {
97
+ alignment: _alignment,
98
+ position,
99
+ isCenterTopOrBottom,
100
+ isSidePlacement,
101
+ } = parseLegendAlignment(alignment);
102
+ const legendMainAlign =
103
+ _alignment == "left"
104
+ ? "flex-start"
105
+ : _alignment == "center"
106
+ ? "center"
107
+ : "flex-end";
90
108
  const length = _series.length;
91
109
 
92
110
  const onClick = useCallback(
@@ -198,6 +216,112 @@ export default memo(
198
216
  return Math.max(maxMeasured, maxCfg);
199
217
  });
200
218
 
219
+ const isPieAdaptive = isPieChart && layoutMode === "Adaptive";
220
+ const isPieFixedWidthGrid =
221
+ isPieChart &&
222
+ (layoutMode === "FixedWidth" ||
223
+ ((layoutMode == null || layoutMode === "") &&
224
+ LegendType == "FixedWidth"));
225
+ const isFixedWidth = isPieChart
226
+ ? isPieFixedWidthGrid
227
+ : LegendType == "FixedWidth";
228
+ const isSideLegend = isPieAdaptive && isSidePlacement;
229
+ const isTopBottomAdaptive = isPieAdaptive && isCenterTopOrBottom;
230
+ const fullWidth = componentWidth ?? chartWidth ?? 0;
231
+ const legendAreaWidth = Math.max(0, fullWidth - marginLeft - marginRight);
232
+ const sideLegendMaxWidth = isSideLegend
233
+ ? Math.max(
234
+ 0,
235
+ position === "left" || _alignment === "left"
236
+ ? marginLeft
237
+ : marginRight,
238
+ )
239
+ : 0;
240
+ const isSideLegendOnRight =
241
+ isSideLegend &&
242
+ (position === "right" ||
243
+ (_alignment === "right" && position !== "left"));
244
+ const fixedColumnsPerRow = Math.min(
245
+ Math.max(1, Number(gridTemplateColumns) || 1),
246
+ length,
247
+ );
248
+ const isPieTopBottomFixedMultiCol =
249
+ isPieChart &&
250
+ isFixedWidth &&
251
+ isCenterTopOrBottom &&
252
+ fixedColumnsPerRow > 1;
253
+
254
+ const formatterExtra = {
255
+ ...config,
256
+ valueMaxWidth,
257
+ percentMaxWidth,
258
+ nameMaxWidth,
259
+ otherData: data,
260
+ columnsSeries,
261
+ fieldColumnKeys,
262
+ fieldsColumnWidths,
263
+ legendPosition: position,
264
+ isPieAdaptive: true,
265
+ ...(isSideLegend && sideLegendMaxWidth > 0
266
+ ? { adaptiveMaxWidth: sideLegendMaxWidth }
267
+ : {}),
268
+ ...(isTopBottomAdaptive ? { chartWidth: legendAreaWidth } : {}),
269
+ };
270
+
271
+ const renderPieSideItem = (series, i) => {
272
+ const {
273
+ type,
274
+ name,
275
+ displayName,
276
+ fieldName,
277
+ icon,
278
+ selected,
279
+ index,
280
+ } = series;
281
+ const _icon = getIcon(type, icon, series?.config?.line?.type);
282
+ return (
283
+ <li
284
+ key={i}
285
+ onClick={onClick(fieldName)}
286
+ data-name={displayName || name}
287
+ data-index={index}
288
+ style={{
289
+ display: "flex",
290
+ width: "max-content",
291
+ maxWidth: sideLegendMaxWidth || undefined,
292
+ opacity: selected === false ? opacity / 100 : 1,
293
+ alignItems: "center",
294
+ cursor: "pointer",
295
+ gap: _icon.gap,
296
+ minWidth: 0,
297
+ overflow: "hidden",
298
+ boxSizing: "border-box",
299
+ }}
300
+ >
301
+ {formatter ? (
302
+ formatter(series, formatterExtra)
303
+ ) : (
304
+ <>
305
+ <span style={{ ..._icon, flexShrink: 0 }} />
306
+ <TextOverflow
307
+ type="ellipsis"
308
+ value={displayName || name}
309
+ style={{
310
+ ...font,
311
+ fontStyle: italic ? "italic" : "normal",
312
+ fontWeight: bold ? "bold" : "normal",
313
+ minWidth: 0,
314
+ flex: "1 1 0%",
315
+ maxWidth: "100%",
316
+ }}
317
+ speed={speed}
318
+ />
319
+ </>
320
+ )}
321
+ </li>
322
+ );
323
+ };
324
+
201
325
  const stylePieOrAxis = formatter
202
326
  ? {
203
327
  display: "flex",
@@ -221,27 +345,137 @@ export default memo(
221
345
  }
222
346
  : {
223
347
  width: "100%",
224
- display: "flex",
225
- flexWrap: "wrap",
226
- alignContent:
227
- alignment.split(" ")[0] == "center" &&
228
- (alignment.split(" ")[1] == "left" ||
229
- alignment.split(" ")[1] == "right")
230
- ? alignment.split(" ")[1] == "left"
231
- ? "flex-start"
232
- : "flex-end"
233
- : alignment.split(" ")[0] == "left"
234
- ? "flex-start"
235
- : alignment.split(" ")[0] == "center"
236
- ? "center"
237
- : "flex-end",
238
- flexDirection: "column",
348
+ maxWidth: "100%",
349
+ boxSizing: "border-box",
239
350
  position: "absolute",
240
351
  ...getPosition(position, _alignment, x, y),
241
352
  height: loop.show ? height : "auto",
242
353
  overflowY: loop.show ? "scroll" : "auto",
243
354
  };
244
- return LegendType == "FixedWidth" ? (
355
+ if (isPieAdaptive && isSideLegend) {
356
+ const pieAdaptiveWrapperStyle = {
357
+ position: "absolute",
358
+ display: "flex",
359
+ flexDirection: "column",
360
+ width: "max-content",
361
+ maxWidth: sideLegendMaxWidth || undefined,
362
+ alignItems: isSideLegendOnRight ? "flex-end" : "flex-start",
363
+ ...getPosition(position, _alignment, x, y),
364
+ height: loop.show ? height : "auto",
365
+ overflowY: loop.show ? "scroll" : "auto",
366
+ };
367
+ const pieAdaptiveListStyle = {
368
+ display: "flex",
369
+ flexDirection: "column",
370
+ width: "max-content",
371
+ maxWidth: "100%",
372
+ alignItems: isSideLegendOnRight ? "flex-end" : "flex-start",
373
+ margin: 0,
374
+ padding: 0,
375
+ listStyle: "none",
376
+ gap: `${gridRowGap}px`,
377
+ };
378
+ return (
379
+ <div
380
+ className="__easyv-legend-wrapper"
381
+ style={pieAdaptiveWrapperStyle}
382
+ ref={ref_container}
383
+ >
384
+ <ul style={pieAdaptiveListStyle}>
385
+ {_series.map((series, i) => renderPieSideItem(series, i))}
386
+ </ul>
387
+ </div>
388
+ );
389
+ }
390
+
391
+ if (isTopBottomAdaptive) {
392
+ const topBottomWrapperStyle = {
393
+ position: "absolute",
394
+ width: legendAreaWidth || "100%",
395
+ maxWidth: legendAreaWidth || "100%",
396
+ boxSizing: "border-box",
397
+ ...getPosition(position, _alignment, x, y),
398
+ height: loop.show ? height : "auto",
399
+ overflowY: loop.show ? "scroll" : "auto",
400
+ };
401
+ const topBottomListStyle = {
402
+ display: "flex",
403
+ flexDirection: "row",
404
+ flexWrap: "wrap",
405
+ width: "100%",
406
+ maxWidth: "100%",
407
+ boxSizing: "border-box",
408
+ margin: 0,
409
+ padding: 0,
410
+ listStyle: "none",
411
+ justifyContent: legendMainAlign,
412
+ gap: `${gridRowGap}px ${gridColumnGap}px`,
413
+ };
414
+ return (
415
+ <div
416
+ className="__easyv-legend-wrapper"
417
+ style={topBottomWrapperStyle}
418
+ ref={ref_container}
419
+ >
420
+ <ul style={topBottomListStyle}>
421
+ {_series.map((series, i) => {
422
+ const {
423
+ type,
424
+ name,
425
+ displayName,
426
+ fieldName,
427
+ icon,
428
+ selected,
429
+ index,
430
+ } = series;
431
+ const _icon = getIcon(type, icon, series?.config?.line?.type);
432
+ return (
433
+ <li
434
+ key={i}
435
+ onClick={onClick(fieldName)}
436
+ data-name={displayName || name}
437
+ data-index={index}
438
+ style={{
439
+ display: "inline-flex",
440
+ alignItems: "center",
441
+ width: "max-content",
442
+ maxWidth: "100%",
443
+ minWidth: 0,
444
+ overflow: "hidden",
445
+ boxSizing: "border-box",
446
+ opacity: selected === false ? opacity / 100 : 1,
447
+ cursor: "pointer",
448
+ gap: _icon.gap,
449
+ flexShrink: 0,
450
+ }}
451
+ >
452
+ {formatter ? (
453
+ formatter(series, formatterExtra)
454
+ ) : (
455
+ <>
456
+ <span style={{ ..._icon, flexShrink: 0 }} />
457
+ <TextOverflow
458
+ type="ellipsis"
459
+ value={displayName || name}
460
+ style={{
461
+ ...font,
462
+ fontStyle: italic ? "italic" : "normal",
463
+ fontWeight: bold ? "bold" : "normal",
464
+ minWidth: 0,
465
+ }}
466
+ speed={speed}
467
+ />
468
+ </>
469
+ )}
470
+ </li>
471
+ );
472
+ })}
473
+ </ul>
474
+ </div>
475
+ );
476
+ }
477
+
478
+ return isFixedWidth ? (
245
479
  <div
246
480
  className="__easyv-legend-wrapper"
247
481
  style={{
@@ -256,10 +490,17 @@ export default memo(
256
490
  <ul
257
491
  style={{
258
492
  display: "grid",
259
- gridGap: gridRowGap + "px " + gridColumnGap + "px",
260
- gridTemplateColumns: formatter
261
- ? `${nameMaxWidth}px ${valueMaxWidth}px ${percentMaxWidth}px`
262
- : "repeat(" + Math.min(gridTemplateColumns, length) + ", 1fr)", //饼图或者柱状图配合不同的图例显示格式
493
+ rowGap: gridRowGap + "px",
494
+ columnGap:
495
+ (isPieTopBottomFixedMultiCol
496
+ ? Math.max(gridColumnGap, 40)
497
+ : gridColumnGap) + "px",
498
+ justifyContent: isPieTopBottomFixedMultiCol
499
+ ? legendMainAlign
500
+ : undefined,
501
+ gridTemplateColumns: isPieTopBottomFixedMultiCol
502
+ ? `repeat(${fixedColumnsPerRow}, max-content)`
503
+ : `repeat(${fixedColumnsPerRow}, minmax(0, 1fr))`,
263
504
  }}
264
505
  >
265
506
  {_series.map((series, i) => {
@@ -326,53 +567,47 @@ export default memo(
326
567
  style={stylePieOrAxis}
327
568
  ref={ref_container}
328
569
  >
329
- {[...Array(Math.ceil(series.length / gridTemplateColumns))].map(
330
- (_, indexs) => (
331
- <ul
332
- key={indexs}
333
- style={{
334
- display: "flex",
335
- width: "fit-content",
336
- justifyContent:
337
- alignment.split(" ")[0] == "left"
338
- ? "flex-start"
339
- : alignment.split(" ")[0] == "center"
340
- ? "center"
341
- : "flex-end",
342
- marginBottom: "0px",
343
- gap: `${gridRowGap}px ${gridColumnGap}px`,
344
- marginBottom: gridRowGap + "px",
345
- }}
346
- >
347
- {_series.map((series, i) => {
348
- if (Math.floor(i / gridTemplateColumns) == indexs) {
349
- const {
350
- type,
351
- name,
352
- displayName,
353
- fieldName,
354
- icon,
355
- selected,
356
- index,
357
- } = series;
358
- const _icon = getIcon(type, icon, series?.config?.line?.type);
359
- return (
360
- <li
361
- key={i}
362
- onClick={onClick(fieldName)}
363
- data-name={displayName || name}
364
- data-index={index}
365
- style={{
366
- display: "flex",
367
- opacity: selected === false ? opacity / 100 : 1,
368
- alignItems: "center",
369
- cursor: "pointer",
370
- gap: _icon.gap,
371
- overflow: formatter ? "visible" : undefined,
372
- }}
373
- >
374
- {formatter ? (
375
- formatter(series, {
570
+ {formatter ? (
571
+ [...Array(Math.ceil(series.length / gridTemplateColumns))].map(
572
+ (_, indexs) => (
573
+ <ul
574
+ key={indexs}
575
+ style={{
576
+ display: "flex",
577
+ width: "fit-content",
578
+ justifyContent: legendMainAlign,
579
+ gap: `${gridRowGap}px ${gridColumnGap}px`,
580
+ marginBottom: gridRowGap + "px",
581
+ }}
582
+ >
583
+ {_series.map((series, i) => {
584
+ if (Math.floor(i / gridTemplateColumns) == indexs) {
585
+ const {
586
+ type,
587
+ name,
588
+ displayName,
589
+ fieldName,
590
+ icon,
591
+ selected,
592
+ index,
593
+ } = series;
594
+ const _icon = getIcon(type, icon, series?.config?.line?.type);
595
+ return (
596
+ <li
597
+ key={i}
598
+ onClick={onClick(fieldName)}
599
+ data-name={displayName || name}
600
+ data-index={index}
601
+ style={{
602
+ display: "flex",
603
+ opacity: selected === false ? opacity / 100 : 1,
604
+ alignItems: "center",
605
+ cursor: "pointer",
606
+ gap: _icon.gap,
607
+ overflow: "visible",
608
+ }}
609
+ >
610
+ {formatter(series, {
376
611
  ...config,
377
612
  valueMaxWidth,
378
613
  percentMaxWidth,
@@ -381,37 +616,77 @@ export default memo(
381
616
  columnsSeries,
382
617
  fieldColumnKeys,
383
618
  fieldsColumnWidths,
384
- })
385
- ) : (
386
- <>
387
- <span style={_icon} />
388
- <TextOverflow
389
- ShowType={LegendType}
390
- type="ellipsis"
391
- value={displayName || name}
392
- style={{
393
- ...font,
394
- fontStyle: italic ? "italic" : "normal",
395
- fontWeight: bold ? "bold" : "normal",
396
- minWidth: getCanvasTextWidth(
397
- displayName
398
- ? displayName.substring(0, 5) ||
399
- name.substring(0, 5)
400
- : "",
401
- font.letterSpacing,
402
- `${font.fontSize}px ${font.fontFamily}`,
403
- ),
404
- }}
405
- speed={speed}
406
- ></TextOverflow>
407
- </>
408
- )}
409
- </li>
410
- );
411
- }
412
- })}
413
- </ul>
414
- ),
619
+ })}
620
+ </li>
621
+ );
622
+ }
623
+ })}
624
+ </ul>
625
+ ),
626
+ )
627
+ ) : (
628
+ <ul
629
+ style={{
630
+ display: "flex",
631
+ flexDirection: "row",
632
+ flexWrap: "wrap",
633
+ width: "100%",
634
+ maxWidth: "100%",
635
+ boxSizing: "border-box",
636
+ margin: 0,
637
+ padding: 0,
638
+ listStyle: "none",
639
+ justifyContent: legendMainAlign,
640
+ gap: `${gridRowGap}px ${gridColumnGap}px`,
641
+ }}
642
+ >
643
+ {_series.map((series, i) => {
644
+ const {
645
+ type,
646
+ name,
647
+ displayName,
648
+ fieldName,
649
+ icon,
650
+ selected,
651
+ index,
652
+ } = series;
653
+ const _icon = getIcon(type, icon, series?.config?.line?.type);
654
+ return (
655
+ <li
656
+ key={i}
657
+ onClick={onClick(fieldName)}
658
+ data-name={displayName || name}
659
+ data-index={index}
660
+ style={{
661
+ display: "inline-flex",
662
+ alignItems: "center",
663
+ maxWidth: "100%",
664
+ minWidth: 0,
665
+ boxSizing: "border-box",
666
+ verticalAlign: "middle",
667
+ opacity: selected === false ? opacity / 100 : 1,
668
+ cursor: "pointer",
669
+ gap: _icon.gap,
670
+ }}
671
+ >
672
+ <span style={{ ..._icon, flexShrink: 0 }} />
673
+ <TextOverflow
674
+ type="ellipsis"
675
+ value={displayName || name}
676
+ style={{
677
+ ...font,
678
+ fontStyle: italic ? "italic" : "normal",
679
+ fontWeight: bold ? "bold" : "normal",
680
+ minWidth: 0,
681
+ flex: "1 1 0%",
682
+ maxWidth: "100%",
683
+ }}
684
+ speed={speed}
685
+ />
686
+ </li>
687
+ );
688
+ })}
689
+ </ul>
415
690
  )}
416
691
  </div>
417
692
  );
@@ -37,7 +37,7 @@ export default memo(
37
37
  const startAnimation = (lineWidth: number) => {
38
38
  const animation = (timestamp: number) => {
39
39
  let frame = Math.round(
40
- ((timestamp * speed_.current) % (lineWidth * 100)) / 100
40
+ ((timestamp * speed_.current) % (lineWidth * 100)) / 100,
41
41
  );
42
42
  target.current.style.transform = `translate(-${frame}px,0px)`;
43
43
  target.current.nextSibling.style.transform = `translate(-${frame}px,0px)`;
@@ -57,7 +57,7 @@ export default memo(
57
57
  const containerWidth = rootRef.current.clientWidth;
58
58
 
59
59
  if (textWidth <= containerWidth) {
60
- console.log("文字全部可视");
60
+ // console.log("文字全部可视");
61
61
  //表示文字全部可视
62
62
  cancelAnimationFrame(timer.current || 0);
63
63
  target.current.style.transform = "translate(0px,0px)"; //重置偏移
@@ -88,7 +88,7 @@ export default memo(
88
88
  root: rootRef.current,
89
89
  rootMargin: "0px 0px 0px 0px",
90
90
  threshold: new Array(101).fill(0).map((d, i) => i / 100), //这里设置了[0-1]之间所有的阈值,保证每一帧的变化都能被观察到
91
- }
91
+ },
92
92
  );
93
93
 
94
94
  // 使用ResizeObserver监听元素尺寸变化,这样即使有3D变换也能准确检测
@@ -142,5 +142,5 @@ export default memo(
142
142
  })}
143
143
  </div>
144
144
  );
145
- })
145
+ }),
146
146
  );