@opendata-ai/openchart-vanilla 8.0.0-rc.2 → 8.0.0-rc.4

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.
@@ -552,6 +552,7 @@ function setupTableAnimationCleanup(wrapper) {
552
552
  }
553
553
 
554
554
  // src/map-camera.ts
555
+ var FOCUS_DIM_OPACITY = 0.25;
555
556
  function focusTargetForFeatures(layout, ids, padding = 16) {
556
557
  const idSet = new Set(ids.map(String));
557
558
  let minX = Infinity;
@@ -588,12 +589,27 @@ function applyMapCamera(svg, camera, layout) {
588
589
  for (const p of paths) {
589
590
  p.setAttribute("vector-effect", "non-scaling-stroke");
590
591
  }
592
+ const points = svg.querySelectorAll(".oc-map-point");
593
+ for (const p of points) {
594
+ const baseR = Number(p.getAttribute("data-base-r") ?? 5);
595
+ const baseSW = Number(p.getAttribute("data-base-stroke-width") ?? 1);
596
+ p.setAttribute("r", String(baseR / camera.k));
597
+ p.setAttribute("stroke-width", String(baseSW / camera.k));
598
+ }
591
599
  } else {
592
600
  cameraGroup.removeAttribute("transform");
593
601
  const paths = svg.querySelectorAll(".oc-map-feature, .oc-map-borders path");
594
602
  for (const p of paths) {
595
603
  p.removeAttribute("vector-effect");
596
604
  }
605
+ const points = svg.querySelectorAll(".oc-map-point");
606
+ for (const p of points) {
607
+ const baseR = Number(p.getAttribute("data-base-r") ?? 5);
608
+ const baseSW = Number(p.getAttribute("data-base-stroke-width") ?? 1);
609
+ p.setAttribute("r", String(baseR));
610
+ p.setAttribute("stroke-width", String(baseSW));
611
+ p.removeAttribute("vector-effect");
612
+ }
597
613
  }
598
614
  }
599
615
 
@@ -720,7 +736,7 @@ function renderBorders(parent, borders) {
720
736
  }
721
737
  parent.appendChild(g);
722
738
  }
723
- function renderFeatures(parent, features, animation, staggerBudget = 0) {
739
+ function renderFeatures(parent, features, animation, staggerBudget = 0, focusIds) {
724
740
  const g = createSVGElement2("g");
725
741
  g.setAttribute("class", "oc-map-features");
726
742
  g.setAttribute("role", "list");
@@ -763,12 +779,19 @@ function renderFeatures(parent, features, animation, staggerBudget = 0) {
763
779
  if (feature.aria?.label) {
764
780
  path.setAttribute("aria-label", feature.aria.label);
765
781
  }
782
+ const dimmed = focusIds ? !focusIds.has(String(feature.id)) : false;
783
+ const s = path.style;
784
+ if (dimmed) {
785
+ s.setProperty("opacity", String(FOCUS_DIM_OPACITY));
786
+ }
766
787
  if (!bulk && animation?.enter) {
767
788
  const idx = feature.animationIndex ?? i;
768
789
  path.setAttribute("data-animation-index", String(idx));
769
- const s = path.style;
770
790
  s.setProperty("--oc-mark-index", String(idx));
771
791
  s.setProperty("--oc-feature-fill", feature.fill);
792
+ if (dimmed) {
793
+ s.setProperty("--oc-feature-target-opacity", String(FOCUS_DIM_OPACITY));
794
+ }
772
795
  if (staggerBudget > 0 && maxIdx > 0) {
773
796
  s.setProperty("--oc-map-delay", `${shuffledDelays[i]}ms`);
774
797
  }
@@ -777,6 +800,37 @@ function renderFeatures(parent, features, animation, staggerBudget = 0) {
777
800
  }
778
801
  parent.appendChild(g);
779
802
  }
803
+ function renderPointMarks(parent, points, animation) {
804
+ if (points.length === 0) return;
805
+ const g = createSVGElement2("g");
806
+ g.setAttribute("class", "oc-map-points");
807
+ for (const pt of points) {
808
+ const circle = createSVGElement2("circle");
809
+ circle.setAttribute("class", "oc-map-point");
810
+ setAttrs2(circle, {
811
+ cx: pt.cx,
812
+ cy: pt.cy,
813
+ r: pt.r,
814
+ fill: pt.fill,
815
+ "fill-opacity": pt.fillOpacity,
816
+ stroke: pt.stroke,
817
+ "stroke-width": pt.strokeWidth
818
+ });
819
+ circle.setAttribute("data-point-key", pt.key);
820
+ circle.setAttribute("data-base-r", String(pt.r));
821
+ circle.setAttribute("data-base-stroke-width", String(pt.strokeWidth));
822
+ if (pt.aria?.label) {
823
+ circle.setAttribute("aria-label", pt.aria.label);
824
+ }
825
+ if (animation?.enter) {
826
+ const s = circle.style;
827
+ s.setProperty("--oc-mark-index", String(pt.animationIndex));
828
+ s.setProperty("transform-origin", `${pt.cx}px ${pt.cy}px`);
829
+ }
830
+ g.appendChild(circle);
831
+ }
832
+ parent.appendChild(g);
833
+ }
780
834
  function renderMapSVG(layout, opts) {
781
835
  const { width, height, features, borders, a11y, watermark, animation } = layout;
782
836
  const animate = opts?.animate && !!animation?.enter;
@@ -807,22 +861,65 @@ function renderMapSVG(layout, opts) {
807
861
  }
808
862
  const defs = createSVGElement2("defs");
809
863
  svg.appendChild(defs);
810
- renderChrome(svg, layout);
811
864
  const mapGroup = createSVGElement2("g");
812
865
  mapGroup.setAttribute("class", "oc-map-group");
813
866
  mapGroup.setAttribute("transform", `translate(${layout.area.x},${layout.area.y})`);
814
867
  const cameraGroup = createSVGElement2("g");
815
868
  cameraGroup.setAttribute("class", "oc-map-camera");
816
869
  cameraGroup.setAttribute("data-oc-map-camera", "");
817
- renderFeatures(cameraGroup, features, animate ? animation : void 0, mapStaggerBudget);
870
+ const focusIdSet = layout.focus ? new Set(layout.focus.ids.map(String)) : void 0;
871
+ renderFeatures(
872
+ cameraGroup,
873
+ features,
874
+ animate ? animation : void 0,
875
+ mapStaggerBudget,
876
+ focusIdSet
877
+ );
818
878
  renderBorders(cameraGroup, borders);
879
+ renderPointMarks(cameraGroup, layout.pointMarks, animate ? animation : void 0);
819
880
  mapGroup.appendChild(cameraGroup);
820
881
  svg.appendChild(mapGroup);
882
+ renderChrome(svg, layout);
821
883
  if (layout.continuousLegend) {
822
884
  renderLegend(svg, layout.continuousLegend);
823
885
  } else if (layout.categoricalLegend) {
824
886
  renderLegend(svg, layout.categoricalLegend);
825
887
  }
888
+ const ptLegend = layout.pointCategoricalLegend ?? layout.pointContinuousLegend;
889
+ if (ptLegend) {
890
+ const isDark = layout.theme.isDark;
891
+ const padX = 10;
892
+ const padY = 8;
893
+ const swatchSize = "swatchSize" in ptLegend ? ptLegend.swatchSize : 10;
894
+ const swatchGap = "swatchGap" in ptLegend ? ptLegend.swatchGap : 6;
895
+ const entryGap = "entryGap" in ptLegend ? ptLegend.entryGap : 16;
896
+ let contentWidth = ptLegend.bounds.width;
897
+ if ("entries" in ptLegend && ptLegend.entries) {
898
+ const cat = ptLegend;
899
+ if (cat && cat.entries.length > 0) {
900
+ const fontSize = ptLegend.labelStyle.fontSize;
901
+ let w = 0;
902
+ for (const entry of cat.entries) {
903
+ w += swatchSize + swatchGap + entry.label.length * fontSize * 0.6 + entryGap;
904
+ }
905
+ w -= entryGap;
906
+ contentWidth = Math.min(w, ptLegend.bounds.width);
907
+ }
908
+ }
909
+ const contentCenterY = ptLegend.bounds.y + swatchSize / 2;
910
+ const bgHeight = swatchSize + padY * 2;
911
+ const bg = createSVGElement2("rect");
912
+ setAttrs2(bg, {
913
+ x: ptLegend.bounds.x - padX,
914
+ y: contentCenterY - bgHeight / 2,
915
+ width: contentWidth + padX * 2,
916
+ height: bgHeight,
917
+ rx: 4,
918
+ fill: isDark ? "rgba(24,24,27,0.9)" : "rgba(255,255,255,0.9)"
919
+ });
920
+ svg.appendChild(bg);
921
+ renderLegend(svg, ptLegend);
922
+ }
826
923
  if (watermark) {
827
924
  renderWatermark(svg, layout);
828
925
  }
@@ -1484,6 +1581,7 @@ function createMap(container, spec, options) {
1484
1581
  }
1485
1582
  if (feature) {
1486
1583
  options?.onMarkHover?.({
1584
+ kind: "feature",
1487
1585
  id: feature.id,
1488
1586
  name: feature.name,
1489
1587
  data: feature.data
@@ -1508,6 +1606,7 @@ function createMap(container, spec, options) {
1508
1606
  const handleClick = () => {
1509
1607
  if (feature) {
1510
1608
  options?.onMarkClick?.({
1609
+ kind: "feature",
1511
1610
  id: feature.id,
1512
1611
  name: feature.name,
1513
1612
  data: feature.data
@@ -1525,6 +1624,66 @@ function createMap(container, spec, options) {
1525
1624
  el.removeEventListener("click", handleClick);
1526
1625
  });
1527
1626
  }
1627
+ const pointMarksByKey = new Map(layout.pointMarks.map((p) => [p.key, p]));
1628
+ const pointElements = svg.querySelectorAll(".oc-map-point");
1629
+ for (const el of pointElements) {
1630
+ const pointKey = el.getAttribute("data-point-key");
1631
+ if (!pointKey) continue;
1632
+ const content = layout.tooltipDescriptors.get(`point:${pointKey}`);
1633
+ const pointMark = pointMarksByKey.get(pointKey);
1634
+ const handlePointMouseEnter = (e) => {
1635
+ const mouseEvent = e;
1636
+ el.style.setProperty("cursor", "pointer");
1637
+ el.style.setProperty("filter", "brightness(1.15)");
1638
+ if (content && tooltipManager && options?.tooltip !== false) {
1639
+ const svgRect = svg.getBoundingClientRect();
1640
+ const x = mouseEvent.clientX - svgRect.left;
1641
+ const y = mouseEvent.clientY - svgRect.top;
1642
+ tooltipManager.show(content, x, y);
1643
+ }
1644
+ if (pointMark) {
1645
+ options?.onMarkHover?.({
1646
+ kind: "point",
1647
+ id: pointKey,
1648
+ data: pointMark.data
1649
+ });
1650
+ }
1651
+ };
1652
+ const handlePointMouseMove = (e) => {
1653
+ if (content && tooltipManager && options?.tooltip !== false) {
1654
+ const mouseEvent = e;
1655
+ const svgRect = svg.getBoundingClientRect();
1656
+ const x = mouseEvent.clientX - svgRect.left;
1657
+ const y = mouseEvent.clientY - svgRect.top;
1658
+ tooltipManager.show(content, x, y);
1659
+ }
1660
+ };
1661
+ const handlePointMouseLeave = () => {
1662
+ el.style.removeProperty("cursor");
1663
+ el.style.removeProperty("filter");
1664
+ tooltipManager?.hide();
1665
+ options?.onMarkHover?.(null);
1666
+ };
1667
+ const handlePointClick = () => {
1668
+ if (pointMark) {
1669
+ options?.onMarkClick?.({
1670
+ kind: "point",
1671
+ id: pointKey,
1672
+ data: pointMark.data
1673
+ });
1674
+ }
1675
+ };
1676
+ el.addEventListener("mouseenter", handlePointMouseEnter);
1677
+ el.addEventListener("mousemove", handlePointMouseMove);
1678
+ el.addEventListener("mouseleave", handlePointMouseLeave);
1679
+ el.addEventListener("click", handlePointClick);
1680
+ cleanups.push(() => {
1681
+ el.removeEventListener("mouseenter", handlePointMouseEnter);
1682
+ el.removeEventListener("mousemove", handlePointMouseMove);
1683
+ el.removeEventListener("mouseleave", handlePointMouseLeave);
1684
+ el.removeEventListener("click", handlePointClick);
1685
+ });
1686
+ }
1528
1687
  return () => {
1529
1688
  for (const cleanup of cleanups) {
1530
1689
  cleanup();
@@ -1592,7 +1751,7 @@ function createMap(container, spec, options) {
1592
1751
  const fid = el.getAttribute("data-feature-id");
1593
1752
  el.style.setProperty(
1594
1753
  "opacity",
1595
- fid && focusSet.has(fid) ? "1" : "0.25"
1754
+ fid && focusSet.has(fid) ? "1" : String(FOCUS_DIM_OPACITY)
1596
1755
  );
1597
1756
  }
1598
1757
  }
@@ -1605,15 +1764,17 @@ function createMap(container, spec, options) {
1605
1764
  fillTransitionHandle.cancel();
1606
1765
  fillTransitionHandle = null;
1607
1766
  }
1608
- if (svgElement) {
1767
+ const oldSvg = svgElement;
1768
+ const newSvg = renderMapSVG(currentLayout, { animate });
1769
+ if (oldSvg) {
1609
1770
  if (cleanupTooltipEvents) {
1610
1771
  cleanupTooltipEvents();
1611
1772
  cleanupTooltipEvents = null;
1612
1773
  }
1613
- svgElement.remove();
1774
+ container.replaceChild(newSvg, oldSvg);
1775
+ } else {
1776
+ container.appendChild(newSvg);
1614
1777
  }
1615
- const newSvg = renderMapSVG(currentLayout, { animate });
1616
- container.appendChild(newSvg);
1617
1778
  svgElement = newSvg;
1618
1779
  cleanupTooltipEvents = wireTooltipAndInteraction(newSvg, currentLayout);
1619
1780
  if (options?.tooltip !== false) {
@@ -6510,4 +6671,4 @@ export {
6510
6671
  createYouDrawIt,
6511
6672
  createChart
6512
6673
  };
6513
- //# sourceMappingURL=chunk-ENXSUXC5.js.map
6674
+ //# sourceMappingURL=chunk-43Y7EBT7.js.map