@opendata-ai/openchart-vanilla 8.3.1 → 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.
@@ -4,7 +4,7 @@ import {
4
4
  exportPNG,
5
5
  exportSVG,
6
6
  exportSVGWithFonts
7
- } from "./chunk-T32UHMZA.js";
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-W76C53MC.js";
18
+ } from "./chunk-YGYMB7KB.js";
19
19
  import {
20
20
  SVG_NS,
21
21
  createSVGElement,
22
22
  setAttrs
23
- } from "./chunk-RM5XS6NR.js";
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 contentKey = `${content.title}|${content.fields.length}|${content.fields[0]?.value}|${content.fields[content.fields.length - 1]?.value}`;
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
- html += '<div class="oc-tooltip-row">';
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")) || 600;
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")) || 200;
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 yField = tip.fields.find(
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
- ) ?? tip.fields[tip.fields.length - 1] ?? null;
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", "4");
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
- const positionAt = (svgX, _svgY, viewBoxToContainer) => {
3782
- const snappedX = findNearestX(snapXs, svgX);
3783
- if (snappedX === null) return false;
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
- const tooltip = buildSliceTooltip(hits);
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", hideAll);
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", hideAll);
3852
- return () => {
3853
- overlay.removeEventListener("mousemove", handleMouseMove);
3854
- overlay.removeEventListener("mouseleave", hideAll);
3855
- overlay.removeEventListener("touchstart", handleTouch);
3856
- overlay.removeEventListener("touchmove", handleTouch);
3857
- overlay.removeEventListener("touchend", hideAll);
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 handleClick = () => {
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
- cleanups.push(() => entry.removeEventListener("click", handleClick));
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 = 2e3;
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.colors.background === "transparent" ? theme.isDark ? cssTokenDefault("--oc-bg", "dark") : cssTokenDefault("--oc-bg", "light") : theme.colors.background;
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
- ["--oc-axis", theme.colors.axis],
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
- cleanupVoronoiEvents = wireVoronoiTooltipEvents(svgElement, currentLayout, tooltipManager);
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": 0.5,
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": 1,
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: isDark ? "rgba(24,24,27,0.9)" : "rgba(255,255,255,0.9)"
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.style.setProperty("filter", "brightness(1.1)");
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.style.removeProperty("filter");
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-GRA2NIRZ.js.map
8343
+ //# sourceMappingURL=chunk-45XZOWYC.js.map