@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.
- package/dist/{chunk-ENXSUXC5.js → chunk-43Y7EBT7.js} +171 -10
- package/dist/chunk-43Y7EBT7.js.map +1 -0
- package/dist/index.d.ts +11 -13
- package/dist/index.js +1 -1
- package/dist/story/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/map.test.ts +116 -0
- package/src/index.ts +1 -1
- package/src/map-camera.ts +23 -0
- package/src/map-mount.ts +92 -22
- package/src/map-renderer.ts +126 -5
- package/dist/chunk-ENXSUXC5.js.map +0 -1
|
@@ -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
|
-
|
|
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" :
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
6674
|
+
//# sourceMappingURL=chunk-43Y7EBT7.js.map
|