@wil-works/evoke-charts 0.5.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-BmXDig6M.js → ai-B5Fk3lBQ.js} +686 -310
- package/dist/ai.mjs +1 -1
- package/dist/evoke-charts.css +37 -31
- package/dist/index.mjs +280 -60
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ 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,7 +121,7 @@ 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;
|
|
@@ -140,7 +140,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
140
140
|
case "sankey":
|
|
141
141
|
case "chord":
|
|
142
142
|
case "arc": return !(opt.sankeyData || opt.chordData || opt.arcData)?.nodes?.length;
|
|
143
|
-
case "venn":
|
|
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
|
+
}
|
|
144
149
|
case "gantt": return !opt.ganttData || opt.ganttData.length === 0;
|
|
145
150
|
case "scatter-matrix": return !opt.matrixFields?.length || !opt.matrixData?.length;
|
|
146
151
|
default: return !opt.labels?.length || !opt.series?.length;
|
|
@@ -236,11 +241,20 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
236
241
|
data: s.data.slice(startIdx, endIdx)
|
|
237
242
|
}));
|
|
238
243
|
if (base.candleData) sliced.candleData = base.candleData.slice(startIdx, endIdx);
|
|
244
|
+
if (base.volumeData) sliced.volumeData = base.volumeData.slice(startIdx, endIdx);
|
|
239
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
|
+
}
|
|
240
254
|
} else if (base.scatterData && base.scatterData.length > 0) {
|
|
241
255
|
const xs = base.scatterData.map((d) => d.x);
|
|
242
|
-
const xMin =
|
|
243
|
-
const span =
|
|
256
|
+
const xMin = minOf(xs);
|
|
257
|
+
const span = maxOf(xs) - xMin || 1;
|
|
244
258
|
const lo = xMin + range.start / 100 * span;
|
|
245
259
|
const hi = xMin + range.end / 100 * span;
|
|
246
260
|
const filtered = base.scatterData.filter((d) => d.x >= lo && d.x <= hi);
|
|
@@ -334,11 +348,11 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
334
348
|
const el = tooltipRef.value;
|
|
335
349
|
const tw = el?.offsetWidth || 120;
|
|
336
350
|
const th = el?.offsetHeight || 40;
|
|
337
|
-
const gap =
|
|
351
|
+
const gap = INTERACTION.tooltip.cursorGap;
|
|
338
352
|
const margin = 2;
|
|
339
353
|
let legendBottom = margin;
|
|
340
354
|
const legend = props.options.legend || {};
|
|
341
|
-
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;
|
|
342
356
|
const spaceRight = containerRect.width - relX - gap;
|
|
343
357
|
const spaceLeft = relX - gap;
|
|
344
358
|
let left;
|
|
@@ -408,7 +422,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
408
422
|
lastHeight = height;
|
|
409
423
|
canvas.width = width * dpr.value;
|
|
410
424
|
canvas.height = height * dpr.value;
|
|
411
|
-
const overlayPadding =
|
|
425
|
+
const overlayPadding = paddingFor(effectiveOptions.value, width);
|
|
412
426
|
overlayInfo.value = {
|
|
413
427
|
plotArea: {
|
|
414
428
|
x: overlayPadding.left,
|
|
@@ -458,7 +472,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
458
472
|
hoveredToolbox: hoveredToolbox.value
|
|
459
473
|
};
|
|
460
474
|
}
|
|
461
|
-
let focusAnimProgress = 1;
|
|
475
|
+
let focusAnimProgress = emphasisSeriesName.value ? 1 : 0;
|
|
462
476
|
let focusAnimFrameId = null;
|
|
463
477
|
function animateFocusProgress() {
|
|
464
478
|
if (focusAnimFrameId !== null) {
|
|
@@ -491,7 +505,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
491
505
|
}
|
|
492
506
|
function snapshotSeriesData(opt) {
|
|
493
507
|
const map = /* @__PURE__ */ new Map();
|
|
494
|
-
(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)));
|
|
495
509
|
return map;
|
|
496
510
|
}
|
|
497
511
|
function sameStructure(a, b) {
|
|
@@ -505,7 +519,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
505
519
|
const series = (next.series || []).map((s) => {
|
|
506
520
|
const old = prev.get(s.name);
|
|
507
521
|
if (!old) return s;
|
|
508
|
-
const data = s.data.map((v, i) => {
|
|
522
|
+
const data = (s.data || []).map((v, i) => {
|
|
509
523
|
if (isMissingValue(v)) return v;
|
|
510
524
|
const from = old[i + offset] ?? 0;
|
|
511
525
|
return from + (v - from) * t;
|
|
@@ -521,7 +535,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
521
535
|
};
|
|
522
536
|
}
|
|
523
537
|
function dataSignature(opt) {
|
|
524
|
-
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);
|
|
525
539
|
return `${(opt.series || []).length}\u7CFB\u5217/${(opt.labels || []).length}\u7C7B\u76EE/\u03A3${Math.round(total)}`;
|
|
526
540
|
}
|
|
527
541
|
function stopTweenAnimation() {
|
|
@@ -539,7 +553,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
539
553
|
if (!(nextOptions.series || []).some((s) => {
|
|
540
554
|
const old = prevSnapshot.get(s.name);
|
|
541
555
|
if (!old) return false;
|
|
542
|
-
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));
|
|
543
557
|
})) return false;
|
|
544
558
|
stopAnimation();
|
|
545
559
|
stopHoverAnimation();
|
|
@@ -604,15 +618,47 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
604
618
|
cancelAnimationFrame(hoverAnimFrameId);
|
|
605
619
|
hoverAnimFrameId = null;
|
|
606
620
|
}
|
|
621
|
+
if (hoverRedrawFrameId !== null) {
|
|
622
|
+
cancelAnimationFrame(hoverRedrawFrameId);
|
|
623
|
+
hoverRedrawFrameId = null;
|
|
624
|
+
}
|
|
607
625
|
hoverAnimDone = null;
|
|
608
626
|
}
|
|
627
|
+
let hoverRedrawFrameId = null;
|
|
628
|
+
function scheduleHoverRedraw() {
|
|
629
|
+
if (hoverRedrawFrameId !== null) return;
|
|
630
|
+
hoverRedrawFrameId = requestAnimationFrame(() => {
|
|
631
|
+
hoverRedrawFrameId = null;
|
|
632
|
+
redraw();
|
|
633
|
+
});
|
|
634
|
+
}
|
|
609
635
|
function usesHoverAnimation() {
|
|
610
636
|
const t = props.options.type;
|
|
611
637
|
return t === "pie" || t === "doughnut" || t === "rose" || t === "sunburst" || t === "treemap" || t === "radar";
|
|
612
638
|
}
|
|
613
639
|
function animatesHoverEnter() {
|
|
614
640
|
const t = props.options.type;
|
|
615
|
-
return
|
|
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);
|
|
616
662
|
}
|
|
617
663
|
function clearHover() {
|
|
618
664
|
if (hoverIndex === -1) return false;
|
|
@@ -695,15 +741,14 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
695
741
|
const theme = getTheme(isDark, props.options.theme, props.options.palette);
|
|
696
742
|
const width = rect.width;
|
|
697
743
|
const height = rect.height;
|
|
698
|
-
const padding =
|
|
744
|
+
const padding = paddingFor(effectiveOptions.value, width);
|
|
699
745
|
const plotArea = {
|
|
700
746
|
x: padding.left,
|
|
701
747
|
y: padding.top,
|
|
702
748
|
width: width - padding.left - padding.right,
|
|
703
749
|
height: height - padding.top - padding.bottom
|
|
704
750
|
};
|
|
705
|
-
const
|
|
706
|
-
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);
|
|
707
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;
|
|
708
753
|
return null;
|
|
709
754
|
}
|
|
@@ -748,7 +793,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
748
793
|
const options = effectiveOptions.value;
|
|
749
794
|
const width = rect.width;
|
|
750
795
|
const height = rect.height;
|
|
751
|
-
const padding =
|
|
796
|
+
const padding = paddingFor(options, width);
|
|
752
797
|
const plotArea = {
|
|
753
798
|
x: padding.left,
|
|
754
799
|
y: padding.top,
|
|
@@ -770,12 +815,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
770
815
|
const dx = canvasX - centerX;
|
|
771
816
|
const dy = canvasY - centerY;
|
|
772
817
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
773
|
-
const innerRadius = options.type === "doughnut" ?
|
|
818
|
+
const innerRadius = options.type === "doughnut" ? doughnutInnerRadius(baseRadius, options) : 0;
|
|
774
819
|
let angle = Math.atan2(dy, dx) + Math.PI / 2;
|
|
775
820
|
if (angle < 0) angle += Math.PI * 2;
|
|
776
821
|
const total = slices.reduce((sum, d) => sum + d.value, 0);
|
|
777
822
|
let currentAngle = 0;
|
|
778
|
-
const maxVal =
|
|
823
|
+
const maxVal = maxOf(slices.map((d) => d.value));
|
|
779
824
|
for (let i = 0; i < slices.length; i++) {
|
|
780
825
|
const sliceAngle = slices[i].value / total * Math.PI * 2;
|
|
781
826
|
if (angle >= currentAngle && angle < currentAngle + sliceAngle) {
|
|
@@ -831,14 +876,10 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
831
876
|
const i = Math.floor((canvasX - plotArea.x) / categoryWidth);
|
|
832
877
|
if (i < 0 || i >= labels2.length) return null;
|
|
833
878
|
const wf = options.waterfall || {};
|
|
834
|
-
const
|
|
835
|
-
const v =
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const vk = deltas[k] || 0;
|
|
839
|
-
cumulative = totalIdx.has(k) ? vk : cumulative + vk;
|
|
840
|
-
}
|
|
841
|
-
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;
|
|
842
883
|
const color = isTotal ? wf.totalColor || theme.colors[0] : v >= 0 ? wf.increaseColor || "#dc2626" : wf.decreaseColor || "#16a34a";
|
|
843
884
|
return {
|
|
844
885
|
index: i,
|
|
@@ -862,18 +903,25 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
862
903
|
...showOutliers ? b.outliers || [] : []
|
|
863
904
|
]);
|
|
864
905
|
const axisCfg = options.boxHorizontal ? options.xAxis || {} : options.yAxis || {};
|
|
865
|
-
const ext = resolveTickExtendedRange(axisCfg.min ??
|
|
906
|
+
const ext = resolveTickExtendedRange(axisCfg.min ?? minOf(allValues), axisCfg.max ?? maxOf(allValues), axisCfg.ticks || 5);
|
|
866
907
|
return boxplotHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value, {
|
|
867
908
|
min: ext.min,
|
|
868
909
|
max: ext.max
|
|
869
910
|
});
|
|
870
911
|
}
|
|
871
912
|
if (options.type === "calendar-heatmap") return calendarHitTest(canvasX, canvasY, plotArea, options, theme);
|
|
872
|
-
if (options.type === "sankey") return sankeyHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value);
|
|
913
|
+
if (options.type === "sankey") return sankeyHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value, sankeyLayoutFor(plotArea, options, theme, hiddenSeries.value, isDark));
|
|
873
914
|
if (options.type === "venn") return vennHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value);
|
|
874
915
|
if (options.type === "chord") return chordHitTest(canvasX, canvasY, plotArea, options, theme, hiddenSeries.value);
|
|
875
|
-
if (options.type === "arc")
|
|
876
|
-
|
|
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));
|
|
877
925
|
if (options.type === "scatter-matrix") {
|
|
878
926
|
const fields = options.matrixFields || [];
|
|
879
927
|
const records = options.matrixData || [];
|
|
@@ -1014,12 +1062,12 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1014
1062
|
}
|
|
1015
1063
|
return null;
|
|
1016
1064
|
}
|
|
1017
|
-
const yValues = scatterData.map((d) => d.y);
|
|
1065
|
+
const yValues = scatterData.map((d) => d.y).filter((v) => typeof v === "number" && Number.isFinite(v));
|
|
1018
1066
|
const axisConfig = options.yAxis || {};
|
|
1019
|
-
const rawMin = axisConfig.min ??
|
|
1020
|
-
const rawMax = axisConfig.max ??
|
|
1067
|
+
const rawMin = axisConfig.min ?? (yValues.length ? minOf(yValues) : 0);
|
|
1068
|
+
const rawMax = axisConfig.max ?? (yValues.length ? maxOf(yValues) : 1);
|
|
1021
1069
|
const yExt = resolveTickExtendedRange(rawMin, rawMax, axisConfig.ticks || 5);
|
|
1022
|
-
const positions =
|
|
1070
|
+
const positions = scatterPositionsFor(scatterData, {
|
|
1023
1071
|
min: yExt.min,
|
|
1024
1072
|
max: yExt.max
|
|
1025
1073
|
}, plotArea, options);
|
|
@@ -1206,7 +1254,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1206
1254
|
const angle = i * angleStep - Math.PI / 2;
|
|
1207
1255
|
const ind = indicators[i];
|
|
1208
1256
|
const span = ind.max - (ind.min || 0);
|
|
1209
|
-
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;
|
|
1210
1258
|
const px = centerX + Math.cos(angle) * maxR;
|
|
1211
1259
|
const py = centerY + Math.sin(angle) * maxR;
|
|
1212
1260
|
const dist = Math.sqrt((canvasX - px) ** 2 + (canvasY - py) ** 2);
|
|
@@ -1297,7 +1345,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1297
1345
|
const canvas = canvasRef.value;
|
|
1298
1346
|
if (!canvas) return null;
|
|
1299
1347
|
const rect = canvas.getBoundingClientRect();
|
|
1300
|
-
const padding =
|
|
1348
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1301
1349
|
const plotArea = {
|
|
1302
1350
|
x: padding.left,
|
|
1303
1351
|
y: padding.top,
|
|
@@ -1361,7 +1409,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1361
1409
|
const rect = canvas.getBoundingClientRect();
|
|
1362
1410
|
const x = e.clientX - rect.left;
|
|
1363
1411
|
const y = e.clientY - rect.top;
|
|
1364
|
-
const padding =
|
|
1412
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1365
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)) {
|
|
1366
1414
|
touchPanning = true;
|
|
1367
1415
|
touchPanStartX = x;
|
|
@@ -1384,7 +1432,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1384
1432
|
const x = e.clientX - rect.left;
|
|
1385
1433
|
const y = e.clientY - rect.top;
|
|
1386
1434
|
if (props.options.brush?.enabled === true) {
|
|
1387
|
-
const padding =
|
|
1435
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1388
1436
|
const plotArea = {
|
|
1389
1437
|
x: padding.left,
|
|
1390
1438
|
y: padding.top,
|
|
@@ -1479,7 +1527,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1479
1527
|
const labels = options.labels || [];
|
|
1480
1528
|
if (canvas && labels.length > 0) {
|
|
1481
1529
|
const rect = canvas.getBoundingClientRect();
|
|
1482
|
-
const padding =
|
|
1530
|
+
const padding = paddingFor(options, rect.width);
|
|
1483
1531
|
const plotArea = {
|
|
1484
1532
|
x: padding.left,
|
|
1485
1533
|
y: padding.top,
|
|
@@ -1522,7 +1570,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1522
1570
|
const rect = canvas.getBoundingClientRect();
|
|
1523
1571
|
const x = e.clientX - rect.left;
|
|
1524
1572
|
const y = e.clientY - rect.top;
|
|
1525
|
-
const padding =
|
|
1573
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1526
1574
|
const plotArea = {
|
|
1527
1575
|
x: padding.left,
|
|
1528
1576
|
y: padding.top,
|
|
@@ -1614,8 +1662,8 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1614
1662
|
} else if (usesHoverAnimation()) {
|
|
1615
1663
|
stopHoverAnimation();
|
|
1616
1664
|
hoverAnimProgress = 1;
|
|
1617
|
-
|
|
1618
|
-
} else
|
|
1665
|
+
scheduleHoverRedraw();
|
|
1666
|
+
} else scheduleHoverRedraw();
|
|
1619
1667
|
if (prevIndex === -1) {
|
|
1620
1668
|
const emitParams = Array.isArray(result.params) ? result.params[0] : result.params;
|
|
1621
1669
|
emit("hover", {
|
|
@@ -1675,6 +1723,9 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1675
1723
|
}
|
|
1676
1724
|
return Number.isInteger(value) ? value.toString() : parseFloat(value.toFixed(4)).toString();
|
|
1677
1725
|
}
|
|
1726
|
+
function escapeHtml(value) {
|
|
1727
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1728
|
+
}
|
|
1678
1729
|
function applyTooltipContent(params) {
|
|
1679
1730
|
const formatter = props.options.tooltip?.formatter;
|
|
1680
1731
|
if (formatter) {
|
|
@@ -1685,23 +1736,23 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1685
1736
|
const title = params[0]?.name || "";
|
|
1686
1737
|
const rows = pickTooltipRows(params).map((p) => `
|
|
1687
1738
|
<div class="ev-chart__tooltip-item">
|
|
1688
|
-
<span class="ev-chart__tooltip-dot" style="background: ${p.color}"></span>
|
|
1689
|
-
<span class="ev-chart__tooltip-name">${p.seriesName}</span>
|
|
1690
|
-
<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>
|
|
1691
1742
|
</div>
|
|
1692
1743
|
`).join("");
|
|
1693
1744
|
tooltipContent.value = `
|
|
1694
|
-
<div class="ev-chart__tooltip-title">${title}</div>
|
|
1745
|
+
<div class="ev-chart__tooltip-title">${escapeHtml(title)}</div>
|
|
1695
1746
|
${rows}
|
|
1696
1747
|
`;
|
|
1697
1748
|
} else {
|
|
1698
1749
|
const p = params;
|
|
1699
1750
|
tooltipContent.value = `
|
|
1700
|
-
<div class="ev-chart__tooltip-title">${p.name}</div>
|
|
1751
|
+
<div class="ev-chart__tooltip-title">${escapeHtml(p.name)}</div>
|
|
1701
1752
|
<div class="ev-chart__tooltip-item">
|
|
1702
|
-
<span class="ev-chart__tooltip-dot" style="background: ${p.color}"></span>
|
|
1703
|
-
<span class="ev-chart__tooltip-name">${p.seriesName}</span>
|
|
1704
|
-
<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>
|
|
1705
1756
|
</div>
|
|
1706
1757
|
`;
|
|
1707
1758
|
}
|
|
@@ -1718,7 +1769,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1718
1769
|
else if (checkToolboxHit(e.clientX, e.clientY)) zone = "toolbox";
|
|
1719
1770
|
else if (checkLegendHit(e.clientX, e.clientY)) zone = "legend";
|
|
1720
1771
|
else {
|
|
1721
|
-
const padding =
|
|
1772
|
+
const padding = paddingFor(effectiveOptions.value, rect.width);
|
|
1722
1773
|
if (x >= padding.left && x <= rect.width - padding.right && y >= padding.top && y <= rect.height - padding.bottom) zone = "plot";
|
|
1723
1774
|
else {
|
|
1724
1775
|
const slider = getSliderGeo();
|
|
@@ -1759,6 +1810,67 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1759
1810
|
ariaLiveText.value = "";
|
|
1760
1811
|
}
|
|
1761
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
|
+
}
|
|
1762
1874
|
let resizeObserver = null;
|
|
1763
1875
|
function setupResizeObserver() {
|
|
1764
1876
|
if (!props.responsive) return;
|
|
@@ -1806,7 +1918,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1806
1918
|
labels: [...opt.labels || []],
|
|
1807
1919
|
series: (opt.series || []).map((s) => ({
|
|
1808
1920
|
...s,
|
|
1809
|
-
data: [...s.data]
|
|
1921
|
+
data: [...s.data || []]
|
|
1810
1922
|
}))
|
|
1811
1923
|
};
|
|
1812
1924
|
}
|
|
@@ -1835,9 +1947,17 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1835
1947
|
watch(() => props.options, (next) => {
|
|
1836
1948
|
cachedDataExtent = null;
|
|
1837
1949
|
cachedPlotArea = null;
|
|
1950
|
+
cachedPadding = null;
|
|
1951
|
+
scatterHitCache = null;
|
|
1952
|
+
legendBoundsCache = null;
|
|
1953
|
+
ganttLayoutCache = null;
|
|
1954
|
+
sankeyLayoutCache = null;
|
|
1838
1955
|
internalError.value = null;
|
|
1839
1956
|
runDevValidation();
|
|
1840
|
-
focusSeries.value
|
|
1957
|
+
if (focusSeries.value !== emphasisSeriesName.value) {
|
|
1958
|
+
focusSeries.value = emphasisSeriesName.value;
|
|
1959
|
+
animateFocusProgress();
|
|
1960
|
+
}
|
|
1841
1961
|
syncScenes();
|
|
1842
1962
|
const zoom = getDataZoomConfig(props.options);
|
|
1843
1963
|
const zoomKey = zoom ? JSON.stringify({
|
|
@@ -1891,6 +2011,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1891
2011
|
};
|
|
1892
2012
|
let unregisterConnector = null;
|
|
1893
2013
|
onMounted(() => {
|
|
2014
|
+
focusSeries.value = emphasisSeriesName.value;
|
|
1894
2015
|
render(true);
|
|
1895
2016
|
syncScenes();
|
|
1896
2017
|
setupResizeObserver();
|
|
@@ -1924,11 +2045,98 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1924
2045
|
});
|
|
1925
2046
|
let cachedDataExtent = null;
|
|
1926
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
|
+
}
|
|
1927
2130
|
__expose({
|
|
1928
2131
|
refresh: () => render(true),
|
|
1929
2132
|
update(newOptions) {
|
|
1930
2133
|
Object.assign(props.options, newOptions);
|
|
1931
2134
|
cachedDataExtent = null;
|
|
2135
|
+
cachedPadding = null;
|
|
2136
|
+
scatterHitCache = null;
|
|
2137
|
+
legendBoundsCache = null;
|
|
2138
|
+
ganttLayoutCache = null;
|
|
2139
|
+
sankeyLayoutCache = null;
|
|
1932
2140
|
specVersion.value++;
|
|
1933
2141
|
debouncedRender(true);
|
|
1934
2142
|
},
|
|
@@ -1938,9 +2146,18 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
1938
2146
|
destroy() {
|
|
1939
2147
|
cleanupResizeObserver();
|
|
1940
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
|
+
}
|
|
1941
2156
|
stopAnimation();
|
|
1942
2157
|
stopHoverAnimation();
|
|
2158
|
+
stopTweenAnimation();
|
|
1943
2159
|
stopSceneTimer();
|
|
2160
|
+
stopFocusAnimation();
|
|
1944
2161
|
if (tooltipRafId !== null) {
|
|
1945
2162
|
cancelAnimationFrame(tooltipRafId);
|
|
1946
2163
|
tooltipRafId = null;
|
|
@@ -2016,7 +2233,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
2016
2233
|
};
|
|
2017
2234
|
const width = canvas.width / dpr.value;
|
|
2018
2235
|
const height = canvas.height / dpr.value;
|
|
2019
|
-
const padding =
|
|
2236
|
+
const padding = paddingFor(effectiveOptions.value, width);
|
|
2020
2237
|
cachedPlotArea = {
|
|
2021
2238
|
x: padding.left,
|
|
2022
2239
|
y: padding.top,
|
|
@@ -2150,7 +2367,10 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
2150
2367
|
class: "ev-chart",
|
|
2151
2368
|
style: normalizeStyle([containerStyle.value, { cursor: cursorStyle.value }]),
|
|
2152
2369
|
role: "img",
|
|
2153
|
-
|
|
2370
|
+
tabindex: isEmpty.value || internalError.value ? -1 : 0,
|
|
2371
|
+
"aria-label": props.options.ariaLabel || ariaLabelText.value,
|
|
2372
|
+
onKeydown: handleChartKeydown,
|
|
2373
|
+
onFocusout: handleFocusOut
|
|
2154
2374
|
}, [
|
|
2155
2375
|
!isEmpty.value && !internalError.value ? (openBlock(), createElementBlock("canvas", {
|
|
2156
2376
|
key: 0,
|
|
@@ -2229,10 +2449,10 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
2229
2449
|
_: 1
|
|
2230
2450
|
}),
|
|
2231
2451
|
createElementVNode("div", _hoisted_11, toDisplayString(ariaLiveText.value), 1)
|
|
2232
|
-
],
|
|
2452
|
+
], 44, _hoisted_1);
|
|
2233
2453
|
};
|
|
2234
2454
|
}
|
|
2235
|
-
}), [["__scopeId", "data-v-
|
|
2455
|
+
}), [["__scopeId", "data-v-62da63e1"]]);
|
|
2236
2456
|
//#endregion
|
|
2237
2457
|
//#region src/useChart.js
|
|
2238
2458
|
var USE_CHART_KEY = /* @__PURE__ */ Symbol("ev-chart-context");
|