@opendata-ai/openchart-vanilla 8.3.0 → 8.4.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/dist/{chunk-PEIX32S3.js → chunk-45XZOWYC.js} +497 -52
- package/dist/chunk-45XZOWYC.js.map +1 -0
- package/dist/{chunk-DCODCLWI.js → chunk-FETYAVTZ.js} +3 -3
- package/dist/{chunk-RM5XS6NR.js → chunk-HGRNXLDF.js} +16 -11
- package/dist/chunk-HGRNXLDF.js.map +1 -0
- package/dist/{chunk-737VNBU6.js → chunk-LNPRDSK3.js} +37 -5
- package/dist/chunk-LNPRDSK3.js.map +1 -0
- package/dist/{chunk-T32UHMZA.js → chunk-UOB422LY.js} +2 -2
- package/dist/{chunk-P32LUJFK.js → chunk-YGYMB7KB.js} +118 -16
- package/dist/chunk-YGYMB7KB.js.map +1 -0
- 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/simulation-worker.js +67 -67
- 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-737VNBU6.js.map +0 -1
- package/dist/chunk-P32LUJFK.js.map +0 -1
- package/dist/chunk-PEIX32S3.js.map +0 -1
- package/dist/chunk-RM5XS6NR.js.map +0 -1
- /package/dist/{chunk-DCODCLWI.js.map → chunk-FETYAVTZ.js.map} +0 -0
- /package/dist/{chunk-T32UHMZA.js.map → chunk-UOB422LY.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
exportPNG,
|
|
5
5
|
exportSVG,
|
|
6
6
|
exportSVGWithFonts
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UOB422LY.js";
|
|
8
8
|
import {
|
|
9
9
|
buildGradientDefs,
|
|
10
10
|
nextSvgId,
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
renderSingleMark,
|
|
16
16
|
resolveMarkFill,
|
|
17
17
|
stampAnimationVars
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YGYMB7KB.js";
|
|
19
19
|
import {
|
|
20
20
|
SVG_NS,
|
|
21
21
|
createSVGElement,
|
|
22
22
|
setAttrs
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-HGRNXLDF.js";
|
|
24
24
|
|
|
25
25
|
// src/resize-observer.ts
|
|
26
26
|
var DEBOUNCE_MS = 16;
|
|
@@ -89,11 +89,12 @@ function createTooltipManager(container) {
|
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
const content = input;
|
|
92
|
-
const
|
|
92
|
+
const emphasisIndex = content.fields.findIndex((f) => f.emphasis);
|
|
93
|
+
const contentKey = `${content.title}|${content.fields.length}|${content.fields[0]?.value}|${content.fields[content.fields.length - 1]?.value}|${emphasisIndex}`;
|
|
93
94
|
if (contentKey !== lastContentKey) {
|
|
94
95
|
lastContentKey = contentKey;
|
|
95
96
|
let html = "";
|
|
96
|
-
const colorRowCount = content.fields.filter((f) => f.color).length;
|
|
97
|
+
const colorRowCount = content.fields.filter((f) => f.color && f.role !== "total").length;
|
|
97
98
|
const perRowSwatches = colorRowCount > 1;
|
|
98
99
|
if (content.title) {
|
|
99
100
|
const titleColor = perRowSwatches ? void 0 : content.fields.find((f) => f.color)?.color;
|
|
@@ -107,7 +108,10 @@ function createTooltipManager(container) {
|
|
|
107
108
|
if (content.fields.length > 0) {
|
|
108
109
|
html += '<div class="oc-tooltip-body">';
|
|
109
110
|
for (const field of content.fields) {
|
|
110
|
-
|
|
111
|
+
let rowClass = "oc-tooltip-row";
|
|
112
|
+
if (field.emphasis) rowClass += " oc-tooltip-row--emphasis";
|
|
113
|
+
if (field.role === "total") rowClass += " oc-tooltip-row--total";
|
|
114
|
+
html += `<div class="${rowClass}">`;
|
|
111
115
|
html += '<span class="oc-tooltip-label">';
|
|
112
116
|
if (perRowSwatches && field.color) {
|
|
113
117
|
html += `<span class="oc-tooltip-row-swatch" style="background:${esc(field.color)}"></span>`;
|
|
@@ -2847,6 +2851,7 @@ function buildTextMarkTweens(prevLayout, nextLayout, marksContainer, dom, tweens
|
|
|
2847
2851
|
}
|
|
2848
2852
|
|
|
2849
2853
|
// src/transition/driver.ts
|
|
2854
|
+
var TRANSITIONING_CLASS = "oc-transitioning";
|
|
2850
2855
|
function runTransition(args) {
|
|
2851
2856
|
const { svg, prevLayout, nextLayout, animation, onComplete, fromSnapshot, manual, canvas } = args;
|
|
2852
2857
|
const update = animation.update;
|
|
@@ -2872,6 +2877,7 @@ function runTransition(args) {
|
|
|
2872
2877
|
totalMs: 0
|
|
2873
2878
|
};
|
|
2874
2879
|
}
|
|
2880
|
+
svg.classList.add(TRANSITIONING_CLASS);
|
|
2875
2881
|
const dom = buildMarkDomIndex(marksContainer);
|
|
2876
2882
|
const tweens = [];
|
|
2877
2883
|
const ghosts = [];
|
|
@@ -2979,6 +2985,7 @@ function runTransition(args) {
|
|
|
2979
2985
|
}
|
|
2980
2986
|
let rafId = null;
|
|
2981
2987
|
let running = true;
|
|
2988
|
+
let pulsedDot = null;
|
|
2982
2989
|
let startTime = null;
|
|
2983
2990
|
let lastElapsed = 0;
|
|
2984
2991
|
const nextLineMarks = nextLayout.marks.filter((m) => m.type === "line");
|
|
@@ -3010,9 +3017,11 @@ function runTransition(args) {
|
|
|
3010
3017
|
if (!running) return;
|
|
3011
3018
|
running = false;
|
|
3012
3019
|
rafId = null;
|
|
3020
|
+
svg.classList.remove(TRANSITIONING_CLASS);
|
|
3013
3021
|
snapToFinal();
|
|
3014
3022
|
removeGhosts();
|
|
3015
3023
|
settleCanvas();
|
|
3024
|
+
pulsedDot = pulseSparklineTerminator(svg, marksContainer);
|
|
3016
3025
|
for (let i = 0; i < secondaryEls.length; i++) {
|
|
3017
3026
|
secondaryEls[i].style.opacity = secondaryOriginalOpacity[i];
|
|
3018
3027
|
}
|
|
@@ -3127,8 +3136,12 @@ function runTransition(args) {
|
|
|
3127
3136
|
}
|
|
3128
3137
|
return {
|
|
3129
3138
|
cancel() {
|
|
3139
|
+
clearPulseTimer(pulsedDot);
|
|
3140
|
+
pulsedDot?.classList.remove(PULSE_CLASS);
|
|
3141
|
+
pulsedDot = null;
|
|
3130
3142
|
if (!running) return;
|
|
3131
3143
|
running = false;
|
|
3144
|
+
svg.classList.remove(TRANSITIONING_CLASS);
|
|
3132
3145
|
if (rafId !== null) {
|
|
3133
3146
|
cancelAnimationFrame(rafId);
|
|
3134
3147
|
rafId = null;
|
|
@@ -3164,6 +3177,37 @@ function runTransition(args) {
|
|
|
3164
3177
|
totalMs
|
|
3165
3178
|
};
|
|
3166
3179
|
}
|
|
3180
|
+
var PULSE_CLASS = "oc-pulse";
|
|
3181
|
+
var PULSE_DURATION_MS = 600;
|
|
3182
|
+
var pulseTimers = /* @__PURE__ */ new WeakMap();
|
|
3183
|
+
function clearPulseTimer(dot) {
|
|
3184
|
+
if (!dot) return;
|
|
3185
|
+
const pending = pulseTimers.get(dot);
|
|
3186
|
+
if (pending !== void 0) {
|
|
3187
|
+
clearTimeout(pending);
|
|
3188
|
+
pulseTimers.delete(dot);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
function pulseSparklineTerminator(svg, marksContainer) {
|
|
3192
|
+
if (!marksContainer || svg.getAttribute("data-display") !== "sparkline") return null;
|
|
3193
|
+
const dots = marksContainer.querySelectorAll("circle.oc-mark-point");
|
|
3194
|
+
const dot = dots[dots.length - 1];
|
|
3195
|
+
if (!dot) return null;
|
|
3196
|
+
const r = dot.getAttribute("r");
|
|
3197
|
+
if (r) dot.style.setProperty("--oc-pulse-r", `${r}px`);
|
|
3198
|
+
clearPulseTimer(dot);
|
|
3199
|
+
dot.classList.remove(PULSE_CLASS);
|
|
3200
|
+
void dot.getBoundingClientRect?.();
|
|
3201
|
+
dot.classList.add(PULSE_CLASS);
|
|
3202
|
+
pulseTimers.set(
|
|
3203
|
+
dot,
|
|
3204
|
+
setTimeout(() => {
|
|
3205
|
+
pulseTimers.delete(dot);
|
|
3206
|
+
dot.classList.remove(PULSE_CLASS);
|
|
3207
|
+
}, PULSE_DURATION_MS)
|
|
3208
|
+
);
|
|
3209
|
+
return dot;
|
|
3210
|
+
}
|
|
3167
3211
|
function collectSecondaryElements(svg) {
|
|
3168
3212
|
const els = [];
|
|
3169
3213
|
const epLabels = svg.querySelector(".oc-endpoint-labels");
|
|
@@ -3536,9 +3580,9 @@ function cancelAnimations(svg) {
|
|
|
3536
3580
|
}
|
|
3537
3581
|
function computeAnimationDuration(svg) {
|
|
3538
3582
|
const style = svg.style;
|
|
3539
|
-
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) ||
|
|
3583
|
+
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 450;
|
|
3540
3584
|
const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
|
|
3541
|
-
const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) ||
|
|
3585
|
+
const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) || 150;
|
|
3542
3586
|
const animatedElements = svg.querySelectorAll("[data-animation-index]").length;
|
|
3543
3587
|
const totalStagger = stagger * Math.max(0, animatedElements - 1);
|
|
3544
3588
|
return totalStagger + duration + annotationDelay + 500;
|
|
@@ -3678,6 +3722,8 @@ function wireChartEvents(svg, layout, specAnnotations, handlers) {
|
|
|
3678
3722
|
|
|
3679
3723
|
// src/interactions/crosshair.ts
|
|
3680
3724
|
import { getRepresentativeColor } from "@opendata-ai/openchart-core";
|
|
3725
|
+
var EMPHASIS_PROXIMITY_PX = 18;
|
|
3726
|
+
var SNAP_DOT_RADIUS = 4.5;
|
|
3681
3727
|
function snapKey(x) {
|
|
3682
3728
|
return Math.round(x);
|
|
3683
3729
|
}
|
|
@@ -3724,43 +3770,46 @@ function findNearestX(sortedXs, x) {
|
|
|
3724
3770
|
}
|
|
3725
3771
|
return candidate;
|
|
3726
3772
|
}
|
|
3727
|
-
function buildSliceTooltip(hits) {
|
|
3773
|
+
function buildSliceTooltip(hits, emphasisKey) {
|
|
3728
3774
|
if (hits.length === 0) return null;
|
|
3729
3775
|
const title = hits[0].point.tooltip?.title;
|
|
3730
3776
|
const fields = [];
|
|
3731
3777
|
const isMulti = hits.length > 1;
|
|
3778
|
+
let total;
|
|
3732
3779
|
for (const { group, point } of hits) {
|
|
3733
3780
|
const tip = point.tooltip;
|
|
3734
3781
|
if (!tip) continue;
|
|
3782
|
+
if (!total) total = tip.fields.find((f) => f.role === "total");
|
|
3735
3783
|
if (isMulti) {
|
|
3736
|
-
const
|
|
3784
|
+
const candidates = tip.fields.filter((f) => f.role !== "total");
|
|
3785
|
+
const yField = candidates.find(
|
|
3737
3786
|
(f) => !f.color && f.label !== title && f.value !== title && f.value !== group.seriesKey
|
|
3738
|
-
) ??
|
|
3787
|
+
) ?? candidates[candidates.length - 1] ?? null;
|
|
3739
3788
|
if (!yField) continue;
|
|
3740
3789
|
fields.push({
|
|
3741
3790
|
label: group.seriesKey,
|
|
3742
3791
|
value: yField.value,
|
|
3743
|
-
color: group.color
|
|
3792
|
+
color: group.color,
|
|
3793
|
+
...emphasisKey === group.seriesKey ? { emphasis: true } : {}
|
|
3744
3794
|
});
|
|
3745
3795
|
} else {
|
|
3746
3796
|
for (const f of tip.fields) {
|
|
3797
|
+
if (f.role === "total") continue;
|
|
3747
3798
|
fields.push({ ...f, color: f.color ?? group.color });
|
|
3748
3799
|
}
|
|
3749
3800
|
}
|
|
3750
3801
|
}
|
|
3751
3802
|
if (fields.length === 0) return null;
|
|
3803
|
+
if (total && isMulti) fields.push(total);
|
|
3752
3804
|
return { title, fields };
|
|
3753
3805
|
}
|
|
3754
|
-
function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
3806
|
+
function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
3755
3807
|
const overlay = svg.querySelector("[data-voronoi-overlay]");
|
|
3756
|
-
if (!overlay) return
|
|
3757
|
-
};
|
|
3808
|
+
if (!overlay) return null;
|
|
3758
3809
|
const groups = collectSeriesGroups(layout);
|
|
3759
|
-
if (groups.length === 0) return
|
|
3760
|
-
};
|
|
3810
|
+
if (groups.length === 0) return null;
|
|
3761
3811
|
const snapXs = collectSnapXs(groups);
|
|
3762
|
-
if (snapXs.length === 0) return
|
|
3763
|
-
};
|
|
3812
|
+
if (snapXs.length === 0) return null;
|
|
3764
3813
|
const crosshair = svg.querySelector("[data-crosshair]");
|
|
3765
3814
|
const dotsLayer = svg.querySelector("[data-snap-dots]");
|
|
3766
3815
|
const dots = [];
|
|
@@ -3768,7 +3817,7 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3768
3817
|
while (dotsLayer.firstChild) dotsLayer.removeChild(dotsLayer.firstChild);
|
|
3769
3818
|
for (const group of groups) {
|
|
3770
3819
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
3771
|
-
circle.setAttribute("r",
|
|
3820
|
+
circle.setAttribute("r", String(SNAP_DOT_RADIUS));
|
|
3772
3821
|
circle.setAttribute("fill", layout.theme.colors.background);
|
|
3773
3822
|
circle.setAttribute("stroke", group.color);
|
|
3774
3823
|
circle.setAttribute("stroke-width", "2");
|
|
@@ -3778,9 +3827,16 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3778
3827
|
dots.push(circle);
|
|
3779
3828
|
}
|
|
3780
3829
|
}
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3830
|
+
let currentIndex = -1;
|
|
3831
|
+
let currentEmphasis = null;
|
|
3832
|
+
function containerScale() {
|
|
3833
|
+
const svgEl = svg;
|
|
3834
|
+
const rect = svgEl.getBoundingClientRect();
|
|
3835
|
+
const viewBox = svgEl.viewBox?.baseVal;
|
|
3836
|
+
const scaleX = viewBox?.width && rect.width ? viewBox.width / rect.width : 1;
|
|
3837
|
+
return scaleX > 0 ? 1 / scaleX : 1;
|
|
3838
|
+
}
|
|
3839
|
+
function showAt(snappedX, emphasisKey, viewBoxToContainer) {
|
|
3784
3840
|
const hits = [];
|
|
3785
3841
|
let anchorY = 0;
|
|
3786
3842
|
let anchorCount = 0;
|
|
@@ -3806,7 +3862,10 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3806
3862
|
crosshair.setAttribute("x2", String(snappedX));
|
|
3807
3863
|
crosshair.style.display = "";
|
|
3808
3864
|
}
|
|
3809
|
-
|
|
3865
|
+
currentEmphasis = emphasisKey;
|
|
3866
|
+
if (emphasisKey) emphasis?.setSeries(emphasisKey);
|
|
3867
|
+
else emphasis?.clear();
|
|
3868
|
+
const tooltip = buildSliceTooltip(hits, emphasisKey);
|
|
3810
3869
|
if (!tooltip) return false;
|
|
3811
3870
|
const containerAnchorX = snappedX * viewBoxToContainer;
|
|
3812
3871
|
const containerAnchorY = anchorCount > 0 ? anchorY / anchorCount * viewBoxToContainer : 0;
|
|
@@ -3814,6 +3873,31 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3814
3873
|
placement: "right"
|
|
3815
3874
|
});
|
|
3816
3875
|
return true;
|
|
3876
|
+
}
|
|
3877
|
+
function keysAt(snappedX) {
|
|
3878
|
+
const keys = [];
|
|
3879
|
+
for (const group of groups) {
|
|
3880
|
+
if (group.pointsByX.has(snappedX)) keys.push(group.seriesKey);
|
|
3881
|
+
}
|
|
3882
|
+
return keys;
|
|
3883
|
+
}
|
|
3884
|
+
const positionAt = (svgX, svgY, viewBoxToContainer) => {
|
|
3885
|
+
const snappedX = findNearestX(snapXs, svgX);
|
|
3886
|
+
if (snappedX === null) return false;
|
|
3887
|
+
currentIndex = snapXs.indexOf(snappedX);
|
|
3888
|
+
let nearestKey = null;
|
|
3889
|
+
let nearestDy = Number.POSITIVE_INFINITY;
|
|
3890
|
+
for (const group of groups) {
|
|
3891
|
+
const point = group.pointsByX.get(snappedX);
|
|
3892
|
+
if (!point) continue;
|
|
3893
|
+
const dy = Math.abs(point.y - svgY);
|
|
3894
|
+
if (dy < nearestDy) {
|
|
3895
|
+
nearestDy = dy;
|
|
3896
|
+
nearestKey = group.seriesKey;
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
const emphasisKey = nearestDy <= EMPHASIS_PROXIMITY_PX ? nearestKey : null;
|
|
3900
|
+
return showAt(snappedX, emphasisKey, viewBoxToContainer);
|
|
3817
3901
|
};
|
|
3818
3902
|
const toSvgCoords = (clientX, clientY) => {
|
|
3819
3903
|
const svgEl = svg;
|
|
@@ -3831,11 +3915,24 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3831
3915
|
const { svgX, svgY, viewBoxToContainer } = toSvgCoords(me.clientX, me.clientY);
|
|
3832
3916
|
positionAt(svgX, svgY, viewBoxToContainer);
|
|
3833
3917
|
};
|
|
3834
|
-
const hideAll = () => {
|
|
3918
|
+
const hideAll = (resetIndex = false) => {
|
|
3835
3919
|
if (crosshair) crosshair.style.display = "none";
|
|
3836
3920
|
for (const dot of dots) dot.style.display = "none";
|
|
3837
3921
|
tooltipManager.hide();
|
|
3922
|
+
currentEmphasis = null;
|
|
3923
|
+
if (resetIndex) currentIndex = -1;
|
|
3924
|
+
emphasis?.clear();
|
|
3838
3925
|
};
|
|
3926
|
+
const hideOnPointerOut = () => hideAll();
|
|
3927
|
+
function stepTo(index) {
|
|
3928
|
+
if (snapXs.length === 0) return;
|
|
3929
|
+
const wrapped = (index % snapXs.length + snapXs.length) % snapXs.length;
|
|
3930
|
+
currentIndex = wrapped;
|
|
3931
|
+
const snappedX = snapXs[wrapped];
|
|
3932
|
+
const keys = keysAt(snappedX);
|
|
3933
|
+
const keep = currentEmphasis && keys.includes(currentEmphasis) ? currentEmphasis : null;
|
|
3934
|
+
showAt(snappedX, keep, containerScale());
|
|
3935
|
+
}
|
|
3839
3936
|
const handleTouch = (e) => {
|
|
3840
3937
|
const te = e;
|
|
3841
3938
|
if (te.touches.length === 0) return;
|
|
@@ -3845,16 +3942,43 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager) {
|
|
|
3845
3942
|
positionAt(svgX, svgY, viewBoxToContainer);
|
|
3846
3943
|
};
|
|
3847
3944
|
overlay.addEventListener("mousemove", handleMouseMove);
|
|
3848
|
-
overlay.addEventListener("mouseleave",
|
|
3945
|
+
overlay.addEventListener("mouseleave", hideOnPointerOut);
|
|
3849
3946
|
overlay.addEventListener("touchstart", handleTouch, { passive: false });
|
|
3850
3947
|
overlay.addEventListener("touchmove", handleTouch, { passive: false });
|
|
3851
|
-
overlay.addEventListener("touchend",
|
|
3852
|
-
return
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3948
|
+
overlay.addEventListener("touchend", hideOnPointerOut);
|
|
3949
|
+
return {
|
|
3950
|
+
cleanup() {
|
|
3951
|
+
overlay.removeEventListener("mousemove", handleMouseMove);
|
|
3952
|
+
overlay.removeEventListener("mouseleave", hideOnPointerOut);
|
|
3953
|
+
overlay.removeEventListener("touchstart", handleTouch);
|
|
3954
|
+
overlay.removeEventListener("touchmove", handleTouch);
|
|
3955
|
+
overlay.removeEventListener("touchend", hideOnPointerOut);
|
|
3956
|
+
},
|
|
3957
|
+
get snapCount() {
|
|
3958
|
+
return snapXs.length;
|
|
3959
|
+
},
|
|
3960
|
+
get currentIndex() {
|
|
3961
|
+
return currentIndex;
|
|
3962
|
+
},
|
|
3963
|
+
stepTo,
|
|
3964
|
+
cycleSeries(delta) {
|
|
3965
|
+
if (currentIndex < 0) {
|
|
3966
|
+
stepTo(0);
|
|
3967
|
+
return;
|
|
3968
|
+
}
|
|
3969
|
+
const snappedX = snapXs[currentIndex];
|
|
3970
|
+
const keys = keysAt(snappedX);
|
|
3971
|
+
if (keys.length === 0) return;
|
|
3972
|
+
const at = currentEmphasis ? keys.indexOf(currentEmphasis) : -1;
|
|
3973
|
+
const next = ((at + delta) % keys.length + keys.length) % keys.length;
|
|
3974
|
+
showAt(snappedX, keys[next], containerScale());
|
|
3975
|
+
},
|
|
3976
|
+
showCurrent() {
|
|
3977
|
+
stepTo(currentIndex < 0 ? 0 : currentIndex);
|
|
3978
|
+
},
|
|
3979
|
+
hide(resetIndex) {
|
|
3980
|
+
hideAll(resetIndex === true);
|
|
3981
|
+
}
|
|
3858
3982
|
};
|
|
3859
3983
|
}
|
|
3860
3984
|
|
|
@@ -4519,11 +4643,175 @@ function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
|
4519
4643
|
};
|
|
4520
4644
|
}
|
|
4521
4645
|
|
|
4646
|
+
// src/interactions/hover-emphasis.ts
|
|
4647
|
+
var MARKS_ACTIVE_CLASS = "oc-hover-active";
|
|
4648
|
+
var MARK_HOVER_CLASS = "oc-mark--hover";
|
|
4649
|
+
var MARK_DIM_CLASS = "oc-mark--dim";
|
|
4650
|
+
var LEGEND_HOVER_CLASS = "oc-legend-entry--hover";
|
|
4651
|
+
var LEGEND_DIM_CLASS = "oc-legend-entry--dim";
|
|
4652
|
+
var NOOP = {
|
|
4653
|
+
setSeries() {
|
|
4654
|
+
},
|
|
4655
|
+
setMark() {
|
|
4656
|
+
},
|
|
4657
|
+
clear() {
|
|
4658
|
+
},
|
|
4659
|
+
destroy() {
|
|
4660
|
+
},
|
|
4661
|
+
multiSeries: false
|
|
4662
|
+
};
|
|
4663
|
+
function countMarkSeries(layout) {
|
|
4664
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4665
|
+
for (const mark of layout.marks) {
|
|
4666
|
+
const key = "seriesKey" in mark ? mark.seriesKey : void 0;
|
|
4667
|
+
if (key) seen.add(key);
|
|
4668
|
+
}
|
|
4669
|
+
return seen.size;
|
|
4670
|
+
}
|
|
4671
|
+
function createHoverEmphasis(svg, layout) {
|
|
4672
|
+
if (layout.markRenderMode === "canvas") return NOOP;
|
|
4673
|
+
const marksGroup = svg.querySelector(".oc-marks");
|
|
4674
|
+
if (!marksGroup) return NOOP;
|
|
4675
|
+
const multiSeries = countMarkSeries(layout) > 1;
|
|
4676
|
+
let markEls = null;
|
|
4677
|
+
let legendEls = null;
|
|
4678
|
+
function marks() {
|
|
4679
|
+
if (!markEls) markEls = Array.from(marksGroup.querySelectorAll(".oc-mark"));
|
|
4680
|
+
return markEls;
|
|
4681
|
+
}
|
|
4682
|
+
function legendEntries() {
|
|
4683
|
+
if (!legendEls) legendEls = Array.from(svg.querySelectorAll("[data-legend-label]"));
|
|
4684
|
+
return legendEls;
|
|
4685
|
+
}
|
|
4686
|
+
function clear() {
|
|
4687
|
+
marksGroup.classList.remove(MARKS_ACTIVE_CLASS);
|
|
4688
|
+
for (const el of marks()) {
|
|
4689
|
+
el.classList.remove(MARK_HOVER_CLASS, MARK_DIM_CLASS);
|
|
4690
|
+
}
|
|
4691
|
+
for (const el of legendEntries()) {
|
|
4692
|
+
el.classList.remove(LEGEND_HOVER_CLASS, LEGEND_DIM_CLASS);
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
function applyLegend(seriesKey) {
|
|
4696
|
+
for (const el of legendEntries()) {
|
|
4697
|
+
if (el.getAttribute("data-legend-active") === "false") continue;
|
|
4698
|
+
const match = el.getAttribute("data-legend-label") === seriesKey;
|
|
4699
|
+
el.classList.toggle(LEGEND_HOVER_CLASS, match);
|
|
4700
|
+
el.classList.toggle(LEGEND_DIM_CLASS, !match);
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
function setSeries(seriesKey) {
|
|
4704
|
+
if (seriesKey === null) {
|
|
4705
|
+
clear();
|
|
4706
|
+
return;
|
|
4707
|
+
}
|
|
4708
|
+
marksGroup.classList.add(MARKS_ACTIVE_CLASS);
|
|
4709
|
+
for (const el of marks()) {
|
|
4710
|
+
const match = el.getAttribute("data-series") === seriesKey;
|
|
4711
|
+
el.classList.toggle(MARK_HOVER_CLASS, match);
|
|
4712
|
+
el.classList.toggle(MARK_DIM_CLASS, !match);
|
|
4713
|
+
}
|
|
4714
|
+
applyLegend(seriesKey);
|
|
4715
|
+
}
|
|
4716
|
+
function setMark(el) {
|
|
4717
|
+
if (!el) {
|
|
4718
|
+
clear();
|
|
4719
|
+
return;
|
|
4720
|
+
}
|
|
4721
|
+
const series = el.getAttribute("data-series");
|
|
4722
|
+
if (multiSeries && series) {
|
|
4723
|
+
setSeries(series);
|
|
4724
|
+
return;
|
|
4725
|
+
}
|
|
4726
|
+
marksGroup.classList.add(MARKS_ACTIVE_CLASS);
|
|
4727
|
+
for (const mark of marks()) {
|
|
4728
|
+
mark.classList.toggle(MARK_HOVER_CLASS, mark === el);
|
|
4729
|
+
mark.classList.remove(MARK_DIM_CLASS);
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
return {
|
|
4733
|
+
setSeries,
|
|
4734
|
+
setMark,
|
|
4735
|
+
clear,
|
|
4736
|
+
destroy() {
|
|
4737
|
+
clear();
|
|
4738
|
+
markEls = null;
|
|
4739
|
+
legendEls = null;
|
|
4740
|
+
},
|
|
4741
|
+
multiSeries
|
|
4742
|
+
};
|
|
4743
|
+
}
|
|
4744
|
+
function wireEndpointLabelHover(svg, emphasis) {
|
|
4745
|
+
const entries = svg.querySelectorAll("[data-endpoint-key]");
|
|
4746
|
+
const cleanups = [];
|
|
4747
|
+
for (const entry of entries) {
|
|
4748
|
+
const key = entry.getAttribute("data-endpoint-key");
|
|
4749
|
+
if (!key) continue;
|
|
4750
|
+
const handleEnter = () => emphasis.setSeries(key);
|
|
4751
|
+
const handleLeave = () => emphasis.clear();
|
|
4752
|
+
entry.addEventListener("mouseenter", handleEnter);
|
|
4753
|
+
entry.addEventListener("mouseleave", handleLeave);
|
|
4754
|
+
cleanups.push(() => {
|
|
4755
|
+
entry.removeEventListener("mouseenter", handleEnter);
|
|
4756
|
+
entry.removeEventListener("mouseleave", handleLeave);
|
|
4757
|
+
});
|
|
4758
|
+
}
|
|
4759
|
+
return () => {
|
|
4760
|
+
for (const cleanup of cleanups) cleanup();
|
|
4761
|
+
};
|
|
4762
|
+
}
|
|
4763
|
+
|
|
4522
4764
|
// src/interactions/keyboard-nav.ts
|
|
4523
|
-
function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, layout) {
|
|
4765
|
+
function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, layout, crosshair) {
|
|
4524
4766
|
container.setAttribute("tabindex", "0");
|
|
4525
4767
|
container.setAttribute("aria-roledescription", "chart");
|
|
4526
4768
|
container.setAttribute("aria-label", layout.a11y.altText);
|
|
4769
|
+
if (tooltipDescriptors.size === 0 && crosshair) {
|
|
4770
|
+
const handleCrosshairKeys = (e) => {
|
|
4771
|
+
const step = crosshair.currentIndex;
|
|
4772
|
+
switch (e.key) {
|
|
4773
|
+
case "ArrowRight":
|
|
4774
|
+
e.preventDefault();
|
|
4775
|
+
crosshair.stepTo(step + 1);
|
|
4776
|
+
break;
|
|
4777
|
+
case "ArrowLeft":
|
|
4778
|
+
e.preventDefault();
|
|
4779
|
+
crosshair.stepTo(step <= 0 ? crosshair.snapCount - 1 : step - 1);
|
|
4780
|
+
break;
|
|
4781
|
+
case "ArrowDown":
|
|
4782
|
+
e.preventDefault();
|
|
4783
|
+
if (step < 0) crosshair.stepTo(0);
|
|
4784
|
+
crosshair.cycleSeries(1);
|
|
4785
|
+
break;
|
|
4786
|
+
case "ArrowUp":
|
|
4787
|
+
e.preventDefault();
|
|
4788
|
+
if (step < 0) crosshair.stepTo(0);
|
|
4789
|
+
crosshair.cycleSeries(-1);
|
|
4790
|
+
break;
|
|
4791
|
+
case "Enter":
|
|
4792
|
+
case " ":
|
|
4793
|
+
e.preventDefault();
|
|
4794
|
+
crosshair.showCurrent();
|
|
4795
|
+
break;
|
|
4796
|
+
case "Escape":
|
|
4797
|
+
e.preventDefault();
|
|
4798
|
+
crosshair.hide(true);
|
|
4799
|
+
break;
|
|
4800
|
+
}
|
|
4801
|
+
};
|
|
4802
|
+
const handleCrosshairBlur = () => {
|
|
4803
|
+
crosshair.hide(true);
|
|
4804
|
+
};
|
|
4805
|
+
container.addEventListener("keydown", handleCrosshairKeys);
|
|
4806
|
+
container.addEventListener("blur", handleCrosshairBlur);
|
|
4807
|
+
return () => {
|
|
4808
|
+
container.removeEventListener("keydown", handleCrosshairKeys);
|
|
4809
|
+
container.removeEventListener("blur", handleCrosshairBlur);
|
|
4810
|
+
container.removeAttribute("tabindex");
|
|
4811
|
+
container.removeAttribute("aria-roledescription");
|
|
4812
|
+
container.removeAttribute("aria-label");
|
|
4813
|
+
};
|
|
4814
|
+
}
|
|
4527
4815
|
const markElements = [];
|
|
4528
4816
|
const allMarkEls = svg.querySelectorAll("[data-mark-id]");
|
|
4529
4817
|
for (const el of allMarkEls) {
|
|
@@ -4596,9 +4884,15 @@ function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, lay
|
|
|
4596
4884
|
}
|
|
4597
4885
|
}
|
|
4598
4886
|
};
|
|
4887
|
+
const handleBlur = () => {
|
|
4888
|
+
tooltipManager.hide();
|
|
4889
|
+
highlightMark(-1);
|
|
4890
|
+
};
|
|
4599
4891
|
container.addEventListener("keydown", handleKeyDown);
|
|
4892
|
+
container.addEventListener("blur", handleBlur);
|
|
4600
4893
|
return () => {
|
|
4601
4894
|
container.removeEventListener("keydown", handleKeyDown);
|
|
4895
|
+
container.removeEventListener("blur", handleBlur);
|
|
4602
4896
|
container.removeAttribute("tabindex");
|
|
4603
4897
|
container.removeAttribute("aria-roledescription");
|
|
4604
4898
|
container.removeAttribute("aria-label");
|
|
@@ -4606,22 +4900,47 @@ function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, lay
|
|
|
4606
4900
|
}
|
|
4607
4901
|
|
|
4608
4902
|
// src/interactions/legend-interaction.ts
|
|
4609
|
-
function wireLegendInteraction(svg, _layout, toggleSeries, onLegendToggle, onEdit) {
|
|
4903
|
+
function wireLegendInteraction(svg, _layout, toggleSeries, onLegendToggle, onEdit, emphasis) {
|
|
4610
4904
|
const legendEntries = svg.querySelectorAll(
|
|
4611
4905
|
".oc-legend:not(.oc-legend--size) [data-legend-index]"
|
|
4612
4906
|
);
|
|
4613
4907
|
const cleanups = [];
|
|
4614
4908
|
for (const entry of legendEntries) {
|
|
4615
4909
|
if (entry.getAttribute("data-legend-overflow") === "true") continue;
|
|
4616
|
-
const
|
|
4910
|
+
const toggle = () => {
|
|
4617
4911
|
const label = entry.getAttribute("data-legend-label");
|
|
4618
4912
|
if (!label) return;
|
|
4619
4913
|
const nowHidden = toggleSeries(label);
|
|
4620
4914
|
onLegendToggle?.(label, !nowHidden);
|
|
4621
4915
|
onEdit?.({ type: "legend-toggle", series: label, hidden: nowHidden });
|
|
4622
4916
|
};
|
|
4917
|
+
const handleClick = () => toggle();
|
|
4918
|
+
const handleKeyDown = (e) => {
|
|
4919
|
+
const ke = e;
|
|
4920
|
+
if (ke.key !== "Enter" && ke.key !== " ") return;
|
|
4921
|
+
ke.preventDefault();
|
|
4922
|
+
toggle();
|
|
4923
|
+
};
|
|
4924
|
+
const handleEnter = () => {
|
|
4925
|
+
const label = entry.getAttribute("data-legend-label");
|
|
4926
|
+
if (!label || entry.getAttribute("data-legend-active") === "false") return;
|
|
4927
|
+
emphasis?.setSeries(label);
|
|
4928
|
+
};
|
|
4929
|
+
const handleLeave = () => emphasis?.clear();
|
|
4623
4930
|
entry.addEventListener("click", handleClick);
|
|
4624
|
-
|
|
4931
|
+
entry.addEventListener("keydown", handleKeyDown);
|
|
4932
|
+
entry.addEventListener("mouseenter", handleEnter);
|
|
4933
|
+
entry.addEventListener("mouseleave", handleLeave);
|
|
4934
|
+
entry.addEventListener("focus", handleEnter);
|
|
4935
|
+
entry.addEventListener("blur", handleLeave);
|
|
4936
|
+
cleanups.push(() => {
|
|
4937
|
+
entry.removeEventListener("click", handleClick);
|
|
4938
|
+
entry.removeEventListener("keydown", handleKeyDown);
|
|
4939
|
+
entry.removeEventListener("mouseenter", handleEnter);
|
|
4940
|
+
entry.removeEventListener("mouseleave", handleLeave);
|
|
4941
|
+
entry.removeEventListener("focus", handleEnter);
|
|
4942
|
+
entry.removeEventListener("blur", handleLeave);
|
|
4943
|
+
});
|
|
4625
4944
|
}
|
|
4626
4945
|
return () => {
|
|
4627
4946
|
for (const cleanup of cleanups) {
|
|
@@ -4850,7 +5169,7 @@ function createScreenReaderTable(layout, container) {
|
|
|
4850
5169
|
}
|
|
4851
5170
|
|
|
4852
5171
|
// src/interactions/tooltip-events.ts
|
|
4853
|
-
function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
5172
|
+
function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager, emphasis) {
|
|
4854
5173
|
const markElements = svg.querySelectorAll("[data-mark-id]");
|
|
4855
5174
|
const cleanups = [];
|
|
4856
5175
|
for (const el of markElements) {
|
|
@@ -4864,6 +5183,7 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4864
5183
|
const x = mouseEvent.clientX - svgRect.left;
|
|
4865
5184
|
const y = mouseEvent.clientY - svgRect.top;
|
|
4866
5185
|
tooltipManager.show(content, x, y);
|
|
5186
|
+
emphasis?.setMark(el);
|
|
4867
5187
|
};
|
|
4868
5188
|
const handleMouseMove = (e) => {
|
|
4869
5189
|
const mouseEvent = e;
|
|
@@ -4874,6 +5194,7 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4874
5194
|
};
|
|
4875
5195
|
const handleMouseLeave = () => {
|
|
4876
5196
|
tooltipManager.hide();
|
|
5197
|
+
emphasis?.clear();
|
|
4877
5198
|
};
|
|
4878
5199
|
const handleTouchStart = (e) => {
|
|
4879
5200
|
const touchEvent = e;
|
|
@@ -4883,17 +5204,23 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4883
5204
|
const x = touch.clientX - svgRect.left;
|
|
4884
5205
|
const y = touch.clientY - svgRect.top;
|
|
4885
5206
|
tooltipManager.show(content, x, y);
|
|
5207
|
+
emphasis?.setMark(el);
|
|
4886
5208
|
}
|
|
4887
5209
|
};
|
|
5210
|
+
const handleTouchEnd = () => {
|
|
5211
|
+
emphasis?.clear();
|
|
5212
|
+
};
|
|
4888
5213
|
el.addEventListener("mouseenter", handleMouseEnter);
|
|
4889
5214
|
el.addEventListener("mousemove", handleMouseMove);
|
|
4890
5215
|
el.addEventListener("mouseleave", handleMouseLeave);
|
|
4891
5216
|
el.addEventListener("touchstart", handleTouchStart);
|
|
5217
|
+
el.addEventListener("touchend", handleTouchEnd);
|
|
4892
5218
|
cleanups.push(() => {
|
|
4893
5219
|
el.removeEventListener("mouseenter", handleMouseEnter);
|
|
4894
5220
|
el.removeEventListener("mousemove", handleMouseMove);
|
|
4895
5221
|
el.removeEventListener("mouseleave", handleMouseLeave);
|
|
4896
5222
|
el.removeEventListener("touchstart", handleTouchStart);
|
|
5223
|
+
el.removeEventListener("touchend", handleTouchEnd);
|
|
4897
5224
|
});
|
|
4898
5225
|
}
|
|
4899
5226
|
return () => {
|
|
@@ -5274,7 +5601,7 @@ function createTween(opts) {
|
|
|
5274
5601
|
}
|
|
5275
5602
|
|
|
5276
5603
|
// src/scatter-canvas/entrance.ts
|
|
5277
|
-
var MAX_TOTAL_STAGGER_MS =
|
|
5604
|
+
var MAX_TOTAL_STAGGER_MS = 300;
|
|
5278
5605
|
var POINT_DURATION_FRACTION = 0.4;
|
|
5279
5606
|
function clampStagger(delay, elementCount) {
|
|
5280
5607
|
if (elementCount <= 1) return 0;
|
|
@@ -5711,9 +6038,13 @@ function createSeriesSearch(options) {
|
|
|
5711
6038
|
|
|
5712
6039
|
// src/theme-tokens.ts
|
|
5713
6040
|
import { adaptForLightLineStroke, cssTokenDefault } from "@opendata-ai/openchart-core";
|
|
6041
|
+
function resolvedSurface(theme) {
|
|
6042
|
+
return theme.colors.neutral.surface;
|
|
6043
|
+
}
|
|
5714
6044
|
function stampThemeProperties(el, theme) {
|
|
5715
6045
|
const accent = theme.colors.categorical[0] ?? cssTokenDefault("--oc-accent", "light");
|
|
5716
|
-
const bg = theme
|
|
6046
|
+
const bg = resolvedSurface(theme);
|
|
6047
|
+
const n = theme.colors.neutral;
|
|
5717
6048
|
const props = [
|
|
5718
6049
|
["--oc-font-family", theme.fonts.family],
|
|
5719
6050
|
["--oc-font-mono", theme.fonts.mono],
|
|
@@ -5729,8 +6060,22 @@ function stampThemeProperties(el, theme) {
|
|
|
5729
6060
|
["--oc-bg", bg],
|
|
5730
6061
|
["--oc-text", theme.colors.text],
|
|
5731
6062
|
["--oc-text-muted", theme.colors.axis],
|
|
6063
|
+
// Secondary grays are derived from the theme's own text/background pair
|
|
6064
|
+
// (ResolvedTheme.colors.neutral), so a warm or cool theme gets warm or
|
|
6065
|
+
// cool grays instead of zinc. One direction only: theme -> CSS.
|
|
6066
|
+
["--oc-text-secondary", n.secondary],
|
|
6067
|
+
["--oc-text-faint", n.faint],
|
|
6068
|
+
["--oc-border", n.border],
|
|
6069
|
+
["--oc-gray-100", n[100]],
|
|
6070
|
+
["--oc-gray-200", n[200]],
|
|
6071
|
+
["--oc-gray-300", n[300]],
|
|
6072
|
+
["--oc-gray-400", n[400]],
|
|
6073
|
+
["--oc-gray-600", n[600]],
|
|
6074
|
+
["--oc-gray-800", n[800]],
|
|
5732
6075
|
["--oc-gridline", theme.colors.gridline],
|
|
5733
|
-
|
|
6076
|
+
// --oc-axis is the hairline the axis line is drawn with, not the ink its
|
|
6077
|
+
// tick labels take (that is --oc-text-muted, above).
|
|
6078
|
+
["--oc-axis", theme.colors.hairline],
|
|
5734
6079
|
["--oc-border-radius", `${theme.borderRadius}px`],
|
|
5735
6080
|
["--oc-accent", accent],
|
|
5736
6081
|
["--oc-accent-strong", adaptForLightLineStroke(accent)],
|
|
@@ -5787,6 +6132,9 @@ function createChart(container, spec, options) {
|
|
|
5787
6132
|
let disconnectResize = null;
|
|
5788
6133
|
let cleanupTooltipEvents = null;
|
|
5789
6134
|
let cleanupVoronoiEvents = null;
|
|
6135
|
+
let cleanupEndpointHover = null;
|
|
6136
|
+
let hoverEmphasis = null;
|
|
6137
|
+
let crosshairController = null;
|
|
5790
6138
|
let canvasLayer = null;
|
|
5791
6139
|
let cleanupCanvasEvents = null;
|
|
5792
6140
|
let canvasEntrance = null;
|
|
@@ -6228,6 +6576,11 @@ function createChart(container, spec, options) {
|
|
|
6228
6576
|
cleanupTooltipEvents = null;
|
|
6229
6577
|
cleanupVoronoiEvents?.();
|
|
6230
6578
|
cleanupVoronoiEvents = null;
|
|
6579
|
+
crosshairController = null;
|
|
6580
|
+
cleanupEndpointHover?.();
|
|
6581
|
+
cleanupEndpointHover = null;
|
|
6582
|
+
hoverEmphasis?.destroy();
|
|
6583
|
+
hoverEmphasis = null;
|
|
6231
6584
|
cleanupCanvasEvents?.();
|
|
6232
6585
|
cleanupCanvasEvents = null;
|
|
6233
6586
|
canvasLayer?.destroy();
|
|
@@ -6293,19 +6646,30 @@ function createChart(container, spec, options) {
|
|
|
6293
6646
|
options
|
|
6294
6647
|
});
|
|
6295
6648
|
}
|
|
6649
|
+
hoverEmphasis = createHoverEmphasis(svgElement, currentLayout);
|
|
6296
6650
|
cleanupTooltipEvents = wireTooltipEvents(
|
|
6297
6651
|
svgElement,
|
|
6298
6652
|
currentLayout.tooltipDescriptors,
|
|
6299
|
-
tooltipManager
|
|
6653
|
+
tooltipManager,
|
|
6654
|
+
hoverEmphasis
|
|
6300
6655
|
);
|
|
6301
|
-
|
|
6656
|
+
const crosshairCtl = wireVoronoiTooltipEvents(
|
|
6657
|
+
svgElement,
|
|
6658
|
+
currentLayout,
|
|
6659
|
+
tooltipManager,
|
|
6660
|
+
hoverEmphasis
|
|
6661
|
+
);
|
|
6662
|
+
crosshairController = crosshairCtl;
|
|
6663
|
+
cleanupVoronoiEvents = crosshairCtl ? () => crosshairCtl.cleanup() : null;
|
|
6664
|
+
cleanupEndpointHover = wireEndpointLabelHover(svgElement, hoverEmphasis);
|
|
6302
6665
|
if (!currentLayout.a11y.hidden) {
|
|
6303
6666
|
cleanupKeyboardNav = wireKeyboardNav(
|
|
6304
6667
|
svgElement,
|
|
6305
6668
|
container,
|
|
6306
6669
|
currentLayout.tooltipDescriptors,
|
|
6307
6670
|
tooltipManager,
|
|
6308
|
-
currentLayout
|
|
6671
|
+
currentLayout,
|
|
6672
|
+
crosshairController
|
|
6309
6673
|
);
|
|
6310
6674
|
}
|
|
6311
6675
|
const editSuppressed = wantsSeriesSearch(currentSpec);
|
|
@@ -6320,7 +6684,8 @@ function createChart(container, spec, options) {
|
|
|
6320
6684
|
currentLayout,
|
|
6321
6685
|
toggleSeriesVisibility,
|
|
6322
6686
|
options?.onLegendToggle,
|
|
6323
|
-
editSuppressed ? void 0 : options?.onEdit
|
|
6687
|
+
editSuppressed ? void 0 : options?.onEdit,
|
|
6688
|
+
hoverEmphasis
|
|
6324
6689
|
);
|
|
6325
6690
|
if (options?.onMarkClick || options?.onMarkHover || options?.onMarkLeave || options?.onAnnotationClick) {
|
|
6326
6691
|
const specAnnotations = "annotations" in currentSpec && Array.isArray(currentSpec.annotations) ? currentSpec.annotations : [];
|
|
@@ -6616,6 +6981,11 @@ function createChart(container, spec, options) {
|
|
|
6616
6981
|
cleanupTooltipEvents = null;
|
|
6617
6982
|
cleanupVoronoiEvents?.();
|
|
6618
6983
|
cleanupVoronoiEvents = null;
|
|
6984
|
+
crosshairController = null;
|
|
6985
|
+
cleanupEndpointHover?.();
|
|
6986
|
+
cleanupEndpointHover = null;
|
|
6987
|
+
hoverEmphasis?.destroy();
|
|
6988
|
+
hoverEmphasis = null;
|
|
6619
6989
|
cleanupCanvasEvents?.();
|
|
6620
6990
|
cleanupCanvasEvents = null;
|
|
6621
6991
|
canvasEntrance?.cancel();
|
|
@@ -6915,6 +7285,24 @@ var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
|
6915
7285
|
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
6916
7286
|
var BRAND_URL = "https://tryopendata.ai";
|
|
6917
7287
|
var BULK_ANIMATION_THRESHOLD = 200;
|
|
7288
|
+
var LEGEND_BACKDROP_ALPHA = 0.9;
|
|
7289
|
+
function legendBackdrop(theme) {
|
|
7290
|
+
const surface = resolvedSurface(theme).trim();
|
|
7291
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(surface);
|
|
7292
|
+
if (hex) {
|
|
7293
|
+
const h = hex[1];
|
|
7294
|
+
const full = h.length === 3 ? h.replace(/./g, (c) => c + c) : h;
|
|
7295
|
+
const r = Number.parseInt(full.slice(0, 2), 16);
|
|
7296
|
+
const g = Number.parseInt(full.slice(2, 4), 16);
|
|
7297
|
+
const b = Number.parseInt(full.slice(4, 6), 16);
|
|
7298
|
+
return `rgba(${r},${g},${b},${LEGEND_BACKDROP_ALPHA})`;
|
|
7299
|
+
}
|
|
7300
|
+
const rgb = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i.exec(surface);
|
|
7301
|
+
if (rgb) {
|
|
7302
|
+
return `rgba(${rgb[1]},${rgb[2]},${rgb[3]},${LEGEND_BACKDROP_ALPHA})`;
|
|
7303
|
+
}
|
|
7304
|
+
return surface;
|
|
7305
|
+
}
|
|
6918
7306
|
function createSVGElement2(tag) {
|
|
6919
7307
|
return document.createElementNS(SVG_NS3, tag);
|
|
6920
7308
|
}
|
|
@@ -7001,7 +7389,7 @@ function renderBorders(parent, borders) {
|
|
|
7001
7389
|
d: borders.interiorPath,
|
|
7002
7390
|
fill: "none",
|
|
7003
7391
|
stroke: borders.interiorStroke,
|
|
7004
|
-
"stroke-width":
|
|
7392
|
+
"stroke-width": borders.interiorWidth,
|
|
7005
7393
|
"stroke-linejoin": "round"
|
|
7006
7394
|
});
|
|
7007
7395
|
interior.setAttribute("class", "oc-map-border-interior");
|
|
@@ -7014,7 +7402,7 @@ function renderBorders(parent, borders) {
|
|
|
7014
7402
|
d: borders.outlinePath,
|
|
7015
7403
|
fill: "none",
|
|
7016
7404
|
stroke: borders.outlineStroke,
|
|
7017
|
-
"stroke-width":
|
|
7405
|
+
"stroke-width": borders.outlineWidth,
|
|
7018
7406
|
"stroke-linejoin": "round"
|
|
7019
7407
|
});
|
|
7020
7408
|
outline.setAttribute("class", "oc-map-border-outline");
|
|
@@ -7060,6 +7448,9 @@ function renderFeatures(parent, features, animation, staggerBudget = 0, focusIds
|
|
|
7060
7448
|
});
|
|
7061
7449
|
path.setAttribute("data-feature-id", String(feature.id));
|
|
7062
7450
|
path.setAttribute("data-key", String(feature.id));
|
|
7451
|
+
if (feature.binIndex !== void 0) {
|
|
7452
|
+
path.setAttribute("data-bin-index", String(feature.binIndex));
|
|
7453
|
+
}
|
|
7063
7454
|
if (feature.name) {
|
|
7064
7455
|
path.setAttribute("data-feature-name", feature.name);
|
|
7065
7456
|
}
|
|
@@ -7179,7 +7570,6 @@ function renderMapSVG(layout, opts) {
|
|
|
7179
7570
|
}
|
|
7180
7571
|
const ptLegend = layout.pointCategoricalLegend ?? layout.pointContinuousLegend;
|
|
7181
7572
|
if (ptLegend) {
|
|
7182
|
-
const isDark = layout.theme.isDark;
|
|
7183
7573
|
const padX = 10;
|
|
7184
7574
|
const padY = 8;
|
|
7185
7575
|
const swatchSize = "swatchSize" in ptLegend ? ptLegend.swatchSize : 10;
|
|
@@ -7209,11 +7599,26 @@ function renderMapSVG(layout, opts) {
|
|
|
7209
7599
|
width: contentWidth + padX * 2,
|
|
7210
7600
|
height: bgHeight,
|
|
7211
7601
|
rx: 4,
|
|
7212
|
-
fill:
|
|
7602
|
+
fill: legendBackdrop(layout.theme)
|
|
7213
7603
|
});
|
|
7214
7604
|
svg.appendChild(bg);
|
|
7215
7605
|
renderLegend(svg, ptLegend);
|
|
7216
7606
|
}
|
|
7607
|
+
if (layout.pointSizeLegend) {
|
|
7608
|
+
const sizeLegend = layout.pointSizeLegend;
|
|
7609
|
+
const pad = 10;
|
|
7610
|
+
const bg = createSVGElement2("rect");
|
|
7611
|
+
setAttrs2(bg, {
|
|
7612
|
+
x: sizeLegend.bounds.x - pad,
|
|
7613
|
+
y: sizeLegend.bounds.y - pad,
|
|
7614
|
+
width: sizeLegend.bounds.width + pad * 2,
|
|
7615
|
+
height: sizeLegend.bounds.height + pad * 2,
|
|
7616
|
+
rx: 6,
|
|
7617
|
+
fill: legendBackdrop(layout.theme)
|
|
7618
|
+
});
|
|
7619
|
+
svg.appendChild(bg);
|
|
7620
|
+
renderLegend(svg, sizeLegend);
|
|
7621
|
+
}
|
|
7217
7622
|
if (watermark) {
|
|
7218
7623
|
renderWatermark(svg, layout);
|
|
7219
7624
|
}
|
|
@@ -7347,6 +7752,27 @@ function focusSignature(focus) {
|
|
|
7347
7752
|
const ids = focus.ids.map(String).sort().join(",");
|
|
7348
7753
|
return `${ids}|${Math.round(t.x)},${Math.round(t.y)},${Math.round(t.width)},${Math.round(t.height)},${t.padding}`;
|
|
7349
7754
|
}
|
|
7755
|
+
var featureOrigin = /* @__PURE__ */ new WeakMap();
|
|
7756
|
+
function raiseFeature(el, svg) {
|
|
7757
|
+
if (svg.classList.contains("oc-animate")) return;
|
|
7758
|
+
const parent = el.parentNode;
|
|
7759
|
+
if (!parent || el === parent.lastChild) return;
|
|
7760
|
+
if (!featureOrigin.has(el)) {
|
|
7761
|
+
featureOrigin.set(el, el.nextSibling);
|
|
7762
|
+
}
|
|
7763
|
+
parent.appendChild(el);
|
|
7764
|
+
}
|
|
7765
|
+
function restoreFeatureOrder(el) {
|
|
7766
|
+
const parent = el.parentNode;
|
|
7767
|
+
if (!parent || !featureOrigin.has(el)) return;
|
|
7768
|
+
const next = featureOrigin.get(el) ?? null;
|
|
7769
|
+
featureOrigin.delete(el);
|
|
7770
|
+
if (next && next.parentNode === parent) {
|
|
7771
|
+
parent.insertBefore(el, next);
|
|
7772
|
+
} else if (!next) {
|
|
7773
|
+
parent.appendChild(el);
|
|
7774
|
+
}
|
|
7775
|
+
}
|
|
7350
7776
|
function prefersReducedMotion2() {
|
|
7351
7777
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
7352
7778
|
try {
|
|
@@ -7419,7 +7845,8 @@ function createGeoMap(container, spec, options) {
|
|
|
7419
7845
|
const handleMouseEnter = (e) => {
|
|
7420
7846
|
const mouseEvent = e;
|
|
7421
7847
|
el.style.setProperty("cursor", "pointer");
|
|
7422
|
-
el
|
|
7848
|
+
raiseFeature(el, svg);
|
|
7849
|
+
el.classList.add("oc-map-feature--hover");
|
|
7423
7850
|
if (content && tooltipManager && options?.tooltip !== false) {
|
|
7424
7851
|
const svgRect = svg.getBoundingClientRect();
|
|
7425
7852
|
const x = mouseEvent.clientX - svgRect.left;
|
|
@@ -7446,7 +7873,8 @@ function createGeoMap(container, spec, options) {
|
|
|
7446
7873
|
};
|
|
7447
7874
|
const handleMouseLeave = () => {
|
|
7448
7875
|
el.style.removeProperty("cursor");
|
|
7449
|
-
el.
|
|
7876
|
+
el.classList.remove("oc-map-feature--hover");
|
|
7877
|
+
restoreFeatureOrder(el);
|
|
7450
7878
|
tooltipManager?.hide();
|
|
7451
7879
|
options?.onMarkHover?.(null);
|
|
7452
7880
|
};
|
|
@@ -7471,6 +7899,22 @@ function createGeoMap(container, spec, options) {
|
|
|
7471
7899
|
el.removeEventListener("click", handleClick);
|
|
7472
7900
|
});
|
|
7473
7901
|
}
|
|
7902
|
+
const featuresGroup = svg.querySelector(".oc-map-features");
|
|
7903
|
+
if (featuresGroup) {
|
|
7904
|
+
for (const bin of svg.querySelectorAll(".oc-legend-bin[data-bin-index]")) {
|
|
7905
|
+
const index = bin.getAttribute("data-bin-index");
|
|
7906
|
+
if (index === null) continue;
|
|
7907
|
+
const enter = () => featuresGroup.setAttribute("data-hover", index);
|
|
7908
|
+
const leave = () => featuresGroup.removeAttribute("data-hover");
|
|
7909
|
+
bin.addEventListener("mouseenter", enter);
|
|
7910
|
+
bin.addEventListener("mouseleave", leave);
|
|
7911
|
+
cleanups.push(() => {
|
|
7912
|
+
bin.removeEventListener("mouseenter", enter);
|
|
7913
|
+
bin.removeEventListener("mouseleave", leave);
|
|
7914
|
+
featuresGroup.removeAttribute("data-hover");
|
|
7915
|
+
});
|
|
7916
|
+
}
|
|
7917
|
+
}
|
|
7474
7918
|
const pointMarksByKey = new Map(layout.pointMarks.map((p) => [p.key, p]));
|
|
7475
7919
|
const pointElements = svg.querySelectorAll(".oc-map-point");
|
|
7476
7920
|
for (const el of pointElements) {
|
|
@@ -7877,6 +8321,7 @@ export {
|
|
|
7877
8321
|
prefersReducedMotion,
|
|
7878
8322
|
createTween,
|
|
7879
8323
|
createTextEditOverlay,
|
|
8324
|
+
resolvedSurface,
|
|
7880
8325
|
DEFAULT_UPDATE_MAX_MARKS,
|
|
7881
8326
|
CANVAS_DEFAULT_UPDATE_MAX_MARKS,
|
|
7882
8327
|
canTransitionSpecShape,
|
|
@@ -7895,4 +8340,4 @@ export {
|
|
|
7895
8340
|
scrubCamera,
|
|
7896
8341
|
createGeoMap
|
|
7897
8342
|
};
|
|
7898
|
-
//# sourceMappingURL=chunk-
|
|
8343
|
+
//# sourceMappingURL=chunk-45XZOWYC.js.map
|