@opendata-ai/openchart-vanilla 8.3.1 → 8.4.1
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/dist/{chunk-ERRV4WFS.js → chunk-3UIUANPI.js} +37 -5
- package/dist/chunk-3UIUANPI.js.map +1 -0
- package/dist/{chunk-GRA2NIRZ.js → chunk-5A2NLYUW.js} +510 -94
- package/dist/chunk-5A2NLYUW.js.map +1 -0
- package/dist/{chunk-RM5XS6NR.js → chunk-5RX6ZQGZ.js} +25 -11
- package/dist/chunk-5RX6ZQGZ.js.map +1 -0
- package/dist/{chunk-T32UHMZA.js → chunk-6B5OBGIO.js} +2 -2
- package/dist/{chunk-W76C53MC.js → chunk-KANNWAEA.js} +192 -22
- package/dist/chunk-KANNWAEA.js.map +1 -0
- package/dist/{chunk-DCODCLWI.js → chunk-RYVV3YIG.js} +3 -3
- package/dist/export-gif.js +3 -3
- package/dist/index.d.ts +6 -1
- package/dist/index.js +306 -103
- package/dist/index.js.map +1 -1
- package/dist/static.js +3 -3
- package/dist/story/index.js +4 -4
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-ERRV4WFS.js.map +0 -1
- package/dist/chunk-GRA2NIRZ.js.map +0 -1
- package/dist/chunk-RM5XS6NR.js.map +0 -1
- package/dist/chunk-W76C53MC.js.map +0 -1
- /package/dist/{chunk-T32UHMZA.js.map → chunk-6B5OBGIO.js.map} +0 -0
- /package/dist/{chunk-DCODCLWI.js.map → chunk-RYVV3YIG.js.map} +0 -0
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
exportPNG,
|
|
5
5
|
exportSVG,
|
|
6
6
|
exportSVGWithFonts
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-6B5OBGIO.js";
|
|
8
8
|
import {
|
|
9
9
|
buildGradientDefs,
|
|
10
|
+
buildPatternDefs,
|
|
10
11
|
nextSvgId,
|
|
11
12
|
rectPathWithCorners,
|
|
12
13
|
renderChartSVG,
|
|
@@ -14,13 +15,16 @@ import {
|
|
|
14
15
|
renderLegend,
|
|
15
16
|
renderSingleMark,
|
|
16
17
|
resolveMarkFill,
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
resolvePatternFill,
|
|
19
|
+
resolvedSurface,
|
|
20
|
+
stampAnimationVars,
|
|
21
|
+
stampThemeProperties
|
|
22
|
+
} from "./chunk-KANNWAEA.js";
|
|
19
23
|
import {
|
|
20
24
|
SVG_NS,
|
|
21
25
|
createSVGElement,
|
|
22
26
|
setAttrs
|
|
23
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-5RX6ZQGZ.js";
|
|
24
28
|
|
|
25
29
|
// src/resize-observer.ts
|
|
26
30
|
var DEBOUNCE_MS = 16;
|
|
@@ -89,11 +93,12 @@ function createTooltipManager(container) {
|
|
|
89
93
|
return;
|
|
90
94
|
}
|
|
91
95
|
const content = input;
|
|
92
|
-
const
|
|
96
|
+
const emphasisIndex = content.fields.findIndex((f) => f.emphasis);
|
|
97
|
+
const contentKey = `${content.title}|${content.fields.length}|${content.fields[0]?.value}|${content.fields[content.fields.length - 1]?.value}|${emphasisIndex}`;
|
|
93
98
|
if (contentKey !== lastContentKey) {
|
|
94
99
|
lastContentKey = contentKey;
|
|
95
100
|
let html = "";
|
|
96
|
-
const colorRowCount = content.fields.filter((f) => f.color).length;
|
|
101
|
+
const colorRowCount = content.fields.filter((f) => f.color && f.role !== "total").length;
|
|
97
102
|
const perRowSwatches = colorRowCount > 1;
|
|
98
103
|
if (content.title) {
|
|
99
104
|
const titleColor = perRowSwatches ? void 0 : content.fields.find((f) => f.color)?.color;
|
|
@@ -107,7 +112,10 @@ function createTooltipManager(container) {
|
|
|
107
112
|
if (content.fields.length > 0) {
|
|
108
113
|
html += '<div class="oc-tooltip-body">';
|
|
109
114
|
for (const field of content.fields) {
|
|
110
|
-
|
|
115
|
+
let rowClass = "oc-tooltip-row";
|
|
116
|
+
if (field.emphasis) rowClass += " oc-tooltip-row--emphasis";
|
|
117
|
+
if (field.role === "total") rowClass += " oc-tooltip-row--total";
|
|
118
|
+
html += `<div class="${rowClass}">`;
|
|
111
119
|
html += '<span class="oc-tooltip-label">';
|
|
112
120
|
if (perRowSwatches && field.color) {
|
|
113
121
|
html += `<span class="oc-tooltip-row-swatch" style="background:${esc(field.color)}"></span>`;
|
|
@@ -2847,6 +2855,7 @@ function buildTextMarkTweens(prevLayout, nextLayout, marksContainer, dom, tweens
|
|
|
2847
2855
|
}
|
|
2848
2856
|
|
|
2849
2857
|
// src/transition/driver.ts
|
|
2858
|
+
var TRANSITIONING_CLASS = "oc-transitioning";
|
|
2850
2859
|
function runTransition(args) {
|
|
2851
2860
|
const { svg, prevLayout, nextLayout, animation, onComplete, fromSnapshot, manual, canvas } = args;
|
|
2852
2861
|
const update = animation.update;
|
|
@@ -2872,6 +2881,7 @@ function runTransition(args) {
|
|
|
2872
2881
|
totalMs: 0
|
|
2873
2882
|
};
|
|
2874
2883
|
}
|
|
2884
|
+
svg.classList.add(TRANSITIONING_CLASS);
|
|
2875
2885
|
const dom = buildMarkDomIndex(marksContainer);
|
|
2876
2886
|
const tweens = [];
|
|
2877
2887
|
const ghosts = [];
|
|
@@ -2979,6 +2989,7 @@ function runTransition(args) {
|
|
|
2979
2989
|
}
|
|
2980
2990
|
let rafId = null;
|
|
2981
2991
|
let running = true;
|
|
2992
|
+
let pulsedDot = null;
|
|
2982
2993
|
let startTime = null;
|
|
2983
2994
|
let lastElapsed = 0;
|
|
2984
2995
|
const nextLineMarks = nextLayout.marks.filter((m) => m.type === "line");
|
|
@@ -3010,9 +3021,11 @@ function runTransition(args) {
|
|
|
3010
3021
|
if (!running) return;
|
|
3011
3022
|
running = false;
|
|
3012
3023
|
rafId = null;
|
|
3024
|
+
svg.classList.remove(TRANSITIONING_CLASS);
|
|
3013
3025
|
snapToFinal();
|
|
3014
3026
|
removeGhosts();
|
|
3015
3027
|
settleCanvas();
|
|
3028
|
+
pulsedDot = pulseSparklineTerminator(svg, marksContainer);
|
|
3016
3029
|
for (let i = 0; i < secondaryEls.length; i++) {
|
|
3017
3030
|
secondaryEls[i].style.opacity = secondaryOriginalOpacity[i];
|
|
3018
3031
|
}
|
|
@@ -3127,8 +3140,12 @@ function runTransition(args) {
|
|
|
3127
3140
|
}
|
|
3128
3141
|
return {
|
|
3129
3142
|
cancel() {
|
|
3143
|
+
clearPulseTimer(pulsedDot);
|
|
3144
|
+
pulsedDot?.classList.remove(PULSE_CLASS);
|
|
3145
|
+
pulsedDot = null;
|
|
3130
3146
|
if (!running) return;
|
|
3131
3147
|
running = false;
|
|
3148
|
+
svg.classList.remove(TRANSITIONING_CLASS);
|
|
3132
3149
|
if (rafId !== null) {
|
|
3133
3150
|
cancelAnimationFrame(rafId);
|
|
3134
3151
|
rafId = null;
|
|
@@ -3164,6 +3181,37 @@ function runTransition(args) {
|
|
|
3164
3181
|
totalMs
|
|
3165
3182
|
};
|
|
3166
3183
|
}
|
|
3184
|
+
var PULSE_CLASS = "oc-pulse";
|
|
3185
|
+
var PULSE_DURATION_MS = 600;
|
|
3186
|
+
var pulseTimers = /* @__PURE__ */ new WeakMap();
|
|
3187
|
+
function clearPulseTimer(dot) {
|
|
3188
|
+
if (!dot) return;
|
|
3189
|
+
const pending = pulseTimers.get(dot);
|
|
3190
|
+
if (pending !== void 0) {
|
|
3191
|
+
clearTimeout(pending);
|
|
3192
|
+
pulseTimers.delete(dot);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
function pulseSparklineTerminator(svg, marksContainer) {
|
|
3196
|
+
if (!marksContainer || svg.getAttribute("data-display") !== "sparkline") return null;
|
|
3197
|
+
const dots = marksContainer.querySelectorAll("circle.oc-mark-point");
|
|
3198
|
+
const dot = dots[dots.length - 1];
|
|
3199
|
+
if (!dot) return null;
|
|
3200
|
+
const r = dot.getAttribute("r");
|
|
3201
|
+
if (r) dot.style.setProperty("--oc-pulse-r", `${r}px`);
|
|
3202
|
+
clearPulseTimer(dot);
|
|
3203
|
+
dot.classList.remove(PULSE_CLASS);
|
|
3204
|
+
void dot.getBoundingClientRect?.();
|
|
3205
|
+
dot.classList.add(PULSE_CLASS);
|
|
3206
|
+
pulseTimers.set(
|
|
3207
|
+
dot,
|
|
3208
|
+
setTimeout(() => {
|
|
3209
|
+
pulseTimers.delete(dot);
|
|
3210
|
+
dot.classList.remove(PULSE_CLASS);
|
|
3211
|
+
}, PULSE_DURATION_MS)
|
|
3212
|
+
);
|
|
3213
|
+
return dot;
|
|
3214
|
+
}
|
|
3167
3215
|
function collectSecondaryElements(svg) {
|
|
3168
3216
|
const els = [];
|
|
3169
3217
|
const epLabels = svg.querySelector(".oc-endpoint-labels");
|
|
@@ -3520,7 +3568,7 @@ function createYouDrawIt(options) {
|
|
|
3520
3568
|
}
|
|
3521
3569
|
|
|
3522
3570
|
// src/mount.ts
|
|
3523
|
-
import { cssTokenDefault
|
|
3571
|
+
import { cssTokenDefault, isGraphSpec, isLayerSpec } from "@opendata-ai/openchart-core";
|
|
3524
3572
|
import {
|
|
3525
3573
|
compileChart,
|
|
3526
3574
|
compileLayer,
|
|
@@ -3536,9 +3584,9 @@ function cancelAnimations(svg) {
|
|
|
3536
3584
|
}
|
|
3537
3585
|
function computeAnimationDuration(svg) {
|
|
3538
3586
|
const style = svg.style;
|
|
3539
|
-
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) ||
|
|
3587
|
+
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 450;
|
|
3540
3588
|
const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
|
|
3541
|
-
const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) ||
|
|
3589
|
+
const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) || 150;
|
|
3542
3590
|
const animatedElements = svg.querySelectorAll("[data-animation-index]").length;
|
|
3543
3591
|
const totalStagger = stagger * Math.max(0, animatedElements - 1);
|
|
3544
3592
|
return totalStagger + duration + annotationDelay + 500;
|
|
@@ -3678,6 +3726,8 @@ function wireChartEvents(svg, layout, specAnnotations, handlers) {
|
|
|
3678
3726
|
|
|
3679
3727
|
// src/interactions/crosshair.ts
|
|
3680
3728
|
import { getRepresentativeColor } from "@opendata-ai/openchart-core";
|
|
3729
|
+
var EMPHASIS_PROXIMITY_PX = 18;
|
|
3730
|
+
var SNAP_DOT_RADIUS = 4.5;
|
|
3681
3731
|
function snapKey(x) {
|
|
3682
3732
|
return Math.round(x);
|
|
3683
3733
|
}
|
|
@@ -3724,43 +3774,46 @@ function findNearestX(sortedXs, x) {
|
|
|
3724
3774
|
}
|
|
3725
3775
|
return candidate;
|
|
3726
3776
|
}
|
|
3727
|
-
function buildSliceTooltip(hits) {
|
|
3777
|
+
function buildSliceTooltip(hits, emphasisKey) {
|
|
3728
3778
|
if (hits.length === 0) return null;
|
|
3729
3779
|
const title = hits[0].point.tooltip?.title;
|
|
3730
3780
|
const fields = [];
|
|
3731
3781
|
const isMulti = hits.length > 1;
|
|
3782
|
+
let total;
|
|
3732
3783
|
for (const { group, point } of hits) {
|
|
3733
3784
|
const tip = point.tooltip;
|
|
3734
3785
|
if (!tip) continue;
|
|
3786
|
+
if (!total) total = tip.fields.find((f) => f.role === "total");
|
|
3735
3787
|
if (isMulti) {
|
|
3736
|
-
const
|
|
3788
|
+
const candidates = tip.fields.filter((f) => f.role !== "total");
|
|
3789
|
+
const yField = candidates.find(
|
|
3737
3790
|
(f) => !f.color && f.label !== title && f.value !== title && f.value !== group.seriesKey
|
|
3738
|
-
) ??
|
|
3791
|
+
) ?? candidates[candidates.length - 1] ?? null;
|
|
3739
3792
|
if (!yField) continue;
|
|
3740
3793
|
fields.push({
|
|
3741
3794
|
label: group.seriesKey,
|
|
3742
3795
|
value: yField.value,
|
|
3743
|
-
color: group.color
|
|
3796
|
+
color: group.color,
|
|
3797
|
+
...emphasisKey === group.seriesKey ? { emphasis: true } : {}
|
|
3744
3798
|
});
|
|
3745
3799
|
} else {
|
|
3746
3800
|
for (const f of tip.fields) {
|
|
3801
|
+
if (f.role === "total") continue;
|
|
3747
3802
|
fields.push({ ...f, color: f.color ?? group.color });
|
|
3748
3803
|
}
|
|
3749
3804
|
}
|
|
3750
3805
|
}
|
|
3751
3806
|
if (fields.length === 0) return null;
|
|
3807
|
+
if (total && isMulti) fields.push(total);
|
|
3752
3808
|
return { title, fields };
|
|
3753
3809
|
}
|
|
3754
|
-
function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
3810
|
+
function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
3755
3811
|
const overlay = svg.querySelector("[data-voronoi-overlay]");
|
|
3756
|
-
if (!overlay) return
|
|
3757
|
-
};
|
|
3812
|
+
if (!overlay) return null;
|
|
3758
3813
|
const groups = collectSeriesGroups(layout);
|
|
3759
|
-
if (groups.length === 0) return
|
|
3760
|
-
};
|
|
3814
|
+
if (groups.length === 0) return null;
|
|
3761
3815
|
const snapXs = collectSnapXs(groups);
|
|
3762
|
-
if (snapXs.length === 0) return
|
|
3763
|
-
};
|
|
3816
|
+
if (snapXs.length === 0) return null;
|
|
3764
3817
|
const crosshair = svg.querySelector("[data-crosshair]");
|
|
3765
3818
|
const dotsLayer = svg.querySelector("[data-snap-dots]");
|
|
3766
3819
|
const dots = [];
|
|
@@ -3768,7 +3821,7 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3768
3821
|
while (dotsLayer.firstChild) dotsLayer.removeChild(dotsLayer.firstChild);
|
|
3769
3822
|
for (const group of groups) {
|
|
3770
3823
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
3771
|
-
circle.setAttribute("r",
|
|
3824
|
+
circle.setAttribute("r", String(SNAP_DOT_RADIUS));
|
|
3772
3825
|
circle.setAttribute("fill", layout.theme.colors.background);
|
|
3773
3826
|
circle.setAttribute("stroke", group.color);
|
|
3774
3827
|
circle.setAttribute("stroke-width", "2");
|
|
@@ -3778,9 +3831,16 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3778
3831
|
dots.push(circle);
|
|
3779
3832
|
}
|
|
3780
3833
|
}
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3834
|
+
let currentIndex = -1;
|
|
3835
|
+
let currentEmphasis = null;
|
|
3836
|
+
function containerScale() {
|
|
3837
|
+
const svgEl = svg;
|
|
3838
|
+
const rect = svgEl.getBoundingClientRect();
|
|
3839
|
+
const viewBox = svgEl.viewBox?.baseVal;
|
|
3840
|
+
const scaleX = viewBox?.width && rect.width ? viewBox.width / rect.width : 1;
|
|
3841
|
+
return scaleX > 0 ? 1 / scaleX : 1;
|
|
3842
|
+
}
|
|
3843
|
+
function showAt(snappedX, emphasisKey, viewBoxToContainer) {
|
|
3784
3844
|
const hits = [];
|
|
3785
3845
|
let anchorY = 0;
|
|
3786
3846
|
let anchorCount = 0;
|
|
@@ -3806,7 +3866,10 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3806
3866
|
crosshair.setAttribute("x2", String(snappedX));
|
|
3807
3867
|
crosshair.style.display = "";
|
|
3808
3868
|
}
|
|
3809
|
-
|
|
3869
|
+
currentEmphasis = emphasisKey;
|
|
3870
|
+
if (emphasisKey) emphasis?.setSeries(emphasisKey);
|
|
3871
|
+
else emphasis?.clear();
|
|
3872
|
+
const tooltip = buildSliceTooltip(hits, emphasisKey);
|
|
3810
3873
|
if (!tooltip) return false;
|
|
3811
3874
|
const containerAnchorX = snappedX * viewBoxToContainer;
|
|
3812
3875
|
const containerAnchorY = anchorCount > 0 ? anchorY / anchorCount * viewBoxToContainer : 0;
|
|
@@ -3814,6 +3877,31 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3814
3877
|
placement: "right"
|
|
3815
3878
|
});
|
|
3816
3879
|
return true;
|
|
3880
|
+
}
|
|
3881
|
+
function keysAt(snappedX) {
|
|
3882
|
+
const keys = [];
|
|
3883
|
+
for (const group of groups) {
|
|
3884
|
+
if (group.pointsByX.has(snappedX)) keys.push(group.seriesKey);
|
|
3885
|
+
}
|
|
3886
|
+
return keys;
|
|
3887
|
+
}
|
|
3888
|
+
const positionAt = (svgX, svgY, viewBoxToContainer) => {
|
|
3889
|
+
const snappedX = findNearestX(snapXs, svgX);
|
|
3890
|
+
if (snappedX === null) return false;
|
|
3891
|
+
currentIndex = snapXs.indexOf(snappedX);
|
|
3892
|
+
let nearestKey = null;
|
|
3893
|
+
let nearestDy = Number.POSITIVE_INFINITY;
|
|
3894
|
+
for (const group of groups) {
|
|
3895
|
+
const point = group.pointsByX.get(snappedX);
|
|
3896
|
+
if (!point) continue;
|
|
3897
|
+
const dy = Math.abs(point.y - svgY);
|
|
3898
|
+
if (dy < nearestDy) {
|
|
3899
|
+
nearestDy = dy;
|
|
3900
|
+
nearestKey = group.seriesKey;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
const emphasisKey = nearestDy <= EMPHASIS_PROXIMITY_PX ? nearestKey : null;
|
|
3904
|
+
return showAt(snappedX, emphasisKey, viewBoxToContainer);
|
|
3817
3905
|
};
|
|
3818
3906
|
const toSvgCoords = (clientX, clientY) => {
|
|
3819
3907
|
const svgEl = svg;
|
|
@@ -3831,11 +3919,24 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3831
3919
|
const { svgX, svgY, viewBoxToContainer } = toSvgCoords(me.clientX, me.clientY);
|
|
3832
3920
|
positionAt(svgX, svgY, viewBoxToContainer);
|
|
3833
3921
|
};
|
|
3834
|
-
const hideAll = () => {
|
|
3922
|
+
const hideAll = (resetIndex = false) => {
|
|
3835
3923
|
if (crosshair) crosshair.style.display = "none";
|
|
3836
3924
|
for (const dot of dots) dot.style.display = "none";
|
|
3837
3925
|
tooltipManager.hide();
|
|
3926
|
+
currentEmphasis = null;
|
|
3927
|
+
if (resetIndex) currentIndex = -1;
|
|
3928
|
+
emphasis?.clear();
|
|
3838
3929
|
};
|
|
3930
|
+
const hideOnPointerOut = () => hideAll();
|
|
3931
|
+
function stepTo(index) {
|
|
3932
|
+
if (snapXs.length === 0) return;
|
|
3933
|
+
const wrapped = (index % snapXs.length + snapXs.length) % snapXs.length;
|
|
3934
|
+
currentIndex = wrapped;
|
|
3935
|
+
const snappedX = snapXs[wrapped];
|
|
3936
|
+
const keys = keysAt(snappedX);
|
|
3937
|
+
const keep = currentEmphasis && keys.includes(currentEmphasis) ? currentEmphasis : null;
|
|
3938
|
+
showAt(snappedX, keep, containerScale());
|
|
3939
|
+
}
|
|
3839
3940
|
const handleTouch = (e) => {
|
|
3840
3941
|
const te = e;
|
|
3841
3942
|
if (te.touches.length === 0) return;
|
|
@@ -3845,16 +3946,43 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3845
3946
|
positionAt(svgX, svgY, viewBoxToContainer);
|
|
3846
3947
|
};
|
|
3847
3948
|
overlay.addEventListener("mousemove", handleMouseMove);
|
|
3848
|
-
overlay.addEventListener("mouseleave",
|
|
3949
|
+
overlay.addEventListener("mouseleave", hideOnPointerOut);
|
|
3849
3950
|
overlay.addEventListener("touchstart", handleTouch, { passive: false });
|
|
3850
3951
|
overlay.addEventListener("touchmove", handleTouch, { passive: false });
|
|
3851
|
-
overlay.addEventListener("touchend",
|
|
3852
|
-
return
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3952
|
+
overlay.addEventListener("touchend", hideOnPointerOut);
|
|
3953
|
+
return {
|
|
3954
|
+
cleanup() {
|
|
3955
|
+
overlay.removeEventListener("mousemove", handleMouseMove);
|
|
3956
|
+
overlay.removeEventListener("mouseleave", hideOnPointerOut);
|
|
3957
|
+
overlay.removeEventListener("touchstart", handleTouch);
|
|
3958
|
+
overlay.removeEventListener("touchmove", handleTouch);
|
|
3959
|
+
overlay.removeEventListener("touchend", hideOnPointerOut);
|
|
3960
|
+
},
|
|
3961
|
+
get snapCount() {
|
|
3962
|
+
return snapXs.length;
|
|
3963
|
+
},
|
|
3964
|
+
get currentIndex() {
|
|
3965
|
+
return currentIndex;
|
|
3966
|
+
},
|
|
3967
|
+
stepTo,
|
|
3968
|
+
cycleSeries(delta) {
|
|
3969
|
+
if (currentIndex < 0) {
|
|
3970
|
+
stepTo(0);
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3973
|
+
const snappedX = snapXs[currentIndex];
|
|
3974
|
+
const keys = keysAt(snappedX);
|
|
3975
|
+
if (keys.length === 0) return;
|
|
3976
|
+
const at = currentEmphasis ? keys.indexOf(currentEmphasis) : -1;
|
|
3977
|
+
const next = ((at + delta) % keys.length + keys.length) % keys.length;
|
|
3978
|
+
showAt(snappedX, keys[next], containerScale());
|
|
3979
|
+
},
|
|
3980
|
+
showCurrent() {
|
|
3981
|
+
stepTo(currentIndex < 0 ? 0 : currentIndex);
|
|
3982
|
+
},
|
|
3983
|
+
hide(resetIndex) {
|
|
3984
|
+
hideAll(resetIndex === true);
|
|
3985
|
+
}
|
|
3858
3986
|
};
|
|
3859
3987
|
}
|
|
3860
3988
|
|
|
@@ -4519,11 +4647,175 @@ function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
|
4519
4647
|
};
|
|
4520
4648
|
}
|
|
4521
4649
|
|
|
4650
|
+
// src/interactions/hover-emphasis.ts
|
|
4651
|
+
var MARKS_ACTIVE_CLASS = "oc-hover-active";
|
|
4652
|
+
var MARK_HOVER_CLASS = "oc-mark--hover";
|
|
4653
|
+
var MARK_DIM_CLASS = "oc-mark--dim";
|
|
4654
|
+
var LEGEND_HOVER_CLASS = "oc-legend-entry--hover";
|
|
4655
|
+
var LEGEND_DIM_CLASS = "oc-legend-entry--dim";
|
|
4656
|
+
var NOOP = {
|
|
4657
|
+
setSeries() {
|
|
4658
|
+
},
|
|
4659
|
+
setMark() {
|
|
4660
|
+
},
|
|
4661
|
+
clear() {
|
|
4662
|
+
},
|
|
4663
|
+
destroy() {
|
|
4664
|
+
},
|
|
4665
|
+
multiSeries: false
|
|
4666
|
+
};
|
|
4667
|
+
function countMarkSeries(layout) {
|
|
4668
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4669
|
+
for (const mark of layout.marks) {
|
|
4670
|
+
const key = "seriesKey" in mark ? mark.seriesKey : void 0;
|
|
4671
|
+
if (key) seen.add(key);
|
|
4672
|
+
}
|
|
4673
|
+
return seen.size;
|
|
4674
|
+
}
|
|
4675
|
+
function createHoverEmphasis(svg, layout) {
|
|
4676
|
+
if (layout.markRenderMode === "canvas") return NOOP;
|
|
4677
|
+
const marksGroup = svg.querySelector(".oc-marks");
|
|
4678
|
+
if (!marksGroup) return NOOP;
|
|
4679
|
+
const multiSeries = countMarkSeries(layout) > 1;
|
|
4680
|
+
let markEls = null;
|
|
4681
|
+
let legendEls = null;
|
|
4682
|
+
function marks() {
|
|
4683
|
+
if (!markEls) markEls = Array.from(marksGroup.querySelectorAll(".oc-mark"));
|
|
4684
|
+
return markEls;
|
|
4685
|
+
}
|
|
4686
|
+
function legendEntries() {
|
|
4687
|
+
if (!legendEls) legendEls = Array.from(svg.querySelectorAll("[data-legend-label]"));
|
|
4688
|
+
return legendEls;
|
|
4689
|
+
}
|
|
4690
|
+
function clear() {
|
|
4691
|
+
marksGroup.classList.remove(MARKS_ACTIVE_CLASS);
|
|
4692
|
+
for (const el of marks()) {
|
|
4693
|
+
el.classList.remove(MARK_HOVER_CLASS, MARK_DIM_CLASS);
|
|
4694
|
+
}
|
|
4695
|
+
for (const el of legendEntries()) {
|
|
4696
|
+
el.classList.remove(LEGEND_HOVER_CLASS, LEGEND_DIM_CLASS);
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
function applyLegend(seriesKey) {
|
|
4700
|
+
for (const el of legendEntries()) {
|
|
4701
|
+
if (el.getAttribute("data-legend-active") === "false") continue;
|
|
4702
|
+
const match = el.getAttribute("data-legend-label") === seriesKey;
|
|
4703
|
+
el.classList.toggle(LEGEND_HOVER_CLASS, match);
|
|
4704
|
+
el.classList.toggle(LEGEND_DIM_CLASS, !match);
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
function setSeries(seriesKey) {
|
|
4708
|
+
if (seriesKey === null) {
|
|
4709
|
+
clear();
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
marksGroup.classList.add(MARKS_ACTIVE_CLASS);
|
|
4713
|
+
for (const el of marks()) {
|
|
4714
|
+
const match = el.getAttribute("data-series") === seriesKey;
|
|
4715
|
+
el.classList.toggle(MARK_HOVER_CLASS, match);
|
|
4716
|
+
el.classList.toggle(MARK_DIM_CLASS, !match);
|
|
4717
|
+
}
|
|
4718
|
+
applyLegend(seriesKey);
|
|
4719
|
+
}
|
|
4720
|
+
function setMark(el) {
|
|
4721
|
+
if (!el) {
|
|
4722
|
+
clear();
|
|
4723
|
+
return;
|
|
4724
|
+
}
|
|
4725
|
+
const series = el.getAttribute("data-series");
|
|
4726
|
+
if (multiSeries && series) {
|
|
4727
|
+
setSeries(series);
|
|
4728
|
+
return;
|
|
4729
|
+
}
|
|
4730
|
+
marksGroup.classList.add(MARKS_ACTIVE_CLASS);
|
|
4731
|
+
for (const mark of marks()) {
|
|
4732
|
+
mark.classList.toggle(MARK_HOVER_CLASS, mark === el);
|
|
4733
|
+
mark.classList.remove(MARK_DIM_CLASS);
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
return {
|
|
4737
|
+
setSeries,
|
|
4738
|
+
setMark,
|
|
4739
|
+
clear,
|
|
4740
|
+
destroy() {
|
|
4741
|
+
clear();
|
|
4742
|
+
markEls = null;
|
|
4743
|
+
legendEls = null;
|
|
4744
|
+
},
|
|
4745
|
+
multiSeries
|
|
4746
|
+
};
|
|
4747
|
+
}
|
|
4748
|
+
function wireEndpointLabelHover(svg, emphasis) {
|
|
4749
|
+
const entries = svg.querySelectorAll("[data-endpoint-key]");
|
|
4750
|
+
const cleanups = [];
|
|
4751
|
+
for (const entry of entries) {
|
|
4752
|
+
const key = entry.getAttribute("data-endpoint-key");
|
|
4753
|
+
if (!key) continue;
|
|
4754
|
+
const handleEnter = () => emphasis.setSeries(key);
|
|
4755
|
+
const handleLeave = () => emphasis.clear();
|
|
4756
|
+
entry.addEventListener("mouseenter", handleEnter);
|
|
4757
|
+
entry.addEventListener("mouseleave", handleLeave);
|
|
4758
|
+
cleanups.push(() => {
|
|
4759
|
+
entry.removeEventListener("mouseenter", handleEnter);
|
|
4760
|
+
entry.removeEventListener("mouseleave", handleLeave);
|
|
4761
|
+
});
|
|
4762
|
+
}
|
|
4763
|
+
return () => {
|
|
4764
|
+
for (const cleanup of cleanups) cleanup();
|
|
4765
|
+
};
|
|
4766
|
+
}
|
|
4767
|
+
|
|
4522
4768
|
// src/interactions/keyboard-nav.ts
|
|
4523
|
-
function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, layout) {
|
|
4769
|
+
function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, layout, crosshair) {
|
|
4524
4770
|
container.setAttribute("tabindex", "0");
|
|
4525
4771
|
container.setAttribute("aria-roledescription", "chart");
|
|
4526
4772
|
container.setAttribute("aria-label", layout.a11y.altText);
|
|
4773
|
+
if (tooltipDescriptors.size === 0 && crosshair) {
|
|
4774
|
+
const handleCrosshairKeys = (e) => {
|
|
4775
|
+
const step = crosshair.currentIndex;
|
|
4776
|
+
switch (e.key) {
|
|
4777
|
+
case "ArrowRight":
|
|
4778
|
+
e.preventDefault();
|
|
4779
|
+
crosshair.stepTo(step + 1);
|
|
4780
|
+
break;
|
|
4781
|
+
case "ArrowLeft":
|
|
4782
|
+
e.preventDefault();
|
|
4783
|
+
crosshair.stepTo(step <= 0 ? crosshair.snapCount - 1 : step - 1);
|
|
4784
|
+
break;
|
|
4785
|
+
case "ArrowDown":
|
|
4786
|
+
e.preventDefault();
|
|
4787
|
+
if (step < 0) crosshair.stepTo(0);
|
|
4788
|
+
crosshair.cycleSeries(1);
|
|
4789
|
+
break;
|
|
4790
|
+
case "ArrowUp":
|
|
4791
|
+
e.preventDefault();
|
|
4792
|
+
if (step < 0) crosshair.stepTo(0);
|
|
4793
|
+
crosshair.cycleSeries(-1);
|
|
4794
|
+
break;
|
|
4795
|
+
case "Enter":
|
|
4796
|
+
case " ":
|
|
4797
|
+
e.preventDefault();
|
|
4798
|
+
crosshair.showCurrent();
|
|
4799
|
+
break;
|
|
4800
|
+
case "Escape":
|
|
4801
|
+
e.preventDefault();
|
|
4802
|
+
crosshair.hide(true);
|
|
4803
|
+
break;
|
|
4804
|
+
}
|
|
4805
|
+
};
|
|
4806
|
+
const handleCrosshairBlur = () => {
|
|
4807
|
+
crosshair.hide(true);
|
|
4808
|
+
};
|
|
4809
|
+
container.addEventListener("keydown", handleCrosshairKeys);
|
|
4810
|
+
container.addEventListener("blur", handleCrosshairBlur);
|
|
4811
|
+
return () => {
|
|
4812
|
+
container.removeEventListener("keydown", handleCrosshairKeys);
|
|
4813
|
+
container.removeEventListener("blur", handleCrosshairBlur);
|
|
4814
|
+
container.removeAttribute("tabindex");
|
|
4815
|
+
container.removeAttribute("aria-roledescription");
|
|
4816
|
+
container.removeAttribute("aria-label");
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4527
4819
|
const markElements = [];
|
|
4528
4820
|
const allMarkEls = svg.querySelectorAll("[data-mark-id]");
|
|
4529
4821
|
for (const el of allMarkEls) {
|
|
@@ -4596,9 +4888,15 @@ function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, lay
|
|
|
4596
4888
|
}
|
|
4597
4889
|
}
|
|
4598
4890
|
};
|
|
4891
|
+
const handleBlur = () => {
|
|
4892
|
+
tooltipManager.hide();
|
|
4893
|
+
highlightMark(-1);
|
|
4894
|
+
};
|
|
4599
4895
|
container.addEventListener("keydown", handleKeyDown);
|
|
4896
|
+
container.addEventListener("blur", handleBlur);
|
|
4600
4897
|
return () => {
|
|
4601
4898
|
container.removeEventListener("keydown", handleKeyDown);
|
|
4899
|
+
container.removeEventListener("blur", handleBlur);
|
|
4602
4900
|
container.removeAttribute("tabindex");
|
|
4603
4901
|
container.removeAttribute("aria-roledescription");
|
|
4604
4902
|
container.removeAttribute("aria-label");
|
|
@@ -4606,22 +4904,47 @@ function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, lay
|
|
|
4606
4904
|
}
|
|
4607
4905
|
|
|
4608
4906
|
// src/interactions/legend-interaction.ts
|
|
4609
|
-
function wireLegendInteraction(svg, _layout, toggleSeries, onLegendToggle, onEdit) {
|
|
4907
|
+
function wireLegendInteraction(svg, _layout, toggleSeries, onLegendToggle, onEdit, emphasis) {
|
|
4610
4908
|
const legendEntries = svg.querySelectorAll(
|
|
4611
4909
|
".oc-legend:not(.oc-legend--size) [data-legend-index]"
|
|
4612
4910
|
);
|
|
4613
4911
|
const cleanups = [];
|
|
4614
4912
|
for (const entry of legendEntries) {
|
|
4615
4913
|
if (entry.getAttribute("data-legend-overflow") === "true") continue;
|
|
4616
|
-
const
|
|
4914
|
+
const toggle = () => {
|
|
4617
4915
|
const label = entry.getAttribute("data-legend-label");
|
|
4618
4916
|
if (!label) return;
|
|
4619
4917
|
const nowHidden = toggleSeries(label);
|
|
4620
4918
|
onLegendToggle?.(label, !nowHidden);
|
|
4621
4919
|
onEdit?.({ type: "legend-toggle", series: label, hidden: nowHidden });
|
|
4622
4920
|
};
|
|
4921
|
+
const handleClick = () => toggle();
|
|
4922
|
+
const handleKeyDown = (e) => {
|
|
4923
|
+
const ke = e;
|
|
4924
|
+
if (ke.key !== "Enter" && ke.key !== " ") return;
|
|
4925
|
+
ke.preventDefault();
|
|
4926
|
+
toggle();
|
|
4927
|
+
};
|
|
4928
|
+
const handleEnter = () => {
|
|
4929
|
+
const label = entry.getAttribute("data-legend-label");
|
|
4930
|
+
if (!label || entry.getAttribute("data-legend-active") === "false") return;
|
|
4931
|
+
emphasis?.setSeries(label);
|
|
4932
|
+
};
|
|
4933
|
+
const handleLeave = () => emphasis?.clear();
|
|
4623
4934
|
entry.addEventListener("click", handleClick);
|
|
4624
|
-
|
|
4935
|
+
entry.addEventListener("keydown", handleKeyDown);
|
|
4936
|
+
entry.addEventListener("mouseenter", handleEnter);
|
|
4937
|
+
entry.addEventListener("mouseleave", handleLeave);
|
|
4938
|
+
entry.addEventListener("focus", handleEnter);
|
|
4939
|
+
entry.addEventListener("blur", handleLeave);
|
|
4940
|
+
cleanups.push(() => {
|
|
4941
|
+
entry.removeEventListener("click", handleClick);
|
|
4942
|
+
entry.removeEventListener("keydown", handleKeyDown);
|
|
4943
|
+
entry.removeEventListener("mouseenter", handleEnter);
|
|
4944
|
+
entry.removeEventListener("mouseleave", handleLeave);
|
|
4945
|
+
entry.removeEventListener("focus", handleEnter);
|
|
4946
|
+
entry.removeEventListener("blur", handleLeave);
|
|
4947
|
+
});
|
|
4625
4948
|
}
|
|
4626
4949
|
return () => {
|
|
4627
4950
|
for (const cleanup of cleanups) {
|
|
@@ -4850,7 +5173,7 @@ function createScreenReaderTable(layout, container) {
|
|
|
4850
5173
|
}
|
|
4851
5174
|
|
|
4852
5175
|
// src/interactions/tooltip-events.ts
|
|
4853
|
-
function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
5176
|
+
function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager, emphasis) {
|
|
4854
5177
|
const markElements = svg.querySelectorAll("[data-mark-id]");
|
|
4855
5178
|
const cleanups = [];
|
|
4856
5179
|
for (const el of markElements) {
|
|
@@ -4864,6 +5187,7 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4864
5187
|
const x = mouseEvent.clientX - svgRect.left;
|
|
4865
5188
|
const y = mouseEvent.clientY - svgRect.top;
|
|
4866
5189
|
tooltipManager.show(content, x, y);
|
|
5190
|
+
emphasis?.setMark(el);
|
|
4867
5191
|
};
|
|
4868
5192
|
const handleMouseMove = (e) => {
|
|
4869
5193
|
const mouseEvent = e;
|
|
@@ -4874,6 +5198,7 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4874
5198
|
};
|
|
4875
5199
|
const handleMouseLeave = () => {
|
|
4876
5200
|
tooltipManager.hide();
|
|
5201
|
+
emphasis?.clear();
|
|
4877
5202
|
};
|
|
4878
5203
|
const handleTouchStart = (e) => {
|
|
4879
5204
|
const touchEvent = e;
|
|
@@ -4883,17 +5208,23 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4883
5208
|
const x = touch.clientX - svgRect.left;
|
|
4884
5209
|
const y = touch.clientY - svgRect.top;
|
|
4885
5210
|
tooltipManager.show(content, x, y);
|
|
5211
|
+
emphasis?.setMark(el);
|
|
4886
5212
|
}
|
|
4887
5213
|
};
|
|
5214
|
+
const handleTouchEnd = () => {
|
|
5215
|
+
emphasis?.clear();
|
|
5216
|
+
};
|
|
4888
5217
|
el.addEventListener("mouseenter", handleMouseEnter);
|
|
4889
5218
|
el.addEventListener("mousemove", handleMouseMove);
|
|
4890
5219
|
el.addEventListener("mouseleave", handleMouseLeave);
|
|
4891
5220
|
el.addEventListener("touchstart", handleTouchStart);
|
|
5221
|
+
el.addEventListener("touchend", handleTouchEnd);
|
|
4892
5222
|
cleanups.push(() => {
|
|
4893
5223
|
el.removeEventListener("mouseenter", handleMouseEnter);
|
|
4894
5224
|
el.removeEventListener("mousemove", handleMouseMove);
|
|
4895
5225
|
el.removeEventListener("mouseleave", handleMouseLeave);
|
|
4896
5226
|
el.removeEventListener("touchstart", handleTouchStart);
|
|
5227
|
+
el.removeEventListener("touchend", handleTouchEnd);
|
|
4897
5228
|
});
|
|
4898
5229
|
}
|
|
4899
5230
|
return () => {
|
|
@@ -5274,7 +5605,7 @@ function createTween(opts) {
|
|
|
5274
5605
|
}
|
|
5275
5606
|
|
|
5276
5607
|
// src/scatter-canvas/entrance.ts
|
|
5277
|
-
var MAX_TOTAL_STAGGER_MS =
|
|
5608
|
+
var MAX_TOTAL_STAGGER_MS = 300;
|
|
5278
5609
|
var POINT_DURATION_FRACTION = 0.4;
|
|
5279
5610
|
function clampStagger(delay, elementCount) {
|
|
5280
5611
|
if (elementCount <= 1) return 0;
|
|
@@ -5709,46 +6040,11 @@ function createSeriesSearch(options) {
|
|
|
5709
6040
|
};
|
|
5710
6041
|
}
|
|
5711
6042
|
|
|
5712
|
-
// src/theme-tokens.ts
|
|
5713
|
-
import { adaptForLightLineStroke, cssTokenDefault } from "@opendata-ai/openchart-core";
|
|
5714
|
-
function stampThemeProperties(el, theme) {
|
|
5715
|
-
const accent = theme.colors.categorical[0] ?? cssTokenDefault("--oc-accent", "light");
|
|
5716
|
-
const bg = theme.colors.background === "transparent" ? theme.isDark ? cssTokenDefault("--oc-bg", "dark") : cssTokenDefault("--oc-bg", "light") : theme.colors.background;
|
|
5717
|
-
const props = [
|
|
5718
|
-
["--oc-font-family", theme.fonts.family],
|
|
5719
|
-
["--oc-font-mono", theme.fonts.mono],
|
|
5720
|
-
["--oc-title-size", `${theme.chrome.title.fontSize}px`],
|
|
5721
|
-
["--oc-title-weight", `${theme.chrome.title.fontWeight}`],
|
|
5722
|
-
["--oc-subtitle-size", `${theme.chrome.subtitle.fontSize}px`],
|
|
5723
|
-
["--oc-subtitle-weight", `${theme.chrome.subtitle.fontWeight}`],
|
|
5724
|
-
["--oc-source-size", `${theme.chrome.source.fontSize}px`],
|
|
5725
|
-
["--oc-source-weight", `${theme.chrome.source.fontWeight}`],
|
|
5726
|
-
["--oc-body-size", `${theme.fonts.sizes.body}px`],
|
|
5727
|
-
["--oc-eyebrow-size", `${theme.chrome.eyebrow.fontSize}px`],
|
|
5728
|
-
["--oc-eyebrow-weight", `${theme.chrome.eyebrow.fontWeight}`],
|
|
5729
|
-
["--oc-bg", bg],
|
|
5730
|
-
["--oc-text", theme.colors.text],
|
|
5731
|
-
["--oc-text-muted", theme.colors.axis],
|
|
5732
|
-
["--oc-gridline", theme.colors.gridline],
|
|
5733
|
-
["--oc-axis", theme.colors.axis],
|
|
5734
|
-
["--oc-border-radius", `${theme.borderRadius}px`],
|
|
5735
|
-
["--oc-accent", accent],
|
|
5736
|
-
["--oc-accent-strong", adaptForLightLineStroke(accent)],
|
|
5737
|
-
["--oc-positive", theme.colors.positive],
|
|
5738
|
-
["--oc-negative", theme.colors.negative],
|
|
5739
|
-
["--oc-space-2", `${theme.spacing.chromeGap * 2}px`],
|
|
5740
|
-
["--oc-space-4", `${theme.spacing.padding}px`]
|
|
5741
|
-
];
|
|
5742
|
-
for (const [name, value] of props) {
|
|
5743
|
-
el.style.setProperty(name, value);
|
|
5744
|
-
}
|
|
5745
|
-
}
|
|
5746
|
-
|
|
5747
6043
|
// src/mount.ts
|
|
5748
6044
|
var FALLBACK_HEIGHT = 400;
|
|
5749
6045
|
var EDITABLE_HOVER_CSS = `
|
|
5750
6046
|
.oc-editable-hover {
|
|
5751
|
-
outline: 1.5px solid ${
|
|
6047
|
+
outline: 1.5px solid ${cssTokenDefault("--oc-editable-hover", "light")};
|
|
5752
6048
|
outline-offset: 2px;
|
|
5753
6049
|
border-radius: 2px;
|
|
5754
6050
|
}
|
|
@@ -5787,6 +6083,9 @@ function createChart(container, spec, options) {
|
|
|
5787
6083
|
let disconnectResize = null;
|
|
5788
6084
|
let cleanupTooltipEvents = null;
|
|
5789
6085
|
let cleanupVoronoiEvents = null;
|
|
6086
|
+
let cleanupEndpointHover = null;
|
|
6087
|
+
let hoverEmphasis = null;
|
|
6088
|
+
let crosshairController = null;
|
|
5790
6089
|
let canvasLayer = null;
|
|
5791
6090
|
let cleanupCanvasEvents = null;
|
|
5792
6091
|
let canvasEntrance = null;
|
|
@@ -6228,6 +6527,11 @@ function createChart(container, spec, options) {
|
|
|
6228
6527
|
cleanupTooltipEvents = null;
|
|
6229
6528
|
cleanupVoronoiEvents?.();
|
|
6230
6529
|
cleanupVoronoiEvents = null;
|
|
6530
|
+
crosshairController = null;
|
|
6531
|
+
cleanupEndpointHover?.();
|
|
6532
|
+
cleanupEndpointHover = null;
|
|
6533
|
+
hoverEmphasis?.destroy();
|
|
6534
|
+
hoverEmphasis = null;
|
|
6231
6535
|
cleanupCanvasEvents?.();
|
|
6232
6536
|
cleanupCanvasEvents = null;
|
|
6233
6537
|
canvasLayer?.destroy();
|
|
@@ -6293,19 +6597,30 @@ function createChart(container, spec, options) {
|
|
|
6293
6597
|
options
|
|
6294
6598
|
});
|
|
6295
6599
|
}
|
|
6600
|
+
hoverEmphasis = createHoverEmphasis(svgElement, currentLayout);
|
|
6296
6601
|
cleanupTooltipEvents = wireTooltipEvents(
|
|
6297
6602
|
svgElement,
|
|
6298
6603
|
currentLayout.tooltipDescriptors,
|
|
6299
|
-
tooltipManager
|
|
6604
|
+
tooltipManager,
|
|
6605
|
+
hoverEmphasis
|
|
6300
6606
|
);
|
|
6301
|
-
|
|
6607
|
+
const crosshairCtl = wireVoronoiTooltipEvents(
|
|
6608
|
+
svgElement,
|
|
6609
|
+
currentLayout,
|
|
6610
|
+
tooltipManager,
|
|
6611
|
+
hoverEmphasis
|
|
6612
|
+
);
|
|
6613
|
+
crosshairController = crosshairCtl;
|
|
6614
|
+
cleanupVoronoiEvents = crosshairCtl ? () => crosshairCtl.cleanup() : null;
|
|
6615
|
+
cleanupEndpointHover = wireEndpointLabelHover(svgElement, hoverEmphasis);
|
|
6302
6616
|
if (!currentLayout.a11y.hidden) {
|
|
6303
6617
|
cleanupKeyboardNav = wireKeyboardNav(
|
|
6304
6618
|
svgElement,
|
|
6305
6619
|
container,
|
|
6306
6620
|
currentLayout.tooltipDescriptors,
|
|
6307
6621
|
tooltipManager,
|
|
6308
|
-
currentLayout
|
|
6622
|
+
currentLayout,
|
|
6623
|
+
crosshairController
|
|
6309
6624
|
);
|
|
6310
6625
|
}
|
|
6311
6626
|
const editSuppressed = wantsSeriesSearch(currentSpec);
|
|
@@ -6320,7 +6635,8 @@ function createChart(container, spec, options) {
|
|
|
6320
6635
|
currentLayout,
|
|
6321
6636
|
toggleSeriesVisibility,
|
|
6322
6637
|
options?.onLegendToggle,
|
|
6323
|
-
editSuppressed ? void 0 : options?.onEdit
|
|
6638
|
+
editSuppressed ? void 0 : options?.onEdit,
|
|
6639
|
+
hoverEmphasis
|
|
6324
6640
|
);
|
|
6325
6641
|
if (options?.onMarkClick || options?.onMarkHover || options?.onMarkLeave || options?.onAnnotationClick) {
|
|
6326
6642
|
const specAnnotations = "annotations" in currentSpec && Array.isArray(currentSpec.annotations) ? currentSpec.annotations : [];
|
|
@@ -6616,6 +6932,11 @@ function createChart(container, spec, options) {
|
|
|
6616
6932
|
cleanupTooltipEvents = null;
|
|
6617
6933
|
cleanupVoronoiEvents?.();
|
|
6618
6934
|
cleanupVoronoiEvents = null;
|
|
6935
|
+
crosshairController = null;
|
|
6936
|
+
cleanupEndpointHover?.();
|
|
6937
|
+
cleanupEndpointHover = null;
|
|
6938
|
+
hoverEmphasis?.destroy();
|
|
6939
|
+
hoverEmphasis = null;
|
|
6619
6940
|
cleanupCanvasEvents?.();
|
|
6620
6941
|
cleanupCanvasEvents = null;
|
|
6621
6942
|
canvasEntrance?.cancel();
|
|
@@ -6915,6 +7236,24 @@ var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
|
6915
7236
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
6916
7237
|
var BRAND_URL = "https://tryopendata.ai";
|
|
6917
7238
|
var BULK_ANIMATION_THRESHOLD = 200;
|
|
7239
|
+
var LEGEND_BACKDROP_ALPHA = 0.9;
|
|
7240
|
+
function legendBackdrop(theme) {
|
|
7241
|
+
const surface = resolvedSurface(theme).trim();
|
|
7242
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(surface);
|
|
7243
|
+
if (hex) {
|
|
7244
|
+
const h = hex[1];
|
|
7245
|
+
const full = h.length === 3 ? h.replace(/./g, (c) => c + c) : h;
|
|
7246
|
+
const r = Number.parseInt(full.slice(0, 2), 16);
|
|
7247
|
+
const g = Number.parseInt(full.slice(2, 4), 16);
|
|
7248
|
+
const b = Number.parseInt(full.slice(4, 6), 16);
|
|
7249
|
+
return `rgba(${r},${g},${b},${LEGEND_BACKDROP_ALPHA})`;
|
|
7250
|
+
}
|
|
7251
|
+
const rgb = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i.exec(surface);
|
|
7252
|
+
if (rgb) {
|
|
7253
|
+
return `rgba(${rgb[1]},${rgb[2]},${rgb[3]},${LEGEND_BACKDROP_ALPHA})`;
|
|
7254
|
+
}
|
|
7255
|
+
return surface;
|
|
7256
|
+
}
|
|
6918
7257
|
function createSVGElement2(tag) {
|
|
6919
7258
|
return document.createElementNS(SVG_NS3, tag);
|
|
6920
7259
|
}
|
|
@@ -7001,7 +7340,7 @@ function renderBorders(parent, borders) {
|
|
|
7001
7340
|
d: borders.interiorPath,
|
|
7002
7341
|
fill: "none",
|
|
7003
7342
|
stroke: borders.interiorStroke,
|
|
7004
|
-
"stroke-width":
|
|
7343
|
+
"stroke-width": borders.interiorWidth,
|
|
7005
7344
|
"stroke-linejoin": "round"
|
|
7006
7345
|
});
|
|
7007
7346
|
interior.setAttribute("class", "oc-map-border-interior");
|
|
@@ -7014,7 +7353,7 @@ function renderBorders(parent, borders) {
|
|
|
7014
7353
|
d: borders.outlinePath,
|
|
7015
7354
|
fill: "none",
|
|
7016
7355
|
stroke: borders.outlineStroke,
|
|
7017
|
-
"stroke-width":
|
|
7356
|
+
"stroke-width": borders.outlineWidth,
|
|
7018
7357
|
"stroke-linejoin": "round"
|
|
7019
7358
|
});
|
|
7020
7359
|
outline.setAttribute("class", "oc-map-border-outline");
|
|
@@ -7023,7 +7362,7 @@ function renderBorders(parent, borders) {
|
|
|
7023
7362
|
}
|
|
7024
7363
|
parent.appendChild(g);
|
|
7025
7364
|
}
|
|
7026
|
-
function renderFeatures(parent, features, animation, staggerBudget = 0, focusIds) {
|
|
7365
|
+
function renderFeatures(parent, features, animation, staggerBudget = 0, focusIds, patternMap) {
|
|
7027
7366
|
const g = createSVGElement2("g");
|
|
7028
7367
|
g.setAttribute("class", "oc-map-features");
|
|
7029
7368
|
g.setAttribute("role", "list");
|
|
@@ -7052,14 +7391,21 @@ function renderFeatures(parent, features, animation, staggerBudget = 0, focusIds
|
|
|
7052
7391
|
const path = createSVGElement2("path");
|
|
7053
7392
|
path.setAttribute("class", "oc-map-feature");
|
|
7054
7393
|
path.setAttribute("role", "listitem");
|
|
7394
|
+
const fill = feature.pattern && patternMap ? resolvePatternFill(feature.pattern, patternMap) : feature.fill;
|
|
7055
7395
|
setAttrs2(path, {
|
|
7056
7396
|
d: feature.path,
|
|
7057
|
-
fill
|
|
7397
|
+
fill,
|
|
7058
7398
|
stroke: feature.stroke,
|
|
7059
7399
|
"stroke-width": feature.strokeWidth
|
|
7060
7400
|
});
|
|
7061
7401
|
path.setAttribute("data-feature-id", String(feature.id));
|
|
7402
|
+
if (feature.noData) {
|
|
7403
|
+
path.setAttribute("data-nodata", "true");
|
|
7404
|
+
}
|
|
7062
7405
|
path.setAttribute("data-key", String(feature.id));
|
|
7406
|
+
if (feature.binIndex !== void 0) {
|
|
7407
|
+
path.setAttribute("data-bin-index", String(feature.binIndex));
|
|
7408
|
+
}
|
|
7063
7409
|
if (feature.name) {
|
|
7064
7410
|
path.setAttribute("data-feature-name", feature.name);
|
|
7065
7411
|
}
|
|
@@ -7075,7 +7421,7 @@ function renderFeatures(parent, features, animation, staggerBudget = 0, focusIds
|
|
|
7075
7421
|
const idx = feature.animationIndex ?? i;
|
|
7076
7422
|
path.setAttribute("data-animation-index", String(idx));
|
|
7077
7423
|
s.setProperty("--oc-mark-index", String(idx));
|
|
7078
|
-
s.setProperty("--oc-feature-fill",
|
|
7424
|
+
s.setProperty("--oc-feature-fill", fill);
|
|
7079
7425
|
if (dimmed) {
|
|
7080
7426
|
s.setProperty("--oc-feature-target-opacity", String(FOCUS_DIM_OPACITY));
|
|
7081
7427
|
}
|
|
@@ -7148,6 +7494,8 @@ function renderMapSVG(layout, opts) {
|
|
|
7148
7494
|
}
|
|
7149
7495
|
const defs = createSVGElement2("defs");
|
|
7150
7496
|
svg.appendChild(defs);
|
|
7497
|
+
const noDataSwatch = layout.continuousLegend?.noData ?? layout.categoricalLegend?.noData;
|
|
7498
|
+
const patternMap = buildPatternDefs(noDataSwatch ? [...features, noDataSwatch] : features, defs);
|
|
7151
7499
|
const mapGroup = createSVGElement2("g");
|
|
7152
7500
|
mapGroup.setAttribute("class", "oc-map-group");
|
|
7153
7501
|
mapGroup.setAttribute("transform", `translate(${layout.area.x},${layout.area.y})`);
|
|
@@ -7160,7 +7508,8 @@ function renderMapSVG(layout, opts) {
|
|
|
7160
7508
|
features,
|
|
7161
7509
|
animate ? animation : void 0,
|
|
7162
7510
|
mapStaggerBudget,
|
|
7163
|
-
focusIdSet
|
|
7511
|
+
focusIdSet,
|
|
7512
|
+
patternMap
|
|
7164
7513
|
);
|
|
7165
7514
|
renderBorders(cameraGroup, borders);
|
|
7166
7515
|
if (animate && animation?.enter && layout.pointMarks.length > 1) {
|
|
@@ -7173,13 +7522,26 @@ function renderMapSVG(layout, opts) {
|
|
|
7173
7522
|
svg.appendChild(mapGroup);
|
|
7174
7523
|
renderChrome(svg, layout);
|
|
7175
7524
|
if (layout.continuousLegend) {
|
|
7176
|
-
|
|
7525
|
+
const contNoData = layout.continuousLegend.noData;
|
|
7526
|
+
renderLegend(
|
|
7527
|
+
svg,
|
|
7528
|
+
contNoData?.pattern ? {
|
|
7529
|
+
...layout.continuousLegend,
|
|
7530
|
+
noData: { ...contNoData, fill: resolvePatternFill(contNoData.pattern, patternMap) }
|
|
7531
|
+
} : layout.continuousLegend
|
|
7532
|
+
);
|
|
7177
7533
|
} else if (layout.categoricalLegend) {
|
|
7178
|
-
|
|
7534
|
+
const catNoData = layout.categoricalLegend.noData;
|
|
7535
|
+
renderLegend(
|
|
7536
|
+
svg,
|
|
7537
|
+
catNoData?.pattern ? {
|
|
7538
|
+
...layout.categoricalLegend,
|
|
7539
|
+
noData: { ...catNoData, fill: resolvePatternFill(catNoData.pattern, patternMap) }
|
|
7540
|
+
} : layout.categoricalLegend
|
|
7541
|
+
);
|
|
7179
7542
|
}
|
|
7180
7543
|
const ptLegend = layout.pointCategoricalLegend ?? layout.pointContinuousLegend;
|
|
7181
7544
|
if (ptLegend) {
|
|
7182
|
-
const isDark = layout.theme.isDark;
|
|
7183
7545
|
const padX = 10;
|
|
7184
7546
|
const padY = 8;
|
|
7185
7547
|
const swatchSize = "swatchSize" in ptLegend ? ptLegend.swatchSize : 10;
|
|
@@ -7209,11 +7571,26 @@ function renderMapSVG(layout, opts) {
|
|
|
7209
7571
|
width: contentWidth + padX * 2,
|
|
7210
7572
|
height: bgHeight,
|
|
7211
7573
|
rx: 4,
|
|
7212
|
-
fill:
|
|
7574
|
+
fill: legendBackdrop(layout.theme)
|
|
7213
7575
|
});
|
|
7214
7576
|
svg.appendChild(bg);
|
|
7215
7577
|
renderLegend(svg, ptLegend);
|
|
7216
7578
|
}
|
|
7579
|
+
if (layout.pointSizeLegend) {
|
|
7580
|
+
const sizeLegend = layout.pointSizeLegend;
|
|
7581
|
+
const pad = 10;
|
|
7582
|
+
const bg = createSVGElement2("rect");
|
|
7583
|
+
setAttrs2(bg, {
|
|
7584
|
+
x: sizeLegend.bounds.x - pad,
|
|
7585
|
+
y: sizeLegend.bounds.y - pad,
|
|
7586
|
+
width: sizeLegend.bounds.width + pad * 2,
|
|
7587
|
+
height: sizeLegend.bounds.height + pad * 2,
|
|
7588
|
+
rx: 6,
|
|
7589
|
+
fill: legendBackdrop(layout.theme)
|
|
7590
|
+
});
|
|
7591
|
+
svg.appendChild(bg);
|
|
7592
|
+
renderLegend(svg, sizeLegend);
|
|
7593
|
+
}
|
|
7217
7594
|
if (watermark) {
|
|
7218
7595
|
renderWatermark(svg, layout);
|
|
7219
7596
|
}
|
|
@@ -7347,6 +7724,27 @@ function focusSignature(focus) {
|
|
|
7347
7724
|
const ids = focus.ids.map(String).sort().join(",");
|
|
7348
7725
|
return `${ids}|${Math.round(t.x)},${Math.round(t.y)},${Math.round(t.width)},${Math.round(t.height)},${t.padding}`;
|
|
7349
7726
|
}
|
|
7727
|
+
var featureOrigin = /* @__PURE__ */ new WeakMap();
|
|
7728
|
+
function raiseFeature(el, svg) {
|
|
7729
|
+
if (svg.classList.contains("oc-animate")) return;
|
|
7730
|
+
const parent = el.parentNode;
|
|
7731
|
+
if (!parent || el === parent.lastChild) return;
|
|
7732
|
+
if (!featureOrigin.has(el)) {
|
|
7733
|
+
featureOrigin.set(el, el.nextSibling);
|
|
7734
|
+
}
|
|
7735
|
+
parent.appendChild(el);
|
|
7736
|
+
}
|
|
7737
|
+
function restoreFeatureOrder(el) {
|
|
7738
|
+
const parent = el.parentNode;
|
|
7739
|
+
if (!parent || !featureOrigin.has(el)) return;
|
|
7740
|
+
const next = featureOrigin.get(el) ?? null;
|
|
7741
|
+
featureOrigin.delete(el);
|
|
7742
|
+
if (next && next.parentNode === parent) {
|
|
7743
|
+
parent.insertBefore(el, next);
|
|
7744
|
+
} else if (!next) {
|
|
7745
|
+
parent.appendChild(el);
|
|
7746
|
+
}
|
|
7747
|
+
}
|
|
7350
7748
|
function prefersReducedMotion2() {
|
|
7351
7749
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
7352
7750
|
try {
|
|
@@ -7419,7 +7817,8 @@ function createGeoMap(container, spec, options) {
|
|
|
7419
7817
|
const handleMouseEnter = (e) => {
|
|
7420
7818
|
const mouseEvent = e;
|
|
7421
7819
|
el.style.setProperty("cursor", "pointer");
|
|
7422
|
-
el
|
|
7820
|
+
raiseFeature(el, svg);
|
|
7821
|
+
el.classList.add("oc-map-feature--hover");
|
|
7423
7822
|
if (content && tooltipManager && options?.tooltip !== false) {
|
|
7424
7823
|
const svgRect = svg.getBoundingClientRect();
|
|
7425
7824
|
const x = mouseEvent.clientX - svgRect.left;
|
|
@@ -7446,7 +7845,8 @@ function createGeoMap(container, spec, options) {
|
|
|
7446
7845
|
};
|
|
7447
7846
|
const handleMouseLeave = () => {
|
|
7448
7847
|
el.style.removeProperty("cursor");
|
|
7449
|
-
el.
|
|
7848
|
+
el.classList.remove("oc-map-feature--hover");
|
|
7849
|
+
restoreFeatureOrder(el);
|
|
7450
7850
|
tooltipManager?.hide();
|
|
7451
7851
|
options?.onMarkHover?.(null);
|
|
7452
7852
|
};
|
|
@@ -7471,6 +7871,22 @@ function createGeoMap(container, spec, options) {
|
|
|
7471
7871
|
el.removeEventListener("click", handleClick);
|
|
7472
7872
|
});
|
|
7473
7873
|
}
|
|
7874
|
+
const featuresGroup = svg.querySelector(".oc-map-features");
|
|
7875
|
+
if (featuresGroup) {
|
|
7876
|
+
for (const bin of svg.querySelectorAll(".oc-legend-bin[data-bin-index]")) {
|
|
7877
|
+
const index = bin.getAttribute("data-bin-index");
|
|
7878
|
+
if (index === null) continue;
|
|
7879
|
+
const enter = () => featuresGroup.setAttribute("data-hover", index);
|
|
7880
|
+
const leave = () => featuresGroup.removeAttribute("data-hover");
|
|
7881
|
+
bin.addEventListener("mouseenter", enter);
|
|
7882
|
+
bin.addEventListener("mouseleave", leave);
|
|
7883
|
+
cleanups.push(() => {
|
|
7884
|
+
bin.removeEventListener("mouseenter", enter);
|
|
7885
|
+
bin.removeEventListener("mouseleave", leave);
|
|
7886
|
+
featuresGroup.removeAttribute("data-hover");
|
|
7887
|
+
});
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7474
7890
|
const pointMarksByKey = new Map(layout.pointMarks.map((p) => [p.key, p]));
|
|
7475
7891
|
const pointElements = svg.querySelectorAll(".oc-map-point");
|
|
7476
7892
|
for (const el of pointElements) {
|
|
@@ -7895,4 +8311,4 @@ export {
|
|
|
7895
8311
|
scrubCamera,
|
|
7896
8312
|
createGeoMap
|
|
7897
8313
|
};
|
|
7898
|
-
//# sourceMappingURL=chunk-
|
|
8314
|
+
//# sourceMappingURL=chunk-5A2NLYUW.js.map
|