@opendata-ai/openchart-vanilla 8.8.0 → 8.9.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-QBTRAQAX.js → chunk-6TD7GWR4.js} +587 -296
- package/dist/chunk-6TD7GWR4.js.map +1 -0
- package/dist/{chunk-DFKLAB67.js → chunk-MGFXAROR.js} +20 -8
- package/dist/chunk-MGFXAROR.js.map +1 -0
- package/dist/{chunk-I73B3DJI.js → chunk-R5TZHNTG.js} +23 -5
- package/dist/chunk-R5TZHNTG.js.map +1 -0
- package/dist/{chunk-SXTHPXG6.js → chunk-TP3GCWCO.js} +78 -7
- package/dist/chunk-TP3GCWCO.js.map +1 -0
- package/dist/{chunk-5IMVUHVC.js → chunk-YBVDBY6P.js} +17 -13
- package/dist/chunk-YBVDBY6P.js.map +1 -0
- package/dist/{chunk-EZQDCLZN.js → chunk-YHZAH6EN.js} +3 -2
- package/dist/{chunk-EZQDCLZN.js.map → chunk-YHZAH6EN.js.map} +1 -1
- package/dist/export-gif.d.ts +2 -2
- package/dist/export-gif.js +2 -2
- package/dist/graph-3d/index.js +1 -1
- package/dist/index.d.ts +27 -5
- package/dist/index.js +633 -212
- package/dist/index.js.map +1 -1
- package/dist/{mount-WiY9BIr9.d.ts → mount-FmUdKlCq.d.ts} +11 -1
- package/dist/static.js +2 -2
- package/dist/story/index.d.ts +2 -2
- package/dist/story/index.js +3 -3
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-5IMVUHVC.js.map +0 -1
- package/dist/chunk-DFKLAB67.js.map +0 -1
- package/dist/chunk-I73B3DJI.js.map +0 -1
- package/dist/chunk-QBTRAQAX.js.map +0 -1
- package/dist/chunk-SXTHPXG6.js.map +0 -1
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
exportPNG,
|
|
5
5
|
exportSVG,
|
|
6
6
|
exportSVGWithFonts
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-R5TZHNTG.js";
|
|
8
8
|
import {
|
|
9
9
|
buildGradientDefs,
|
|
10
10
|
buildPatternDefs,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
resolveMarkFill,
|
|
18
18
|
resolvePatternFill,
|
|
19
19
|
stampAnimationVars
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-TP3GCWCO.js";
|
|
21
21
|
import {
|
|
22
22
|
SVG_NS,
|
|
23
23
|
createSVGElement,
|
|
@@ -453,6 +453,140 @@ function materializeCanvasModeSVG(layout, opts) {
|
|
|
453
453
|
return materializeRasterMarkSVG(layout);
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
// src/transition/keys.ts
|
|
457
|
+
function annotationKey(a) {
|
|
458
|
+
if (a.id) return `id:${a.id}`;
|
|
459
|
+
return `${a.type}:${a.label?.text ?? ""}`;
|
|
460
|
+
}
|
|
461
|
+
function keyAnnotations(annotations) {
|
|
462
|
+
const seen = /* @__PURE__ */ new Map();
|
|
463
|
+
return annotations.map((a) => {
|
|
464
|
+
const base = annotationKey(a);
|
|
465
|
+
const n = seen.get(base) ?? 0;
|
|
466
|
+
seen.set(base, n + 1);
|
|
467
|
+
return n === 0 ? base : `${base}#${n}`;
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// src/transition/gate.ts
|
|
472
|
+
var TRANSITIONABLE_MARKS = /* @__PURE__ */ new Set([
|
|
473
|
+
"bar",
|
|
474
|
+
"line",
|
|
475
|
+
"area",
|
|
476
|
+
"point",
|
|
477
|
+
"beeswarm",
|
|
478
|
+
"arc",
|
|
479
|
+
"histogram",
|
|
480
|
+
"density"
|
|
481
|
+
]);
|
|
482
|
+
var DEFAULT_UPDATE_MAX_MARKS = 500;
|
|
483
|
+
var CANVAS_DEFAULT_UPDATE_MAX_MARKS = 2e4;
|
|
484
|
+
function canTransitionSpecShape(prevSpec, nextSpec) {
|
|
485
|
+
const prev = prevSpec;
|
|
486
|
+
const next = nextSpec;
|
|
487
|
+
if (!prev || !next || !("mark" in prev) || !("mark" in next)) return false;
|
|
488
|
+
const prevMark = typeof prev.mark === "string" ? prev.mark : prev.mark?.type;
|
|
489
|
+
const nextMark = typeof next.mark === "string" ? next.mark : next.mark?.type;
|
|
490
|
+
if (prevMark !== nextMark) return false;
|
|
491
|
+
if (!TRANSITIONABLE_MARKS.has(prevMark)) return false;
|
|
492
|
+
const prevEnc = prev.encoding;
|
|
493
|
+
const nextEnc = next.encoding;
|
|
494
|
+
if (!prevEnc || !nextEnc) return false;
|
|
495
|
+
if (JSON.stringify(prev.transform) !== JSON.stringify(next.transform)) return false;
|
|
496
|
+
for (const channel of ["x", "y", "theta", "radius"]) {
|
|
497
|
+
for (const property of ["aggregate", "bin", "stack", "timeUnit"]) {
|
|
498
|
+
if (JSON.stringify(prevEnc[channel]?.[property]) !== JSON.stringify(nextEnc[channel]?.[property]))
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
const field = prevEnc[channel]?.field;
|
|
502
|
+
if (field) {
|
|
503
|
+
const a = prev.fields?.[field];
|
|
504
|
+
const b = next.fields?.[field];
|
|
505
|
+
if (["unit", "measure", "representation"].some((key) => a?.[key] !== b?.[key])) return false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return !(prevEnc.x?.field !== nextEnc.x?.field || prevEnc.x?.type !== nextEnc.x?.type || prevEnc.x?.scale?.type !== nextEnc.x?.scale?.type || prevEnc.y?.field !== nextEnc.y?.field || prevEnc.y?.type !== nextEnc.y?.type || prevEnc.y?.scale?.type !== nextEnc.y?.scale?.type || prevEnc.theta?.field !== nextEnc.theta?.field || prevEnc.theta?.type !== nextEnc.theta?.type || prevEnc.color?.field !== nextEnc.color?.field || prevEnc.key?.field !== nextEnc.key?.field || prevEnc.facet?.field !== nextEnc.facet?.field || prevEnc.row?.field !== nextEnc.row?.field || prevEnc.column?.field !== nextEnc.column?.field);
|
|
509
|
+
}
|
|
510
|
+
function canTransition(args) {
|
|
511
|
+
const { prevLayout, nextLayout, prevSpec, nextSpec, isFirstRender, entranceInFlight } = args;
|
|
512
|
+
if (!prevLayout || !prevSpec || isFirstRender) return false;
|
|
513
|
+
if (!nextLayout.animation?.update) return false;
|
|
514
|
+
if (!canTransitionSpecShape(prevSpec, nextSpec)) return false;
|
|
515
|
+
if (entranceInFlight) return false;
|
|
516
|
+
const cap = nextLayout.animation.update.maxMarks ?? (nextLayout.markRenderMode === "canvas" ? CANVAS_DEFAULT_UPDATE_MAX_MARKS : DEFAULT_UPDATE_MAX_MARKS);
|
|
517
|
+
if (Math.max(prevLayout.marks.length, nextLayout.marks.length) > cap) return false;
|
|
518
|
+
if (!hasVisibleChange(prevLayout, nextLayout)) return false;
|
|
519
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
520
|
+
try {
|
|
521
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
function hasVisibleChange(prevLayout, nextLayout) {
|
|
528
|
+
return hasGeometryChanged(prevLayout, nextLayout) || hasColorChanged(prevLayout, nextLayout) || hasAnnotationChanged(prevLayout, nextLayout);
|
|
529
|
+
}
|
|
530
|
+
function hasColorChanged(prevLayout, nextLayout) {
|
|
531
|
+
const prevMarks = prevLayout.marks;
|
|
532
|
+
const nextMarks = nextLayout.marks;
|
|
533
|
+
if (prevMarks.length !== nextMarks.length) return true;
|
|
534
|
+
for (let i = 0; i < prevMarks.length; i++) {
|
|
535
|
+
const p = prevMarks[i];
|
|
536
|
+
const n = nextMarks[i];
|
|
537
|
+
if (p.fill !== n.fill || p.stroke !== n.stroke) return true;
|
|
538
|
+
}
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
function hasAnnotationChanged(prevLayout, nextLayout) {
|
|
542
|
+
const prev = prevLayout.annotations ?? [];
|
|
543
|
+
const next = nextLayout.annotations ?? [];
|
|
544
|
+
if (prev.length !== next.length) return true;
|
|
545
|
+
const prevKeys = keyAnnotations(prev);
|
|
546
|
+
const nextKeys = keyAnnotations(next);
|
|
547
|
+
for (let i = 0; i < prev.length; i++) {
|
|
548
|
+
if (prevKeys[i] !== nextKeys[i]) return true;
|
|
549
|
+
const p = prev[i];
|
|
550
|
+
const n = next[i];
|
|
551
|
+
if (p.label?.x !== n.label?.x || p.label?.y !== n.label?.y) return true;
|
|
552
|
+
if (p.opacity !== n.opacity) return true;
|
|
553
|
+
}
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
function hasGeometryChanged(prevLayout, nextLayout) {
|
|
557
|
+
const prevMarks = prevLayout.marks;
|
|
558
|
+
const nextMarks = nextLayout.marks;
|
|
559
|
+
if (prevMarks.length !== nextMarks.length) return true;
|
|
560
|
+
for (let i = 0; i < prevMarks.length; i++) {
|
|
561
|
+
const p = prevMarks[i];
|
|
562
|
+
const n = nextMarks[i];
|
|
563
|
+
if (p.type !== n.type || p.key !== n.key) return true;
|
|
564
|
+
if (p.type === "rect" && n.type === "rect") {
|
|
565
|
+
if (p.x !== n.x || p.y !== n.y || p.width !== n.width || p.height !== n.height) return true;
|
|
566
|
+
} else if (p.type === "line" && n.type === "line") {
|
|
567
|
+
if (p.path !== n.path || p.points.length !== n.points.length) return true;
|
|
568
|
+
for (let j = 0; j < p.points.length; j++) {
|
|
569
|
+
if (p.points[j].x !== n.points[j].x || p.points[j].y !== n.points[j].y) return true;
|
|
570
|
+
}
|
|
571
|
+
} else if (p.type === "area" && n.type === "area") {
|
|
572
|
+
if (p.path !== n.path) return true;
|
|
573
|
+
} else if (p.type === "arc" && n.type === "arc") {
|
|
574
|
+
if (p.startAngle !== n.startAngle || p.endAngle !== n.endAngle || p.innerRadius !== n.innerRadius || p.outerRadius !== n.outerRadius || p.center.x !== n.center.x || p.center.y !== n.center.y) {
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
} else if (p.type === "point" && n.type === "point") {
|
|
578
|
+
if (p.cx !== n.cx || p.cy !== n.cy || p.r !== n.r) return true;
|
|
579
|
+
} else if (p.type === "rule" && n.type === "rule") {
|
|
580
|
+
if (p.x1 !== n.x1 || p.y1 !== n.y1 || p.x2 !== n.x2 || p.y2 !== n.y2) return true;
|
|
581
|
+
} else if (p.type === "tick" && n.type === "tick") {
|
|
582
|
+
if (p.x !== n.x || p.y !== n.y) return true;
|
|
583
|
+
} else if (p.type === "textMark" && n.type === "textMark") {
|
|
584
|
+
if (p.x !== n.x || p.y !== n.y) return true;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
|
|
456
590
|
// src/text-edit-overlay.ts
|
|
457
591
|
function getScale(svg) {
|
|
458
592
|
const viewBox = svg.viewBox?.baseVal;
|
|
@@ -705,138 +839,91 @@ function createTextEditOverlayAtPosition(config) {
|
|
|
705
839
|
return { destroy };
|
|
706
840
|
}
|
|
707
841
|
|
|
708
|
-
// src/transition/
|
|
709
|
-
function
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
"
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
const prevEnc = prev.encoding;
|
|
745
|
-
const nextEnc = next.encoding;
|
|
746
|
-
if (!prevEnc || !nextEnc) return false;
|
|
747
|
-
return !(prevEnc.x?.field !== nextEnc.x?.field || prevEnc.x?.type !== nextEnc.x?.type || prevEnc.y?.field !== nextEnc.y?.field || prevEnc.y?.type !== nextEnc.y?.type || prevEnc.theta?.field !== nextEnc.theta?.field || prevEnc.theta?.type !== nextEnc.theta?.type || prevEnc.color?.field !== nextEnc.color?.field || prevEnc.key?.field !== nextEnc.key?.field || prevEnc.facet?.field !== nextEnc.facet?.field || prevEnc.row?.field !== nextEnc.row?.field || prevEnc.column?.field !== nextEnc.column?.field);
|
|
748
|
-
}
|
|
749
|
-
function canTransition(args) {
|
|
750
|
-
const { prevLayout, nextLayout, prevSpec, nextSpec, isFirstRender, entranceInFlight } = args;
|
|
751
|
-
if (!prevLayout || !prevSpec || isFirstRender) return false;
|
|
752
|
-
if (!nextLayout.animation?.update) return false;
|
|
753
|
-
if (!canTransitionSpecShape(prevSpec, nextSpec)) return false;
|
|
754
|
-
if (entranceInFlight) return false;
|
|
755
|
-
if (prevLayout.dimensions.width !== nextLayout.dimensions.width || prevLayout.dimensions.height !== nextLayout.dimensions.height) {
|
|
756
|
-
return false;
|
|
757
|
-
}
|
|
758
|
-
const cap = nextLayout.animation.update.maxMarks ?? (nextLayout.markRenderMode === "canvas" ? CANVAS_DEFAULT_UPDATE_MAX_MARKS : DEFAULT_UPDATE_MAX_MARKS);
|
|
759
|
-
if (Math.max(prevLayout.marks.length, nextLayout.marks.length) > cap) return false;
|
|
760
|
-
if (!hasVisibleChange(prevLayout, nextLayout)) return false;
|
|
761
|
-
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
762
|
-
try {
|
|
763
|
-
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
|
|
764
|
-
} catch {
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
return true;
|
|
768
|
-
}
|
|
769
|
-
function hasVisibleChange(prevLayout, nextLayout) {
|
|
770
|
-
return hasGeometryChanged(prevLayout, nextLayout) || hasColorChanged(prevLayout, nextLayout) || hasAnnotationChanged(prevLayout, nextLayout);
|
|
771
|
-
}
|
|
772
|
-
function hasColorChanged(prevLayout, nextLayout) {
|
|
773
|
-
const prevMarks = prevLayout.marks;
|
|
774
|
-
const nextMarks = nextLayout.marks;
|
|
775
|
-
if (prevMarks.length !== nextMarks.length) return true;
|
|
776
|
-
for (let i = 0; i < prevMarks.length; i++) {
|
|
777
|
-
const p = prevMarks[i];
|
|
778
|
-
const n = nextMarks[i];
|
|
779
|
-
if (p.fill !== n.fill || p.stroke !== n.stroke) return true;
|
|
780
|
-
}
|
|
781
|
-
return false;
|
|
782
|
-
}
|
|
783
|
-
function hasAnnotationChanged(prevLayout, nextLayout) {
|
|
784
|
-
const prev = prevLayout.annotations ?? [];
|
|
785
|
-
const next = nextLayout.annotations ?? [];
|
|
786
|
-
if (prev.length !== next.length) return true;
|
|
787
|
-
const prevKeys = keyAnnotations(prev);
|
|
788
|
-
const nextKeys = keyAnnotations(next);
|
|
789
|
-
for (let i = 0; i < prev.length; i++) {
|
|
790
|
-
if (prevKeys[i] !== nextKeys[i]) return true;
|
|
791
|
-
const p = prev[i];
|
|
792
|
-
const n = next[i];
|
|
793
|
-
if (p.label?.x !== n.label?.x || p.label?.y !== n.label?.y) return true;
|
|
794
|
-
if (p.opacity !== n.opacity) return true;
|
|
795
|
-
}
|
|
796
|
-
return false;
|
|
797
|
-
}
|
|
798
|
-
function hasGeometryChanged(prevLayout, nextLayout) {
|
|
799
|
-
const prevMarks = prevLayout.marks;
|
|
800
|
-
const nextMarks = nextLayout.marks;
|
|
801
|
-
if (prevMarks.length !== nextMarks.length) return true;
|
|
802
|
-
for (let i = 0; i < prevMarks.length; i++) {
|
|
803
|
-
const p = prevMarks[i];
|
|
804
|
-
const n = nextMarks[i];
|
|
805
|
-
if (p.type !== n.type || p.key !== n.key) return true;
|
|
806
|
-
if (p.type === "rect" && n.type === "rect") {
|
|
807
|
-
if (p.x !== n.x || p.y !== n.y || p.width !== n.width || p.height !== n.height) return true;
|
|
808
|
-
} else if (p.type === "line" && n.type === "line") {
|
|
809
|
-
if (p.path !== n.path || p.points.length !== n.points.length) return true;
|
|
810
|
-
for (let j = 0; j < p.points.length; j++) {
|
|
811
|
-
if (p.points[j].x !== n.points[j].x || p.points[j].y !== n.points[j].y) return true;
|
|
842
|
+
// src/transition/labels.ts
|
|
843
|
+
function buildLabelMotion(svg, previous, next, snapshot) {
|
|
844
|
+
const attributes = [];
|
|
845
|
+
const add = (element, name, delta, key) => {
|
|
846
|
+
const raw = element.getAttribute(name);
|
|
847
|
+
if (raw === null) return;
|
|
848
|
+
const to = Number(raw);
|
|
849
|
+
if (!Number.isFinite(to)) return;
|
|
850
|
+
const captured = snapshot?.get(key);
|
|
851
|
+
attributes.push({
|
|
852
|
+
element,
|
|
853
|
+
name,
|
|
854
|
+
from: captured?.type === "attribute" ? captured.value : to + delta,
|
|
855
|
+
to,
|
|
856
|
+
key
|
|
857
|
+
});
|
|
858
|
+
};
|
|
859
|
+
for (const group of svg.querySelectorAll("[data-endpoint-key]")) {
|
|
860
|
+
const key = group.getAttribute("data-endpoint-key");
|
|
861
|
+
const leading = group.getAttribute("data-endpoint-side") === "leading";
|
|
862
|
+
const before = (leading ? previous.endpointLabels?.leading : previous.endpointLabels?.entries)?.find((entry) => entry.seriesKey === key);
|
|
863
|
+
const after = (leading ? next.endpointLabels?.leading : next.endpointLabels?.entries)?.find(
|
|
864
|
+
(entry) => entry.seriesKey === key
|
|
865
|
+
);
|
|
866
|
+
if (!before || !after) continue;
|
|
867
|
+
const beforeBounds = leading ? previous.endpointLabels?.leadingBounds : previous.endpointLabels?.bounds;
|
|
868
|
+
const afterBounds = leading ? next.endpointLabels?.leadingBounds : next.endpointLabels?.bounds;
|
|
869
|
+
const dx = (beforeBounds?.x ?? 0) - (afterBounds?.x ?? 0);
|
|
870
|
+
const dy = before.labelY - after.labelY;
|
|
871
|
+
Array.from(group.querySelectorAll("text, tspan, line, circle")).forEach((element, index) => {
|
|
872
|
+
const marker = element.classList.contains("oc-endpoint-marker");
|
|
873
|
+
const leader = element.classList.contains("oc-endpoint-leader");
|
|
874
|
+
const prefix = `endpoint:${key}:${leading}:${index}`;
|
|
875
|
+
for (const name of ["x", "x1", "x2", "cx"]) {
|
|
876
|
+
const offset = marker ? (before.marker?.x ?? 0) - (after.marker?.x ?? 0) : leader && name === "x2" ? previous.area.x + (leading ? 0 : previous.area.width) - next.area.x - (leading ? 0 : next.area.width) : dx;
|
|
877
|
+
add(element, name, offset, `${prefix}:${name}`);
|
|
812
878
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
if (p.startAngle !== n.startAngle || p.endAngle !== n.endAngle || p.innerRadius !== n.innerRadius || p.outerRadius !== n.outerRadius || p.center.x !== n.center.x || p.center.y !== n.center.y) {
|
|
817
|
-
return true;
|
|
879
|
+
for (const name of ["y", "y1", "y2", "cy"]) {
|
|
880
|
+
const offset = marker || leader && name === "y2" ? before.dataY - after.dataY : dy;
|
|
881
|
+
add(element, name, offset, `${prefix}:${name}`);
|
|
818
882
|
}
|
|
819
|
-
}
|
|
820
|
-
if (p.cx !== n.cx || p.cy !== n.cy || p.r !== n.r) return true;
|
|
821
|
-
} else if (p.type === "rule" && n.type === "rule") {
|
|
822
|
-
if (p.x1 !== n.x1 || p.y1 !== n.y1 || p.x2 !== n.x2 || p.y2 !== n.y2) return true;
|
|
823
|
-
} else if (p.type === "tick" && n.type === "tick") {
|
|
824
|
-
if (p.x !== n.x || p.y !== n.y) return true;
|
|
825
|
-
} else if (p.type === "textMark" && n.type === "textMark") {
|
|
826
|
-
if (p.x !== n.x || p.y !== n.y) return true;
|
|
827
|
-
}
|
|
883
|
+
});
|
|
828
884
|
}
|
|
829
|
-
|
|
885
|
+
const beforeMarks = new Map(
|
|
886
|
+
previous.marks.filter((mark) => mark.key).map((mark) => [mark.key, mark])
|
|
887
|
+
);
|
|
888
|
+
const afterMarks = new Map(next.marks.filter((mark) => mark.key).map((mark) => [mark.key, mark]));
|
|
889
|
+
for (const label of svg.querySelectorAll("[data-label-key]")) {
|
|
890
|
+
const key = label.getAttribute("data-label-key");
|
|
891
|
+
const before = beforeMarks.get(key);
|
|
892
|
+
const after = afterMarks.get(key);
|
|
893
|
+
if (!before || !after || !("label" in before) || !("label" in after) || !before.label || !after.label)
|
|
894
|
+
continue;
|
|
895
|
+
const dx = before.type === "arc" && after.type === "arc" ? before.center.x - after.center.x : 0;
|
|
896
|
+
const dy = before.type === "arc" && after.type === "arc" ? before.center.y - after.center.y : 0;
|
|
897
|
+
add(label, "x", before.label.x - after.label.x - dx, `label:${key}:x`);
|
|
898
|
+
add(label, "y", before.label.y - after.label.y - dy, `label:${key}:y`);
|
|
899
|
+
}
|
|
900
|
+
let progress = 0;
|
|
901
|
+
return {
|
|
902
|
+
apply(value) {
|
|
903
|
+
progress = value;
|
|
904
|
+
for (const item of attributes)
|
|
905
|
+
item.element.setAttribute(item.name, String(item.from + (item.to - item.from) * value));
|
|
906
|
+
},
|
|
907
|
+
snapshot(target) {
|
|
908
|
+
for (const item of attributes)
|
|
909
|
+
target.set(item.key, {
|
|
910
|
+
type: "attribute",
|
|
911
|
+
value: item.from + (item.to - item.from) * progress
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
};
|
|
830
915
|
}
|
|
831
916
|
|
|
832
917
|
// src/transition/driver.ts
|
|
833
|
-
import { isGradientDef } from "@opendata-ai/openchart-core";
|
|
918
|
+
import { isGradientDef, sampleMotion as sampleMotion3 } from "@opendata-ai/openchart-core";
|
|
834
919
|
import { buildAreaPath as buildAreaPath2, buildLinePath as buildLinePath2, EXIT_DEFAULTS } from "@opendata-ai/openchart-engine";
|
|
835
920
|
|
|
836
921
|
// src/transition/apply.ts
|
|
922
|
+
import { sampleMotion as sampleMotion2 } from "@opendata-ai/openchart-core";
|
|
837
923
|
import { buildArcPath } from "@opendata-ai/openchart-engine";
|
|
838
924
|
|
|
839
925
|
// src/transition/canvas-tweens.ts
|
|
926
|
+
import { sampleMotion } from "@opendata-ai/openchart-core";
|
|
840
927
|
import { serializeKeyValue as serializeKeyValue2 } from "@opendata-ai/openchart-engine";
|
|
841
928
|
|
|
842
929
|
// src/transition/chrome-tweens.ts
|
|
@@ -1183,7 +1270,7 @@ function buildCanvasPointsTween(prevLayout, nextLayout, layer, tweens, fromSnaps
|
|
|
1183
1270
|
}
|
|
1184
1271
|
function applyCanvasPointsState(tw, elapsed, update, exit, enterDelay, enterDuration) {
|
|
1185
1272
|
const soa = tw.layer.state.marks;
|
|
1186
|
-
const easedUpdate =
|
|
1273
|
+
const easedUpdate = sampleMotion(update, elapsed).position;
|
|
1187
1274
|
for (let s = 0; s < tw.soaIndex.length; s++) {
|
|
1188
1275
|
const i = tw.soaIndex[s];
|
|
1189
1276
|
soa.x[i] = tw.fromCx[s] + (tw.toCx[s] - tw.fromCx[s]) * easedUpdate;
|
|
@@ -1199,7 +1286,7 @@ function applyCanvasPointsState(tw, elapsed, update, exit, enterDelay, enterDura
|
|
|
1199
1286
|
}
|
|
1200
1287
|
}
|
|
1201
1288
|
if (tw.exitState) {
|
|
1202
|
-
const easedExit =
|
|
1289
|
+
const easedExit = sampleMotion(exit, elapsed).position;
|
|
1203
1290
|
tw.exitState.alpha = 1 - easedExit;
|
|
1204
1291
|
tw.layer.state.exiting = tw.exitState;
|
|
1205
1292
|
}
|
|
@@ -1266,7 +1353,7 @@ function buildCanvasGridlinesTween(prevLayout, nextLayout, layer, tweens) {
|
|
|
1266
1353
|
}
|
|
1267
1354
|
function applyCanvasGridlinesState(tw, elapsed, update, enterDelay, enterDuration) {
|
|
1268
1355
|
const live = tw.layer.state.gridlines;
|
|
1269
|
-
const easedUpdate =
|
|
1356
|
+
const easedUpdate = sampleMotion(update, elapsed).position;
|
|
1270
1357
|
const tEnter = elapsed < enterDelay ? 0 : Math.min((elapsed - enterDelay) / enterDuration, 1);
|
|
1271
1358
|
const easedEnter = cubicOut(tEnter);
|
|
1272
1359
|
for (let i = 0; i < tw.index.length; i++) {
|
|
@@ -1614,19 +1701,9 @@ function applyTweenState(tw, elapsed, update, exit, enterDelay, enterDuration) {
|
|
|
1614
1701
|
applyCanvasGridlinesState(tw, elapsed, update, enterDelay, enterDuration);
|
|
1615
1702
|
return;
|
|
1616
1703
|
}
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
} else if (tw.kind === "enter") {
|
|
1621
|
-
if (elapsed < enterDelay) {
|
|
1622
|
-
tLocal = 0;
|
|
1623
|
-
} else {
|
|
1624
|
-
tLocal = Math.min((elapsed - enterDelay) / enterDuration, 1);
|
|
1625
|
-
}
|
|
1626
|
-
} else {
|
|
1627
|
-
tLocal = Math.min(elapsed / update.duration, 1);
|
|
1628
|
-
}
|
|
1629
|
-
const eased = cubicOut(tLocal);
|
|
1704
|
+
const phase = tw.kind === "exit" ? exit : tw.kind === "enter" ? { duration: enterDuration } : update;
|
|
1705
|
+
const elapsedMs = tw.kind === "enter" ? Math.max(0, elapsed - enterDelay) : elapsed;
|
|
1706
|
+
const eased = sampleMotion2(phase, elapsedMs).position;
|
|
1630
1707
|
switch (tw.tweenType) {
|
|
1631
1708
|
case "rect": {
|
|
1632
1709
|
const geom = lerpGeom(tw.from, tw.to, eased);
|
|
@@ -1640,11 +1717,13 @@ function applyTweenState(tw, elapsed, update, exit, enterDelay, enterDuration) {
|
|
|
1640
1717
|
break;
|
|
1641
1718
|
}
|
|
1642
1719
|
case "line": {
|
|
1720
|
+
if (tw.kind !== "update") break;
|
|
1643
1721
|
tw.ptsBuffer = interpolatePoints(tw.fromPts, tw.toPts, eased, tw.ptsBuffer);
|
|
1644
1722
|
applyLinePath(tw.el, tw.ptsBuffer, tw.interpolate, tw.pathEl);
|
|
1645
1723
|
break;
|
|
1646
1724
|
}
|
|
1647
1725
|
case "area": {
|
|
1726
|
+
if (tw.kind !== "update") break;
|
|
1648
1727
|
tw.topBuffer = interpolatePoints(tw.fromTop, tw.toTop, eased, tw.topBuffer);
|
|
1649
1728
|
tw.bottomBuffer = interpolatePoints(tw.fromBottom, tw.toBottom, eased, tw.bottomBuffer);
|
|
1650
1729
|
applyAreaPaths(
|
|
@@ -1750,9 +1829,11 @@ function snapTweenToFinal(tw) {
|
|
|
1750
1829
|
}
|
|
1751
1830
|
break;
|
|
1752
1831
|
case "line":
|
|
1832
|
+
if (tw.kind !== "update") break;
|
|
1753
1833
|
applyLinePath(tw.el, tw.toPts, tw.interpolate, tw.pathEl);
|
|
1754
1834
|
break;
|
|
1755
1835
|
case "area":
|
|
1836
|
+
if (tw.kind !== "update") break;
|
|
1756
1837
|
applyAreaPaths(
|
|
1757
1838
|
tw.el,
|
|
1758
1839
|
tw.toTop,
|
|
@@ -1976,7 +2057,7 @@ function buildLineTweens(prevLayout, nextLayout, marksContainer, dom, tweens, gh
|
|
|
1976
2057
|
const el = dom.any(key);
|
|
1977
2058
|
if (!el) continue;
|
|
1978
2059
|
const snap = fromSnapshot?.get(key);
|
|
1979
|
-
if (snap && snap.type === "line") {
|
|
2060
|
+
if (snap && snap.type === "line" && !snap.points) {
|
|
1980
2061
|
const ghost = el.cloneNode(true);
|
|
1981
2062
|
ghost.classList.add("oc-ghost");
|
|
1982
2063
|
ghost.setAttribute("aria-hidden", "true");
|
|
@@ -2014,11 +2095,12 @@ function buildLineTweens(prevLayout, nextLayout, marksContainer, dom, tweens, gh
|
|
|
2014
2095
|
});
|
|
2015
2096
|
continue;
|
|
2016
2097
|
}
|
|
2017
|
-
const prevPKs = prev.pointKeys ?? prev.points.map((_, i) => `${i}`);
|
|
2098
|
+
const prevPKs = snap?.type === "line" && snap.pointKeys ? snap.pointKeys : prev.pointKeys ?? prev.points.map((_, i) => `${i}`);
|
|
2018
2099
|
const nextPKs = next.pointKeys ?? next.points.map((_, i) => `${i}`);
|
|
2019
2100
|
const prevKeySet = new Set(prevPKs);
|
|
2020
2101
|
const hasSurvivors = nextPKs.some((k) => prevKeySet.has(k));
|
|
2021
|
-
|
|
2102
|
+
const hasGaps = (prev.path?.match(/M/g)?.length ?? 0) > 1 || (next.path?.match(/M/g)?.length ?? 0) > 1;
|
|
2103
|
+
if (!hasSurvivors || hasGaps) {
|
|
2022
2104
|
const ghost = renderSingleMark(prev, 0);
|
|
2023
2105
|
if (ghost) {
|
|
2024
2106
|
ghost.classList.add("oc-ghost");
|
|
@@ -2054,13 +2136,19 @@ function buildLineTweens(prevLayout, nextLayout, marksContainer, dom, tweens, gh
|
|
|
2054
2136
|
pathEl: resolveLinePathEl(el)
|
|
2055
2137
|
});
|
|
2056
2138
|
} else {
|
|
2057
|
-
const [fromPts, toPts] = normalizePointArrays(
|
|
2139
|
+
const [fromPts, toPts] = normalizePointArrays(
|
|
2140
|
+
snap?.type === "line" && snap.points ? snap.points : prev.points,
|
|
2141
|
+
next.points,
|
|
2142
|
+
prevPKs,
|
|
2143
|
+
nextPKs
|
|
2144
|
+
);
|
|
2058
2145
|
tweens.push({
|
|
2059
2146
|
tweenType: "line",
|
|
2060
2147
|
kind: "update",
|
|
2061
2148
|
el,
|
|
2062
2149
|
fromPts,
|
|
2063
2150
|
toPts,
|
|
2151
|
+
pointKeys: mergeKeyOrder(prevPKs, nextPKs),
|
|
2064
2152
|
finalPoints: next.points,
|
|
2065
2153
|
interpolate: next.interpolate,
|
|
2066
2154
|
pathEl: resolveLinePathEl(el)
|
|
@@ -2179,7 +2267,8 @@ function buildAreaTweens(prevLayout, nextLayout, marksContainer, dom, tweens, gh
|
|
|
2179
2267
|
const nextPKs = next.pointKeys ?? next.topPoints.map((_, i) => `${i}`);
|
|
2180
2268
|
const prevKeySet = new Set(prevPKs);
|
|
2181
2269
|
const hasSurvivors = nextPKs.some((k) => prevKeySet.has(k));
|
|
2182
|
-
|
|
2270
|
+
const hasGaps = (prev.path?.match(/M/g)?.length ?? 0) > 1 || (next.path?.match(/M/g)?.length ?? 0) > 1;
|
|
2271
|
+
if (!hasSurvivors || hasGaps) {
|
|
2183
2272
|
const ghost = renderSingleMark(prev, 0);
|
|
2184
2273
|
if (ghost) {
|
|
2185
2274
|
ghost.classList.add("oc-ghost");
|
|
@@ -2237,6 +2326,7 @@ function buildAreaTweens(prevLayout, nextLayout, marksContainer, dom, tweens, gh
|
|
|
2237
2326
|
);
|
|
2238
2327
|
tweens.push({
|
|
2239
2328
|
tweenType: "area",
|
|
2329
|
+
pointKeys: mergeKeyOrder(prevPKs, nextPKs),
|
|
2240
2330
|
kind: "update",
|
|
2241
2331
|
el,
|
|
2242
2332
|
fromTop,
|
|
@@ -2865,16 +2955,13 @@ function runTransition(args) {
|
|
|
2865
2955
|
}
|
|
2866
2956
|
buildColorTweens(prevLayout, nextLayout, dom, tweens);
|
|
2867
2957
|
buildAnnotationTweens(svg, prevLayout, nextLayout, tweens);
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2958
|
+
const labelMotion = buildLabelMotion(svg, prevLayout, nextLayout, fromSnapshot);
|
|
2959
|
+
labelMotion.apply(0);
|
|
2870
2960
|
for (const tw of tweens) {
|
|
2871
2961
|
applyTweenState(tw, 0, update, exit, enterDelay, enterDuration);
|
|
2872
2962
|
}
|
|
2873
2963
|
canvas?.repaint();
|
|
2874
2964
|
canvas?.setInteractionSuspended(true);
|
|
2875
|
-
for (const el of secondaryEls) {
|
|
2876
|
-
el.style.opacity = "0";
|
|
2877
|
-
}
|
|
2878
2965
|
let rafId = null;
|
|
2879
2966
|
let running = true;
|
|
2880
2967
|
let pulsedDot = null;
|
|
@@ -2891,7 +2978,7 @@ function runTransition(args) {
|
|
|
2891
2978
|
for (const tw of tweens) {
|
|
2892
2979
|
applyTweenState(tw, elapsed, update, exit, enterDelay, enterDuration);
|
|
2893
2980
|
}
|
|
2894
|
-
|
|
2981
|
+
labelMotion.apply(sampleMotion3(update, elapsed).position);
|
|
2895
2982
|
canvas?.repaint();
|
|
2896
2983
|
if (tGlobal >= 1) {
|
|
2897
2984
|
finish();
|
|
@@ -2914,9 +3001,6 @@ function runTransition(args) {
|
|
|
2914
3001
|
removeGhosts();
|
|
2915
3002
|
settleCanvas();
|
|
2916
3003
|
pulsedDot = pulseSparklineTerminator(svg, marksContainer);
|
|
2917
|
-
for (let i = 0; i < secondaryEls.length; i++) {
|
|
2918
|
-
secondaryEls[i].style.opacity = secondaryOriginalOpacity[i];
|
|
2919
|
-
}
|
|
2920
3004
|
onComplete();
|
|
2921
3005
|
}
|
|
2922
3006
|
function snapToFinal() {
|
|
@@ -2935,16 +3019,16 @@ function runTransition(args) {
|
|
|
2935
3019
|
}
|
|
2936
3020
|
}
|
|
2937
3021
|
function removeGhosts() {
|
|
3022
|
+
labelMotion.apply(1);
|
|
2938
3023
|
for (const ghost of ghosts) {
|
|
2939
3024
|
ghost.parentNode?.removeChild(ghost);
|
|
2940
3025
|
}
|
|
2941
3026
|
}
|
|
2942
3027
|
function captureSnapshot() {
|
|
2943
3028
|
const snap = /* @__PURE__ */ new Map();
|
|
2944
|
-
const tUpdate = Math.min(lastElapsed / update.duration, 1);
|
|
2945
|
-
const easedUpdate = cubicOut(tUpdate);
|
|
2946
3029
|
for (const tw of tweens) {
|
|
2947
|
-
|
|
3030
|
+
const easedUpdate = tw.kind === "enter" ? sampleMotion3({ duration: enterDuration }, Math.max(0, lastElapsed - enterDelay)).position : sampleMotion3(update, lastElapsed).position;
|
|
3031
|
+
if (tw.kind === "exit") continue;
|
|
2948
3032
|
if (tw.tweenType === "canvasPoints") {
|
|
2949
3033
|
for (let s = 0; s < tw.soaIndex.length; s++) {
|
|
2950
3034
|
snap.set(tw.soaKeys[s], {
|
|
@@ -2995,19 +3079,26 @@ function runTransition(args) {
|
|
|
2995
3079
|
}
|
|
2996
3080
|
case "line": {
|
|
2997
3081
|
const pts = interpolatePoints(tw.fromPts, tw.toPts, easedUpdate);
|
|
2998
|
-
const d = buildLinePath2(pts, tw.interpolate);
|
|
2999
|
-
snap.set(key, { type: "line", d });
|
|
3082
|
+
const d = tw.kind === "update" ? buildLinePath2(pts, tw.interpolate) : tw.pathEl?.getAttribute("d") ?? "";
|
|
3083
|
+
snap.set(key, { type: "line", d, points: pts, pointKeys: tw.pointKeys });
|
|
3000
3084
|
break;
|
|
3001
3085
|
}
|
|
3002
3086
|
case "area": {
|
|
3003
3087
|
const top = interpolatePoints(tw.fromTop, tw.toTop, easedUpdate);
|
|
3004
3088
|
const bottom = interpolatePoints(tw.fromBottom, tw.toBottom, easedUpdate);
|
|
3005
|
-
const d = buildAreaPath2(top, bottom, tw.interpolate);
|
|
3089
|
+
const d = tw.kind === "update" ? buildAreaPath2(top, bottom, tw.interpolate) : tw.fillPathEl?.getAttribute("d") ?? "";
|
|
3006
3090
|
let topD;
|
|
3007
3091
|
if (tw.hasStroke) {
|
|
3008
|
-
topD = buildLinePath2(top, tw.interpolate);
|
|
3092
|
+
topD = tw.kind === "update" ? buildLinePath2(top, tw.interpolate) : tw.topPathEl?.getAttribute("d") ?? "";
|
|
3009
3093
|
}
|
|
3010
|
-
snap.set(key, {
|
|
3094
|
+
snap.set(key, {
|
|
3095
|
+
type: "area",
|
|
3096
|
+
d,
|
|
3097
|
+
topD,
|
|
3098
|
+
points: top,
|
|
3099
|
+
bottomPoints: bottom,
|
|
3100
|
+
pointKeys: tw.pointKeys
|
|
3101
|
+
});
|
|
3011
3102
|
break;
|
|
3012
3103
|
}
|
|
3013
3104
|
case "arc": {
|
|
@@ -3021,6 +3112,7 @@ function runTransition(args) {
|
|
|
3021
3112
|
}
|
|
3022
3113
|
}
|
|
3023
3114
|
}
|
|
3115
|
+
labelMotion.snapshot(snap);
|
|
3024
3116
|
return snap;
|
|
3025
3117
|
}
|
|
3026
3118
|
if (!manual) {
|
|
@@ -3041,9 +3133,6 @@ function runTransition(args) {
|
|
|
3041
3133
|
snapToFinal();
|
|
3042
3134
|
removeGhosts();
|
|
3043
3135
|
settleCanvas();
|
|
3044
|
-
for (let i = 0; i < secondaryEls.length; i++) {
|
|
3045
|
-
secondaryEls[i].style.opacity = secondaryOriginalOpacity[i];
|
|
3046
|
-
}
|
|
3047
3136
|
},
|
|
3048
3137
|
get running() {
|
|
3049
3138
|
return running;
|
|
@@ -3056,7 +3145,7 @@ function runTransition(args) {
|
|
|
3056
3145
|
for (const tw of tweens) {
|
|
3057
3146
|
applyTweenState(tw, elapsedMs, update, exit, enterDelay, enterDuration);
|
|
3058
3147
|
}
|
|
3059
|
-
|
|
3148
|
+
labelMotion.apply(sampleMotion3(update, elapsedMs).position);
|
|
3060
3149
|
if (elapsedMs >= totalMs) {
|
|
3061
3150
|
snapToFinal();
|
|
3062
3151
|
removeGhosts();
|
|
@@ -3100,27 +3189,6 @@ function pulseSparklineTerminator(svg, marksContainer) {
|
|
|
3100
3189
|
);
|
|
3101
3190
|
return dot;
|
|
3102
3191
|
}
|
|
3103
|
-
function collectSecondaryElements(svg) {
|
|
3104
|
-
const els = [];
|
|
3105
|
-
const epLabels = svg.querySelector(".oc-endpoint-labels");
|
|
3106
|
-
if (epLabels) els.push(epLabels);
|
|
3107
|
-
const markLabels = svg.querySelector(".oc-mark-labels");
|
|
3108
|
-
if (markLabels) els.push(markLabels);
|
|
3109
|
-
return els;
|
|
3110
|
-
}
|
|
3111
|
-
function applySecondaryOpacity(els, elapsed, enterDelay, enterDuration) {
|
|
3112
|
-
if (els.length === 0) return;
|
|
3113
|
-
let t;
|
|
3114
|
-
if (elapsed < enterDelay) {
|
|
3115
|
-
t = 0;
|
|
3116
|
-
} else {
|
|
3117
|
-
t = Math.min((elapsed - enterDelay) / enterDuration, 1);
|
|
3118
|
-
}
|
|
3119
|
-
const opacity = String(cubicOut(t));
|
|
3120
|
-
for (const el of els) {
|
|
3121
|
-
el.style.opacity = opacity;
|
|
3122
|
-
}
|
|
3123
|
-
}
|
|
3124
3192
|
|
|
3125
3193
|
// src/dom-helpers.ts
|
|
3126
3194
|
function applySrOnlyStyles(el) {
|
|
@@ -3170,10 +3238,12 @@ function createYouDrawIt(options) {
|
|
|
3170
3238
|
return pattern;
|
|
3171
3239
|
}
|
|
3172
3240
|
function findTargetLinePath(svg, seriesKey) {
|
|
3173
|
-
if (seriesKey) {
|
|
3174
|
-
const
|
|
3175
|
-
|
|
3176
|
-
|
|
3241
|
+
if (seriesKey !== void 0) {
|
|
3242
|
+
const group = [...svg.querySelectorAll(".oc-mark-line")].find(
|
|
3243
|
+
(element) => element.getAttribute("data-series") === seriesKey
|
|
3244
|
+
);
|
|
3245
|
+
const path = group?.querySelector("path");
|
|
3246
|
+
if (path) return path;
|
|
3177
3247
|
}
|
|
3178
3248
|
return svg.querySelector(".oc-mark-line path");
|
|
3179
3249
|
}
|
|
@@ -3584,6 +3654,78 @@ function createYouDrawIt(options) {
|
|
|
3584
3654
|
};
|
|
3585
3655
|
}
|
|
3586
3656
|
|
|
3657
|
+
// src/transition/reconcile.ts
|
|
3658
|
+
function identity(element, index) {
|
|
3659
|
+
for (const attr of ["data-key", "data-label-key", "data-endpoint-key", "data-tick-value"]) {
|
|
3660
|
+
const value = element.getAttribute(attr);
|
|
3661
|
+
if (value !== null)
|
|
3662
|
+
return `${element.tagName}:${attr}:${value}:${element.getAttribute("data-endpoint-side") ?? ""}`;
|
|
3663
|
+
}
|
|
3664
|
+
return `${element.tagName}:${element.getAttribute("class") ?? ""}:${index}`;
|
|
3665
|
+
}
|
|
3666
|
+
function reconcileSVG(current, next) {
|
|
3667
|
+
for (const attribute of Array.from(current.attributes)) {
|
|
3668
|
+
if (!next.hasAttribute(attribute.name)) current.removeAttribute(attribute.name);
|
|
3669
|
+
}
|
|
3670
|
+
for (const attribute of Array.from(next.attributes)) {
|
|
3671
|
+
if (current.getAttribute(attribute.name) !== attribute.value) {
|
|
3672
|
+
current.setAttribute(attribute.name, attribute.value);
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
const candidates = new Map(
|
|
3676
|
+
Array.from(current.children, (child, index) => [identity(child, index), child])
|
|
3677
|
+
);
|
|
3678
|
+
const survivors = /* @__PURE__ */ new Set();
|
|
3679
|
+
let elementIndex = 0;
|
|
3680
|
+
Array.from(next.childNodes).forEach((child, index) => {
|
|
3681
|
+
const match = child.nodeType === 1 ? candidates.get(identity(child, elementIndex++)) : void 0;
|
|
3682
|
+
const node = match ?? child;
|
|
3683
|
+
if (match) reconcileSVG(match, child);
|
|
3684
|
+
survivors.add(node);
|
|
3685
|
+
const at = current.childNodes[index];
|
|
3686
|
+
if (at !== node) current.insertBefore(node, at ?? null);
|
|
3687
|
+
});
|
|
3688
|
+
for (const child of Array.from(current.childNodes)) {
|
|
3689
|
+
if (!survivors.has(child)) current.removeChild(child);
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
// src/transition/scene.ts
|
|
3694
|
+
function sampleLayout(layout, snapshot) {
|
|
3695
|
+
if (!snapshot?.size) return layout;
|
|
3696
|
+
const marks = layout.marks.map((mark) => {
|
|
3697
|
+
const geometry = mark.key ? snapshot.get(mark.key) : void 0;
|
|
3698
|
+
if (!geometry) return mark;
|
|
3699
|
+
if ((mark.type === "line" && geometry.type === "line" || mark.type === "area" && geometry.type === "area") && geometry.points) {
|
|
3700
|
+
const positions = new Map(
|
|
3701
|
+
(geometry.pointKeys ?? mark.pointKeys ?? []).map((key, index) => [
|
|
3702
|
+
key,
|
|
3703
|
+
geometry.points[index]
|
|
3704
|
+
])
|
|
3705
|
+
);
|
|
3706
|
+
return {
|
|
3707
|
+
...mark,
|
|
3708
|
+
...mark.type === "line" ? { points: geometry.points } : {
|
|
3709
|
+
topPoints: geometry.points,
|
|
3710
|
+
bottomPoints: geometry.type === "area" ? geometry.bottomPoints ?? mark.bottomPoints : mark.bottomPoints
|
|
3711
|
+
},
|
|
3712
|
+
path: geometry.d,
|
|
3713
|
+
dataPoints: mark.dataPoints?.map((point, index) => ({
|
|
3714
|
+
...point,
|
|
3715
|
+
...positions.get(mark.pointKeys?.[index] ?? "") ?? point
|
|
3716
|
+
}))
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
if (mark.type === "arc" && geometry.type === "arc")
|
|
3720
|
+
return { ...mark, ...geometry, center: { x: geometry.cx, y: geometry.cy } };
|
|
3721
|
+
if (mark.type === "rect" && geometry.type === "rect") return { ...mark, ...geometry };
|
|
3722
|
+
if (mark.type === "point" && geometry.type === "point")
|
|
3723
|
+
return { ...mark, ...geometry, r: geometry.r ?? mark.r };
|
|
3724
|
+
return mark;
|
|
3725
|
+
});
|
|
3726
|
+
return { ...layout, marks };
|
|
3727
|
+
}
|
|
3728
|
+
|
|
3587
3729
|
// src/mount.ts
|
|
3588
3730
|
import { cssTokenDefault, isGraphSpec, isLayerSpec } from "@opendata-ai/openchart-core";
|
|
3589
3731
|
import {
|
|
@@ -3619,7 +3761,7 @@ function setupAnimationCleanup(svg, onComplete, totalMs) {
|
|
|
3619
3761
|
cancelAnimations(svg);
|
|
3620
3762
|
};
|
|
3621
3763
|
}
|
|
3622
|
-
function setupTableAnimationCleanup(wrapper) {
|
|
3764
|
+
function setupTableAnimationCleanup(wrapper, onComplete) {
|
|
3623
3765
|
const style = wrapper.style;
|
|
3624
3766
|
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 500;
|
|
3625
3767
|
const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
|
|
@@ -3628,6 +3770,7 @@ function setupTableAnimationCleanup(wrapper) {
|
|
|
3628
3770
|
const totalTime = totalStagger + duration + 300;
|
|
3629
3771
|
const timer = setTimeout(() => {
|
|
3630
3772
|
wrapper.classList.remove("oc-animate");
|
|
3773
|
+
onComplete?.();
|
|
3631
3774
|
}, totalTime);
|
|
3632
3775
|
return () => {
|
|
3633
3776
|
clearTimeout(timer);
|
|
@@ -3642,13 +3785,13 @@ function buildMarkDataMap(layout) {
|
|
|
3642
3785
|
const mark = layout.marks[i];
|
|
3643
3786
|
switch (mark.type) {
|
|
3644
3787
|
case "line":
|
|
3645
|
-
map.set(`line-${
|
|
3788
|
+
map.set(`line-${i}`, {
|
|
3646
3789
|
datum: mark.data[0] ?? {},
|
|
3647
3790
|
series: mark.seriesKey
|
|
3648
3791
|
});
|
|
3649
3792
|
break;
|
|
3650
3793
|
case "area":
|
|
3651
|
-
map.set(`area-${
|
|
3794
|
+
map.set(`area-${i}`, {
|
|
3652
3795
|
datum: mark.data[0] ?? {},
|
|
3653
3796
|
series: mark.seriesKey
|
|
3654
3797
|
});
|
|
@@ -3754,7 +3897,9 @@ function collectSeriesGroups(layout) {
|
|
|
3754
3897
|
for (let i = 0; i < layout.marks.length; i++) {
|
|
3755
3898
|
const mark = layout.marks[i];
|
|
3756
3899
|
if ((mark.type === "line" || mark.type === "area") && mark.dataPoints?.length) {
|
|
3757
|
-
const
|
|
3900
|
+
const scope = mark.key?.match(/^layer:\d+:/)?.[0] ?? "";
|
|
3901
|
+
const seriesKey = mark.seriesKey ?? "__default__";
|
|
3902
|
+
const key = `${scope}${seriesKey}`;
|
|
3758
3903
|
const existingType = markTypeByKey.get(key);
|
|
3759
3904
|
if (existingType === "line" && mark.type === "area") continue;
|
|
3760
3905
|
const color = mark.type === "line" ? mark.stroke : getRepresentativeColor(mark.fill);
|
|
@@ -3762,7 +3907,7 @@ function collectSeriesGroups(layout) {
|
|
|
3762
3907
|
for (const dp of mark.dataPoints) {
|
|
3763
3908
|
pointsByX.set(snapKey(dp.x), { ...dp });
|
|
3764
3909
|
}
|
|
3765
|
-
byKey.set(key, { seriesKey:
|
|
3910
|
+
byKey.set(key, { key, seriesKey, markId: `${mark.type}-${i}`, color, pointsByX });
|
|
3766
3911
|
markTypeByKey.set(key, mark.type);
|
|
3767
3912
|
}
|
|
3768
3913
|
}
|
|
@@ -3796,6 +3941,9 @@ function buildSliceTooltip(hits, emphasisKey) {
|
|
|
3796
3941
|
const title = hits[0].point.tooltip?.title;
|
|
3797
3942
|
const fields = [];
|
|
3798
3943
|
const isMulti = hits.length > 1;
|
|
3944
|
+
const seriesCounts = /* @__PURE__ */ new Map();
|
|
3945
|
+
for (const { group } of hits)
|
|
3946
|
+
seriesCounts.set(group.seriesKey, (seriesCounts.get(group.seriesKey) ?? 0) + 1);
|
|
3799
3947
|
let total;
|
|
3800
3948
|
for (const { group, point } of hits) {
|
|
3801
3949
|
const tip = point.tooltip;
|
|
@@ -3808,10 +3956,10 @@ function buildSliceTooltip(hits, emphasisKey) {
|
|
|
3808
3956
|
) ?? candidates[candidates.length - 1] ?? null;
|
|
3809
3957
|
if (!yField) continue;
|
|
3810
3958
|
fields.push({
|
|
3811
|
-
label: group.seriesKey,
|
|
3959
|
+
label: group.seriesKey === "__default__" ? yField.label : (seriesCounts.get(group.seriesKey) ?? 0) > 1 ? `${group.seriesKey} \xB7 ${yField.label}` : group.seriesKey,
|
|
3812
3960
|
value: yField.value,
|
|
3813
3961
|
color: group.color,
|
|
3814
|
-
...emphasisKey === group.
|
|
3962
|
+
...emphasisKey === group.key ? { emphasis: true } : {}
|
|
3815
3963
|
});
|
|
3816
3964
|
} else {
|
|
3817
3965
|
for (const f of tip.fields) {
|
|
@@ -3884,8 +4032,10 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
|
3884
4032
|
crosshair.style.display = "";
|
|
3885
4033
|
}
|
|
3886
4034
|
currentEmphasis = emphasisKey;
|
|
3887
|
-
|
|
3888
|
-
|
|
4035
|
+
const emphasizedGroup = groups.find((group) => group.key === emphasisKey);
|
|
4036
|
+
if (emphasizedGroup) {
|
|
4037
|
+
emphasis?.setMark(svg.querySelector(`[data-mark-id="${emphasizedGroup.markId}"]`));
|
|
4038
|
+
} else emphasis?.clear();
|
|
3889
4039
|
const tooltip = buildSliceTooltip(hits, emphasisKey);
|
|
3890
4040
|
if (!tooltip) return false;
|
|
3891
4041
|
const containerAnchorX = snappedX * viewBoxToContainer;
|
|
@@ -3898,7 +4048,7 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
|
3898
4048
|
function keysAt(snappedX) {
|
|
3899
4049
|
const keys = [];
|
|
3900
4050
|
for (const group of groups) {
|
|
3901
|
-
if (group.pointsByX.has(snappedX)) keys.push(group.
|
|
4051
|
+
if (group.pointsByX.has(snappedX)) keys.push(group.key);
|
|
3902
4052
|
}
|
|
3903
4053
|
return keys;
|
|
3904
4054
|
}
|
|
@@ -3914,7 +4064,7 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
|
3914
4064
|
const dy = Math.abs(point.y - svgY);
|
|
3915
4065
|
if (dy < nearestDy) {
|
|
3916
4066
|
nearestDy = dy;
|
|
3917
|
-
nearestKey = group.
|
|
4067
|
+
nearestKey = group.key;
|
|
3918
4068
|
}
|
|
3919
4069
|
}
|
|
3920
4070
|
const emphasisKey = nearestDy <= EMPHASIS_PROXIMITY_PX ? nearestKey : null;
|
|
@@ -3959,13 +4109,12 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
|
3959
4109
|
if (te.touches.length === 0) return;
|
|
3960
4110
|
const t = te.touches[0];
|
|
3961
4111
|
const { svgX, svgY, viewBoxToContainer } = toSvgCoords(t.clientX, t.clientY);
|
|
3962
|
-
if (te.cancelable) te.preventDefault();
|
|
3963
4112
|
positionAt(svgX, svgY, viewBoxToContainer);
|
|
3964
4113
|
};
|
|
3965
4114
|
overlay.addEventListener("mousemove", handleMouseMove);
|
|
3966
4115
|
overlay.addEventListener("mouseleave", hideOnPointerOut);
|
|
3967
|
-
overlay.addEventListener("touchstart", handleTouch, { passive:
|
|
3968
|
-
overlay.addEventListener("touchmove", handleTouch, { passive:
|
|
4116
|
+
overlay.addEventListener("touchstart", handleTouch, { passive: true });
|
|
4117
|
+
overlay.addEventListener("touchmove", handleTouch, { passive: true });
|
|
3969
4118
|
overlay.addEventListener("touchend", hideOnPointerOut);
|
|
3970
4119
|
return {
|
|
3971
4120
|
cleanup() {
|
|
@@ -4269,7 +4418,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4269
4418
|
};
|
|
4270
4419
|
}
|
|
4271
4420
|
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4272
|
-
const
|
|
4421
|
+
const SVG_NS4 = "http://www.w3.org/2000/svg";
|
|
4273
4422
|
const cleanups = [];
|
|
4274
4423
|
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4275
4424
|
for (const el of annotationGroups) {
|
|
@@ -4307,7 +4456,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4307
4456
|
const createdHandles = [];
|
|
4308
4457
|
for (const ep of endpoints) {
|
|
4309
4458
|
if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
|
|
4310
|
-
const handleEl = document.createElementNS(
|
|
4459
|
+
const handleEl = document.createElementNS(SVG_NS4, "circle");
|
|
4311
4460
|
handleEl.setAttribute("class", "oc-connector-handle");
|
|
4312
4461
|
handleEl.setAttribute("data-endpoint", ep.name);
|
|
4313
4462
|
handleEl.setAttribute("cx", String(ep.cx));
|
|
@@ -4584,7 +4733,7 @@ function wireSeriesLabelDrag(svg, spec, onEdit, setDragging) {
|
|
|
4584
4733
|
function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
4585
4734
|
if (!onEdit || !layout.xInvert || !layout.yInvert) return () => {
|
|
4586
4735
|
};
|
|
4587
|
-
const
|
|
4736
|
+
const SVG_NS4 = "http://www.w3.org/2000/svg";
|
|
4588
4737
|
const cleanups = [];
|
|
4589
4738
|
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4590
4739
|
for (const el of annotationGroups) {
|
|
@@ -4599,7 +4748,7 @@ function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
|
4599
4748
|
if (!resolved?.dot) continue;
|
|
4600
4749
|
const anchorX = resolved.dot.x;
|
|
4601
4750
|
const anchorY = resolved.dot.y;
|
|
4602
|
-
const handleEl = document.createElementNS(
|
|
4751
|
+
const handleEl = document.createElementNS(SVG_NS4, "circle");
|
|
4603
4752
|
handleEl.setAttribute("class", "oc-anchor-handle");
|
|
4604
4753
|
handleEl.setAttribute("cx", String(anchorX));
|
|
4605
4754
|
handleEl.setAttribute("cy", String(anchorY));
|
|
@@ -5412,6 +5561,7 @@ function wireCanvasInteractions({
|
|
|
5412
5561
|
}
|
|
5413
5562
|
|
|
5414
5563
|
// src/scatter-canvas/entrance.ts
|
|
5564
|
+
import { sampleMotion as sampleMotion4 } from "@opendata-ai/openchart-core";
|
|
5415
5565
|
var MAX_TOTAL_STAGGER_MS = 300;
|
|
5416
5566
|
var POINT_DURATION_FRACTION = 0.4;
|
|
5417
5567
|
function clampStagger(delay, elementCount) {
|
|
@@ -5430,7 +5580,7 @@ function entranceAlphaAt(t, markIndex, enter, markCount, ease) {
|
|
|
5430
5580
|
const local = (t - stagger * markIndex) / pointDuration;
|
|
5431
5581
|
if (local <= 0) return 0;
|
|
5432
5582
|
if (local >= 1) return 1;
|
|
5433
|
-
return ease(local);
|
|
5583
|
+
return enter.spring ? sampleMotion4(enter, local * enter.duration).position : ease(local);
|
|
5434
5584
|
}
|
|
5435
5585
|
function applyEntranceAt(state, t, enter, ease) {
|
|
5436
5586
|
const alpha = state.enterAlpha;
|
|
@@ -5914,8 +6064,8 @@ function createChart(container, spec, options) {
|
|
|
5914
6064
|
let isAutoHeight = null;
|
|
5915
6065
|
let lastRenderedWidth = null;
|
|
5916
6066
|
let lastRenderedSvgHeight = null;
|
|
6067
|
+
let lastHostHeight = FALLBACK_HEIGHT;
|
|
5917
6068
|
let transitionHandle = null;
|
|
5918
|
-
let transitionSnapshot = null;
|
|
5919
6069
|
let fontsReloadPending = false;
|
|
5920
6070
|
let selectedElement = options?.selectedElement ?? null;
|
|
5921
6071
|
let overlayElement = null;
|
|
@@ -5929,13 +6079,13 @@ function createChart(container, spec, options) {
|
|
|
5929
6079
|
let youDrawIt = null;
|
|
5930
6080
|
let ydiEditSuppressWarned = false;
|
|
5931
6081
|
container.classList.add("oc-root");
|
|
5932
|
-
function resolveEffectiveFont() {
|
|
5933
|
-
return options?.theme?.fonts?.family ??
|
|
6082
|
+
function resolveEffectiveFont(candidate = currentSpec) {
|
|
6083
|
+
return options?.theme?.fonts?.family ?? candidate.theme?.fonts?.family ?? resolveFontFamily(container);
|
|
5934
6084
|
}
|
|
5935
6085
|
let fontFamily = resolveEffectiveFont();
|
|
5936
6086
|
let measureText = createMeasureText(fontFamily);
|
|
5937
6087
|
let renderGen = 0;
|
|
5938
|
-
function compileAt(width, height) {
|
|
6088
|
+
function compileAt(width, height, candidate, measure, runtime) {
|
|
5939
6089
|
const darkMode = resolveDarkMode(options?.darkMode);
|
|
5940
6090
|
const compileOpts = {
|
|
5941
6091
|
width,
|
|
@@ -5944,12 +6094,11 @@ function createChart(container, spec, options) {
|
|
|
5944
6094
|
darkMode,
|
|
5945
6095
|
renderer: options?.renderer,
|
|
5946
6096
|
watermark: options?.watermark,
|
|
5947
|
-
measureText
|
|
6097
|
+
measureText: measure
|
|
5948
6098
|
};
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
return compileChart(withRuntimeHidden(currentSpec), compileOpts);
|
|
6099
|
+
const effective = runtime ? withRuntimeHidden(candidate) : candidate;
|
|
6100
|
+
if (isLayerSpec(effective)) return compileLayer(effective, compileOpts);
|
|
6101
|
+
return compileChart(effective, compileOpts);
|
|
5953
6102
|
}
|
|
5954
6103
|
function verticalOverheads(layout, width) {
|
|
5955
6104
|
let topLegendBlock = 0;
|
|
@@ -5968,19 +6117,19 @@ function createChart(container, spec, options) {
|
|
|
5968
6117
|
}
|
|
5969
6118
|
return layout.chrome.topHeight + layout.chrome.bottomHeight + topLegendBlock + (layout.metrics?.height ?? 0) + facetGrowth;
|
|
5970
6119
|
}
|
|
5971
|
-
function compile() {
|
|
5972
|
-
const { width, height } = getContainerDimensions();
|
|
5973
|
-
const growsWithChrome = isAutoHeight === true && !isLayerSpec(
|
|
6120
|
+
function compile(candidate = currentSpec, measure = measureText, runtime = true) {
|
|
6121
|
+
const { width, height } = getContainerDimensions(candidate);
|
|
6122
|
+
const growsWithChrome = isAutoHeight === true && !isLayerSpec(candidate) && !isGraphSpec(candidate) && candidate.display !== "sparkline";
|
|
5974
6123
|
if (!growsWithChrome) {
|
|
5975
|
-
return compileAt(width, height);
|
|
6124
|
+
return compileAt(width, height, candidate, measure, runtime);
|
|
5976
6125
|
}
|
|
5977
|
-
let layout = compileAt(width, height);
|
|
6126
|
+
let layout = compileAt(width, height, candidate, measure, runtime);
|
|
5978
6127
|
const overheads = verticalOverheads(layout, width);
|
|
5979
6128
|
if (overheads > 0) {
|
|
5980
|
-
layout = compileAt(width, height + overheads);
|
|
6129
|
+
layout = compileAt(width, height + overheads, candidate, measure, runtime);
|
|
5981
6130
|
const overheads2 = verticalOverheads(layout, width);
|
|
5982
6131
|
if (overheads2 !== overheads) {
|
|
5983
|
-
layout = compileAt(width, height + overheads2);
|
|
6132
|
+
layout = compileAt(width, height + overheads2, candidate, measure, runtime);
|
|
5984
6133
|
}
|
|
5985
6134
|
}
|
|
5986
6135
|
return layout;
|
|
@@ -6062,9 +6211,9 @@ function createChart(container, spec, options) {
|
|
|
6062
6211
|
if (!("field" in colorEnc) || !colorEnc.field) return void 0;
|
|
6063
6212
|
return colorEnc.field;
|
|
6064
6213
|
}
|
|
6065
|
-
function getContainerDimensions() {
|
|
6214
|
+
function getContainerDimensions(candidate = currentSpec) {
|
|
6066
6215
|
const rect = container.getBoundingClientRect();
|
|
6067
|
-
const isSparkline = "display" in
|
|
6216
|
+
const isSparkline = "display" in candidate && candidate.display === "sparkline";
|
|
6068
6217
|
if (isSparkline) {
|
|
6069
6218
|
let width = rect.width;
|
|
6070
6219
|
let height = rect.height;
|
|
@@ -6081,12 +6230,14 @@ function createChart(container, spec, options) {
|
|
|
6081
6230
|
if (isAutoHeight === null && (rect.width > 0 || rect.height > 0)) {
|
|
6082
6231
|
isAutoHeight = rect.height === 0 && rect.width > 0;
|
|
6083
6232
|
}
|
|
6233
|
+
const ownHeight = lastRenderedSvgHeight !== null && Math.abs(rect.height - lastRenderedSvgHeight) < 0.5;
|
|
6234
|
+
if (!ownHeight && rect.height > 0) lastHostHeight = rect.height;
|
|
6084
6235
|
return {
|
|
6085
6236
|
width: Math.max(rect.width || 600, 100),
|
|
6086
6237
|
// Latched-auto mounts always compile against the fixed budget: after
|
|
6087
6238
|
// the first render the container's measured height is just our own
|
|
6088
6239
|
// SVG, so reading it back would re-pin whatever we rendered last.
|
|
6089
|
-
height: isAutoHeight === true ? FALLBACK_HEIGHT : Math.max(rect.height || FALLBACK_HEIGHT, 100)
|
|
6240
|
+
height: isAutoHeight === true ? FALLBACK_HEIGHT : Math.max(ownHeight ? lastHostHeight : rect.height || FALLBACK_HEIGHT, 100)
|
|
6090
6241
|
};
|
|
6091
6242
|
}
|
|
6092
6243
|
function getSpecAnnotations() {
|
|
@@ -6314,11 +6465,13 @@ function createChart(container, spec, options) {
|
|
|
6314
6465
|
svg.removeEventListener("keydown", handleKeyDown);
|
|
6315
6466
|
};
|
|
6316
6467
|
}
|
|
6317
|
-
function render() {
|
|
6468
|
+
function render(retainScene = false, prepared) {
|
|
6469
|
+
const retainedSVG = retainScene ? svgElement : null;
|
|
6318
6470
|
if (isDragging) {
|
|
6319
6471
|
pendingRender = true;
|
|
6320
6472
|
return;
|
|
6321
6473
|
}
|
|
6474
|
+
const nextLayout = prepared ?? compile();
|
|
6322
6475
|
if (transitionHandle) {
|
|
6323
6476
|
transitionHandle.cancel();
|
|
6324
6477
|
transitionHandle = null;
|
|
@@ -6363,7 +6516,7 @@ function createChart(container, spec, options) {
|
|
|
6363
6516
|
isTextEditingActive = false;
|
|
6364
6517
|
}
|
|
6365
6518
|
overlayElement = null;
|
|
6366
|
-
if (svgElement?.parentNode) {
|
|
6519
|
+
if (!retainedSVG && svgElement?.parentNode) {
|
|
6367
6520
|
svgElement.parentNode.removeChild(svgElement);
|
|
6368
6521
|
}
|
|
6369
6522
|
if (tooltipManager) {
|
|
@@ -6373,7 +6526,7 @@ function createChart(container, spec, options) {
|
|
|
6373
6526
|
srTableWrapper.parentNode.removeChild(srTableWrapper);
|
|
6374
6527
|
srTableWrapper = null;
|
|
6375
6528
|
}
|
|
6376
|
-
currentLayout =
|
|
6529
|
+
currentLayout = nextLayout;
|
|
6377
6530
|
lastRenderedWidth = currentLayout.dimensions.width;
|
|
6378
6531
|
lastRenderedSvgHeight = currentLayout.dimensions.height;
|
|
6379
6532
|
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enter;
|
|
@@ -6381,6 +6534,7 @@ function createChart(container, spec, options) {
|
|
|
6381
6534
|
const canvasMode = currentLayout.markRenderMode === "canvas";
|
|
6382
6535
|
if (canvasMode) {
|
|
6383
6536
|
canvasLayer = createScatterCanvasLayer(container, currentLayout);
|
|
6537
|
+
if (retainedSVG) container.insertBefore(canvasLayer.canvas, retainedSVG);
|
|
6384
6538
|
canvasLayer.repaint();
|
|
6385
6539
|
if (!warnedCanvasEditMode && (options?.editable || hasEditingCallbacks(options) || options?.onAnnotationEdit)) {
|
|
6386
6540
|
warnedCanvasEditMode = true;
|
|
@@ -6389,11 +6543,16 @@ function createChart(container, spec, options) {
|
|
|
6389
6543
|
);
|
|
6390
6544
|
}
|
|
6391
6545
|
}
|
|
6392
|
-
|
|
6546
|
+
const sceneHost = retainedSVG ? document.createElement("div") : container;
|
|
6547
|
+
svgElement = renderChartSVG(currentLayout, sceneHost, {
|
|
6393
6548
|
animate: shouldAnimate,
|
|
6394
6549
|
crosshair,
|
|
6395
6550
|
canvasMarks: canvasMode
|
|
6396
6551
|
});
|
|
6552
|
+
if (retainedSVG && svgElement) {
|
|
6553
|
+
reconcileSVG(retainedSVG, svgElement);
|
|
6554
|
+
svgElement = retainedSVG;
|
|
6555
|
+
}
|
|
6397
6556
|
tooltipManager = createTooltipManager(container);
|
|
6398
6557
|
if (canvasMode && canvasLayer) {
|
|
6399
6558
|
cleanupCanvasEvents = wireCanvasInteractions({
|
|
@@ -6589,30 +6748,32 @@ function createChart(container, spec, options) {
|
|
|
6589
6748
|
function beginManualUpdate(newSpec) {
|
|
6590
6749
|
return doUpdate(newSpec, void 0, true);
|
|
6591
6750
|
}
|
|
6592
|
-
function doUpdate(newSpec, updateOpts, manual) {
|
|
6751
|
+
function doUpdate(newSpec, updateOpts, manual, layoutOnly = false) {
|
|
6593
6752
|
if (destroyed) return null;
|
|
6594
6753
|
const prevSpec = currentSpec;
|
|
6595
6754
|
const prevLayout = currentLayout;
|
|
6596
6755
|
const entranceWasRunning = cleanupAnimations != null;
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6756
|
+
const nextFont = resolveEffectiveFont(newSpec);
|
|
6757
|
+
const nextMeasure = nextFont === fontFamily ? measureText : createMeasureText(nextFont);
|
|
6758
|
+
const nextLayout = compile(newSpec, nextMeasure, layoutOnly);
|
|
6759
|
+
const snapshot = transitionHandle?.running ? transitionHandle.snapshot() : void 0;
|
|
6600
6760
|
currentSpec = newSpec;
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6761
|
+
fontFamily = nextFont;
|
|
6762
|
+
measureText = nextMeasure;
|
|
6763
|
+
if (!layoutOnly) {
|
|
6764
|
+
runtimeHiddenSeries.clear();
|
|
6765
|
+
runtimeShownSeries.clear();
|
|
6766
|
+
}
|
|
6767
|
+
if (!layoutOnly) {
|
|
6768
|
+
searchBaseline = authoredHighlight(newSpec);
|
|
6769
|
+
seriesSearch?.setSelected([]);
|
|
6770
|
+
}
|
|
6610
6771
|
ydiEditSuppressWarned = false;
|
|
6611
6772
|
editSuppressWarned = false;
|
|
6612
6773
|
if (updateOpts && "selectedElement" in updateOpts) {
|
|
6613
6774
|
selectedElement = updateOpts.selectedElement ?? null;
|
|
6614
6775
|
}
|
|
6615
|
-
render();
|
|
6776
|
+
render(canTransitionSpecShape(prevSpec, newSpec), nextLayout);
|
|
6616
6777
|
if (svgElement && canTransition({
|
|
6617
6778
|
prevLayout,
|
|
6618
6779
|
nextLayout: currentLayout,
|
|
@@ -6621,14 +6782,12 @@ function createChart(container, spec, options) {
|
|
|
6621
6782
|
isFirstRender: false,
|
|
6622
6783
|
entranceInFlight: entranceWasRunning
|
|
6623
6784
|
})) {
|
|
6624
|
-
const snapshot = transitionSnapshot;
|
|
6625
|
-
transitionSnapshot = null;
|
|
6626
6785
|
const handle = runTransition({
|
|
6627
6786
|
svg: svgElement,
|
|
6628
6787
|
prevLayout,
|
|
6629
6788
|
nextLayout: currentLayout,
|
|
6630
6789
|
animation: currentLayout.animation,
|
|
6631
|
-
fromSnapshot: snapshot
|
|
6790
|
+
fromSnapshot: snapshot,
|
|
6632
6791
|
manual,
|
|
6633
6792
|
// render() above already rebuilt the layer for the NEXT layout, so this
|
|
6634
6793
|
// is the destination-mode layer the tween writes into.
|
|
@@ -6637,10 +6796,9 @@ function createChart(container, spec, options) {
|
|
|
6637
6796
|
transitionHandle = null;
|
|
6638
6797
|
}
|
|
6639
6798
|
});
|
|
6640
|
-
|
|
6799
|
+
transitionHandle = handle;
|
|
6641
6800
|
return handle;
|
|
6642
6801
|
}
|
|
6643
|
-
transitionSnapshot = null;
|
|
6644
6802
|
return null;
|
|
6645
6803
|
}
|
|
6646
6804
|
function resize() {
|
|
@@ -6657,7 +6815,7 @@ function createChart(container, spec, options) {
|
|
|
6657
6815
|
pendingResize = true;
|
|
6658
6816
|
return;
|
|
6659
6817
|
}
|
|
6660
|
-
|
|
6818
|
+
doUpdate(currentSpec, void 0, false, true);
|
|
6661
6819
|
}
|
|
6662
6820
|
function handleSearchChange(selected) {
|
|
6663
6821
|
const merged = [...searchBaseline];
|
|
@@ -6694,7 +6852,7 @@ function createChart(container, spec, options) {
|
|
|
6694
6852
|
}
|
|
6695
6853
|
const svg = canvasLayer && currentLayout.markRenderMode === "canvas" ? materializeCanvasModeSVG(currentLayout, {
|
|
6696
6854
|
forceVector: format === "png" || format === "jpg" || format === "gif"
|
|
6697
|
-
}) :
|
|
6855
|
+
}) : renderChartSVG(currentLayout, document.createElement("div"), { animate: false });
|
|
6698
6856
|
const theme = "theme" in currentLayout ? currentLayout.theme : void 0;
|
|
6699
6857
|
const withTheme = (o) => ({ theme, ...o ?? {} });
|
|
6700
6858
|
switch (format) {
|
|
@@ -6728,7 +6886,6 @@ function createChart(container, spec, options) {
|
|
|
6728
6886
|
transitionHandle.cancel();
|
|
6729
6887
|
transitionHandle = null;
|
|
6730
6888
|
}
|
|
6731
|
-
transitionSnapshot = null;
|
|
6732
6889
|
if (cleanupAnimations) {
|
|
6733
6890
|
cleanupAnimations();
|
|
6734
6891
|
cleanupAnimations = null;
|
|
@@ -6810,10 +6967,10 @@ function createChart(container, spec, options) {
|
|
|
6810
6967
|
container.dataset.ocFontsState = pending ? "pending" : "ready";
|
|
6811
6968
|
if (options?.responsive !== false) {
|
|
6812
6969
|
disconnectResize = observeResize(container, (width, height) => {
|
|
6970
|
+
const widthChanged = lastRenderedWidth === null || Math.abs(width - lastRenderedWidth) >= 0.5;
|
|
6971
|
+
const selfInducedHeight = lastRenderedSvgHeight !== null && Math.abs(height - lastRenderedSvgHeight) < 0.5;
|
|
6972
|
+
if (!widthChanged && selfInducedHeight) return;
|
|
6813
6973
|
if (isAutoHeight === true) {
|
|
6814
|
-
const widthChanged = width !== lastRenderedWidth;
|
|
6815
|
-
const selfInducedHeight = height === lastRenderedSvgHeight;
|
|
6816
|
-
if (!widthChanged && selfInducedHeight) return;
|
|
6817
6974
|
if (!selfInducedHeight) {
|
|
6818
6975
|
isAutoHeight = null;
|
|
6819
6976
|
}
|
|
@@ -6827,6 +6984,10 @@ function createChart(container, spec, options) {
|
|
|
6827
6984
|
update,
|
|
6828
6985
|
beginManualUpdate,
|
|
6829
6986
|
resize,
|
|
6987
|
+
sampleLayout: () => sampleLayout(
|
|
6988
|
+
currentLayout,
|
|
6989
|
+
transitionHandle?.running ? transitionHandle.snapshot() : void 0
|
|
6990
|
+
),
|
|
6830
6991
|
export: doExport,
|
|
6831
6992
|
destroy,
|
|
6832
6993
|
get layout() {
|
|
@@ -6976,6 +7137,84 @@ function scrubCamera(step, stepProgress, fitted, holdUntil, ease) {
|
|
|
6976
7137
|
// src/map-mount.ts
|
|
6977
7138
|
import { compileGeoMap } from "@opendata-ai/openchart-engine";
|
|
6978
7139
|
|
|
7140
|
+
// src/interactions/mark-navigation.ts
|
|
7141
|
+
function captureMarkFocus(svg) {
|
|
7142
|
+
const active = document.activeElement;
|
|
7143
|
+
if (!svg || !active || !svg.contains(active)) return null;
|
|
7144
|
+
return active.getAttribute("data-navigation-key") ?? "";
|
|
7145
|
+
}
|
|
7146
|
+
function restoreMarkFocus(svg, key) {
|
|
7147
|
+
if (key === null) return;
|
|
7148
|
+
const matches = [...svg.querySelectorAll("[data-navigation-key]")].filter(
|
|
7149
|
+
(element) => element.getAttribute("data-navigation-key") === key
|
|
7150
|
+
);
|
|
7151
|
+
const target = matches.length === 1 ? matches[0] : svg;
|
|
7152
|
+
if (target === svg && !svg.hasAttribute("tabindex")) svg.setAttribute("tabindex", "-1");
|
|
7153
|
+
target.focus({ preventScroll: true });
|
|
7154
|
+
}
|
|
7155
|
+
function wireMarkNavigation(svg, marks) {
|
|
7156
|
+
const cleanups = [];
|
|
7157
|
+
if (svg.getAttribute("role") === "img") svg.setAttribute("role", "group");
|
|
7158
|
+
marks.forEach((mark, index) => {
|
|
7159
|
+
const element = mark.element;
|
|
7160
|
+
element.setAttribute("tabindex", index === 0 ? "0" : "-1");
|
|
7161
|
+
element.setAttribute("data-navigation-key", mark.key);
|
|
7162
|
+
const focus = () => {
|
|
7163
|
+
for (const other of marks) other.element.setAttribute("tabindex", "-1");
|
|
7164
|
+
element.setAttribute("tabindex", "0");
|
|
7165
|
+
const bounds = element.getBoundingClientRect();
|
|
7166
|
+
mark.enter(
|
|
7167
|
+
new MouseEvent("focus", {
|
|
7168
|
+
clientX: bounds.left + bounds.width / 2,
|
|
7169
|
+
clientY: bounds.top + bounds.height / 2
|
|
7170
|
+
})
|
|
7171
|
+
);
|
|
7172
|
+
};
|
|
7173
|
+
const keydown = (event) => {
|
|
7174
|
+
let next;
|
|
7175
|
+
switch (event.key) {
|
|
7176
|
+
case "ArrowRight":
|
|
7177
|
+
case "ArrowDown":
|
|
7178
|
+
next = Math.min(index + 1, marks.length - 1);
|
|
7179
|
+
break;
|
|
7180
|
+
case "ArrowLeft":
|
|
7181
|
+
case "ArrowUp":
|
|
7182
|
+
next = Math.max(index - 1, 0);
|
|
7183
|
+
break;
|
|
7184
|
+
case "Home":
|
|
7185
|
+
next = 0;
|
|
7186
|
+
break;
|
|
7187
|
+
case "End":
|
|
7188
|
+
next = marks.length - 1;
|
|
7189
|
+
break;
|
|
7190
|
+
case "Enter":
|
|
7191
|
+
case " ":
|
|
7192
|
+
mark.activate();
|
|
7193
|
+
break;
|
|
7194
|
+
case "Escape":
|
|
7195
|
+
mark.leave();
|
|
7196
|
+
break;
|
|
7197
|
+
default:
|
|
7198
|
+
return;
|
|
7199
|
+
}
|
|
7200
|
+
event.preventDefault();
|
|
7201
|
+
event.stopPropagation();
|
|
7202
|
+
if (next !== void 0) marks[next].element.focus({ preventScroll: true });
|
|
7203
|
+
};
|
|
7204
|
+
element.addEventListener("focus", focus);
|
|
7205
|
+
element.addEventListener("blur", mark.leave);
|
|
7206
|
+
element.addEventListener("keydown", keydown);
|
|
7207
|
+
cleanups.push(() => {
|
|
7208
|
+
element.removeEventListener("focus", focus);
|
|
7209
|
+
element.removeEventListener("blur", mark.leave);
|
|
7210
|
+
element.removeEventListener("keydown", keydown);
|
|
7211
|
+
});
|
|
7212
|
+
});
|
|
7213
|
+
return () => {
|
|
7214
|
+
for (const cleanup of cleanups) cleanup();
|
|
7215
|
+
};
|
|
7216
|
+
}
|
|
7217
|
+
|
|
6979
7218
|
// src/map-camera.ts
|
|
6980
7219
|
var FOCUS_DIM_OPACITY = 0.25;
|
|
6981
7220
|
function focusTargetForFeatures(layout, ids, padding = 16) {
|
|
@@ -7073,6 +7312,21 @@ function applyMapCamera(svg, camera, layout) {
|
|
|
7073
7312
|
}
|
|
7074
7313
|
}
|
|
7075
7314
|
}
|
|
7315
|
+
function ensureMapClip(svg, layout, clipId) {
|
|
7316
|
+
if (svg.querySelector(`#${clipId}`)) return;
|
|
7317
|
+
let defs = svg.querySelector("defs");
|
|
7318
|
+
if (!defs) {
|
|
7319
|
+
defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
7320
|
+
svg.insertBefore(defs, svg.firstChild);
|
|
7321
|
+
}
|
|
7322
|
+
const clip = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
|
|
7323
|
+
clip.setAttribute("id", clipId);
|
|
7324
|
+
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
7325
|
+
rect.setAttribute("width", String(layout.area.width));
|
|
7326
|
+
rect.setAttribute("height", String(layout.area.height));
|
|
7327
|
+
clip.appendChild(rect);
|
|
7328
|
+
defs.appendChild(clip);
|
|
7329
|
+
}
|
|
7076
7330
|
|
|
7077
7331
|
// src/map-renderer.ts
|
|
7078
7332
|
var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
@@ -7318,6 +7572,14 @@ function renderMapSVG(layout, opts) {
|
|
|
7318
7572
|
}
|
|
7319
7573
|
const classes = animate ? "oc-map oc-animate" : "oc-map";
|
|
7320
7574
|
svg.setAttribute("class", classes);
|
|
7575
|
+
const background = createSVGElement2("rect");
|
|
7576
|
+
setAttrs2(background, {
|
|
7577
|
+
width,
|
|
7578
|
+
height,
|
|
7579
|
+
fill: layout.theme.colors.background,
|
|
7580
|
+
"aria-hidden": "true"
|
|
7581
|
+
});
|
|
7582
|
+
svg.appendChild(background);
|
|
7321
7583
|
const bulk = features.length > BULK_ANIMATION_THRESHOLD;
|
|
7322
7584
|
let mapStaggerBudget = 0;
|
|
7323
7585
|
if (animate && animation?.enter) {
|
|
@@ -7330,6 +7592,7 @@ function renderMapSVG(layout, opts) {
|
|
|
7330
7592
|
const stagger = !bulk && n > 1 ? mapStaggerBudget / (n - 1) : 0;
|
|
7331
7593
|
stampAnimationVars(svg, {
|
|
7332
7594
|
duration: perFeature,
|
|
7595
|
+
spring: animation.enter.spring,
|
|
7333
7596
|
stagger,
|
|
7334
7597
|
annotationDelay: animation.annotationDelay,
|
|
7335
7598
|
ease: animation.enter.ease
|
|
@@ -7498,7 +7761,7 @@ function runMapFillTransition(svg, prev, opts) {
|
|
|
7498
7761
|
const key = p.getAttribute("data-key");
|
|
7499
7762
|
const to = p.getAttribute("fill");
|
|
7500
7763
|
const from = key ? snapshot.fills.get(key) : void 0;
|
|
7501
|
-
if (from && to && from !== to) {
|
|
7764
|
+
if (from && to && from !== to && !from.startsWith("url(") && !to.startsWith("url(")) {
|
|
7502
7765
|
tweens.push({ kind: "fill", el: p, to, interp: interpolateRgb2(from, to) });
|
|
7503
7766
|
p.setAttribute("fill", from);
|
|
7504
7767
|
}
|
|
@@ -7566,7 +7829,6 @@ var KEY_PAN_FRACTION = 0.1;
|
|
|
7566
7829
|
var DRAG_THRESHOLD = 3;
|
|
7567
7830
|
var HINT_MS = 1500;
|
|
7568
7831
|
var EPS = 1e-3;
|
|
7569
|
-
var SVG_NS4 = "http://www.w3.org/2000/svg";
|
|
7570
7832
|
var clipIdCounter = 0;
|
|
7571
7833
|
var ICONS = {
|
|
7572
7834
|
in: '<path d="M8 3v10M3 8h10"/>',
|
|
@@ -7921,21 +8183,6 @@ function createMapZoomController(host) {
|
|
|
7921
8183
|
overlay.style.width = `${area.width / width * 100}%`;
|
|
7922
8184
|
overlay.style.height = `${area.height / height * 100}%`;
|
|
7923
8185
|
}
|
|
7924
|
-
function ensureClip(svg, layout) {
|
|
7925
|
-
if (svg.querySelector(`#${clipId}`)) return;
|
|
7926
|
-
let defs = svg.querySelector("defs");
|
|
7927
|
-
if (!defs) {
|
|
7928
|
-
defs = document.createElementNS(SVG_NS4, "defs");
|
|
7929
|
-
svg.insertBefore(defs, svg.firstChild);
|
|
7930
|
-
}
|
|
7931
|
-
const clip = document.createElementNS(SVG_NS4, "clipPath");
|
|
7932
|
-
clip.setAttribute("id", clipId);
|
|
7933
|
-
const rect = document.createElementNS(SVG_NS4, "rect");
|
|
7934
|
-
rect.setAttribute("width", String(layout.area.width));
|
|
7935
|
-
rect.setAttribute("height", String(layout.area.height));
|
|
7936
|
-
clip.appendChild(rect);
|
|
7937
|
-
defs.appendChild(clip);
|
|
7938
|
-
}
|
|
7939
8186
|
function onRender(restoreFocus) {
|
|
7940
8187
|
const cfg = host.getConfig();
|
|
7941
8188
|
const svg = host.getSvg();
|
|
@@ -7946,7 +8193,7 @@ function createMapZoomController(host) {
|
|
|
7946
8193
|
const layout = host.getLayout();
|
|
7947
8194
|
svg.setAttribute("tabindex", "0");
|
|
7948
8195
|
svg.setAttribute("aria-keyshortcuts", "+ - 0 ArrowUp ArrowDown ArrowLeft ArrowRight");
|
|
7949
|
-
|
|
8196
|
+
ensureMapClip(svg, layout, clipId);
|
|
7950
8197
|
ensureOverlay(cfg.controls);
|
|
7951
8198
|
placeOverlay(layout);
|
|
7952
8199
|
if (restoreFocus) svg.focus({ preventScroll: true });
|
|
@@ -8077,8 +8324,8 @@ function createGeoMap(container, spec, options) {
|
|
|
8077
8324
|
let isFirstRender = true;
|
|
8078
8325
|
let fontsReloadPending = false;
|
|
8079
8326
|
container.classList.add("oc-map-root");
|
|
8080
|
-
function resolveEffectiveFont() {
|
|
8081
|
-
return options?.theme?.fonts?.family ??
|
|
8327
|
+
function resolveEffectiveFont(candidate = currentSpec) {
|
|
8328
|
+
return options?.theme?.fonts?.family ?? candidate.theme?.fonts?.family ?? resolveFontFamily(container);
|
|
8082
8329
|
}
|
|
8083
8330
|
let fontFamily = resolveEffectiveFont();
|
|
8084
8331
|
let measureText = createMeasureText(fontFamily);
|
|
@@ -8095,7 +8342,7 @@ function createGeoMap(container, spec, options) {
|
|
|
8095
8342
|
}
|
|
8096
8343
|
return { width, height: Math.round(width * 0.625) };
|
|
8097
8344
|
}
|
|
8098
|
-
function compile() {
|
|
8345
|
+
function compile(candidate = currentSpec, measure = measureText) {
|
|
8099
8346
|
const { width, height } = getContainerDimensions();
|
|
8100
8347
|
const darkMode = resolveDarkMode(options?.darkMode);
|
|
8101
8348
|
const compileOpts = {
|
|
@@ -8104,12 +8351,14 @@ function createGeoMap(container, spec, options) {
|
|
|
8104
8351
|
theme: options?.theme,
|
|
8105
8352
|
darkMode,
|
|
8106
8353
|
watermark: options?.watermark,
|
|
8107
|
-
measureText
|
|
8354
|
+
measureText: measure
|
|
8108
8355
|
};
|
|
8109
|
-
return compileGeoMap(
|
|
8356
|
+
return compileGeoMap(candidate, compileOpts);
|
|
8110
8357
|
}
|
|
8111
8358
|
function wireTooltipAndInteraction(svg, layout) {
|
|
8112
8359
|
const cleanups = [];
|
|
8360
|
+
const navigation = [];
|
|
8361
|
+
let activeLeave = null;
|
|
8113
8362
|
const featuresById = /* @__PURE__ */ new Map();
|
|
8114
8363
|
for (const f of layout.features) {
|
|
8115
8364
|
const key = String(f.id);
|
|
@@ -8122,9 +8371,11 @@ function createGeoMap(container, spec, options) {
|
|
|
8122
8371
|
const content = layout.tooltipDescriptors.get(featureId);
|
|
8123
8372
|
const feature = featuresById.get(featureId);
|
|
8124
8373
|
const handleMouseEnter = (e) => {
|
|
8374
|
+
activeLeave?.();
|
|
8375
|
+
activeLeave = handleMouseLeave;
|
|
8125
8376
|
const mouseEvent = e;
|
|
8126
8377
|
el.style.setProperty("cursor", "pointer");
|
|
8127
|
-
raiseFeature(el, svg);
|
|
8378
|
+
if (document.activeElement !== el) raiseFeature(el, svg);
|
|
8128
8379
|
el.classList.add("oc-map-feature--hover");
|
|
8129
8380
|
if (content && tooltipManager && options?.tooltip !== false) {
|
|
8130
8381
|
const svgRect = svg.getBoundingClientRect();
|
|
@@ -8151,6 +8402,8 @@ function createGeoMap(container, spec, options) {
|
|
|
8151
8402
|
}
|
|
8152
8403
|
};
|
|
8153
8404
|
const handleMouseLeave = () => {
|
|
8405
|
+
if (activeLeave !== handleMouseLeave) return;
|
|
8406
|
+
activeLeave = null;
|
|
8154
8407
|
el.style.removeProperty("cursor");
|
|
8155
8408
|
el.classList.remove("oc-map-feature--hover");
|
|
8156
8409
|
restoreFeatureOrder(el);
|
|
@@ -8167,6 +8420,13 @@ function createGeoMap(container, spec, options) {
|
|
|
8167
8420
|
});
|
|
8168
8421
|
}
|
|
8169
8422
|
};
|
|
8423
|
+
navigation.push({
|
|
8424
|
+
element: el,
|
|
8425
|
+
key: `feature:${featureId}`,
|
|
8426
|
+
enter: handleMouseEnter,
|
|
8427
|
+
leave: handleMouseLeave,
|
|
8428
|
+
activate: handleClick
|
|
8429
|
+
});
|
|
8170
8430
|
el.addEventListener("mouseenter", handleMouseEnter);
|
|
8171
8431
|
el.addEventListener("mousemove", handleMouseMove);
|
|
8172
8432
|
el.addEventListener("mouseleave", handleMouseLeave);
|
|
@@ -8202,6 +8462,8 @@ function createGeoMap(container, spec, options) {
|
|
|
8202
8462
|
const content = layout.tooltipDescriptors.get(`point:${pointKey}`);
|
|
8203
8463
|
const pointMark = pointMarksByKey.get(pointKey);
|
|
8204
8464
|
const handlePointMouseEnter = (e) => {
|
|
8465
|
+
activeLeave?.();
|
|
8466
|
+
activeLeave = handlePointMouseLeave;
|
|
8205
8467
|
const mouseEvent = e;
|
|
8206
8468
|
el.style.setProperty("cursor", "pointer");
|
|
8207
8469
|
el.style.setProperty("filter", "brightness(1.15)");
|
|
@@ -8229,6 +8491,8 @@ function createGeoMap(container, spec, options) {
|
|
|
8229
8491
|
}
|
|
8230
8492
|
};
|
|
8231
8493
|
const handlePointMouseLeave = () => {
|
|
8494
|
+
if (activeLeave !== handlePointMouseLeave) return;
|
|
8495
|
+
activeLeave = null;
|
|
8232
8496
|
el.style.removeProperty("cursor");
|
|
8233
8497
|
el.style.removeProperty("filter");
|
|
8234
8498
|
tooltipManager?.hide();
|
|
@@ -8243,6 +8507,13 @@ function createGeoMap(container, spec, options) {
|
|
|
8243
8507
|
});
|
|
8244
8508
|
}
|
|
8245
8509
|
};
|
|
8510
|
+
navigation.push({
|
|
8511
|
+
element: el,
|
|
8512
|
+
key: `point:${pointKey}`,
|
|
8513
|
+
enter: handlePointMouseEnter,
|
|
8514
|
+
leave: handlePointMouseLeave,
|
|
8515
|
+
activate: handlePointClick
|
|
8516
|
+
});
|
|
8246
8517
|
el.addEventListener("mouseenter", handlePointMouseEnter);
|
|
8247
8518
|
el.addEventListener("mousemove", handlePointMouseMove);
|
|
8248
8519
|
el.addEventListener("mouseleave", handlePointMouseLeave);
|
|
@@ -8254,7 +8525,9 @@ function createGeoMap(container, spec, options) {
|
|
|
8254
8525
|
el.removeEventListener("click", handlePointClick);
|
|
8255
8526
|
});
|
|
8256
8527
|
}
|
|
8528
|
+
cleanups.push(wireMarkNavigation(svg, navigation));
|
|
8257
8529
|
return () => {
|
|
8530
|
+
activeLeave?.();
|
|
8258
8531
|
for (const cleanup of cleanups) {
|
|
8259
8532
|
cleanup();
|
|
8260
8533
|
}
|
|
@@ -8370,9 +8643,9 @@ function createGeoMap(container, spec, options) {
|
|
|
8370
8643
|
});
|
|
8371
8644
|
liveEl.textContent = `Zoomed to ${names.join(", ")}`;
|
|
8372
8645
|
}
|
|
8373
|
-
function applyFocusDim(ids) {
|
|
8374
|
-
if (!
|
|
8375
|
-
const features =
|
|
8646
|
+
function applyFocusDim(ids, target = svgElement) {
|
|
8647
|
+
if (!target) return;
|
|
8648
|
+
const features = target.querySelectorAll(".oc-map-feature");
|
|
8376
8649
|
if (!ids || ids.length === 0) {
|
|
8377
8650
|
for (const el of features) {
|
|
8378
8651
|
el.style.removeProperty("opacity");
|
|
@@ -8389,6 +8662,8 @@ function createGeoMap(container, spec, options) {
|
|
|
8389
8662
|
}
|
|
8390
8663
|
}
|
|
8391
8664
|
function render(animate = false) {
|
|
8665
|
+
const focusedMark = captureMarkFocus(svgElement);
|
|
8666
|
+
tooltipManager?.hide();
|
|
8392
8667
|
if (animationCleanup) {
|
|
8393
8668
|
animationCleanup();
|
|
8394
8669
|
animationCleanup = null;
|
|
@@ -8419,6 +8694,7 @@ function createGeoMap(container, spec, options) {
|
|
|
8419
8694
|
const isAnimating = animate && !!currentLayout.animation?.enter;
|
|
8420
8695
|
if (isAnimating) {
|
|
8421
8696
|
animationCleanup = setupAnimationCleanup(newSvg, () => {
|
|
8697
|
+
animationCleanup = null;
|
|
8422
8698
|
if (pendingResize && !destroyed) {
|
|
8423
8699
|
pendingResize = false;
|
|
8424
8700
|
resize();
|
|
@@ -8451,20 +8727,22 @@ function createGeoMap(container, spec, options) {
|
|
|
8451
8727
|
}
|
|
8452
8728
|
isFirstRender = false;
|
|
8453
8729
|
syncZoomController(oldSvgHadFocus);
|
|
8730
|
+
if (svgElement) restoreMarkFocus(svgElement, focusedMark);
|
|
8454
8731
|
}
|
|
8455
8732
|
function update(newSpec) {
|
|
8733
|
+
if (destroyed) return;
|
|
8734
|
+
const nextFont = resolveEffectiveFont(newSpec);
|
|
8735
|
+
const nextMeasure = nextFont === fontFamily ? measureText : createMeasureText(nextFont);
|
|
8736
|
+
const nextLayout = compile(newSpec, nextMeasure);
|
|
8456
8737
|
currentSpec = newSpec;
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
fontFamily = nextFont;
|
|
8460
|
-
measureText = createMeasureText(fontFamily);
|
|
8461
|
-
}
|
|
8738
|
+
fontFamily = nextFont;
|
|
8739
|
+
measureText = nextMeasure;
|
|
8462
8740
|
const canAnimate = !animationCleanup && !prefersReducedMotion2();
|
|
8463
8741
|
const prevPaint = canAnimate ? captureMapSnapshot(svgElement) : null;
|
|
8464
8742
|
const prevFocusIds = currentFocusIds ? currentFocusIds.map(String).sort().join(",") : null;
|
|
8465
8743
|
const prevFocusSig = focusSignature(currentLayout.focus);
|
|
8466
8744
|
const prevMapSize = currentLayout.mapSize;
|
|
8467
|
-
currentLayout =
|
|
8745
|
+
currentLayout = nextLayout;
|
|
8468
8746
|
if (!currentFocusIds) rescaleFreeCamera(prevMapSize);
|
|
8469
8747
|
render();
|
|
8470
8748
|
if (canAnimate && prevPaint && (prevPaint.fills.size > 0 || prevPaint.points.size > 0) && svgElement && currentLayout.animation?.update) {
|
|
@@ -8512,15 +8790,25 @@ function createGeoMap(container, spec, options) {
|
|
|
8512
8790
|
}
|
|
8513
8791
|
function exportChart(format, options_) {
|
|
8514
8792
|
if (!svgElement) return "";
|
|
8793
|
+
const terminalSvg = renderMapSVG(currentLayout, { animate: false });
|
|
8794
|
+
applyMapCamera(terminalSvg, currentCamera, currentLayout);
|
|
8795
|
+
const clip = svgElement.querySelector(".oc-map-group")?.getAttribute("clip-path");
|
|
8796
|
+
if (clip) {
|
|
8797
|
+
const clipId = clip.slice(5, -1);
|
|
8798
|
+
ensureMapClip(terminalSvg, currentLayout, clipId);
|
|
8799
|
+
terminalSvg.querySelector(".oc-map-group")?.setAttribute("clip-path", clip);
|
|
8800
|
+
}
|
|
8801
|
+
applyFocusDim(currentFocusIds, terminalSvg);
|
|
8802
|
+
const resolvedOptions = { theme: currentLayout.theme, ...options_ };
|
|
8515
8803
|
switch (format) {
|
|
8516
8804
|
case "svg":
|
|
8517
|
-
return exportSVG(
|
|
8805
|
+
return exportSVG(terminalSvg);
|
|
8518
8806
|
case "svg-with-fonts":
|
|
8519
|
-
return exportSVGWithFonts(
|
|
8807
|
+
return exportSVGWithFonts(terminalSvg, resolvedOptions);
|
|
8520
8808
|
case "png":
|
|
8521
|
-
return exportPNG(
|
|
8809
|
+
return exportPNG(terminalSvg, resolvedOptions);
|
|
8522
8810
|
case "jpg":
|
|
8523
|
-
return exportJPG(
|
|
8811
|
+
return exportJPG(terminalSvg, resolvedOptions);
|
|
8524
8812
|
default:
|
|
8525
8813
|
return "";
|
|
8526
8814
|
}
|
|
@@ -8640,16 +8928,19 @@ export {
|
|
|
8640
8928
|
cancelAnimations,
|
|
8641
8929
|
setupAnimationCleanup,
|
|
8642
8930
|
setupTableAnimationCleanup,
|
|
8931
|
+
captureMarkFocus,
|
|
8932
|
+
restoreMarkFocus,
|
|
8933
|
+
wireMarkNavigation,
|
|
8643
8934
|
createMeasureText,
|
|
8644
8935
|
resolveFontFamily,
|
|
8645
8936
|
scheduleFontReload,
|
|
8646
8937
|
VECTOR_EXPORT_MAX_POINTS,
|
|
8647
8938
|
materializeCanvasModeSVG,
|
|
8648
|
-
applySrOnlyStyles,
|
|
8649
|
-
createTextEditOverlay,
|
|
8650
8939
|
DEFAULT_UPDATE_MAX_MARKS,
|
|
8651
8940
|
CANVAS_DEFAULT_UPDATE_MAX_MARKS,
|
|
8652
8941
|
canTransitionSpecShape,
|
|
8942
|
+
applySrOnlyStyles,
|
|
8943
|
+
createTextEditOverlay,
|
|
8653
8944
|
createYouDrawIt,
|
|
8654
8945
|
createChart,
|
|
8655
8946
|
easingFns,
|
|
@@ -8665,4 +8956,4 @@ export {
|
|
|
8665
8956
|
scrubCamera,
|
|
8666
8957
|
createGeoMap
|
|
8667
8958
|
};
|
|
8668
|
-
//# sourceMappingURL=chunk-
|
|
8959
|
+
//# sourceMappingURL=chunk-6TD7GWR4.js.map
|