@wil-works/evoke-charts 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/{ai-BdpMydhW.js → ai-B5Fk3lBQ.js} +4214 -1420
- package/dist/ai.mjs +1 -1
- package/dist/evoke-charts.css +37 -31
- package/dist/index.mjs +471 -106
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { $ as resolveChartPalette, A as squarifyTreemap, B as getToolboxBounds, C as chordHitTest, D as computeSunburstGeometry, E as boxplotHitTest, F as doughnutInnerRadius, G as createAnimation, H as funnelStepColor, I as matrixFieldExtent, J as isMissingValue, K as getPadding, L as scatterGroupColors, M as computeFacetGrids, N as computeMatrixCells, O as sunburstValue, P as computePieMaxRadius, Q as CHART_PALETTES, R as scatterPointPositions, S as arcHitTest, T as vennHitTest, U as getHeatmapCategories, V as calendarHitTest, W as xToCategoryIndex, X as updateAnimation, Y as resolveTickExtendedRange, Z as waterfallSteps, _ as getSliderGeometry, at as INTERACTION, b as zoomToSlice, ct as resolveCursor, et as DEFAULT_I18N_ZH, f as chartOptionsSchema, g as getDataZoomConfig, h as createSvgRecorder, it as minOf, j as buildPieSlices, k as computeBins, m as renderChart, n as buildChartPrompt, ot as applyWheelZoom, p as validateOptions, q as getTheme, rt as maxOf, s as generateChartSpec, st as pickTooltipRows, t as lintChartSpec, u as parseDataTable, v as windowToX, w as sankeyHitTest, x as ganttHitTest, y as xToPercent, z as getLegendBounds } from "./ai-B5Fk3lBQ.js";
|
|
2
2
|
import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, h, inject, nextTick, normalizeClass, normalizeStyle, onMounted, onUnmounted, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, watch, withCtx } from "vue";
|
|
3
3
|
//#region src/connect.js
|
|
4
4
|
var connectGroups = /* @__PURE__ */ new Map();
|
|
@@ -30,7 +30,7 @@ var _plugin_vue_export_helper_default = (sfc, props) => {
|
|
|
30
30
|
};
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/chart.vue
|
|
33
|
-
var _hoisted_1 = ["aria-label"];
|
|
33
|
+
var _hoisted_1 = ["tabindex", "aria-label"];
|
|
34
34
|
var _hoisted_2 = {
|
|
35
35
|
key: 1,
|
|
36
36
|
class: "ev-chart__overlay"
|
|
@@ -121,11 +121,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
121
121
|
switch (opt.type) {
|
|
122
122
|
case "pie":
|
|
123
123
|
case "doughnut":
|
|
124
|
-
case "rose": return !opt.pieData || opt.pieData.length === 0;
|
|
124
|
+
case "rose": return !opt.pieData || opt.pieData.length === 0 || opt.pieData.every((d) => !(d.value > 0));
|
|
125
125
|
case "scatter": return !opt.scatterData || opt.scatterData.length === 0;
|
|
126
126
|
case "funnel": return !opt.funnelData || opt.funnelData.length === 0;
|
|
127
127
|
case "gauge": return !opt.gauge;
|
|
128
128
|
case "heatmap": return !opt.heatmapData || opt.heatmapData.length === 0;
|
|
129
|
+
case "calendar-heatmap": return !opt.calendarData || opt.calendarData.length === 0;
|
|
129
130
|
case "candle": return !opt.candleData || opt.candleData.length === 0;
|
|
130
131
|
case "bullet": return !opt.bulletData || opt.bulletData.length === 0;
|
|
131
132
|
case "treemap": return !opt.treemapData || opt.treemapData.length === 0;
|
|
@@ -136,6 +137,17 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
136
137
|
case "bin": return !opt.series || opt.series.length === 0 || opt.series.every((s) => !s.data || s.data.length === 0);
|
|
137
138
|
case "sparkline": return !opt.series || opt.series.length === 0 || !opt.series[0]?.data?.length;
|
|
138
139
|
case "radar": return !opt.radarIndicators?.length || !opt.radarSeries?.length;
|
|
140
|
+
case "sankey":
|
|
141
|
+
case "chord":
|
|
142
|
+
case "arc": return !(opt.sankeyData || opt.chordData || opt.arcData)?.nodes?.length;
|
|
143
|
+
case "venn":
|
|
144
|
+
if (!opt.vennData || opt.vennData.length === 0) return true;
|
|
145
|
+
{
|
|
146
|
+
const circles = opt.vennData.filter((d) => !d.sets || d.sets.length === 1);
|
|
147
|
+
return circles.length === 0 || circles.length > 3;
|
|
148
|
+
}
|
|
149
|
+
case "gantt": return !opt.ganttData || opt.ganttData.length === 0;
|
|
150
|
+
case "scatter-matrix": return !opt.matrixFields?.length || !opt.matrixData?.length;
|
|
139
151
|
default: return !opt.labels?.length || !opt.series?.length;
|
|
140
152
|
}
|
|
141
153
|
});
|
|
@@ -159,6 +171,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
159
171
|
let hoverIndex = -1;
|
|
160
172
|
let hoverAnimProgress = 0;
|
|
161
173
|
let hoverAnimFrameId = null;
|
|
174
|
+
let hoverAnimDone = null;
|
|
162
175
|
const hiddenSeries = ref(/* @__PURE__ */ new Set());
|
|
163
176
|
const focusSeries = ref(null);
|
|
164
177
|
const emphasisSeriesName = computed(() => {
|
|
@@ -228,11 +241,20 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
228
241
|
data: s.data.slice(startIdx, endIdx)
|
|
229
242
|
}));
|
|
230
243
|
if (base.candleData) sliced.candleData = base.candleData.slice(startIdx, endIdx);
|
|
244
|
+
if (base.volumeData) sliced.volumeData = base.volumeData.slice(startIdx, endIdx);
|
|
231
245
|
sliced.__sliceStart = startIdx;
|
|
246
|
+
} else if (base.candleData) {
|
|
247
|
+
const cn = base.candleData.length;
|
|
248
|
+
if (cn > 0) {
|
|
249
|
+
const { startIdx, endIdx } = zoomToSlice(range, cn);
|
|
250
|
+
sliced.candleData = base.candleData.slice(startIdx, endIdx);
|
|
251
|
+
if (base.volumeData) sliced.volumeData = base.volumeData.slice(startIdx, endIdx);
|
|
252
|
+
sliced.__sliceStart = startIdx;
|
|
253
|
+
}
|
|
232
254
|
} else if (base.scatterData && base.scatterData.length > 0) {
|
|
233
255
|
const xs = base.scatterData.map((d) => d.x);
|
|
234
|
-
const xMin =
|
|
235
|
-
const span =
|
|
256
|
+
const xMin = minOf(xs);
|
|
257
|
+
const span = maxOf(xs) - xMin || 1;
|
|
236
258
|
const lo = xMin + range.start / 100 * span;
|
|
237
259
|
const hi = xMin + range.end / 100 * span;
|
|
238
260
|
const filtered = base.scatterData.filter((d) => d.x >= lo && d.x <= hi);
|
|
@@ -326,11 +348,11 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
326
348
|
const el = tooltipRef.value;
|
|
327
349
|
const tw = el?.offsetWidth || 120;
|
|
328
350
|
const th = el?.offsetHeight || 40;
|
|
329
|
-
const gap =
|
|
351
|
+
const gap = INTERACTION.tooltip.cursorGap;
|
|
330
352
|
const margin = 2;
|
|
331
353
|
let legendBottom = margin;
|
|
332
354
|
const legend = props.options.legend || {};
|
|
333
|
-
if (legend.show !== false && (legend.position || "bottom") === "top") legendBottom =
|
|
355
|
+
if (legend.show !== false && (legend.position || "bottom") === "top") legendBottom = paddingFor(props.options, containerRect.width).top;
|
|
334
356
|
const spaceRight = containerRect.width - relX - gap;
|
|
335
357
|
const spaceLeft = relX - gap;
|
|
336
358
|
let left;
|
|
@@ -400,7 +422,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
400
422
|
lastHeight = height;
|
|
401
423
|
canvas.width = width * dpr.value;
|
|
402
424
|
canvas.height = height * dpr.value;
|
|
403
|
-
const overlayPadding =
|
|
425
|
+
const overlayPadding = paddingFor(effectiveOptions.value, width);
|
|
404
426
|
overlayInfo.value = {
|
|
405
427
|
plotArea: {
|
|
406
428
|
x: overlayPadding.left,
|
|
@@ -443,15 +465,47 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
443
465
|
mouseY,
|
|
444
466
|
showCrosshair: hoverIndex >= 0,
|
|
445
467
|
hoverAnimProgress,
|
|
468
|
+
focusAnimProgress,
|
|
446
469
|
zoomRange: isZoomEnabled() ? zoomRange.value : void 0,
|
|
447
470
|
brushRect: brushRect.value,
|
|
448
471
|
focusSeries: focusSeries.value,
|
|
449
472
|
hoveredToolbox: hoveredToolbox.value
|
|
450
473
|
};
|
|
451
474
|
}
|
|
475
|
+
let focusAnimProgress = emphasisSeriesName.value ? 1 : 0;
|
|
476
|
+
let focusAnimFrameId = null;
|
|
477
|
+
function animateFocusProgress() {
|
|
478
|
+
if (focusAnimFrameId !== null) {
|
|
479
|
+
cancelAnimationFrame(focusAnimFrameId);
|
|
480
|
+
focusAnimFrameId = null;
|
|
481
|
+
}
|
|
482
|
+
const target = focusSeries.value ? 1 : 0;
|
|
483
|
+
if (props.options.animation?.enabled === false || target === focusAnimProgress) {
|
|
484
|
+
focusAnimProgress = target;
|
|
485
|
+
redraw();
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const start = focusAnimProgress;
|
|
489
|
+
const startTime = performance.now();
|
|
490
|
+
const duration = 180;
|
|
491
|
+
function frame(now) {
|
|
492
|
+
const tRaw = Math.min((now - startTime) / duration, 1);
|
|
493
|
+
focusAnimProgress = start + (target - start) * (1 - Math.pow(1 - tRaw, 3));
|
|
494
|
+
redraw();
|
|
495
|
+
if (tRaw < 1) focusAnimFrameId = requestAnimationFrame(frame);
|
|
496
|
+
else focusAnimFrameId = null;
|
|
497
|
+
}
|
|
498
|
+
focusAnimFrameId = requestAnimationFrame(frame);
|
|
499
|
+
}
|
|
500
|
+
function stopFocusAnimation() {
|
|
501
|
+
if (focusAnimFrameId !== null) {
|
|
502
|
+
cancelAnimationFrame(focusAnimFrameId);
|
|
503
|
+
focusAnimFrameId = null;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
452
506
|
function snapshotSeriesData(opt) {
|
|
453
507
|
const map = /* @__PURE__ */ new Map();
|
|
454
|
-
(opt.series || []).forEach((s) => map.set(s.name, s.data.map((v) => isMissingValue(v) ? 0 : v)));
|
|
508
|
+
(opt.series || []).forEach((s) => map.set(s.name, (s.data || []).map((v) => isMissingValue(v) ? 0 : v)));
|
|
455
509
|
return map;
|
|
456
510
|
}
|
|
457
511
|
function sameStructure(a, b) {
|
|
@@ -465,7 +519,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
465
519
|
const series = (next.series || []).map((s) => {
|
|
466
520
|
const old = prev.get(s.name);
|
|
467
521
|
if (!old) return s;
|
|
468
|
-
const data = s.data.map((v, i) => {
|
|
522
|
+
const data = (s.data || []).map((v, i) => {
|
|
469
523
|
if (isMissingValue(v)) return v;
|
|
470
524
|
const from = old[i + offset] ?? 0;
|
|
471
525
|
return from + (v - from) * t;
|
|
@@ -481,7 +535,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
481
535
|
};
|
|
482
536
|
}
|
|
483
537
|
function dataSignature(opt) {
|
|
484
|
-
const total = (opt.series || []).reduce((sum, s) => sum + s.data.reduce((acc, v) => acc + (isMissingValue(v) ? 0 : v), 0), 0);
|
|
538
|
+
const total = (opt.series || []).reduce((sum, s) => sum + (s.data || []).reduce((acc, v) => acc + (isMissingValue(v) ? 0 : v), 0), 0);
|
|
485
539
|
return `${(opt.series || []).length}\u7CFB\u5217/${(opt.labels || []).length}\u7C7B\u76EE/\u03A3${Math.round(total)}`;
|
|
486
540
|
}
|
|
487
541
|
function stopTweenAnimation() {
|
|
@@ -499,7 +553,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
499
553
|
if (!(nextOptions.series || []).some((s) => {
|
|
500
554
|
const old = prevSnapshot.get(s.name);
|
|
501
555
|
if (!old) return false;
|
|
502
|
-
return s.data.some((v, i) => (isMissingValue(v) ? 0 : v) !== (old[i] ?? 0));
|
|
556
|
+
return (s.data || []).some((v, i) => (isMissingValue(v) ? 0 : v) !== (old[i] ?? 0));
|
|
503
557
|
})) return false;
|
|
504
558
|
stopAnimation();
|
|
505
559
|
stopHoverAnimation();
|
|
@@ -564,31 +618,72 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
564
618
|
cancelAnimationFrame(hoverAnimFrameId);
|
|
565
619
|
hoverAnimFrameId = null;
|
|
566
620
|
}
|
|
621
|
+
if (hoverRedrawFrameId !== null) {
|
|
622
|
+
cancelAnimationFrame(hoverRedrawFrameId);
|
|
623
|
+
hoverRedrawFrameId = null;
|
|
624
|
+
}
|
|
625
|
+
hoverAnimDone = null;
|
|
626
|
+
}
|
|
627
|
+
let hoverRedrawFrameId = null;
|
|
628
|
+
function scheduleHoverRedraw() {
|
|
629
|
+
if (hoverRedrawFrameId !== null) return;
|
|
630
|
+
hoverRedrawFrameId = requestAnimationFrame(() => {
|
|
631
|
+
hoverRedrawFrameId = null;
|
|
632
|
+
redraw();
|
|
633
|
+
});
|
|
567
634
|
}
|
|
568
635
|
function usesHoverAnimation() {
|
|
569
636
|
const t = props.options.type;
|
|
570
|
-
return t === "pie" || t === "doughnut" || t === "rose" || t === "sunburst" || t === "treemap";
|
|
637
|
+
return t === "pie" || t === "doughnut" || t === "rose" || t === "sunburst" || t === "treemap" || t === "radar";
|
|
638
|
+
}
|
|
639
|
+
function animatesHoverEnter() {
|
|
640
|
+
const t = props.options.type;
|
|
641
|
+
return [
|
|
642
|
+
"pie",
|
|
643
|
+
"doughnut",
|
|
644
|
+
"rose",
|
|
645
|
+
"radar",
|
|
646
|
+
"venn",
|
|
647
|
+
"sankey",
|
|
648
|
+
"chord",
|
|
649
|
+
"arc",
|
|
650
|
+
"bar",
|
|
651
|
+
"stacked-bar",
|
|
652
|
+
"horizontal-bar",
|
|
653
|
+
"waterfall",
|
|
654
|
+
"mixed",
|
|
655
|
+
"boxplot",
|
|
656
|
+
"candle",
|
|
657
|
+
"heatmap",
|
|
658
|
+
"calendar-heatmap",
|
|
659
|
+
"funnel",
|
|
660
|
+
"gantt"
|
|
661
|
+
].includes(t);
|
|
571
662
|
}
|
|
572
663
|
function clearHover() {
|
|
573
664
|
if (hoverIndex === -1) return false;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
665
|
+
if (animatesHoverEnter()) startHoverAnimation(-1, () => {
|
|
666
|
+
hoverIndex = -1;
|
|
667
|
+
redraw();
|
|
668
|
+
});
|
|
577
669
|
else {
|
|
670
|
+
hoverIndex = -1;
|
|
578
671
|
stopHoverAnimation();
|
|
579
672
|
hoverAnimProgress = 0;
|
|
580
673
|
redraw();
|
|
581
674
|
}
|
|
582
675
|
return true;
|
|
583
676
|
}
|
|
584
|
-
function startHoverAnimation(dir) {
|
|
585
|
-
if (!
|
|
677
|
+
function startHoverAnimation(dir, onDone) {
|
|
678
|
+
if (!animatesHoverEnter()) return;
|
|
586
679
|
if (props.options.animation?.enabled === false) {
|
|
587
680
|
hoverAnimProgress = dir > 0 ? 1 : 0;
|
|
588
681
|
redraw();
|
|
682
|
+
if (onDone) onDone();
|
|
589
683
|
return;
|
|
590
684
|
}
|
|
591
685
|
stopHoverAnimation();
|
|
686
|
+
hoverAnimDone = onDone || null;
|
|
592
687
|
const duration = 220;
|
|
593
688
|
const startTime = performance.now();
|
|
594
689
|
const startProgress = hoverAnimProgress;
|
|
@@ -604,7 +699,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
604
699
|
}
|
|
605
700
|
renderChart(canvasRef.value, buildRenderParams(1));
|
|
606
701
|
if (tRaw < 1) hoverAnimFrameId = requestAnimationFrame(frame);
|
|
607
|
-
else
|
|
702
|
+
else {
|
|
703
|
+
hoverAnimFrameId = null;
|
|
704
|
+
const done = hoverAnimDone;
|
|
705
|
+
hoverAnimDone = null;
|
|
706
|
+
if (done) done();
|
|
707
|
+
}
|
|
608
708
|
}
|
|
609
709
|
hoverAnimFrameId = requestAnimationFrame(frame);
|
|
610
710
|
}
|
|
@@ -624,7 +724,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
624
724
|
"rose",
|
|
625
725
|
"radar",
|
|
626
726
|
"funnel",
|
|
627
|
-
"scatter"
|
|
727
|
+
"scatter",
|
|
728
|
+
"sankey",
|
|
729
|
+
"chord",
|
|
730
|
+
"arc",
|
|
731
|
+
"venn",
|
|
732
|
+
"candle"
|
|
628
733
|
];
|
|
629
734
|
function checkLegendHit(x, y) {
|
|
630
735
|
const canvas = canvasRef.value;
|
|
@@ -636,15 +741,14 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
636
741
|
const theme = getTheme(isDark, props.options.theme, props.options.palette);
|
|
637
742
|
const width = rect.width;
|
|
638
743
|
const height = rect.height;
|
|
639
|
-
const padding =
|
|
744
|
+
const padding = paddingFor(effectiveOptions.value, width);
|
|
640
745
|
const plotArea = {
|
|
641
746
|
x: padding.left,
|
|
642
747
|
y: padding.top,
|
|
643
748
|
width: width - padding.left - padding.right,
|
|
644
749
|
height: height - padding.top - padding.bottom
|
|
645
750
|
};
|
|
646
|
-
const
|
|
647
|
-
const bounds = getLegendBounds(ctx2d, effectiveOptions.value, plotArea, width, height, theme, hiddenSeries.value);
|
|
751
|
+
const bounds = legendBoundsFor(canvas.getContext("2d"), effectiveOptions.value, plotArea, width, height, theme, hiddenSeries.value, isDark);
|
|
648
752
|
for (const bound of bounds) if (canvasX >= bound.x && canvasX <= bound.x + bound.width && canvasY >= bound.y && canvasY <= bound.y + bound.height) return bound.name;
|
|
649
753
|
return null;
|
|
650
754
|
}
|
|
@@ -689,7 +793,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
689
793
|
const options = effectiveOptions.value;
|
|
690
794
|
const width = rect.width;
|
|
691
795
|
const height = rect.height;
|
|
692
|
-
const padding =
|
|
796
|
+
const padding = paddingFor(options, width);
|
|
693
797
|
const plotArea = {
|
|
694
798
|
x: padding.left,
|
|
695
799
|
y: padding.top,
|
|
@@ -711,12 +815,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
711
815
|
const dx = canvasX - centerX;
|
|
712
816
|
const dy = canvasY - centerY;
|
|
713
817
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
714
|
-
const innerRadius = options.type === "doughnut" ?
|
|
818
|
+
const innerRadius = options.type === "doughnut" ? doughnutInnerRadius(baseRadius, options) : 0;
|
|
715
819
|
let angle = Math.atan2(dy, dx) + Math.PI / 2;
|
|
716
820
|
if (angle < 0) angle += Math.PI * 2;
|
|
717
821
|
const total = slices.reduce((sum, d) => sum + d.value, 0);
|
|
718
822
|
let currentAngle = 0;
|
|
719
|
-
const maxVal =
|
|
823
|
+
const maxVal = maxOf(slices.map((d) => d.value));
|
|
720
824
|
for (let i = 0; i < slices.length; i++) {
|
|
721
825
|
const sliceAngle = slices[i].value / total * Math.PI * 2;
|
|
722
826
|
if (angle >= currentAngle && angle < currentAngle + sliceAngle) {
|
|
@@ -751,7 +855,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
751
855
|
const i = Math.floor(relativeY / stepHeight);
|
|
752
856
|
if (i < 0 || i >= drawData.length) return null;
|
|
753
857
|
const data = drawData[i];
|
|
754
|
-
const color = data.
|
|
858
|
+
const color = funnelStepColor(data, allFunnel.indexOf(data) < 0 ? i : allFunnel.indexOf(data), theme);
|
|
755
859
|
return {
|
|
756
860
|
index: i,
|
|
757
861
|
params: {
|
|
@@ -772,14 +876,10 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
772
876
|
const i = Math.floor((canvasX - plotArea.x) / categoryWidth);
|
|
773
877
|
if (i < 0 || i >= labels2.length) return null;
|
|
774
878
|
const wf = options.waterfall || {};
|
|
775
|
-
const
|
|
776
|
-
const v =
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const vk = deltas[k] || 0;
|
|
780
|
-
cumulative = totalIdx.has(k) ? vk : cumulative + vk;
|
|
781
|
-
}
|
|
782
|
-
const isTotal = totalIdx.has(i);
|
|
879
|
+
const step = waterfallSteps(deltas, wf.totalIndices)[i];
|
|
880
|
+
const v = step.value;
|
|
881
|
+
const cumulative = step.to;
|
|
882
|
+
const isTotal = step.isTotal;
|
|
783
883
|
const color = isTotal ? wf.totalColor || theme.colors[0] : v >= 0 ? wf.increaseColor || "#dc2626" : wf.decreaseColor || "#16a34a";
|
|
784
884
|
return {
|
|
785
885
|
index: i,
|
|
@@ -794,27 +894,68 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
794
894
|
};
|
|
795
895
|
}
|
|
796
896
|
if (options.type === "boxplot") {
|
|
797
|
-
const
|
|
798
|
-
if (
|
|
799
|
-
const
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
897
|
+
const visible = (options.boxData || []).filter((b) => !(b.group && hiddenSeries.value.has(b.group)) && !hiddenSeries.value.has(b.label));
|
|
898
|
+
if (visible.length === 0) return null;
|
|
899
|
+
const showOutliers = options.showOutliers !== false;
|
|
900
|
+
const allValues = visible.flatMap((b) => [
|
|
901
|
+
b.min,
|
|
902
|
+
b.max,
|
|
903
|
+
...showOutliers ? b.outliers || [] : []
|
|
904
|
+
]);
|
|
905
|
+
const axisCfg = options.boxHorizontal ? options.xAxis || {} : options.yAxis || {};
|
|
906
|
+
const ext = resolveTickExtendedRange(axisCfg.min ?? minOf(allValues), axisCfg.max ?? maxOf(allValues), axisCfg.ticks || 5);
|
|
907
|
+
return boxplotHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value, {
|
|
908
|
+
min: ext.min,
|
|
909
|
+
max: ext.max
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
if (options.type === "calendar-heatmap") return calendarHitTest(canvasX, canvasY, plotArea, options, theme);
|
|
913
|
+
if (options.type === "sankey") return sankeyHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value, sankeyLayoutFor(plotArea, options, theme, hiddenSeries.value, isDark));
|
|
914
|
+
if (options.type === "venn") return vennHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value);
|
|
915
|
+
if (options.type === "chord") return chordHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value);
|
|
916
|
+
if (options.type === "arc") {
|
|
917
|
+
if (options.arcCircular === true) return chordHitTest(canvasX, canvasY, plotArea, {
|
|
918
|
+
...options,
|
|
919
|
+
chordData: options.arcData,
|
|
920
|
+
chordMode: "curve"
|
|
921
|
+
}, theme, hiddenSeries.value);
|
|
922
|
+
return arcHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value);
|
|
923
|
+
}
|
|
924
|
+
if (options.type === "gantt") return ganttHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value, ganttLayoutFor(plotArea, options, theme, hiddenSeries.value, isDark));
|
|
925
|
+
if (options.type === "scatter-matrix") {
|
|
926
|
+
const fields = options.matrixFields || [];
|
|
927
|
+
const records = options.matrixData || [];
|
|
928
|
+
if (fields.length === 0 || records.length === 0) return null;
|
|
929
|
+
const cells = computeMatrixCells(plotArea, fields);
|
|
930
|
+
const cell = cells.find((c) => canvasX >= c.x && canvasX <= c.x + c.width && canvasY >= c.y && canvasY <= c.y + c.height);
|
|
931
|
+
if (!cell || cell.row === cell.col) return null;
|
|
932
|
+
const extX = matrixFieldExtent(records, cell.xField);
|
|
933
|
+
const extY = matrixFieldExtent(records, cell.yField);
|
|
934
|
+
const px = (v) => cell.x + 5 + (v - extX.min) / (extX.max - extX.min) * (cell.width - 10);
|
|
935
|
+
const py = (v) => cell.y + cell.height - 5 - (v - extY.min) / (extY.max - extY.min) * (cell.height - 10);
|
|
936
|
+
let nearest = -1;
|
|
937
|
+
let minDist = Infinity;
|
|
938
|
+
records.forEach((record, ri) => {
|
|
939
|
+
const xv = record[cell.xField];
|
|
940
|
+
const yv = record[cell.yField];
|
|
941
|
+
if (typeof xv !== "number" || typeof yv !== "number") return;
|
|
942
|
+
const dist = Math.sqrt((canvasX - px(xv)) ** 2 + (canvasY - py(yv)) ** 2);
|
|
943
|
+
if (dist < minDist) {
|
|
944
|
+
minDist = dist;
|
|
945
|
+
nearest = ri;
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
if (nearest < 0 || minDist > 18) return null;
|
|
949
|
+
const record = records[nearest];
|
|
950
|
+
const cellIndex = cells.indexOf(cell);
|
|
804
951
|
return {
|
|
805
|
-
index:
|
|
952
|
+
index: cellIndex,
|
|
806
953
|
params: {
|
|
807
|
-
seriesName:
|
|
808
|
-
name: `${
|
|
809
|
-
value: [
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
b.median,
|
|
813
|
-
b.q3,
|
|
814
|
-
b.max
|
|
815
|
-
],
|
|
816
|
-
color,
|
|
817
|
-
dataIndex: i + sliceStartOffset(),
|
|
954
|
+
seriesName: record.label || `${cell.yField} × ${cell.xField}`,
|
|
955
|
+
name: `${cell.xField}=${record[cell.xField]}, ${cell.yField}=${record[cell.yField]}`,
|
|
956
|
+
value: record[cell.yField],
|
|
957
|
+
color: record.color || theme.colors[0],
|
|
958
|
+
dataIndex: cellIndex,
|
|
818
959
|
seriesIndex: 0
|
|
819
960
|
}
|
|
820
961
|
};
|
|
@@ -886,22 +1027,56 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
886
1027
|
if (options.type === "scatter") {
|
|
887
1028
|
const scatterData = options.scatterData || [];
|
|
888
1029
|
if (scatterData.length === 0) return null;
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1030
|
+
if (options.facet === true) {
|
|
1031
|
+
const grid = computeFacetGrids(scatterData, plotArea, options);
|
|
1032
|
+
if (!grid) return null;
|
|
1033
|
+
for (const facet of grid.facets) {
|
|
1034
|
+
const a = facet.area;
|
|
1035
|
+
if (canvasX < a.x || canvasX > a.x + a.width || canvasY < a.y || canvasY > a.y + a.height) continue;
|
|
1036
|
+
let nearestLocal = -1;
|
|
1037
|
+
let minDistance = Infinity;
|
|
1038
|
+
facet.data.forEach((point, i) => {
|
|
1039
|
+
const ppx = facet.toX(point.x);
|
|
1040
|
+
const ppy = facet.toY(point.y);
|
|
1041
|
+
const dist = Math.sqrt((canvasX - ppx) ** 2 + (canvasY - ppy) ** 2);
|
|
1042
|
+
if (dist < minDistance) {
|
|
1043
|
+
minDistance = dist;
|
|
1044
|
+
nearestLocal = i;
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
if (nearestLocal < 0 || minDistance > 24) return null;
|
|
1048
|
+
const point = facet.data[nearestLocal];
|
|
1049
|
+
const globalIdx = scatterData.indexOf(point);
|
|
1050
|
+
const color = point.color || theme.colors[globalIdx % theme.colors.length];
|
|
1051
|
+
return {
|
|
1052
|
+
index: globalIdx,
|
|
1053
|
+
params: {
|
|
1054
|
+
seriesName: point.group || point.label || `Point ${globalIdx + 1}`,
|
|
1055
|
+
name: `(${point.x}, ${point.y})`,
|
|
1056
|
+
value: point.y,
|
|
1057
|
+
color,
|
|
1058
|
+
dataIndex: globalIdx,
|
|
1059
|
+
seriesIndex: 0
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
return null;
|
|
1064
|
+
}
|
|
1065
|
+
const yValues = scatterData.map((d) => d.y).filter((v) => typeof v === "number" && Number.isFinite(v));
|
|
894
1066
|
const axisConfig = options.yAxis || {};
|
|
895
|
-
const rawMin = axisConfig.min ??
|
|
896
|
-
const rawMax = axisConfig.max ??
|
|
1067
|
+
const rawMin = axisConfig.min ?? (yValues.length ? minOf(yValues) : 0);
|
|
1068
|
+
const rawMax = axisConfig.max ?? (yValues.length ? maxOf(yValues) : 1);
|
|
897
1069
|
const yExt = resolveTickExtendedRange(rawMin, rawMax, axisConfig.ticks || 5);
|
|
898
|
-
const
|
|
1070
|
+
const positions = scatterPositionsFor(scatterData, {
|
|
1071
|
+
min: yExt.min,
|
|
1072
|
+
max: yExt.max
|
|
1073
|
+
}, plotArea, options);
|
|
1074
|
+
const groupColors = scatterGroupColors(scatterData, theme);
|
|
899
1075
|
let nearestIndex = -1;
|
|
900
1076
|
let minDistance = Infinity;
|
|
901
1077
|
scatterData.forEach((point, i) => {
|
|
902
|
-
|
|
903
|
-
const
|
|
904
|
-
const py = plotArea.y + plotArea.height - normalizedY * plotArea.height;
|
|
1078
|
+
if (hiddenSeries.value.has(point.label || `P${i + 1}`) || point.group && hiddenSeries.value.has(point.group)) return;
|
|
1079
|
+
const [px, py] = positions[i];
|
|
905
1080
|
const dist = Math.sqrt((canvasX - px) ** 2 + (canvasY - py) ** 2);
|
|
906
1081
|
if (dist < minDistance) {
|
|
907
1082
|
minDistance = dist;
|
|
@@ -910,11 +1085,11 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
910
1085
|
});
|
|
911
1086
|
if (nearestIndex >= 0 && minDistance < 30) {
|
|
912
1087
|
const point = scatterData[nearestIndex];
|
|
913
|
-
const color = point.color || theme.colors[nearestIndex % theme.colors.length];
|
|
1088
|
+
const color = point.color || (point.group && groupColors.has(point.group) ? groupColors.get(point.group) : theme.colors[nearestIndex % theme.colors.length]);
|
|
914
1089
|
return {
|
|
915
1090
|
index: nearestIndex,
|
|
916
1091
|
params: {
|
|
917
|
-
seriesName: point.label || `Point ${nearestIndex + 1}`,
|
|
1092
|
+
seriesName: point.label || point.group || `Point ${nearestIndex + 1}`,
|
|
918
1093
|
name: `(${point.x}, ${point.y})`,
|
|
919
1094
|
value: point.y,
|
|
920
1095
|
color,
|
|
@@ -944,6 +1119,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
944
1119
|
candle.high,
|
|
945
1120
|
candle.low
|
|
946
1121
|
],
|
|
1122
|
+
volume: (options.volumeData || [])[i],
|
|
947
1123
|
color,
|
|
948
1124
|
dataIndex: i,
|
|
949
1125
|
seriesIndex: 0
|
|
@@ -1062,13 +1238,23 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1062
1238
|
const centerY = plotArea.y + plotArea.height / 2;
|
|
1063
1239
|
const radius = Math.max(40, Math.min(plotArea.width, plotArea.height) / 2 - 30);
|
|
1064
1240
|
const angleStep = Math.PI * 2 / indicators.length;
|
|
1241
|
+
const labelOffset = options.radarLabelOffset ?? 12;
|
|
1242
|
+
let labelHit = -1;
|
|
1243
|
+
indicators.forEach((indicator, i) => {
|
|
1244
|
+
if (labelHit >= 0) return;
|
|
1245
|
+
const angle = i * angleStep - Math.PI / 2;
|
|
1246
|
+
const lx = centerX + Math.cos(angle) * (radius + labelOffset);
|
|
1247
|
+
const ly = centerY + Math.sin(angle) * (radius + labelOffset);
|
|
1248
|
+
if (Math.sqrt((canvasX - lx) ** 2 + (canvasY - ly) ** 2) <= 18) labelHit = i;
|
|
1249
|
+
});
|
|
1250
|
+
if (labelHit >= 0) return radarAxisHover(labelHit, indicators, series, options, theme);
|
|
1065
1251
|
let nearestIndex = -1;
|
|
1066
1252
|
let minDist = Infinity;
|
|
1067
1253
|
indicators.forEach((_, i) => {
|
|
1068
1254
|
const angle = i * angleStep - Math.PI / 2;
|
|
1069
1255
|
const ind = indicators[i];
|
|
1070
1256
|
const span = ind.max - (ind.min || 0);
|
|
1071
|
-
const maxR = span > 0 ?
|
|
1257
|
+
const maxR = span > 0 ? maxOf(series.map((s) => radius * (Math.max(0, (s.data[i] || 0) - (ind.min || 0)) / span))) : 0;
|
|
1072
1258
|
const px = centerX + Math.cos(angle) * maxR;
|
|
1073
1259
|
const py = centerY + Math.sin(angle) * maxR;
|
|
1074
1260
|
const dist = Math.sqrt((canvasX - px) ** 2 + (canvasY - py) ** 2);
|
|
@@ -1078,18 +1264,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1078
1264
|
}
|
|
1079
1265
|
});
|
|
1080
1266
|
if (nearestIndex < 0 || minDist > 30) return null;
|
|
1081
|
-
|
|
1082
|
-
seriesName: s.name,
|
|
1083
|
-
name: indicators[nearestIndex].name,
|
|
1084
|
-
value: s.data[nearestIndex],
|
|
1085
|
-
color: s.color || theme.colors[options.series.indexOf(s) % theme.colors.length],
|
|
1086
|
-
dataIndex: nearestIndex,
|
|
1087
|
-
seriesIndex: sIdx
|
|
1088
|
-
}));
|
|
1089
|
-
return {
|
|
1090
|
-
index: nearestIndex,
|
|
1091
|
-
params
|
|
1092
|
-
};
|
|
1267
|
+
return radarAxisHover(nearestIndex, indicators, series, options, theme);
|
|
1093
1268
|
}
|
|
1094
1269
|
const labels = options.labels || [];
|
|
1095
1270
|
if (labels.length === 0) return null;
|
|
@@ -1097,6 +1272,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1097
1272
|
if (dataIndex < 0 || dataIndex >= labels.length) return null;
|
|
1098
1273
|
const visibleSeries = (options.series || []).filter((s) => !hiddenSeries.value.has(s.name));
|
|
1099
1274
|
if (visibleSeries.length === 0) return null;
|
|
1275
|
+
const volume = (options.volumeData || [])[dataIndex];
|
|
1100
1276
|
if (props.options.tooltip?.showAllSeries !== false && visibleSeries.length > 1) {
|
|
1101
1277
|
if (visibleSeries.every((s) => isMissingValue(s.data[dataIndex]))) return null;
|
|
1102
1278
|
return {
|
|
@@ -1105,6 +1281,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1105
1281
|
seriesName: s.name,
|
|
1106
1282
|
name: labels[dataIndex],
|
|
1107
1283
|
value: s.data[dataIndex],
|
|
1284
|
+
volume,
|
|
1108
1285
|
color: s.color || theme.colors[options.series.indexOf(s) % theme.colors.length],
|
|
1109
1286
|
dataIndex: dataIndex + sliceStartOffset(),
|
|
1110
1287
|
seriesIndex: sIdx
|
|
@@ -1120,6 +1297,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1120
1297
|
seriesName: series.name,
|
|
1121
1298
|
name: labels[dataIndex],
|
|
1122
1299
|
value: series.data[dataIndex],
|
|
1300
|
+
volume,
|
|
1123
1301
|
color,
|
|
1124
1302
|
dataIndex: dataIndex + sliceStartOffset(),
|
|
1125
1303
|
seriesIndex: 0
|
|
@@ -1127,6 +1305,20 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1127
1305
|
};
|
|
1128
1306
|
}
|
|
1129
1307
|
}
|
|
1308
|
+
/** 雷达轴悬浮:返回该维度上全部可见系列的 tooltip 行 */
|
|
1309
|
+
function radarAxisHover(axisIndex, indicators, series, options, theme) {
|
|
1310
|
+
return {
|
|
1311
|
+
index: axisIndex,
|
|
1312
|
+
params: series.map((s, sIdx) => ({
|
|
1313
|
+
seriesName: s.name,
|
|
1314
|
+
name: indicators[axisIndex].name,
|
|
1315
|
+
value: s.data[axisIndex],
|
|
1316
|
+
color: s.color || theme.colors[(options.radarSeries || []).indexOf(s) % theme.colors.length],
|
|
1317
|
+
dataIndex: axisIndex,
|
|
1318
|
+
seriesIndex: sIdx
|
|
1319
|
+
}))
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1130
1322
|
function setZoomRange(next) {
|
|
1131
1323
|
const minSpan = INTERACTION.zoom.minSpan;
|
|
1132
1324
|
let { start, end } = next;
|
|
@@ -1153,7 +1345,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1153
1345
|
const canvas = canvasRef.value;
|
|
1154
1346
|
if (!canvas) return null;
|
|
1155
1347
|
const rect = canvas.getBoundingClientRect();
|
|
1156
|
-
const padding =
|
|
1348
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1157
1349
|
const plotArea = {
|
|
1158
1350
|
x: padding.left,
|
|
1159
1351
|
y: padding.top,
|
|
@@ -1217,7 +1409,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1217
1409
|
const rect = canvas.getBoundingClientRect();
|
|
1218
1410
|
const x = e.clientX - rect.left;
|
|
1219
1411
|
const y = e.clientY - rect.top;
|
|
1220
|
-
const padding =
|
|
1412
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1221
1413
|
if (x >= padding.left && x <= padding.left + (rect.width - padding.left - padding.right) && y >= padding.top && y <= padding.top + (rect.height - padding.top - padding.bottom)) {
|
|
1222
1414
|
touchPanning = true;
|
|
1223
1415
|
touchPanStartX = x;
|
|
@@ -1240,7 +1432,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1240
1432
|
const x = e.clientX - rect.left;
|
|
1241
1433
|
const y = e.clientY - rect.top;
|
|
1242
1434
|
if (props.options.brush?.enabled === true) {
|
|
1243
|
-
const padding =
|
|
1435
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1244
1436
|
const plotArea = {
|
|
1245
1437
|
x: padding.left,
|
|
1246
1438
|
y: padding.top,
|
|
@@ -1335,7 +1527,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1335
1527
|
const labels = options.labels || [];
|
|
1336
1528
|
if (canvas && labels.length > 0) {
|
|
1337
1529
|
const rect = canvas.getBoundingClientRect();
|
|
1338
|
-
const padding =
|
|
1530
|
+
const padding = paddingFor(options, rect.width);
|
|
1339
1531
|
const plotArea = {
|
|
1340
1532
|
x: padding.left,
|
|
1341
1533
|
y: padding.top,
|
|
@@ -1378,7 +1570,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1378
1570
|
const rect = canvas.getBoundingClientRect();
|
|
1379
1571
|
const x = e.clientX - rect.left;
|
|
1380
1572
|
const y = e.clientY - rect.top;
|
|
1381
|
-
const padding =
|
|
1573
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1382
1574
|
const plotArea = {
|
|
1383
1575
|
x: padding.left,
|
|
1384
1576
|
y: padding.top,
|
|
@@ -1444,7 +1636,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1444
1636
|
const nextFocus = props.options.legend?.hoverEmphasis !== false && legendName ? legendName : null;
|
|
1445
1637
|
if (nextFocus !== focusSeries.value) {
|
|
1446
1638
|
focusSeries.value = nextFocus;
|
|
1447
|
-
|
|
1639
|
+
animateFocusProgress();
|
|
1448
1640
|
}
|
|
1449
1641
|
}
|
|
1450
1642
|
if (legendName) {
|
|
@@ -1454,13 +1646,13 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1454
1646
|
}
|
|
1455
1647
|
if (e.pointerType === "touch") return;
|
|
1456
1648
|
const result = getHoveredData(e.clientX, e.clientY);
|
|
1457
|
-
const
|
|
1649
|
+
const enterAnimated = animatesHoverEnter();
|
|
1458
1650
|
if (result && props.options.tooltip?.show !== false) {
|
|
1459
1651
|
const newIndex = result.index;
|
|
1460
1652
|
if (newIndex !== hoverIndex) {
|
|
1461
1653
|
const prevIndex = hoverIndex;
|
|
1462
1654
|
hoverIndex = newIndex;
|
|
1463
|
-
if (
|
|
1655
|
+
if (enterAnimated) {
|
|
1464
1656
|
if (prevIndex === -1) startHoverAnimation(1);
|
|
1465
1657
|
else {
|
|
1466
1658
|
stopHoverAnimation();
|
|
@@ -1470,8 +1662,8 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1470
1662
|
} else if (usesHoverAnimation()) {
|
|
1471
1663
|
stopHoverAnimation();
|
|
1472
1664
|
hoverAnimProgress = 1;
|
|
1473
|
-
|
|
1474
|
-
} else
|
|
1665
|
+
scheduleHoverRedraw();
|
|
1666
|
+
} else scheduleHoverRedraw();
|
|
1475
1667
|
if (prevIndex === -1) {
|
|
1476
1668
|
const emitParams = Array.isArray(result.params) ? result.params[0] : result.params;
|
|
1477
1669
|
emit("hover", {
|
|
@@ -1484,7 +1676,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1484
1676
|
});
|
|
1485
1677
|
ariaLiveText.value = `${emitParams.seriesName}: ${formatTooltipValue(emitParams.value)}`;
|
|
1486
1678
|
}
|
|
1487
|
-
}
|
|
1679
|
+
} else if (enterAnimated && hoverAnimProgress < 1) startHoverAnimation(1);
|
|
1488
1680
|
applyTooltipContent(result.params);
|
|
1489
1681
|
if (props.options.tooltip?.trigger === "click") return;
|
|
1490
1682
|
lastMouseClientX = e.clientX;
|
|
@@ -1531,6 +1723,9 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1531
1723
|
}
|
|
1532
1724
|
return Number.isInteger(value) ? value.toString() : parseFloat(value.toFixed(4)).toString();
|
|
1533
1725
|
}
|
|
1726
|
+
function escapeHtml(value) {
|
|
1727
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1728
|
+
}
|
|
1534
1729
|
function applyTooltipContent(params) {
|
|
1535
1730
|
const formatter = props.options.tooltip?.formatter;
|
|
1536
1731
|
if (formatter) {
|
|
@@ -1541,23 +1736,23 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1541
1736
|
const title = params[0]?.name || "";
|
|
1542
1737
|
const rows = pickTooltipRows(params).map((p) => `
|
|
1543
1738
|
<div class="ev-chart__tooltip-item">
|
|
1544
|
-
<span class="ev-chart__tooltip-dot" style="background: ${p.color}"></span>
|
|
1545
|
-
<span class="ev-chart__tooltip-name">${p.seriesName}</span>
|
|
1546
|
-
<span class="ev-chart__tooltip-value">${formatTooltipValue(p.value)}</span>
|
|
1739
|
+
<span class="ev-chart__tooltip-dot" style="background: ${escapeHtml(p.color)}"></span>
|
|
1740
|
+
<span class="ev-chart__tooltip-name">${escapeHtml(p.seriesName)}</span>
|
|
1741
|
+
<span class="ev-chart__tooltip-value">${escapeHtml(formatTooltipValue(p.value))}</span>
|
|
1547
1742
|
</div>
|
|
1548
1743
|
`).join("");
|
|
1549
1744
|
tooltipContent.value = `
|
|
1550
|
-
<div class="ev-chart__tooltip-title">${title}</div>
|
|
1745
|
+
<div class="ev-chart__tooltip-title">${escapeHtml(title)}</div>
|
|
1551
1746
|
${rows}
|
|
1552
1747
|
`;
|
|
1553
1748
|
} else {
|
|
1554
1749
|
const p = params;
|
|
1555
1750
|
tooltipContent.value = `
|
|
1556
|
-
<div class="ev-chart__tooltip-title">${p.name}</div>
|
|
1751
|
+
<div class="ev-chart__tooltip-title">${escapeHtml(p.name)}</div>
|
|
1557
1752
|
<div class="ev-chart__tooltip-item">
|
|
1558
|
-
<span class="ev-chart__tooltip-dot" style="background: ${p.color}"></span>
|
|
1559
|
-
<span class="ev-chart__tooltip-name">${p.seriesName}</span>
|
|
1560
|
-
<span class="ev-chart__tooltip-value">${formatTooltipValue(p.value)}</span>
|
|
1753
|
+
<span class="ev-chart__tooltip-dot" style="background: ${escapeHtml(p.color)}"></span>
|
|
1754
|
+
<span class="ev-chart__tooltip-name">${escapeHtml(p.seriesName)}</span>
|
|
1755
|
+
<span class="ev-chart__tooltip-value">${escapeHtml(formatTooltipValue(p.value))}</span>
|
|
1561
1756
|
</div>
|
|
1562
1757
|
`;
|
|
1563
1758
|
}
|
|
@@ -1574,7 +1769,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1574
1769
|
else if (checkToolboxHit(e.clientX, e.clientY)) zone = "toolbox";
|
|
1575
1770
|
else if (checkLegendHit(e.clientX, e.clientY)) zone = "legend";
|
|
1576
1771
|
else {
|
|
1577
|
-
const padding =
|
|
1772
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1578
1773
|
if (x >= padding.left && x <= rect.width - padding.right && y >= padding.top && y <= rect.height - padding.bottom) zone = "plot";
|
|
1579
1774
|
else {
|
|
1580
1775
|
const slider = getSliderGeo();
|
|
@@ -1615,6 +1810,67 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1615
1810
|
ariaLiveText.value = "";
|
|
1616
1811
|
}
|
|
1617
1812
|
}
|
|
1813
|
+
const KEYNAV_TYPES = /* @__PURE__ */ new Set([
|
|
1814
|
+
"line",
|
|
1815
|
+
"area",
|
|
1816
|
+
"bar",
|
|
1817
|
+
"stacked-bar",
|
|
1818
|
+
"waterfall",
|
|
1819
|
+
"mixed",
|
|
1820
|
+
"horizontal-bar"
|
|
1821
|
+
]);
|
|
1822
|
+
let keyboardClientX = -1;
|
|
1823
|
+
let keyboardClientY = -1;
|
|
1824
|
+
function handleChartKeydown(e) {
|
|
1825
|
+
const options = props.options;
|
|
1826
|
+
if (!KEYNAV_TYPES.has(options.type)) return;
|
|
1827
|
+
const count = (options.labels || []).length;
|
|
1828
|
+
if (count === 0) return;
|
|
1829
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1830
|
+
e.preventDefault();
|
|
1831
|
+
if (hoverIndex < 0 || keyboardClientX < 0) return;
|
|
1832
|
+
const result = getHoveredData(keyboardClientX, keyboardClientY);
|
|
1833
|
+
if (result) {
|
|
1834
|
+
const params = Array.isArray(result.params) ? result.params[0] : result.params;
|
|
1835
|
+
emit("click", params);
|
|
1836
|
+
}
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
const horizontal = options.type === "horizontal-bar";
|
|
1840
|
+
let i = hoverIndex;
|
|
1841
|
+
if (e.key === "Home") i = 0;
|
|
1842
|
+
else if (e.key === "End") i = count - 1;
|
|
1843
|
+
else if (horizontal && e.key === "ArrowUp") i = hoverIndex < 0 ? 0 : Math.min(count - 1, hoverIndex + 1);
|
|
1844
|
+
else if (horizontal && e.key === "ArrowDown") i = hoverIndex < 0 ? 0 : Math.max(0, hoverIndex - 1);
|
|
1845
|
+
else if (!horizontal && e.key === "ArrowRight") i = hoverIndex < 0 ? 0 : Math.min(count - 1, hoverIndex + 1);
|
|
1846
|
+
else if (!horizontal && e.key === "ArrowLeft") i = hoverIndex < 0 ? 0 : Math.max(0, hoverIndex - 1);
|
|
1847
|
+
else return;
|
|
1848
|
+
e.preventDefault();
|
|
1849
|
+
if (i === hoverIndex) return;
|
|
1850
|
+
const canvas = canvasRef.value;
|
|
1851
|
+
if (!canvas) return;
|
|
1852
|
+
const rect = canvas.getBoundingClientRect();
|
|
1853
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1854
|
+
const plotWidth = rect.width - padding.left - padding.right;
|
|
1855
|
+
const plotHeight = rect.height - padding.top - padding.bottom;
|
|
1856
|
+
const clientX = horizontal ? rect.left + padding.left + plotWidth / 2 : rect.left + padding.left + (i + .5) * (plotWidth / count);
|
|
1857
|
+
const clientY = horizontal ? rect.top + padding.top + plotHeight - (i + .5) * (plotHeight / count) : rect.top + padding.top + plotHeight / 2;
|
|
1858
|
+
keyboardClientX = clientX;
|
|
1859
|
+
keyboardClientY = clientY;
|
|
1860
|
+
mouseX = clientX - rect.left;
|
|
1861
|
+
mouseY = clientY - rect.top;
|
|
1862
|
+
handlePointerMove({
|
|
1863
|
+
clientX,
|
|
1864
|
+
clientY,
|
|
1865
|
+
pointerType: "mouse"
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
function handleFocusOut() {
|
|
1869
|
+
if (clearTransientState()) {
|
|
1870
|
+
emit("unhover");
|
|
1871
|
+
ariaLiveText.value = "";
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1618
1874
|
let resizeObserver = null;
|
|
1619
1875
|
function setupResizeObserver() {
|
|
1620
1876
|
if (!props.responsive) return;
|
|
@@ -1662,7 +1918,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1662
1918
|
labels: [...opt.labels || []],
|
|
1663
1919
|
series: (opt.series || []).map((s) => ({
|
|
1664
1920
|
...s,
|
|
1665
|
-
data: [...s.data]
|
|
1921
|
+
data: [...s.data || []]
|
|
1666
1922
|
}))
|
|
1667
1923
|
};
|
|
1668
1924
|
}
|
|
@@ -1691,9 +1947,17 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1691
1947
|
watch(() => props.options, (next) => {
|
|
1692
1948
|
cachedDataExtent = null;
|
|
1693
1949
|
cachedPlotArea = null;
|
|
1950
|
+
cachedPadding = null;
|
|
1951
|
+
scatterHitCache = null;
|
|
1952
|
+
legendBoundsCache = null;
|
|
1953
|
+
ganttLayoutCache = null;
|
|
1954
|
+
sankeyLayoutCache = null;
|
|
1694
1955
|
internalError.value = null;
|
|
1695
1956
|
runDevValidation();
|
|
1696
|
-
focusSeries.value
|
|
1957
|
+
if (focusSeries.value !== emphasisSeriesName.value) {
|
|
1958
|
+
focusSeries.value = emphasisSeriesName.value;
|
|
1959
|
+
animateFocusProgress();
|
|
1960
|
+
}
|
|
1697
1961
|
syncScenes();
|
|
1698
1962
|
const zoom = getDataZoomConfig(props.options);
|
|
1699
1963
|
const zoomKey = zoom ? JSON.stringify({
|
|
@@ -1747,6 +2011,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1747
2011
|
};
|
|
1748
2012
|
let unregisterConnector = null;
|
|
1749
2013
|
onMounted(() => {
|
|
2014
|
+
focusSeries.value = emphasisSeriesName.value;
|
|
1750
2015
|
render(true);
|
|
1751
2016
|
syncScenes();
|
|
1752
2017
|
setupResizeObserver();
|
|
@@ -1771,6 +2036,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1771
2036
|
stopHoverAnimation();
|
|
1772
2037
|
stopTweenAnimation();
|
|
1773
2038
|
stopSceneTimer();
|
|
2039
|
+
stopFocusAnimation();
|
|
1774
2040
|
if (tooltipRafId !== null) {
|
|
1775
2041
|
cancelAnimationFrame(tooltipRafId);
|
|
1776
2042
|
tooltipRafId = null;
|
|
@@ -1779,11 +2045,98 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1779
2045
|
});
|
|
1780
2046
|
let cachedDataExtent = null;
|
|
1781
2047
|
let cachedPlotArea = null;
|
|
2048
|
+
let cachedPadding = null;
|
|
2049
|
+
function paddingFor(options, width) {
|
|
2050
|
+
if (cachedPadding && cachedPadding.options === options && cachedPadding.width === width) return cachedPadding.value;
|
|
2051
|
+
const value = getPadding(options, width);
|
|
2052
|
+
cachedPadding = {
|
|
2053
|
+
options,
|
|
2054
|
+
width,
|
|
2055
|
+
value
|
|
2056
|
+
};
|
|
2057
|
+
return value;
|
|
2058
|
+
}
|
|
2059
|
+
let scatterHitCache = null;
|
|
2060
|
+
function scatterPositionsFor(scatterData, yRange, plotArea, options) {
|
|
2061
|
+
const c = scatterHitCache;
|
|
2062
|
+
if (c && c.scatterData === scatterData && c.options === options && c.yMin === yRange.min && c.yMax === yRange.max && c.px === plotArea.x && c.py === plotArea.y && c.pw === plotArea.width && c.ph === plotArea.height) return c.positions;
|
|
2063
|
+
const positions = scatterPointPositions(scatterData, yRange, plotArea, options);
|
|
2064
|
+
scatterHitCache = {
|
|
2065
|
+
scatterData,
|
|
2066
|
+
options,
|
|
2067
|
+
yMin: yRange.min,
|
|
2068
|
+
yMax: yRange.max,
|
|
2069
|
+
px: plotArea.x,
|
|
2070
|
+
py: plotArea.y,
|
|
2071
|
+
pw: plotArea.width,
|
|
2072
|
+
ph: plotArea.height,
|
|
2073
|
+
positions
|
|
2074
|
+
};
|
|
2075
|
+
return positions;
|
|
2076
|
+
}
|
|
2077
|
+
let legendBoundsCache = null;
|
|
2078
|
+
function legendBoundsFor(ctx2d, options, plotArea, width, height, theme, hiddenSeries, isDark) {
|
|
2079
|
+
const c = legendBoundsCache;
|
|
2080
|
+
if (c && c.options === options && c.width === width && c.height === height && c.dark === isDark && c.hidden === hiddenSeries && c.px === plotArea.x && c.py === plotArea.y && c.pw === plotArea.width && c.ph === plotArea.height) return c.bounds;
|
|
2081
|
+
const bounds = getLegendBounds(ctx2d, options, plotArea, width, height, theme, hiddenSeries);
|
|
2082
|
+
legendBoundsCache = {
|
|
2083
|
+
options,
|
|
2084
|
+
width,
|
|
2085
|
+
height,
|
|
2086
|
+
dark: isDark,
|
|
2087
|
+
hidden: hiddenSeries,
|
|
2088
|
+
px: plotArea.x,
|
|
2089
|
+
py: plotArea.y,
|
|
2090
|
+
pw: plotArea.width,
|
|
2091
|
+
ph: plotArea.height,
|
|
2092
|
+
bounds
|
|
2093
|
+
};
|
|
2094
|
+
return bounds;
|
|
2095
|
+
}
|
|
2096
|
+
let ganttLayoutCache = null;
|
|
2097
|
+
function ganttLayoutFor(plotArea, options, theme, hiddenSeries, isDark) {
|
|
2098
|
+
const c = ganttLayoutCache;
|
|
2099
|
+
if (c && c.options === options && c.dark === isDark && c.hidden === hiddenSeries && c.px === plotArea.x && c.py === plotArea.y && c.pw === plotArea.width && c.ph === plotArea.height) return c.layout;
|
|
2100
|
+
const layout = computeGanttLayout(plotArea, options, theme, hiddenSeries);
|
|
2101
|
+
ganttLayoutCache = {
|
|
2102
|
+
options,
|
|
2103
|
+
dark: isDark,
|
|
2104
|
+
hidden: hiddenSeries,
|
|
2105
|
+
px: plotArea.x,
|
|
2106
|
+
py: plotArea.y,
|
|
2107
|
+
pw: plotArea.width,
|
|
2108
|
+
ph: plotArea.height,
|
|
2109
|
+
layout
|
|
2110
|
+
};
|
|
2111
|
+
return layout;
|
|
2112
|
+
}
|
|
2113
|
+
let sankeyLayoutCache = null;
|
|
2114
|
+
function sankeyLayoutFor(plotArea, options, theme, hiddenSeries, isDark) {
|
|
2115
|
+
const c = sankeyLayoutCache;
|
|
2116
|
+
if (c && c.options === options && c.dark === isDark && c.hidden === hiddenSeries && c.px === plotArea.x && c.py === plotArea.y && c.pw === plotArea.width && c.ph === plotArea.height) return c.layout;
|
|
2117
|
+
const layout = computeSankeyLayout(plotArea, options, theme, hiddenSeries);
|
|
2118
|
+
sankeyLayoutCache = {
|
|
2119
|
+
options,
|
|
2120
|
+
dark: isDark,
|
|
2121
|
+
hidden: hiddenSeries,
|
|
2122
|
+
px: plotArea.x,
|
|
2123
|
+
py: plotArea.y,
|
|
2124
|
+
pw: plotArea.width,
|
|
2125
|
+
ph: plotArea.height,
|
|
2126
|
+
layout
|
|
2127
|
+
};
|
|
2128
|
+
return layout;
|
|
2129
|
+
}
|
|
1782
2130
|
__expose({
|
|
1783
2131
|
refresh: () => render(true),
|
|
1784
2132
|
update(newOptions) {
|
|
1785
2133
|
Object.assign(props.options, newOptions);
|
|
1786
2134
|
cachedDataExtent = null;
|
|
2135
|
+
cachedPadding = null;
|
|
2136
|
+
scatterHitCache = null;
|
|
2137
|
+
legendBoundsCache = null;
|
|
2138
|
+
ganttLayoutCache = null;
|
|
2139
|
+
sankeyLayoutCache = null;
|
|
1787
2140
|
specVersion.value++;
|
|
1788
2141
|
debouncedRender(true);
|
|
1789
2142
|
},
|
|
@@ -1793,9 +2146,18 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1793
2146
|
destroy() {
|
|
1794
2147
|
cleanupResizeObserver();
|
|
1795
2148
|
cleanupDarkModeObserver();
|
|
2149
|
+
window.removeEventListener("pointerup", onWindowPointerUp);
|
|
2150
|
+
window.removeEventListener("keydown", handleEscapeKey);
|
|
2151
|
+
containerRef.value?.removeEventListener("wheel", handleWheel);
|
|
2152
|
+
if (unregisterConnector) {
|
|
2153
|
+
unregisterConnector();
|
|
2154
|
+
unregisterConnector = null;
|
|
2155
|
+
}
|
|
1796
2156
|
stopAnimation();
|
|
1797
2157
|
stopHoverAnimation();
|
|
2158
|
+
stopTweenAnimation();
|
|
1798
2159
|
stopSceneTimer();
|
|
2160
|
+
stopFocusAnimation();
|
|
1799
2161
|
if (tooltipRafId !== null) {
|
|
1800
2162
|
cancelAnimationFrame(tooltipRafId);
|
|
1801
2163
|
tooltipRafId = null;
|
|
@@ -1871,7 +2233,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1871
2233
|
};
|
|
1872
2234
|
const width = canvas.width / dpr.value;
|
|
1873
2235
|
const height = canvas.height / dpr.value;
|
|
1874
|
-
const padding =
|
|
2236
|
+
const padding = paddingFor(effectiveOptions.value, width);
|
|
1875
2237
|
cachedPlotArea = {
|
|
1876
2238
|
x: padding.left,
|
|
1877
2239
|
y: padding.top,
|
|
@@ -2005,7 +2367,10 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
2005
2367
|
class: "ev-chart",
|
|
2006
2368
|
style: normalizeStyle([containerStyle.value, { cursor: cursorStyle.value }]),
|
|
2007
2369
|
role: "img",
|
|
2008
|
-
|
|
2370
|
+
tabindex: isEmpty.value || internalError.value ? -1 : 0,
|
|
2371
|
+
"aria-label": props.options.ariaLabel || ariaLabelText.value,
|
|
2372
|
+
onKeydown: handleChartKeydown,
|
|
2373
|
+
onFocusout: handleFocusOut
|
|
2009
2374
|
}, [
|
|
2010
2375
|
!isEmpty.value && !internalError.value ? (openBlock(), createElementBlock("canvas", {
|
|
2011
2376
|
key: 0,
|
|
@@ -2084,10 +2449,10 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
2084
2449
|
_: 1
|
|
2085
2450
|
}),
|
|
2086
2451
|
createElementVNode("div", _hoisted_11, toDisplayString(ariaLiveText.value), 1)
|
|
2087
|
-
],
|
|
2452
|
+
], 44, _hoisted_1);
|
|
2088
2453
|
};
|
|
2089
2454
|
}
|
|
2090
|
-
}), [["__scopeId", "data-v-
|
|
2455
|
+
}), [["__scopeId", "data-v-62da63e1"]]);
|
|
2091
2456
|
//#endregion
|
|
2092
2457
|
//#region src/useChart.js
|
|
2093
2458
|
var USE_CHART_KEY = /* @__PURE__ */ Symbol("ev-chart-context");
|