@opendata-ai/openchart-vanilla 8.9.0 → 8.9.1
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-TP3GCWCO.js → chunk-APMUBW72.js} +103 -65
- package/dist/chunk-APMUBW72.js.map +1 -0
- package/dist/{chunk-6TD7GWR4.js → chunk-CCRFYGHU.js} +816 -574
- package/dist/chunk-CCRFYGHU.js.map +1 -0
- package/dist/{chunk-YBVDBY6P.js → chunk-GX6BWMJD.js} +7 -9
- package/dist/chunk-GX6BWMJD.js.map +1 -0
- package/dist/{chunk-YHZAH6EN.js → chunk-Q7YEEF2X.js} +2 -2
- package/dist/{chunk-2OJZBMXM.js → chunk-REH5UQCL.js} +46 -46
- package/dist/{chunk-2OJZBMXM.js.map → chunk-REH5UQCL.js.map} +1 -1
- package/dist/{chunk-NG7CVCI2.js → chunk-UC4IHEUO.js} +16 -1
- package/dist/chunk-UC4IHEUO.js.map +1 -0
- package/dist/graph-3d/index.d.ts +0 -27
- package/dist/graph-3d/index.js +9 -5
- package/dist/graph-3d/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -6
- package/dist/{mount-FmUdKlCq.d.ts → mount-TEjIdF0m.d.ts} +5 -0
- package/dist/static.js +3 -3
- package/dist/story/index.d.ts +2 -2
- package/dist/story/index.js +4 -4
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-6TD7GWR4.js.map +0 -1
- package/dist/chunk-NG7CVCI2.js.map +0 -1
- package/dist/chunk-TP3GCWCO.js.map +0 -1
- package/dist/chunk-YBVDBY6P.js.map +0 -1
- /package/dist/{chunk-YHZAH6EN.js.map → chunk-Q7YEEF2X.js.map} +0 -0
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
renderSingleMark,
|
|
17
17
|
resolveMarkFill,
|
|
18
18
|
resolvePatternFill,
|
|
19
|
-
stampAnimationVars
|
|
20
|
-
|
|
19
|
+
stampAnimationVars,
|
|
20
|
+
updateAnnotationGeometry
|
|
21
|
+
} from "./chunk-APMUBW72.js";
|
|
21
22
|
import {
|
|
22
23
|
SVG_NS,
|
|
23
24
|
createSVGElement,
|
|
@@ -37,11 +38,12 @@ import {
|
|
|
37
38
|
prefersReducedMotion,
|
|
38
39
|
resolveDarkMode,
|
|
39
40
|
resolveEase
|
|
40
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-REH5UQCL.js";
|
|
41
42
|
import {
|
|
42
43
|
resolvedSurface,
|
|
43
|
-
stampThemeProperties
|
|
44
|
-
|
|
44
|
+
stampThemeProperties,
|
|
45
|
+
warnDeprecated
|
|
46
|
+
} from "./chunk-UC4IHEUO.js";
|
|
45
47
|
|
|
46
48
|
// src/scatter-canvas/renderer.ts
|
|
47
49
|
var TWO_PI = Math.PI * 2;
|
|
@@ -548,8 +550,7 @@ function hasAnnotationChanged(prevLayout, nextLayout) {
|
|
|
548
550
|
if (prevKeys[i] !== nextKeys[i]) return true;
|
|
549
551
|
const p = prev[i];
|
|
550
552
|
const n = next[i];
|
|
551
|
-
if (p
|
|
552
|
-
if (p.opacity !== n.opacity) return true;
|
|
553
|
+
if (JSON.stringify(p) !== JSON.stringify(n)) return true;
|
|
553
554
|
}
|
|
554
555
|
return false;
|
|
555
556
|
}
|
|
@@ -920,7 +921,7 @@ import { buildAreaPath as buildAreaPath2, buildLinePath as buildLinePath2, EXIT_
|
|
|
920
921
|
|
|
921
922
|
// src/transition/apply.ts
|
|
922
923
|
import { sampleMotion as sampleMotion2 } from "@opendata-ai/openchart-core";
|
|
923
|
-
import { buildArcPath } from "@opendata-ai/openchart-engine";
|
|
924
|
+
import { buildArcPath, interpolateAnnotationGeometry } from "@opendata-ai/openchart-engine";
|
|
924
925
|
|
|
925
926
|
// src/transition/canvas-tweens.ts
|
|
926
927
|
import { sampleMotion } from "@opendata-ai/openchart-core";
|
|
@@ -986,22 +987,19 @@ function buildAnnotationTweens(svg, prevLayout, nextLayout, tweens) {
|
|
|
986
987
|
tweenType: "annotation",
|
|
987
988
|
kind: "enter",
|
|
988
989
|
el,
|
|
989
|
-
fromDx: 0,
|
|
990
|
-
fromDy: 0,
|
|
991
990
|
fromOpacity: 0,
|
|
992
991
|
toOpacity: 1
|
|
993
992
|
});
|
|
994
993
|
continue;
|
|
995
994
|
}
|
|
996
|
-
|
|
997
|
-
const dy = (before.label?.y ?? 0) - (next[i].label?.y ?? 0);
|
|
998
|
-
if (dx === 0 && dy === 0) continue;
|
|
995
|
+
if (JSON.stringify(before) === JSON.stringify(next[i])) continue;
|
|
999
996
|
tweens.push({
|
|
1000
997
|
tweenType: "annotation",
|
|
1001
998
|
kind: "update",
|
|
1002
999
|
el,
|
|
1003
|
-
|
|
1004
|
-
|
|
1000
|
+
before,
|
|
1001
|
+
after: next[i],
|
|
1002
|
+
background: nextLayout.theme.colors.neutral.surface
|
|
1005
1003
|
});
|
|
1006
1004
|
}
|
|
1007
1005
|
}
|
|
@@ -1796,11 +1794,12 @@ function applyTweenState(tw, elapsed, update, exit, enterDelay, enterDuration) {
|
|
|
1796
1794
|
break;
|
|
1797
1795
|
}
|
|
1798
1796
|
case "annotation": {
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1797
|
+
if (tw.before && tw.after)
|
|
1798
|
+
updateAnnotationGeometry(
|
|
1799
|
+
tw.el,
|
|
1800
|
+
interpolateAnnotationGeometry(tw.before, tw.after, eased),
|
|
1801
|
+
tw.background
|
|
1802
|
+
);
|
|
1804
1803
|
break;
|
|
1805
1804
|
}
|
|
1806
1805
|
}
|
|
@@ -1892,6 +1891,7 @@ function snapTweenToFinal(tw) {
|
|
|
1892
1891
|
tw.el.setAttribute(tw.attr, tw.to);
|
|
1893
1892
|
break;
|
|
1894
1893
|
case "annotation":
|
|
1894
|
+
if (tw.after) updateAnnotationGeometry(tw.el, tw.after, tw.background);
|
|
1895
1895
|
tw.el.removeAttribute("transform");
|
|
1896
1896
|
break;
|
|
1897
1897
|
}
|
|
@@ -3654,6 +3654,394 @@ function createYouDrawIt(options) {
|
|
|
3654
3654
|
};
|
|
3655
3655
|
}
|
|
3656
3656
|
|
|
3657
|
+
// src/interactions/annotation-nudges.ts
|
|
3658
|
+
import { annotationSource as annotationSource3 } from "@opendata-ai/openchart-core";
|
|
3659
|
+
import {
|
|
3660
|
+
createAnnotationEditContext,
|
|
3661
|
+
inverseLabelPlacement,
|
|
3662
|
+
previewAnnotationMove
|
|
3663
|
+
} from "@opendata-ai/openchart-engine";
|
|
3664
|
+
|
|
3665
|
+
// src/interactions/selection.ts
|
|
3666
|
+
import { elementRef } from "@opendata-ai/openchart-core";
|
|
3667
|
+
|
|
3668
|
+
// src/interactions/edit-transactions.ts
|
|
3669
|
+
import { annotationSource } from "@opendata-ai/openchart-core";
|
|
3670
|
+
import { annotationSource as annotationSource2 } from "@opendata-ai/openchart-core";
|
|
3671
|
+
var object = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3672
|
+
function editToTransaction(spec, edit, revision) {
|
|
3673
|
+
const root = object(spec) ?? {};
|
|
3674
|
+
const source = annotationSource2(spec);
|
|
3675
|
+
const ops = [];
|
|
3676
|
+
const set = (path, value, expect) => ops.push({ op: "set", path, value, ...expect ? { expect } : {} });
|
|
3677
|
+
if (edit.type === "legend-toggle") return null;
|
|
3678
|
+
if (edit.type === "add") {
|
|
3679
|
+
ops.push({ op: "add", path: `${source.path}/-`, value: edit.annotation });
|
|
3680
|
+
} else if ("element" in edit && edit.element.type === "annotation") {
|
|
3681
|
+
const expectedPath = `${source.path}/${edit.element.index}`;
|
|
3682
|
+
if (edit.element.path !== void 0 && edit.element.path !== expectedPath && !edit.element.path.startsWith(`${expectedPath}/`))
|
|
3683
|
+
return null;
|
|
3684
|
+
const annotation = source.annotations[edit.element.index];
|
|
3685
|
+
if (!annotation || edit.element.id !== void 0 && annotation.id !== edit.element.id)
|
|
3686
|
+
return null;
|
|
3687
|
+
const authored = "annotation" in edit ? edit.annotation : annotation;
|
|
3688
|
+
const expected = {
|
|
3689
|
+
type: authored.type,
|
|
3690
|
+
text: authored.type === "text" ? authored.text : authored.label ?? "",
|
|
3691
|
+
...authored.id !== void 0 ? { id: authored.id } : {}
|
|
3692
|
+
};
|
|
3693
|
+
if (authored.type !== "text") expected.text = void 0;
|
|
3694
|
+
const path = `${source.path}/${edit.element.index}`;
|
|
3695
|
+
switch (edit.type) {
|
|
3696
|
+
case "annotation":
|
|
3697
|
+
if (edit.anchor !== void 0) set(`${path}/anchor`, edit.anchor, expected);
|
|
3698
|
+
set(`${path}/offset`, edit.offset, expected);
|
|
3699
|
+
if (edit.responsive !== void 0) set(`${path}/responsive`, edit.responsive, expected);
|
|
3700
|
+
break;
|
|
3701
|
+
case "annotation-connector":
|
|
3702
|
+
set(
|
|
3703
|
+
`${path}/connectorOffset`,
|
|
3704
|
+
{
|
|
3705
|
+
...authored.type === "text" ? authored.connectorOffset : {},
|
|
3706
|
+
[edit.endpoint]: edit.offset
|
|
3707
|
+
},
|
|
3708
|
+
expected
|
|
3709
|
+
);
|
|
3710
|
+
break;
|
|
3711
|
+
case "range-label":
|
|
3712
|
+
case "refline-label":
|
|
3713
|
+
set(`${path}/labelOffset`, edit.labelOffset, expected);
|
|
3714
|
+
break;
|
|
3715
|
+
case "annotation-anchor":
|
|
3716
|
+
set(`${path}/x`, edit.x, expected);
|
|
3717
|
+
set(`${path}/y`, edit.y, expected);
|
|
3718
|
+
break;
|
|
3719
|
+
case "text-edit":
|
|
3720
|
+
set(`${path}/${annotation.type === "text" ? "text" : "label"}`, edit.newText, expected);
|
|
3721
|
+
break;
|
|
3722
|
+
case "delete":
|
|
3723
|
+
ops.push({ op: "remove", path, expect: expected });
|
|
3724
|
+
break;
|
|
3725
|
+
}
|
|
3726
|
+
} else if (edit.type === "chrome" || "element" in edit && edit.element.type === "chrome") {
|
|
3727
|
+
const key = edit.type === "chrome" ? edit.key : edit.element.type === "chrome" ? edit.element.key : "title";
|
|
3728
|
+
const chrome = object(root.chrome);
|
|
3729
|
+
const sugar = !Object.hasOwn(chrome ?? {}, key) && Object.hasOwn(root, key);
|
|
3730
|
+
const path = sugar ? `/${key}` : `/chrome/${key}`;
|
|
3731
|
+
const old = sugar ? root[key] : chrome?.[key];
|
|
3732
|
+
if (edit.type === "delete") {
|
|
3733
|
+
ops.push({ op: "remove", path });
|
|
3734
|
+
} else {
|
|
3735
|
+
const text = edit.type === "text-edit" ? edit.newText : edit.type === "chrome" ? edit.text : "";
|
|
3736
|
+
const value = edit.type === "text-edit" && sugar && typeof old === "string" ? text : {
|
|
3737
|
+
...object(old) ?? {},
|
|
3738
|
+
text,
|
|
3739
|
+
...edit.type === "chrome" ? { offset: edit.offset } : {}
|
|
3740
|
+
};
|
|
3741
|
+
ops.push({ op: chrome || sugar ? "set" : "add", path, value });
|
|
3742
|
+
}
|
|
3743
|
+
} else if (edit.type === "legend") {
|
|
3744
|
+
ops.push({
|
|
3745
|
+
op: "add",
|
|
3746
|
+
path: "/legend",
|
|
3747
|
+
value: { ...object(root.legend) ?? {}, offset: edit.offset }
|
|
3748
|
+
});
|
|
3749
|
+
} else if (edit.type === "delete" && edit.element.type === "legend") {
|
|
3750
|
+
ops.push({
|
|
3751
|
+
op: "add",
|
|
3752
|
+
path: "/legend",
|
|
3753
|
+
value: { ...object(root.legend) ?? {}, show: false }
|
|
3754
|
+
});
|
|
3755
|
+
} else if (edit.type === "series-label") {
|
|
3756
|
+
const key = edit.series.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
3757
|
+
ops.push({ op: "add", path: `/labels/offsets/${key}`, value: edit.offset });
|
|
3758
|
+
}
|
|
3759
|
+
return ops.length ? { ...revision !== void 0 ? { revision } : {}, ops } : null;
|
|
3760
|
+
}
|
|
3761
|
+
|
|
3762
|
+
// src/interactions/selection.ts
|
|
3763
|
+
function findElementByRef(svg, ref) {
|
|
3764
|
+
switch (ref.type) {
|
|
3765
|
+
case "annotation": {
|
|
3766
|
+
const matches = [...svg.querySelectorAll("[data-annotation-index]")].filter(
|
|
3767
|
+
(el) => Number(el.getAttribute("data-annotation-index")) === ref.index && (ref.id === void 0 || el.getAttribute("data-annotation-id") === ref.id) && (ref.path === void 0 || ref.path === (el.getAttribute("data-annotation-path") ?? `/annotations/${ref.index}`) || ref.path.startsWith(
|
|
3768
|
+
`${el.getAttribute("data-annotation-path") ?? `/annotations/${ref.index}`}/`
|
|
3769
|
+
)) && (ref.facetInstance === void 0 || el.getAttribute("data-facet-instance") === ref.facetInstance)
|
|
3770
|
+
);
|
|
3771
|
+
return matches[0] ?? null;
|
|
3772
|
+
}
|
|
3773
|
+
case "chrome":
|
|
3774
|
+
return svg.querySelector(`[data-chrome-key="${ref.key}"]`);
|
|
3775
|
+
case "series-label":
|
|
3776
|
+
return svg.querySelector(`.oc-mark-label[data-series="${ref.series}"]`);
|
|
3777
|
+
// The editable legend is the color legend. Excluding the size legend
|
|
3778
|
+
// explicitly rather than relying on it rendering second: `querySelector`
|
|
3779
|
+
// takes the first match, so a bare `.oc-legend` would silently retarget
|
|
3780
|
+
// (and drag) the wrong legend if render order ever changed.
|
|
3781
|
+
case "legend":
|
|
3782
|
+
return svg.querySelector(".oc-legend:not(.oc-legend--size)");
|
|
3783
|
+
case "legend-entry":
|
|
3784
|
+
return svg.querySelector(
|
|
3785
|
+
`.oc-legend:not(.oc-legend--size) [data-legend-index="${ref.index}"]`
|
|
3786
|
+
);
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
function encodePointerToken(token) {
|
|
3790
|
+
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
3791
|
+
}
|
|
3792
|
+
function buildElementRef(element, _specAnnotations) {
|
|
3793
|
+
const annotationEl = element.closest("[data-annotation-index]");
|
|
3794
|
+
if (annotationEl) {
|
|
3795
|
+
const index = Number(annotationEl.getAttribute("data-annotation-index"));
|
|
3796
|
+
const id = annotationEl.getAttribute("data-annotation-id") ?? void 0;
|
|
3797
|
+
return {
|
|
3798
|
+
...elementRef.annotation(
|
|
3799
|
+
index,
|
|
3800
|
+
id,
|
|
3801
|
+
annotationEl.getAttribute("data-annotation-path") ?? `/annotations/${index}`
|
|
3802
|
+
),
|
|
3803
|
+
...annotationEl.hasAttribute("data-facet-instance") ? { facetInstance: annotationEl.getAttribute("data-facet-instance") } : {}
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
const chromeEl = element.closest("[data-chrome-key]");
|
|
3807
|
+
if (chromeEl) {
|
|
3808
|
+
const key = chromeEl.getAttribute("data-chrome-key");
|
|
3809
|
+
if (key) return elementRef.chrome(key, `/chrome/${key}`);
|
|
3810
|
+
}
|
|
3811
|
+
const seriesLabelEl = element.closest(".oc-mark-label[data-series]");
|
|
3812
|
+
if (seriesLabelEl) {
|
|
3813
|
+
const series = seriesLabelEl.getAttribute("data-series");
|
|
3814
|
+
if (series)
|
|
3815
|
+
return elementRef.seriesLabel(series, `/labels/offsets/${encodePointerToken(series)}`);
|
|
3816
|
+
}
|
|
3817
|
+
const legendEntryEl = element.closest("[data-legend-index]");
|
|
3818
|
+
if (legendEntryEl) {
|
|
3819
|
+
const index = Number(legendEntryEl.getAttribute("data-legend-index"));
|
|
3820
|
+
const series = legendEntryEl.getAttribute("data-legend-label") ?? "";
|
|
3821
|
+
return elementRef.legendEntry(series, index);
|
|
3822
|
+
}
|
|
3823
|
+
const legendEl = element.closest(".oc-legend");
|
|
3824
|
+
if (legendEl && !legendEl.classList.contains("oc-legend--size"))
|
|
3825
|
+
return elementRef.legend("/legend");
|
|
3826
|
+
return null;
|
|
3827
|
+
}
|
|
3828
|
+
function getEditableElements(spec, layout) {
|
|
3829
|
+
const refs = [];
|
|
3830
|
+
const chromeKeys = ["title", "subtitle", "source", "byline", "footer"];
|
|
3831
|
+
for (const key of chromeKeys) {
|
|
3832
|
+
if (layout.chrome[key]) {
|
|
3833
|
+
refs.push(elementRef.chrome(key));
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
const { annotations, path } = annotationSource(spec);
|
|
3837
|
+
for (let i = 0; i < annotations.length; i++) {
|
|
3838
|
+
const panels = layout.facet?.panels;
|
|
3839
|
+
if (panels) {
|
|
3840
|
+
for (const panel of panels) {
|
|
3841
|
+
if (panel.annotations.some((a) => a.specIndex === i))
|
|
3842
|
+
refs.push({
|
|
3843
|
+
...elementRef.annotation(i, annotations[i].id, `${path}/${i}`),
|
|
3844
|
+
facetInstance: panel.key
|
|
3845
|
+
});
|
|
3846
|
+
}
|
|
3847
|
+
} else if (layout.annotations.some((a, j) => (a.specIndex ?? j) === i))
|
|
3848
|
+
refs.push(elementRef.annotation(i, annotations[i].id, `${path}/${i}`));
|
|
3849
|
+
}
|
|
3850
|
+
const seriesLabels = [];
|
|
3851
|
+
for (const mark of layout.marks) {
|
|
3852
|
+
if (mark.type === "line" && mark.label?.visible && mark.seriesKey) {
|
|
3853
|
+
seriesLabels.push(mark.seriesKey);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
seriesLabels.sort();
|
|
3857
|
+
for (const series of seriesLabels) {
|
|
3858
|
+
refs.push(elementRef.seriesLabel(series));
|
|
3859
|
+
}
|
|
3860
|
+
if ("entries" in layout.legend && layout.legend.entries.length > 0) {
|
|
3861
|
+
refs.push(elementRef.legend());
|
|
3862
|
+
}
|
|
3863
|
+
return refs;
|
|
3864
|
+
}
|
|
3865
|
+
function isTextEditable(ref, specAnnotations) {
|
|
3866
|
+
if (ref.type === "chrome") return true;
|
|
3867
|
+
if (ref.type === "annotation") {
|
|
3868
|
+
const annotation = specAnnotations[ref.index];
|
|
3869
|
+
return annotation?.type === "text";
|
|
3870
|
+
}
|
|
3871
|
+
return false;
|
|
3872
|
+
}
|
|
3873
|
+
function getElementText(ref, spec) {
|
|
3874
|
+
if (ref.type === "chrome") {
|
|
3875
|
+
const chromeConfig = "chrome" in spec ? spec.chrome : void 0;
|
|
3876
|
+
const entry = chromeConfig?.[ref.key] ?? spec[ref.key];
|
|
3877
|
+
if (typeof entry === "string") return entry;
|
|
3878
|
+
if (typeof entry === "object" && entry !== null && "text" in entry) {
|
|
3879
|
+
return entry.text;
|
|
3880
|
+
}
|
|
3881
|
+
return null;
|
|
3882
|
+
}
|
|
3883
|
+
if (ref.type === "annotation") {
|
|
3884
|
+
const { annotations } = annotationSource(spec);
|
|
3885
|
+
const annotation = annotations[ref.index];
|
|
3886
|
+
if (annotation?.type === "text") return annotation.text ?? null;
|
|
3887
|
+
if (annotation?.label) return annotation.label;
|
|
3888
|
+
return null;
|
|
3889
|
+
}
|
|
3890
|
+
return null;
|
|
3891
|
+
}
|
|
3892
|
+
function refsEqual(a, b) {
|
|
3893
|
+
if (a === null || b === null) return a === b;
|
|
3894
|
+
if (a.type !== b.type) return false;
|
|
3895
|
+
switch (a.type) {
|
|
3896
|
+
case "annotation": {
|
|
3897
|
+
const bAnno = b;
|
|
3898
|
+
if (a.facetInstance !== bAnno.facetInstance) return false;
|
|
3899
|
+
if (a.id && bAnno.id) return a.id === bAnno.id && a.index === bAnno.index;
|
|
3900
|
+
return a.index === bAnno.index;
|
|
3901
|
+
}
|
|
3902
|
+
case "chrome":
|
|
3903
|
+
return a.key === b.key;
|
|
3904
|
+
case "series-label":
|
|
3905
|
+
return a.series === b.series;
|
|
3906
|
+
case "legend":
|
|
3907
|
+
return true;
|
|
3908
|
+
case "legend-entry": {
|
|
3909
|
+
const bEntry = b;
|
|
3910
|
+
return a.index === bEntry.index && a.series === bEntry.series;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
function renderSelectionOverlay(svg, ref, layout) {
|
|
3915
|
+
const target = findElementByRef(svg, ref);
|
|
3916
|
+
if (!target) return null;
|
|
3917
|
+
const bbox = target.getBBox();
|
|
3918
|
+
const padding = 4;
|
|
3919
|
+
const accentColor = layout.theme.colors.categorical?.[0] ?? "#4f46e5";
|
|
3920
|
+
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
3921
|
+
g.setAttribute("class", "oc-selection-overlay");
|
|
3922
|
+
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
3923
|
+
rect.setAttribute("x", String(bbox.x - padding));
|
|
3924
|
+
rect.setAttribute("y", String(bbox.y - padding));
|
|
3925
|
+
rect.setAttribute("width", String(bbox.width + padding * 2));
|
|
3926
|
+
rect.setAttribute("height", String(bbox.height + padding * 2));
|
|
3927
|
+
rect.setAttribute("rx", "3");
|
|
3928
|
+
rect.setAttribute("fill", "transparent");
|
|
3929
|
+
rect.setAttribute("stroke", accentColor);
|
|
3930
|
+
rect.setAttribute("stroke-width", "1.5");
|
|
3931
|
+
rect.setAttribute("pointer-events", "none");
|
|
3932
|
+
g.appendChild(rect);
|
|
3933
|
+
svg.appendChild(g);
|
|
3934
|
+
return g;
|
|
3935
|
+
}
|
|
3936
|
+
function createScreenReaderTable(layout, container) {
|
|
3937
|
+
const data = layout.a11y.dataTableFallback;
|
|
3938
|
+
if (!data || data.length === 0) return null;
|
|
3939
|
+
const wrapper = document.createElement("div");
|
|
3940
|
+
wrapper.className = "oc-sr-only";
|
|
3941
|
+
applySrOnlyStyles(wrapper);
|
|
3942
|
+
const table = document.createElement("table");
|
|
3943
|
+
table.setAttribute("role", "table");
|
|
3944
|
+
table.setAttribute("aria-label", `Data table: ${layout.a11y.altText}`);
|
|
3945
|
+
const totalRows = layout.a11y.totalRows;
|
|
3946
|
+
if (totalRows != null) {
|
|
3947
|
+
const caption = document.createElement("caption");
|
|
3948
|
+
caption.textContent = `Showing first ${(data.length - 1).toLocaleString()} of ${totalRows.toLocaleString()} rows; full data via CSV export`;
|
|
3949
|
+
table.appendChild(caption);
|
|
3950
|
+
}
|
|
3951
|
+
if (data.length > 0) {
|
|
3952
|
+
const thead = document.createElement("thead");
|
|
3953
|
+
const headerRow = document.createElement("tr");
|
|
3954
|
+
const headers = data[0];
|
|
3955
|
+
for (const header of headers) {
|
|
3956
|
+
const th = document.createElement("th");
|
|
3957
|
+
th.textContent = String(header ?? "");
|
|
3958
|
+
th.setAttribute("scope", "col");
|
|
3959
|
+
headerRow.appendChild(th);
|
|
3960
|
+
}
|
|
3961
|
+
thead.appendChild(headerRow);
|
|
3962
|
+
table.appendChild(thead);
|
|
3963
|
+
}
|
|
3964
|
+
if (data.length > 1) {
|
|
3965
|
+
const tbody = document.createElement("tbody");
|
|
3966
|
+
for (let i = 1; i < data.length; i++) {
|
|
3967
|
+
const tr = document.createElement("tr");
|
|
3968
|
+
const cells = data[i];
|
|
3969
|
+
for (const cell of cells) {
|
|
3970
|
+
const td = document.createElement("td");
|
|
3971
|
+
td.textContent = String(cell ?? "");
|
|
3972
|
+
tr.appendChild(td);
|
|
3973
|
+
}
|
|
3974
|
+
tbody.appendChild(tr);
|
|
3975
|
+
}
|
|
3976
|
+
table.appendChild(tbody);
|
|
3977
|
+
}
|
|
3978
|
+
wrapper.appendChild(table);
|
|
3979
|
+
container.appendChild(wrapper);
|
|
3980
|
+
return wrapper;
|
|
3981
|
+
}
|
|
3982
|
+
|
|
3983
|
+
// src/interactions/annotation-nudges.ts
|
|
3984
|
+
function createAnnotationNudges(read, emit) {
|
|
3985
|
+
let pending;
|
|
3986
|
+
let timer;
|
|
3987
|
+
const flush = () => {
|
|
3988
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3989
|
+
timer = void 0;
|
|
3990
|
+
const batch = pending;
|
|
3991
|
+
pending = void 0;
|
|
3992
|
+
if (!batch) return;
|
|
3993
|
+
const preview = previewAnnotationMove(batch.ctx, batch.ref, batch);
|
|
3994
|
+
const placement = inverseLabelPlacement(batch.ctx, batch.ref, preview.bounds);
|
|
3995
|
+
const { svg, layout } = read();
|
|
3996
|
+
const target = svg && findElementByRef(svg, batch.ref);
|
|
3997
|
+
if (target)
|
|
3998
|
+
updateAnnotationGeometry(target, batch.original, layout.theme.colors.neutral.surface);
|
|
3999
|
+
emit({
|
|
4000
|
+
type: "annotation",
|
|
4001
|
+
element: batch.ref,
|
|
4002
|
+
annotation: batch.authored,
|
|
4003
|
+
offset: placement.offset,
|
|
4004
|
+
anchor: placement.anchor,
|
|
4005
|
+
responsive: false
|
|
4006
|
+
});
|
|
4007
|
+
};
|
|
4008
|
+
const keydown = (event) => {
|
|
4009
|
+
if (!event.altKey || !["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key))
|
|
4010
|
+
return false;
|
|
4011
|
+
const { layout, spec, svg, selected } = read();
|
|
4012
|
+
if (!svg || selected?.type !== "annotation") return false;
|
|
4013
|
+
const authored = annotationSource3(spec).annotations[selected.index];
|
|
4014
|
+
if (authored?.type !== "text") return false;
|
|
4015
|
+
if (!pending) {
|
|
4016
|
+
const ctx = createAnnotationEditContext(layout, spec);
|
|
4017
|
+
try {
|
|
4018
|
+
previewAnnotationMove(ctx, selected, { dx: 0, dy: 0 });
|
|
4019
|
+
} catch {
|
|
4020
|
+
return false;
|
|
4021
|
+
}
|
|
4022
|
+
const annotations = selected.facetInstance !== void 0 ? layout.facet?.panels.find((panel) => panel.key === selected.facetInstance)?.annotations : layout.annotations;
|
|
4023
|
+
const original = annotations?.find((annotation) => annotation.specIndex === selected.index);
|
|
4024
|
+
if (!original) return false;
|
|
4025
|
+
pending = { ctx, ref: selected, authored, original, dx: 0, dy: 0 };
|
|
4026
|
+
}
|
|
4027
|
+
event.preventDefault();
|
|
4028
|
+
const step = event.shiftKey ? 10 : 1;
|
|
4029
|
+
pending.dx += event.key === "ArrowLeft" ? -step : event.key === "ArrowRight" ? step : 0;
|
|
4030
|
+
pending.dy += event.key === "ArrowUp" ? -step : event.key === "ArrowDown" ? step : 0;
|
|
4031
|
+
const target = findElementByRef(svg, pending.ref);
|
|
4032
|
+
if (target)
|
|
4033
|
+
updateAnnotationGeometry(
|
|
4034
|
+
target,
|
|
4035
|
+
previewAnnotationMove(pending.ctx, pending.ref, pending),
|
|
4036
|
+
layout.theme.colors.neutral.surface
|
|
4037
|
+
);
|
|
4038
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
4039
|
+
timer = setTimeout(flush, 120);
|
|
4040
|
+
return true;
|
|
4041
|
+
};
|
|
4042
|
+
return { keydown, flush };
|
|
4043
|
+
}
|
|
4044
|
+
|
|
3657
4045
|
// src/transition/reconcile.ts
|
|
3658
4046
|
function identity(element, index) {
|
|
3659
4047
|
for (const attr of ["data-key", "data-label-key", "data-endpoint-key", "data-tick-value"]) {
|
|
@@ -3727,7 +4115,12 @@ function sampleLayout(layout, snapshot) {
|
|
|
3727
4115
|
}
|
|
3728
4116
|
|
|
3729
4117
|
// src/mount.ts
|
|
3730
|
-
import {
|
|
4118
|
+
import {
|
|
4119
|
+
applyEditTransaction,
|
|
4120
|
+
cssTokenDefault,
|
|
4121
|
+
isGraphSpec,
|
|
4122
|
+
isLayerSpec
|
|
4123
|
+
} from "@opendata-ai/openchart-core";
|
|
3731
4124
|
import {
|
|
3732
4125
|
compileChart,
|
|
3733
4126
|
compileLayer,
|
|
@@ -3864,14 +4257,22 @@ function wireChartEvents(svg, layout, specAnnotations, handlers) {
|
|
|
3864
4257
|
}
|
|
3865
4258
|
}
|
|
3866
4259
|
if (handlers.onAnnotationClick) {
|
|
3867
|
-
const annotationElements = svg.querySelectorAll(".oc-annotation");
|
|
4260
|
+
const annotationElements = svg.querySelectorAll(".oc-annotation, [data-note-spec-index]");
|
|
3868
4261
|
for (let i = 0; i < annotationElements.length; i++) {
|
|
3869
4262
|
const el = annotationElements[i];
|
|
3870
|
-
const
|
|
4263
|
+
const noteIndex = el.getAttribute("data-note-spec-index");
|
|
4264
|
+
const ref = noteIndex !== null ? {
|
|
4265
|
+
type: "annotation",
|
|
4266
|
+
index: Number(noteIndex),
|
|
4267
|
+
id: el.getAttribute("data-note-id") ?? void 0,
|
|
4268
|
+
path: el.getAttribute("data-note-path") ?? `/annotations/${noteIndex}`
|
|
4269
|
+
} : buildElementRef(el, specAnnotations);
|
|
4270
|
+
if (!ref || ref.type !== "annotation") continue;
|
|
4271
|
+
const specAnnotation = specAnnotations[ref.index];
|
|
3871
4272
|
if (!specAnnotation) continue;
|
|
3872
4273
|
const handleClick = (e) => {
|
|
3873
4274
|
const mouseEvent = e;
|
|
3874
|
-
handlers.onAnnotationClick(specAnnotation, mouseEvent);
|
|
4275
|
+
handlers.onAnnotationClick(specAnnotation, mouseEvent, ref);
|
|
3875
4276
|
};
|
|
3876
4277
|
el.addEventListener("click", handleClick);
|
|
3877
4278
|
cleanups.push(() => el.removeEventListener("click", handleClick));
|
|
@@ -4155,152 +4556,123 @@ function wireVoronoiTooltipEvents(svg, layout, tooltipManager, emphasis) {
|
|
|
4155
4556
|
// src/interactions/drag-handler.ts
|
|
4156
4557
|
function createDragHandler(config) {
|
|
4157
4558
|
const { element, svg, onMove, onEnd, setDragging, threshold = 3 } = config;
|
|
4158
|
-
|
|
4159
|
-
let activeDocMouseMove = null;
|
|
4160
|
-
let activeDocMouseUp = null;
|
|
4161
|
-
let activeDocTouchMove = null;
|
|
4162
|
-
let activeDocTouchEnd = null;
|
|
4163
|
-
let activeDocTouchCancel = null;
|
|
4164
|
-
function getScale2() {
|
|
4165
|
-
const viewBox = svg.viewBox?.baseVal;
|
|
4166
|
-
const svgRect = svg.getBoundingClientRect();
|
|
4167
|
-
return {
|
|
4168
|
-
scaleX: viewBox?.width && svgRect.width ? viewBox.width / svgRect.width : 1,
|
|
4169
|
-
scaleY: viewBox?.height && svgRect.height ? viewBox.height / svgRect.height : 1
|
|
4170
|
-
};
|
|
4171
|
-
}
|
|
4559
|
+
let cancelActive;
|
|
4172
4560
|
function startDrag(startX, startY) {
|
|
4561
|
+
cancelActive?.();
|
|
4562
|
+
config.onStart?.();
|
|
4173
4563
|
setDragging(true);
|
|
4174
|
-
const
|
|
4564
|
+
const viewBox = svg.viewBox?.baseVal;
|
|
4565
|
+
const rect = svg.getBoundingClientRect();
|
|
4566
|
+
const scaleX = viewBox?.width && rect.width ? viewBox.width / rect.width : 1;
|
|
4567
|
+
const scaleY = viewBox?.height && rect.height ? viewBox.height / rect.height : 1;
|
|
4175
4568
|
element.style.cursor = "grabbing";
|
|
4176
4569
|
svg.style.userSelect = "none";
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4570
|
+
let frame;
|
|
4571
|
+
let dx = 0;
|
|
4572
|
+
let dy = 0;
|
|
4573
|
+
let active = true;
|
|
4574
|
+
const preview = () => {
|
|
4575
|
+
frame = void 0;
|
|
4576
|
+
if (active) onMove(dx, dy);
|
|
4181
4577
|
};
|
|
4182
|
-
const
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
if (activeDocMouseUp) {
|
|
4188
|
-
document.removeEventListener("mouseup", activeDocMouseUp);
|
|
4189
|
-
activeDocMouseUp = null;
|
|
4190
|
-
}
|
|
4191
|
-
if (activeDocTouchMove) {
|
|
4192
|
-
document.removeEventListener("touchmove", activeDocTouchMove);
|
|
4193
|
-
activeDocTouchMove = null;
|
|
4194
|
-
}
|
|
4195
|
-
if (activeDocTouchEnd) {
|
|
4196
|
-
document.removeEventListener("touchend", activeDocTouchEnd);
|
|
4197
|
-
activeDocTouchEnd = null;
|
|
4198
|
-
}
|
|
4199
|
-
if (activeDocTouchCancel) {
|
|
4200
|
-
document.removeEventListener("touchcancel", activeDocTouchCancel);
|
|
4201
|
-
activeDocTouchCancel = null;
|
|
4202
|
-
}
|
|
4578
|
+
const move = (x, y) => {
|
|
4579
|
+
dx = (x - startX) * scaleX;
|
|
4580
|
+
dy = (y - startY) * scaleY;
|
|
4581
|
+
if (!config.animationFrame) preview();
|
|
4582
|
+
else if (frame === void 0) frame = requestAnimationFrame(preview);
|
|
4203
4583
|
};
|
|
4204
|
-
const
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
);
|
|
4217
|
-
}
|
|
4584
|
+
const detach = () => {
|
|
4585
|
+
active = false;
|
|
4586
|
+
if (frame !== void 0) cancelAnimationFrame(frame);
|
|
4587
|
+
frame = void 0;
|
|
4588
|
+
document.removeEventListener("mousemove", mouseMove);
|
|
4589
|
+
document.removeEventListener("mouseup", mouseUp);
|
|
4590
|
+
document.removeEventListener("touchmove", touchMove);
|
|
4591
|
+
document.removeEventListener("touchend", touchEnd);
|
|
4592
|
+
document.removeEventListener("touchcancel", cancel);
|
|
4593
|
+
document.removeEventListener("pointercancel", cancel);
|
|
4594
|
+
document.removeEventListener("keydown", keyDown, true);
|
|
4595
|
+
svg.removeEventListener("oc-cancel-drag", cancel);
|
|
4218
4596
|
element.style.cursor = "grab";
|
|
4219
4597
|
svg.style.userSelect = "";
|
|
4220
|
-
|
|
4598
|
+
cancelActive = void 0;
|
|
4599
|
+
};
|
|
4600
|
+
const cancel = () => {
|
|
4601
|
+
if (!active) return;
|
|
4602
|
+
detach();
|
|
4603
|
+
config.onCancel?.();
|
|
4221
4604
|
setDragging(false);
|
|
4222
4605
|
};
|
|
4223
|
-
const
|
|
4224
|
-
|
|
4606
|
+
const end = (x, y) => {
|
|
4607
|
+
if (!active) return;
|
|
4608
|
+
dx = (x - startX) * scaleX;
|
|
4609
|
+
dy = (y - startY) * scaleY;
|
|
4610
|
+
const moved = Math.abs(dx) > threshold || Math.abs(dy) > threshold;
|
|
4611
|
+
detach();
|
|
4612
|
+
onEnd(dx, dy, moved);
|
|
4613
|
+
if (moved)
|
|
4614
|
+
element.addEventListener("click", (event) => event.stopPropagation(), {
|
|
4615
|
+
capture: true,
|
|
4616
|
+
once: true
|
|
4617
|
+
});
|
|
4618
|
+
setDragging(false);
|
|
4225
4619
|
};
|
|
4226
|
-
const
|
|
4227
|
-
|
|
4620
|
+
const mouseMove = (event) => move(event.clientX, event.clientY);
|
|
4621
|
+
const mouseUp = (event) => end(event.clientX, event.clientY);
|
|
4622
|
+
const touchMove = (event) => {
|
|
4623
|
+
if (!event.touches.length) return;
|
|
4624
|
+
event.preventDefault();
|
|
4625
|
+
move(event.touches[0].clientX, event.touches[0].clientY);
|
|
4228
4626
|
};
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
activeDocMouseUp = onMouseUp;
|
|
4233
|
-
const onTouchMove = (moveEvent) => {
|
|
4234
|
-
if (moveEvent.touches.length > 0) {
|
|
4235
|
-
moveEvent.preventDefault();
|
|
4236
|
-
handleMove(moveEvent.touches[0].clientX, moveEvent.touches[0].clientY);
|
|
4237
|
-
}
|
|
4627
|
+
const touchEnd = (event) => {
|
|
4628
|
+
const touch = event.changedTouches[0];
|
|
4629
|
+
end(touch?.clientX ?? startX, touch?.clientY ?? startY);
|
|
4238
4630
|
};
|
|
4239
|
-
const
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
} else {
|
|
4244
|
-
handleEnd(startX, startY);
|
|
4631
|
+
const keyDown = (event) => {
|
|
4632
|
+
if (event.key === "Escape") {
|
|
4633
|
+
event.preventDefault();
|
|
4634
|
+
cancel();
|
|
4245
4635
|
}
|
|
4246
4636
|
};
|
|
4247
|
-
document.addEventListener("
|
|
4248
|
-
document.addEventListener("
|
|
4249
|
-
document.addEventListener("
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4637
|
+
document.addEventListener("mousemove", mouseMove);
|
|
4638
|
+
document.addEventListener("mouseup", mouseUp);
|
|
4639
|
+
document.addEventListener("touchmove", touchMove, { passive: false });
|
|
4640
|
+
document.addEventListener("touchend", touchEnd);
|
|
4641
|
+
document.addEventListener("touchcancel", cancel);
|
|
4642
|
+
document.addEventListener("pointercancel", cancel);
|
|
4643
|
+
document.addEventListener("keydown", keyDown, true);
|
|
4644
|
+
svg.addEventListener("oc-cancel-drag", cancel);
|
|
4645
|
+
cancelActive = cancel;
|
|
4646
|
+
}
|
|
4647
|
+
const mouseDown = (event) => {
|
|
4648
|
+
if (event.button !== 0) return;
|
|
4649
|
+
event.preventDefault();
|
|
4650
|
+
startDrag(event.clientX, event.clientY);
|
|
4258
4651
|
};
|
|
4259
|
-
const
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
startDrag(touchEvent.touches[0].clientX, touchEvent.touches[0].clientY);
|
|
4264
|
-
}
|
|
4652
|
+
const touchStart = (event) => {
|
|
4653
|
+
if (event.touches.length !== 1) return;
|
|
4654
|
+
event.preventDefault();
|
|
4655
|
+
startDrag(event.touches[0].clientX, event.touches[0].clientY);
|
|
4265
4656
|
};
|
|
4266
|
-
element.addEventListener("mousedown",
|
|
4267
|
-
element.addEventListener("touchstart",
|
|
4268
|
-
cleanups.push(() => {
|
|
4269
|
-
element.removeEventListener("mousedown", handleMouseDown);
|
|
4270
|
-
element.removeEventListener("touchstart", handleTouchStart);
|
|
4271
|
-
});
|
|
4657
|
+
element.addEventListener("mousedown", mouseDown);
|
|
4658
|
+
element.addEventListener("touchstart", touchStart, { passive: false });
|
|
4272
4659
|
return () => {
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
if (activeDocMouseMove) {
|
|
4277
|
-
document.removeEventListener("mousemove", activeDocMouseMove);
|
|
4278
|
-
activeDocMouseMove = null;
|
|
4279
|
-
}
|
|
4280
|
-
if (activeDocMouseUp) {
|
|
4281
|
-
document.removeEventListener("mouseup", activeDocMouseUp);
|
|
4282
|
-
activeDocMouseUp = null;
|
|
4283
|
-
}
|
|
4284
|
-
if (activeDocTouchMove) {
|
|
4285
|
-
document.removeEventListener("touchmove", activeDocTouchMove);
|
|
4286
|
-
activeDocTouchMove = null;
|
|
4287
|
-
}
|
|
4288
|
-
if (activeDocTouchEnd) {
|
|
4289
|
-
document.removeEventListener("touchend", activeDocTouchEnd);
|
|
4290
|
-
activeDocTouchEnd = null;
|
|
4291
|
-
}
|
|
4292
|
-
if (activeDocTouchCancel) {
|
|
4293
|
-
document.removeEventListener("touchcancel", activeDocTouchCancel);
|
|
4294
|
-
activeDocTouchCancel = null;
|
|
4295
|
-
}
|
|
4296
|
-
svg.style.userSelect = "";
|
|
4660
|
+
element.removeEventListener("mousedown", mouseDown);
|
|
4661
|
+
element.removeEventListener("touchstart", touchStart);
|
|
4662
|
+
cancelActive?.();
|
|
4297
4663
|
};
|
|
4298
4664
|
}
|
|
4299
4665
|
|
|
4300
4666
|
// src/interactions/editing-drags.ts
|
|
4301
4667
|
import {
|
|
4302
|
-
elementRef
|
|
4668
|
+
elementRef as elementRef2
|
|
4303
4669
|
} from "@opendata-ai/openchart-core";
|
|
4670
|
+
import {
|
|
4671
|
+
createAnnotationEditContext as createAnnotationEditContext2,
|
|
4672
|
+
inverseLabelPlacement as inverseLabelPlacement2,
|
|
4673
|
+
previewAnnotationMove as previewAnnotationMove2,
|
|
4674
|
+
previewConnectorOffset
|
|
4675
|
+
} from "@opendata-ai/openchart-engine";
|
|
4304
4676
|
|
|
4305
4677
|
// src/interactions/invert.ts
|
|
4306
4678
|
function invertScale(invert, pixel) {
|
|
@@ -4345,212 +4717,153 @@ function invertScale(invert, pixel) {
|
|
|
4345
4717
|
}
|
|
4346
4718
|
|
|
4347
4719
|
// src/interactions/editing-drags.ts
|
|
4348
|
-
function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setDragging) {
|
|
4349
|
-
const annotationElements = svg.querySelectorAll(".oc-annotation-text");
|
|
4720
|
+
function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setDragging, layout, spec) {
|
|
4350
4721
|
const cleanups = [];
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
const
|
|
4356
|
-
if (
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
const
|
|
4365
|
-
const
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
if (moved) {
|
|
4393
|
-
const newOffset = {
|
|
4394
|
-
dx: origDx + dx,
|
|
4395
|
-
dy: origDy + dy
|
|
4396
|
-
};
|
|
4397
|
-
onAnnotationEdit?.(textAnnotation, newOffset);
|
|
4722
|
+
const ctx = createAnnotationEditContext2(layout, spec);
|
|
4723
|
+
for (const group of svg.querySelectorAll(".oc-annotation-text")) {
|
|
4724
|
+
const ref = buildElementRef(group, specAnnotations);
|
|
4725
|
+
if (ref?.type !== "annotation") continue;
|
|
4726
|
+
const authored = specAnnotations[ref.index];
|
|
4727
|
+
if (authored?.type !== "text") continue;
|
|
4728
|
+
let initial;
|
|
4729
|
+
try {
|
|
4730
|
+
initial = previewAnnotationMove2(ctx, ref, { dx: 0, dy: 0 });
|
|
4731
|
+
} catch {
|
|
4732
|
+
continue;
|
|
4733
|
+
}
|
|
4734
|
+
if (!initial.label?.visible || initial.footnoteIndex !== void 0) continue;
|
|
4735
|
+
const original = initial;
|
|
4736
|
+
const restore = () => updateAnnotationGeometry(group, original, layout.theme.colors.neutral.surface);
|
|
4737
|
+
group.style.cursor = "grab";
|
|
4738
|
+
cleanups.push(
|
|
4739
|
+
createDragHandler({
|
|
4740
|
+
element: group,
|
|
4741
|
+
svg,
|
|
4742
|
+
setDragging,
|
|
4743
|
+
animationFrame: true,
|
|
4744
|
+
onMove: (dx, dy) => updateAnnotationGeometry(
|
|
4745
|
+
group,
|
|
4746
|
+
previewAnnotationMove2(ctx, ref, { dx, dy }),
|
|
4747
|
+
layout.theme.colors.neutral.surface
|
|
4748
|
+
),
|
|
4749
|
+
onCancel: restore,
|
|
4750
|
+
onEnd: (dx, dy, moved) => {
|
|
4751
|
+
restore();
|
|
4752
|
+
if (!moved) return;
|
|
4753
|
+
const preview = previewAnnotationMove2(ctx, ref, { dx, dy });
|
|
4754
|
+
const placement = inverseLabelPlacement2(ctx, ref, preview.bounds);
|
|
4755
|
+
onAnnotationEdit?.(authored, {
|
|
4756
|
+
dx: Math.round(
|
|
4757
|
+
((authored.offset?.dx ?? 0) + preview.label.x - original.label.x) * 1e10
|
|
4758
|
+
) / 1e10,
|
|
4759
|
+
dy: Math.round(
|
|
4760
|
+
((authored.offset?.dy ?? 0) + preview.label.y - original.label.y) * 1e10
|
|
4761
|
+
) / 1e10
|
|
4762
|
+
});
|
|
4398
4763
|
onEdit?.({
|
|
4399
4764
|
type: "annotation",
|
|
4400
|
-
element:
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
annotation: textAnnotation,
|
|
4406
|
-
offset: newOffset
|
|
4765
|
+
element: { ...ref, path: `${ref.path}/offset` },
|
|
4766
|
+
annotation: authored,
|
|
4767
|
+
offset: placement.offset,
|
|
4768
|
+
anchor: placement.anchor,
|
|
4769
|
+
responsive: false
|
|
4407
4770
|
});
|
|
4408
4771
|
}
|
|
4409
|
-
}
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
const
|
|
4433
|
-
const
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
const
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
e.stopPropagation();
|
|
4474
|
-
};
|
|
4475
|
-
handleEl.addEventListener("mousedown", stopProp);
|
|
4476
|
-
handleEl.addEventListener("touchstart", stopProp);
|
|
4477
|
-
cleanups.push(() => {
|
|
4478
|
-
handleEl.removeEventListener("mousedown", stopProp);
|
|
4479
|
-
handleEl.removeEventListener("touchstart", stopProp);
|
|
4480
|
-
});
|
|
4481
|
-
const cleanup = createDragHandler({
|
|
4482
|
-
element: handleEl,
|
|
4483
|
-
svg,
|
|
4484
|
-
onMove: (dx, dy) => {
|
|
4485
|
-
handleEl.setAttribute("cx", String(origCx + dx));
|
|
4486
|
-
handleEl.setAttribute("cy", String(origCy + dy));
|
|
4487
|
-
if (connectorLine) {
|
|
4488
|
-
if (ep.name === "from") {
|
|
4489
|
-
connectorLine.setAttribute("x1", String(origCx + dx));
|
|
4490
|
-
connectorLine.setAttribute("y1", String(origCy + dy));
|
|
4491
|
-
} else {
|
|
4492
|
-
connectorLine.setAttribute("x2", String(origCx + dx));
|
|
4493
|
-
connectorLine.setAttribute("y2", String(origCy + dy));
|
|
4494
|
-
}
|
|
4495
|
-
}
|
|
4496
|
-
},
|
|
4497
|
-
onEnd: (dx, dy, moved) => {
|
|
4498
|
-
handleEl.setAttribute("cx", String(origCx));
|
|
4499
|
-
handleEl.setAttribute("cy", String(origCy));
|
|
4500
|
-
if (connectorLine) {
|
|
4501
|
-
if (ep.name === "from") {
|
|
4502
|
-
connectorLine.setAttribute("x1", String(origCx));
|
|
4503
|
-
connectorLine.setAttribute("y1", String(origCy));
|
|
4504
|
-
} else {
|
|
4505
|
-
connectorLine.setAttribute("x2", String(origCx));
|
|
4506
|
-
connectorLine.setAttribute("y2", String(origCy));
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4509
|
-
if (moved) {
|
|
4510
|
-
const existingOffset = textAnnotation.connectorOffset?.[ep.name];
|
|
4511
|
-
const origEndDx = existingOffset?.dx ?? 0;
|
|
4512
|
-
const origEndDy = existingOffset?.dy ?? 0;
|
|
4772
|
+
})
|
|
4773
|
+
);
|
|
4774
|
+
}
|
|
4775
|
+
return () => {
|
|
4776
|
+
for (const cleanup of cleanups) cleanup();
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging, layout, spec) {
|
|
4780
|
+
const cleanups = [];
|
|
4781
|
+
const ctx = createAnnotationEditContext2(layout, spec);
|
|
4782
|
+
for (const group of svg.querySelectorAll(".oc-annotation-text")) {
|
|
4783
|
+
const ref = buildElementRef(group, specAnnotations);
|
|
4784
|
+
if (ref?.type !== "annotation") continue;
|
|
4785
|
+
const authored = specAnnotations[ref.index];
|
|
4786
|
+
if (authored?.type !== "text") continue;
|
|
4787
|
+
let original;
|
|
4788
|
+
try {
|
|
4789
|
+
original = previewAnnotationMove2(ctx, ref, { dx: 0, dy: 0 });
|
|
4790
|
+
} catch {
|
|
4791
|
+
continue;
|
|
4792
|
+
}
|
|
4793
|
+
const connector = original.label?.connector;
|
|
4794
|
+
if (!connector || original.footnoteIndex !== void 0) continue;
|
|
4795
|
+
const handles = [];
|
|
4796
|
+
for (const end of ["from", "to"]) {
|
|
4797
|
+
const point = connector[end];
|
|
4798
|
+
const handle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
4799
|
+
handle.setAttribute("class", "oc-connector-handle");
|
|
4800
|
+
handle.setAttribute("data-endpoint", end);
|
|
4801
|
+
handle.setAttribute("cx", String(point.x));
|
|
4802
|
+
handle.setAttribute("cy", String(point.y));
|
|
4803
|
+
handle.setAttribute("r", "4");
|
|
4804
|
+
handle.setAttribute("opacity", "0");
|
|
4805
|
+
handle.setAttribute("fill", "currentColor");
|
|
4806
|
+
handle.setAttribute("stroke", "currentColor");
|
|
4807
|
+
handle.style.cursor = "grab";
|
|
4808
|
+
group.appendChild(handle);
|
|
4809
|
+
handles.push(handle);
|
|
4810
|
+
const stop = (event) => event.stopPropagation();
|
|
4811
|
+
handle.addEventListener("mousedown", stop);
|
|
4812
|
+
handle.addEventListener("touchstart", stop);
|
|
4813
|
+
const restore = () => {
|
|
4814
|
+
updateAnnotationGeometry(group, original, layout.theme.colors.neutral.surface);
|
|
4815
|
+
handle.setAttribute("cx", String(point.x));
|
|
4816
|
+
handle.setAttribute("cy", String(point.y));
|
|
4817
|
+
};
|
|
4818
|
+
cleanups.push(
|
|
4819
|
+
createDragHandler({
|
|
4820
|
+
element: handle,
|
|
4821
|
+
svg,
|
|
4822
|
+
setDragging,
|
|
4823
|
+
animationFrame: true,
|
|
4824
|
+
onMove: (dx, dy) => {
|
|
4825
|
+
const preview = previewConnectorOffset(ctx, ref, end, { dx, dy });
|
|
4826
|
+
updateAnnotationGeometry(group, preview, layout.theme.colors.neutral.surface);
|
|
4827
|
+
const position = preview.label?.connector?.[end] ?? point;
|
|
4828
|
+
handle.setAttribute("cx", String(position.x));
|
|
4829
|
+
handle.setAttribute("cy", String(position.y));
|
|
4830
|
+
},
|
|
4831
|
+
onCancel: restore,
|
|
4832
|
+
onEnd: (dx, dy, moved) => {
|
|
4833
|
+
restore();
|
|
4834
|
+
if (!moved) return;
|
|
4835
|
+
const previous = authored.connectorOffset?.[end];
|
|
4513
4836
|
onEdit({
|
|
4514
4837
|
type: "annotation-connector",
|
|
4515
|
-
element:
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
),
|
|
4520
|
-
annotation: textAnnotation,
|
|
4521
|
-
endpoint: ep.name,
|
|
4522
|
-
offset: { dx: origEndDx + dx, dy: origEndDy + dy }
|
|
4838
|
+
element: { ...ref, path: `${ref.path}/connectorOffset/${end}` },
|
|
4839
|
+
annotation: authored,
|
|
4840
|
+
endpoint: end,
|
|
4841
|
+
offset: { dx: (previous?.dx ?? 0) + dx, dy: (previous?.dy ?? 0) + dy }
|
|
4523
4842
|
});
|
|
4524
4843
|
}
|
|
4525
|
-
}
|
|
4526
|
-
|
|
4844
|
+
})
|
|
4845
|
+
);
|
|
4846
|
+
cleanups.push(() => {
|
|
4847
|
+
handle.removeEventListener("mousedown", stop);
|
|
4848
|
+
handle.removeEventListener("touchstart", stop);
|
|
4849
|
+
handle.remove();
|
|
4527
4850
|
});
|
|
4528
|
-
cleanups.push(cleanup);
|
|
4529
4851
|
}
|
|
4530
|
-
const
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
}
|
|
4539
|
-
};
|
|
4540
|
-
annotationG.addEventListener("mouseenter", showHandles);
|
|
4541
|
-
annotationG.addEventListener("mouseleave", hideHandles);
|
|
4852
|
+
const show = () => handles.forEach((handle) => {
|
|
4853
|
+
handle.setAttribute("opacity", "0.6");
|
|
4854
|
+
});
|
|
4855
|
+
const hide = () => handles.forEach((handle) => {
|
|
4856
|
+
handle.setAttribute("opacity", "0");
|
|
4857
|
+
});
|
|
4858
|
+
group.addEventListener("mouseenter", show);
|
|
4859
|
+
group.addEventListener("mouseleave", hide);
|
|
4542
4860
|
cleanups.push(() => {
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
for (const h of createdHandles) {
|
|
4546
|
-
h.remove();
|
|
4547
|
-
}
|
|
4861
|
+
group.removeEventListener("mouseenter", show);
|
|
4862
|
+
group.removeEventListener("mouseleave", hide);
|
|
4548
4863
|
});
|
|
4549
4864
|
}
|
|
4550
4865
|
return () => {
|
|
4551
|
-
for (const cleanup of cleanups)
|
|
4552
|
-
cleanup();
|
|
4553
|
-
}
|
|
4866
|
+
for (const cleanup of cleanups) cleanup();
|
|
4554
4867
|
};
|
|
4555
4868
|
}
|
|
4556
4869
|
function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
@@ -4581,16 +4894,19 @@ function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4581
4894
|
onMove: (dx, dy) => {
|
|
4582
4895
|
labelEl.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
4583
4896
|
},
|
|
4897
|
+
onCancel: () => {
|
|
4898
|
+
labelEl.style.transform = "";
|
|
4899
|
+
},
|
|
4584
4900
|
onEnd: (dx, dy, moved) => {
|
|
4585
4901
|
labelEl.style.transform = "";
|
|
4586
4902
|
if (moved) {
|
|
4587
4903
|
if (isRange) {
|
|
4588
4904
|
onEdit({
|
|
4589
4905
|
type: "range-label",
|
|
4590
|
-
element:
|
|
4906
|
+
element: elementRef2.annotation(
|
|
4591
4907
|
index,
|
|
4592
4908
|
specAnnotation.id,
|
|
4593
|
-
`/annotations/${index}/labelOffset`
|
|
4909
|
+
`${annotationG.getAttribute("data-annotation-path") ?? `/annotations/${index}`}/labelOffset`
|
|
4594
4910
|
),
|
|
4595
4911
|
annotation: specAnnotation,
|
|
4596
4912
|
labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy }
|
|
@@ -4598,10 +4914,10 @@ function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4598
4914
|
} else {
|
|
4599
4915
|
onEdit({
|
|
4600
4916
|
type: "refline-label",
|
|
4601
|
-
element:
|
|
4917
|
+
element: elementRef2.annotation(
|
|
4602
4918
|
index,
|
|
4603
4919
|
specAnnotation.id,
|
|
4604
|
-
`/annotations/${index}/labelOffset`
|
|
4920
|
+
`${annotationG.getAttribute("data-annotation-path") ?? `/annotations/${index}`}/labelOffset`
|
|
4605
4921
|
),
|
|
4606
4922
|
annotation: specAnnotation,
|
|
4607
4923
|
labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy }
|
|
@@ -4639,6 +4955,9 @@ function wireChromeDrag(svg, spec, onEdit, setDragging) {
|
|
|
4639
4955
|
onMove: (dx, dy) => {
|
|
4640
4956
|
textEl.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
4641
4957
|
},
|
|
4958
|
+
onCancel: () => {
|
|
4959
|
+
textEl.style.transform = "";
|
|
4960
|
+
},
|
|
4642
4961
|
onEnd: (dx, dy, moved) => {
|
|
4643
4962
|
textEl.style.transform = "";
|
|
4644
4963
|
if (moved) {
|
|
@@ -4676,6 +4995,9 @@ function wireLegendDrag(svg, spec, onEdit, setDragging) {
|
|
|
4676
4995
|
onMove: (dx, dy) => {
|
|
4677
4996
|
legendG.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
4678
4997
|
},
|
|
4998
|
+
onCancel: () => {
|
|
4999
|
+
legendG.style.transform = "";
|
|
5000
|
+
},
|
|
4679
5001
|
onEnd: (dx, dy, moved) => {
|
|
4680
5002
|
legendG.style.transform = "";
|
|
4681
5003
|
if (moved) {
|
|
@@ -4710,6 +5032,9 @@ function wireSeriesLabelDrag(svg, spec, onEdit, setDragging) {
|
|
|
4710
5032
|
onMove: (dx, dy) => {
|
|
4711
5033
|
labelEl.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
4712
5034
|
},
|
|
5035
|
+
onCancel: () => {
|
|
5036
|
+
labelEl.style.transform = "";
|
|
5037
|
+
},
|
|
4713
5038
|
onEnd: (dx, dy, moved) => {
|
|
4714
5039
|
labelEl.style.transform = "";
|
|
4715
5040
|
if (moved) {
|
|
@@ -4774,6 +5099,10 @@ function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
|
4774
5099
|
handleEl.setAttribute("cx", String(anchorX + dx));
|
|
4775
5100
|
handleEl.setAttribute("cy", String(anchorY + dy));
|
|
4776
5101
|
},
|
|
5102
|
+
onCancel: () => {
|
|
5103
|
+
handleEl.setAttribute("cx", String(anchorX));
|
|
5104
|
+
handleEl.setAttribute("cy", String(anchorY));
|
|
5105
|
+
},
|
|
4777
5106
|
onEnd: (dx, dy, moved) => {
|
|
4778
5107
|
handleEl.setAttribute("cx", String(anchorX));
|
|
4779
5108
|
handleEl.setAttribute("cy", String(anchorY));
|
|
@@ -4782,7 +5111,11 @@ function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
|
4782
5111
|
const newY = invertScale(layout.yInvert, anchorY + dy);
|
|
4783
5112
|
onEdit({
|
|
4784
5113
|
type: "annotation-anchor",
|
|
4785
|
-
element:
|
|
5114
|
+
element: elementRef2.annotation(
|
|
5115
|
+
index,
|
|
5116
|
+
textAnnotation.id,
|
|
5117
|
+
annotationG.getAttribute("data-annotation-path") ?? `/annotations/${index}`
|
|
5118
|
+
),
|
|
4786
5119
|
annotation: textAnnotation,
|
|
4787
5120
|
x: newX,
|
|
4788
5121
|
y: newY
|
|
@@ -5119,210 +5452,6 @@ function wireLegendInteraction(svg, _layout, toggleSeries, onLegendToggle, onEdi
|
|
|
5119
5452
|
};
|
|
5120
5453
|
}
|
|
5121
5454
|
|
|
5122
|
-
// src/interactions/selection.ts
|
|
5123
|
-
import { elementRef as elementRef2 } from "@opendata-ai/openchart-core";
|
|
5124
|
-
function findElementByRef(svg, ref) {
|
|
5125
|
-
switch (ref.type) {
|
|
5126
|
-
case "annotation": {
|
|
5127
|
-
if (ref.id) {
|
|
5128
|
-
const byId = svg.querySelector(`[data-annotation-id="${ref.id}"]`);
|
|
5129
|
-
if (byId) return byId;
|
|
5130
|
-
}
|
|
5131
|
-
return svg.querySelector(`[data-annotation-index="${ref.index}"]`);
|
|
5132
|
-
}
|
|
5133
|
-
case "chrome":
|
|
5134
|
-
return svg.querySelector(`[data-chrome-key="${ref.key}"]`);
|
|
5135
|
-
case "series-label":
|
|
5136
|
-
return svg.querySelector(`.oc-mark-label[data-series="${ref.series}"]`);
|
|
5137
|
-
// The editable legend is the color legend. Excluding the size legend
|
|
5138
|
-
// explicitly rather than relying on it rendering second: `querySelector`
|
|
5139
|
-
// takes the first match, so a bare `.oc-legend` would silently retarget
|
|
5140
|
-
// (and drag) the wrong legend if render order ever changed.
|
|
5141
|
-
case "legend":
|
|
5142
|
-
return svg.querySelector(".oc-legend:not(.oc-legend--size)");
|
|
5143
|
-
case "legend-entry":
|
|
5144
|
-
return svg.querySelector(
|
|
5145
|
-
`.oc-legend:not(.oc-legend--size) [data-legend-index="${ref.index}"]`
|
|
5146
|
-
);
|
|
5147
|
-
}
|
|
5148
|
-
}
|
|
5149
|
-
function encodePointerToken(token) {
|
|
5150
|
-
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
5151
|
-
}
|
|
5152
|
-
function buildElementRef(element, _specAnnotations) {
|
|
5153
|
-
const annotationEl = element.closest("[data-annotation-index]");
|
|
5154
|
-
if (annotationEl) {
|
|
5155
|
-
const index = Number(annotationEl.getAttribute("data-annotation-index"));
|
|
5156
|
-
const id = annotationEl.getAttribute("data-annotation-id") ?? void 0;
|
|
5157
|
-
return elementRef2.annotation(index, id, `/annotations/${index}`);
|
|
5158
|
-
}
|
|
5159
|
-
const chromeEl = element.closest("[data-chrome-key]");
|
|
5160
|
-
if (chromeEl) {
|
|
5161
|
-
const key = chromeEl.getAttribute("data-chrome-key");
|
|
5162
|
-
if (key) return elementRef2.chrome(key, `/chrome/${key}`);
|
|
5163
|
-
}
|
|
5164
|
-
const seriesLabelEl = element.closest(".oc-mark-label[data-series]");
|
|
5165
|
-
if (seriesLabelEl) {
|
|
5166
|
-
const series = seriesLabelEl.getAttribute("data-series");
|
|
5167
|
-
if (series)
|
|
5168
|
-
return elementRef2.seriesLabel(series, `/labels/offsets/${encodePointerToken(series)}`);
|
|
5169
|
-
}
|
|
5170
|
-
const legendEntryEl = element.closest("[data-legend-index]");
|
|
5171
|
-
if (legendEntryEl) {
|
|
5172
|
-
const index = Number(legendEntryEl.getAttribute("data-legend-index"));
|
|
5173
|
-
const series = legendEntryEl.getAttribute("data-legend-label") ?? "";
|
|
5174
|
-
return elementRef2.legendEntry(series, index);
|
|
5175
|
-
}
|
|
5176
|
-
const legendEl = element.closest(".oc-legend");
|
|
5177
|
-
if (legendEl && !legendEl.classList.contains("oc-legend--size"))
|
|
5178
|
-
return elementRef2.legend("/legend");
|
|
5179
|
-
return null;
|
|
5180
|
-
}
|
|
5181
|
-
function getEditableElements(spec, layout) {
|
|
5182
|
-
const refs = [];
|
|
5183
|
-
const chromeKeys = ["title", "subtitle", "source", "byline", "footer"];
|
|
5184
|
-
for (const key of chromeKeys) {
|
|
5185
|
-
if (layout.chrome[key]) {
|
|
5186
|
-
refs.push(elementRef2.chrome(key));
|
|
5187
|
-
}
|
|
5188
|
-
}
|
|
5189
|
-
const annotations = "annotations" in spec && Array.isArray(spec.annotations) ? spec.annotations : [];
|
|
5190
|
-
for (let i = 0; i < annotations.length; i++) {
|
|
5191
|
-
refs.push(elementRef2.annotation(i, annotations[i].id));
|
|
5192
|
-
}
|
|
5193
|
-
const seriesLabels = [];
|
|
5194
|
-
for (const mark of layout.marks) {
|
|
5195
|
-
if (mark.type === "line" && mark.label?.visible && mark.seriesKey) {
|
|
5196
|
-
seriesLabels.push(mark.seriesKey);
|
|
5197
|
-
}
|
|
5198
|
-
}
|
|
5199
|
-
seriesLabels.sort();
|
|
5200
|
-
for (const series of seriesLabels) {
|
|
5201
|
-
refs.push(elementRef2.seriesLabel(series));
|
|
5202
|
-
}
|
|
5203
|
-
if ("entries" in layout.legend && layout.legend.entries.length > 0) {
|
|
5204
|
-
refs.push(elementRef2.legend());
|
|
5205
|
-
}
|
|
5206
|
-
return refs;
|
|
5207
|
-
}
|
|
5208
|
-
function isTextEditable(ref, specAnnotations) {
|
|
5209
|
-
if (ref.type === "chrome") return true;
|
|
5210
|
-
if (ref.type === "annotation") {
|
|
5211
|
-
const annotation = specAnnotations[ref.index];
|
|
5212
|
-
return annotation?.type === "text";
|
|
5213
|
-
}
|
|
5214
|
-
return false;
|
|
5215
|
-
}
|
|
5216
|
-
function getElementText(ref, spec) {
|
|
5217
|
-
if (ref.type === "chrome") {
|
|
5218
|
-
const chromeConfig = "chrome" in spec ? spec.chrome : void 0;
|
|
5219
|
-
if (!chromeConfig) return null;
|
|
5220
|
-
const entry = chromeConfig[ref.key];
|
|
5221
|
-
if (typeof entry === "string") return entry;
|
|
5222
|
-
if (typeof entry === "object" && entry !== null && "text" in entry) {
|
|
5223
|
-
return entry.text;
|
|
5224
|
-
}
|
|
5225
|
-
return null;
|
|
5226
|
-
}
|
|
5227
|
-
if (ref.type === "annotation") {
|
|
5228
|
-
const annotations = "annotations" in spec && Array.isArray(spec.annotations) ? spec.annotations : [];
|
|
5229
|
-
const annotation = annotations[ref.index];
|
|
5230
|
-
if (annotation?.type === "text") return annotation.text ?? null;
|
|
5231
|
-
if (annotation?.label) return annotation.label;
|
|
5232
|
-
return null;
|
|
5233
|
-
}
|
|
5234
|
-
return null;
|
|
5235
|
-
}
|
|
5236
|
-
function refsEqual(a, b) {
|
|
5237
|
-
if (a === null || b === null) return a === b;
|
|
5238
|
-
if (a.type !== b.type) return false;
|
|
5239
|
-
switch (a.type) {
|
|
5240
|
-
case "annotation": {
|
|
5241
|
-
const bAnno = b;
|
|
5242
|
-
if (a.id && bAnno.id) return a.id === bAnno.id;
|
|
5243
|
-
return a.index === bAnno.index;
|
|
5244
|
-
}
|
|
5245
|
-
case "chrome":
|
|
5246
|
-
return a.key === b.key;
|
|
5247
|
-
case "series-label":
|
|
5248
|
-
return a.series === b.series;
|
|
5249
|
-
case "legend":
|
|
5250
|
-
return true;
|
|
5251
|
-
case "legend-entry": {
|
|
5252
|
-
const bEntry = b;
|
|
5253
|
-
return a.index === bEntry.index && a.series === bEntry.series;
|
|
5254
|
-
}
|
|
5255
|
-
}
|
|
5256
|
-
}
|
|
5257
|
-
function renderSelectionOverlay(svg, ref, layout) {
|
|
5258
|
-
const target = findElementByRef(svg, ref);
|
|
5259
|
-
if (!target) return null;
|
|
5260
|
-
const bbox = target.getBBox();
|
|
5261
|
-
const padding = 4;
|
|
5262
|
-
const accentColor = layout.theme.colors.categorical?.[0] ?? "#4f46e5";
|
|
5263
|
-
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
5264
|
-
g.setAttribute("class", "oc-selection-overlay");
|
|
5265
|
-
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
5266
|
-
rect.setAttribute("x", String(bbox.x - padding));
|
|
5267
|
-
rect.setAttribute("y", String(bbox.y - padding));
|
|
5268
|
-
rect.setAttribute("width", String(bbox.width + padding * 2));
|
|
5269
|
-
rect.setAttribute("height", String(bbox.height + padding * 2));
|
|
5270
|
-
rect.setAttribute("rx", "3");
|
|
5271
|
-
rect.setAttribute("fill", "transparent");
|
|
5272
|
-
rect.setAttribute("stroke", accentColor);
|
|
5273
|
-
rect.setAttribute("stroke-width", "1.5");
|
|
5274
|
-
rect.setAttribute("pointer-events", "none");
|
|
5275
|
-
g.appendChild(rect);
|
|
5276
|
-
svg.appendChild(g);
|
|
5277
|
-
return g;
|
|
5278
|
-
}
|
|
5279
|
-
function createScreenReaderTable(layout, container) {
|
|
5280
|
-
const data = layout.a11y.dataTableFallback;
|
|
5281
|
-
if (!data || data.length === 0) return null;
|
|
5282
|
-
const wrapper = document.createElement("div");
|
|
5283
|
-
wrapper.className = "oc-sr-only";
|
|
5284
|
-
applySrOnlyStyles(wrapper);
|
|
5285
|
-
const table = document.createElement("table");
|
|
5286
|
-
table.setAttribute("role", "table");
|
|
5287
|
-
table.setAttribute("aria-label", `Data table: ${layout.a11y.altText}`);
|
|
5288
|
-
const totalRows = layout.a11y.totalRows;
|
|
5289
|
-
if (totalRows != null) {
|
|
5290
|
-
const caption = document.createElement("caption");
|
|
5291
|
-
caption.textContent = `Showing first ${(data.length - 1).toLocaleString()} of ${totalRows.toLocaleString()} rows; full data via CSV export`;
|
|
5292
|
-
table.appendChild(caption);
|
|
5293
|
-
}
|
|
5294
|
-
if (data.length > 0) {
|
|
5295
|
-
const thead = document.createElement("thead");
|
|
5296
|
-
const headerRow = document.createElement("tr");
|
|
5297
|
-
const headers = data[0];
|
|
5298
|
-
for (const header of headers) {
|
|
5299
|
-
const th = document.createElement("th");
|
|
5300
|
-
th.textContent = String(header ?? "");
|
|
5301
|
-
th.setAttribute("scope", "col");
|
|
5302
|
-
headerRow.appendChild(th);
|
|
5303
|
-
}
|
|
5304
|
-
thead.appendChild(headerRow);
|
|
5305
|
-
table.appendChild(thead);
|
|
5306
|
-
}
|
|
5307
|
-
if (data.length > 1) {
|
|
5308
|
-
const tbody = document.createElement("tbody");
|
|
5309
|
-
for (let i = 1; i < data.length; i++) {
|
|
5310
|
-
const tr = document.createElement("tr");
|
|
5311
|
-
const cells = data[i];
|
|
5312
|
-
for (const cell of cells) {
|
|
5313
|
-
const td = document.createElement("td");
|
|
5314
|
-
td.textContent = String(cell ?? "");
|
|
5315
|
-
tr.appendChild(td);
|
|
5316
|
-
}
|
|
5317
|
-
tbody.appendChild(tr);
|
|
5318
|
-
}
|
|
5319
|
-
table.appendChild(tbody);
|
|
5320
|
-
}
|
|
5321
|
-
wrapper.appendChild(table);
|
|
5322
|
-
container.appendChild(wrapper);
|
|
5323
|
-
return wrapper;
|
|
5324
|
-
}
|
|
5325
|
-
|
|
5326
5455
|
// src/interactions/tooltip-events.ts
|
|
5327
5456
|
function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager, emphasis) {
|
|
5328
5457
|
const markElements = svg.querySelectorAll("[data-mark-id]");
|
|
@@ -6014,7 +6143,7 @@ function makeEditable(svg) {
|
|
|
6014
6143
|
svg.insertBefore(style, svg.firstChild);
|
|
6015
6144
|
}
|
|
6016
6145
|
function hasEditingCallbacks(opts) {
|
|
6017
|
-
return !!(opts?.onEdit || opts?.onSelect || opts?.onDeselect || opts?.onTextEdit);
|
|
6146
|
+
return !!(opts?.onEditTransaction || opts?.onEdit || opts?.onSelect || opts?.onDeselect || opts?.onTextEdit);
|
|
6018
6147
|
}
|
|
6019
6148
|
function authoredHighlight(spec) {
|
|
6020
6149
|
if (isLayerSpec(spec) || isGraphSpec(spec)) return [];
|
|
@@ -6058,6 +6187,7 @@ function createChart(container, spec, options) {
|
|
|
6058
6187
|
let destroyed = false;
|
|
6059
6188
|
let isDragging = false;
|
|
6060
6189
|
let pendingRender = false;
|
|
6190
|
+
let gestureWidth = 0;
|
|
6061
6191
|
let isFirstRender = true;
|
|
6062
6192
|
let cleanupAnimations = null;
|
|
6063
6193
|
let pendingResize = false;
|
|
@@ -6115,7 +6245,10 @@ function createChart(container, spec, options) {
|
|
|
6115
6245
|
facetGrowth = needed - budgetForPanels;
|
|
6116
6246
|
}
|
|
6117
6247
|
}
|
|
6118
|
-
return
|
|
6248
|
+
return Math.max(
|
|
6249
|
+
0,
|
|
6250
|
+
layout.chrome.topHeight + layout.chrome.bottomHeight + topLegendBlock + (layout.metrics?.height ?? 0) + facetGrowth + (layout.chrome.grownHeight !== void 0 ? layout.chrome.noteBandHeight ?? 0 : 0) - (layout.chrome.grownHeight ?? 0)
|
|
6251
|
+
);
|
|
6119
6252
|
}
|
|
6120
6253
|
function compile(candidate = currentSpec, measure = measureText, runtime = true) {
|
|
6121
6254
|
const { width, height } = getContainerDimensions(candidate);
|
|
@@ -6240,10 +6373,40 @@ function createChart(container, spec, options) {
|
|
|
6240
6373
|
height: isAutoHeight === true ? FALLBACK_HEIGHT : Math.max(ownHeight ? lastHostHeight : rect.height || FALLBACK_HEIGHT, 100)
|
|
6241
6374
|
};
|
|
6242
6375
|
}
|
|
6376
|
+
if (options?.onDataPointClick)
|
|
6377
|
+
warnDeprecated("onDataPointClick is deprecated and has no effect; use onMarkClick.");
|
|
6378
|
+
if (options?.onAnnotationEdit)
|
|
6379
|
+
warnDeprecated("onAnnotationEdit is deprecated; use onEditTransaction.");
|
|
6380
|
+
if (options?.onTextEdit) warnDeprecated("onTextEdit is deprecated; use onEditTransaction.");
|
|
6381
|
+
if (options?.responsive !== void 0)
|
|
6382
|
+
warnDeprecated("The mount responsive option is deprecated; use autoResize.");
|
|
6383
|
+
let editRevision = options?.revision;
|
|
6384
|
+
const annotationNudges = createAnnotationNudges(
|
|
6385
|
+
() => ({
|
|
6386
|
+
layout: currentLayout,
|
|
6387
|
+
spec: currentSpec,
|
|
6388
|
+
svg: svgElement,
|
|
6389
|
+
selected: selectedElement
|
|
6390
|
+
}),
|
|
6391
|
+
emitEdit
|
|
6392
|
+
);
|
|
6393
|
+
function emitEdit(edit) {
|
|
6394
|
+
const tx = editToTransaction(currentSpec, edit, editRevision);
|
|
6395
|
+
if (!tx && edit.type !== "legend-toggle") return;
|
|
6396
|
+
if (tx && !applyEditTransaction(currentSpec, tx).ok) return;
|
|
6397
|
+
if ("element" in edit && edit.element.type === "annotation") {
|
|
6398
|
+
const base = `${annotationSource(currentSpec).path}/${edit.element.index}`;
|
|
6399
|
+
const suffix = edit.element.path?.replace(/^.*\/annotations\/\d+/, "") ?? "";
|
|
6400
|
+
edit = { ...edit, element: { ...edit.element, path: base + suffix } };
|
|
6401
|
+
}
|
|
6402
|
+
if (tx) options?.onEditTransaction?.(tx);
|
|
6403
|
+
options?.onEdit?.(edit);
|
|
6404
|
+
}
|
|
6243
6405
|
function getSpecAnnotations() {
|
|
6244
|
-
return
|
|
6406
|
+
return annotationSource(currentSpec).annotations;
|
|
6245
6407
|
}
|
|
6246
6408
|
function selectElement(ref) {
|
|
6409
|
+
annotationNudges.flush();
|
|
6247
6410
|
if (!svgElement) return;
|
|
6248
6411
|
const target = findElementByRef(svgElement, ref);
|
|
6249
6412
|
if (!target) return;
|
|
@@ -6256,6 +6419,7 @@ function createChart(container, spec, options) {
|
|
|
6256
6419
|
svgElement.focus();
|
|
6257
6420
|
}
|
|
6258
6421
|
function deselectElement() {
|
|
6422
|
+
annotationNudges.flush();
|
|
6259
6423
|
if (!selectedElement) return;
|
|
6260
6424
|
if (isTextEditingActive && textEditCleanup) {
|
|
6261
6425
|
textEditCleanup();
|
|
@@ -6292,7 +6456,7 @@ function createChart(container, spec, options) {
|
|
|
6292
6456
|
textEditCleanup = null;
|
|
6293
6457
|
if (newText !== currentText) {
|
|
6294
6458
|
options?.onTextEdit?.(editRef, currentText, newText);
|
|
6295
|
-
|
|
6459
|
+
emitEdit({
|
|
6296
6460
|
type: "text-edit",
|
|
6297
6461
|
element: editRef,
|
|
6298
6462
|
oldText: currentText,
|
|
@@ -6358,7 +6522,7 @@ function createChart(container, spec, options) {
|
|
|
6358
6522
|
enterTextEditing();
|
|
6359
6523
|
return;
|
|
6360
6524
|
}
|
|
6361
|
-
if (!ref && options?.onEdit && currentLayout?.xInvert && currentLayout?.yInvert) {
|
|
6525
|
+
if (!ref && (options?.onEdit || options?.onEditTransaction) && currentLayout?.xInvert && currentLayout?.yInvert) {
|
|
6362
6526
|
const svgEl = svg;
|
|
6363
6527
|
const viewBox = svgEl.viewBox?.baseVal;
|
|
6364
6528
|
const svgRect = svgEl.getBoundingClientRect();
|
|
@@ -6381,7 +6545,7 @@ function createChart(container, spec, options) {
|
|
|
6381
6545
|
isTextEditingActive = false;
|
|
6382
6546
|
textEditCleanup = null;
|
|
6383
6547
|
const annotation = { type: "text", x: dataX, y: dataY, text };
|
|
6384
|
-
|
|
6548
|
+
emitEdit({ type: "add", annotation });
|
|
6385
6549
|
},
|
|
6386
6550
|
onCancel: () => {
|
|
6387
6551
|
isTextEditingActive = false;
|
|
@@ -6405,12 +6569,15 @@ function createChart(container, spec, options) {
|
|
|
6405
6569
|
const svg = svgElement;
|
|
6406
6570
|
const handleKeyDown = (e) => {
|
|
6407
6571
|
const specAnnotations = getSpecAnnotations();
|
|
6572
|
+
if (!isTextEditingActive && annotationNudges.keydown(e)) return;
|
|
6573
|
+
if (e.key === "Escape" || e.key === "Delete" || e.key === "Backspace")
|
|
6574
|
+
annotationNudges.flush();
|
|
6408
6575
|
switch (e.key) {
|
|
6409
6576
|
case "Delete":
|
|
6410
6577
|
case "Backspace": {
|
|
6411
6578
|
if (selectedElement && !isTextEditingActive) {
|
|
6412
6579
|
e.preventDefault();
|
|
6413
|
-
|
|
6580
|
+
emitEdit({ type: "delete", element: selectedElement });
|
|
6414
6581
|
}
|
|
6415
6582
|
break;
|
|
6416
6583
|
}
|
|
@@ -6461,8 +6628,49 @@ function createChart(container, spec, options) {
|
|
|
6461
6628
|
}
|
|
6462
6629
|
};
|
|
6463
6630
|
svg.addEventListener("keydown", handleKeyDown);
|
|
6631
|
+
const flushNudges = () => annotationNudges.flush();
|
|
6632
|
+
svg.addEventListener("keyup", flushNudges);
|
|
6633
|
+
const startGesture = (event) => {
|
|
6634
|
+
const target = event.target;
|
|
6635
|
+
const ref = buildElementRef(target, getSpecAnnotations());
|
|
6636
|
+
const previousLayout = currentLayout;
|
|
6637
|
+
const role = target.closest(".oc-connector-handle, .oc-anchor-handle, .oc-annotation-label");
|
|
6638
|
+
const roleClass = role?.getAttribute("class");
|
|
6639
|
+
const endpoint = role?.getAttribute("data-endpoint");
|
|
6640
|
+
annotationNudges.flush();
|
|
6641
|
+
if (destroyed || !ref || !svgElement || currentLayout === previousLayout) return;
|
|
6642
|
+
const group = findElementByRef(svgElement, ref);
|
|
6643
|
+
if (!group) return;
|
|
6644
|
+
const replacement = roleClass ? [...group.querySelectorAll("*")].find(
|
|
6645
|
+
(el) => el.getAttribute("class") === roleClass && el.getAttribute("data-endpoint") === endpoint
|
|
6646
|
+
) ?? group : group;
|
|
6647
|
+
event.stopImmediatePropagation();
|
|
6648
|
+
event.preventDefault();
|
|
6649
|
+
const init = { bubbles: true, cancelable: true };
|
|
6650
|
+
replacement.dispatchEvent(
|
|
6651
|
+
event.type === "mousedown" ? new MouseEvent("mousedown", {
|
|
6652
|
+
...init,
|
|
6653
|
+
clientX: event.clientX,
|
|
6654
|
+
clientY: event.clientY,
|
|
6655
|
+
button: event.button
|
|
6656
|
+
}) : new TouchEvent("touchstart", {
|
|
6657
|
+
...init,
|
|
6658
|
+
touches: [...event.touches],
|
|
6659
|
+
changedTouches: [...event.changedTouches]
|
|
6660
|
+
})
|
|
6661
|
+
);
|
|
6662
|
+
};
|
|
6663
|
+
svg.addEventListener("mousedown", startGesture, true);
|
|
6664
|
+
svg.addEventListener("touchstart", startGesture, true);
|
|
6665
|
+
document.addEventListener("pointercancel", flushNudges, true);
|
|
6666
|
+
document.addEventListener("touchcancel", flushNudges, true);
|
|
6464
6667
|
return () => {
|
|
6465
6668
|
svg.removeEventListener("keydown", handleKeyDown);
|
|
6669
|
+
svg.removeEventListener("keyup", flushNudges);
|
|
6670
|
+
svg.removeEventListener("mousedown", startGesture, true);
|
|
6671
|
+
svg.removeEventListener("touchstart", startGesture, true);
|
|
6672
|
+
document.removeEventListener("pointercancel", flushNudges, true);
|
|
6673
|
+
document.removeEventListener("touchcancel", flushNudges, true);
|
|
6466
6674
|
};
|
|
6467
6675
|
}
|
|
6468
6676
|
function render(retainScene = false, prepared) {
|
|
@@ -6596,41 +6804,63 @@ function createChart(container, spec, options) {
|
|
|
6596
6804
|
"[openchart] seriesSearch and edit mode are mutually exclusive; edit interactions are disabled while seriesSearch is enabled."
|
|
6597
6805
|
);
|
|
6598
6806
|
}
|
|
6807
|
+
const authoredAnnotations = annotationSource(currentSpec);
|
|
6808
|
+
for (const el of svgElement.querySelectorAll("[data-annotation-index]")) {
|
|
6809
|
+
el.setAttribute(
|
|
6810
|
+
"data-annotation-path",
|
|
6811
|
+
`${authoredAnnotations.path}/${el.getAttribute("data-annotation-index")}`
|
|
6812
|
+
);
|
|
6813
|
+
}
|
|
6814
|
+
for (const el of svgElement.querySelectorAll("[data-note-spec-index]")) {
|
|
6815
|
+
el.setAttribute(
|
|
6816
|
+
"data-note-path",
|
|
6817
|
+
`${authoredAnnotations.path}/${el.getAttribute("data-note-spec-index")}`
|
|
6818
|
+
);
|
|
6819
|
+
}
|
|
6599
6820
|
cleanupLegend = wireLegendInteraction(
|
|
6600
6821
|
svgElement,
|
|
6601
6822
|
currentLayout,
|
|
6602
6823
|
toggleSeriesVisibility,
|
|
6603
6824
|
options?.onLegendToggle,
|
|
6604
|
-
editSuppressed ? void 0 :
|
|
6825
|
+
editSuppressed ? void 0 : emitEdit,
|
|
6605
6826
|
hoverEmphasis
|
|
6606
6827
|
);
|
|
6607
6828
|
if (options?.onMarkClick || options?.onMarkHover || options?.onMarkLeave || options?.onAnnotationClick) {
|
|
6608
|
-
const specAnnotations =
|
|
6829
|
+
const specAnnotations = getSpecAnnotations();
|
|
6609
6830
|
cleanupChartEvents = wireChartEvents(svgElement, currentLayout, specAnnotations, options);
|
|
6610
6831
|
}
|
|
6611
6832
|
const setDragging = (dragging) => {
|
|
6833
|
+
if (dragging) gestureWidth = getContainerDimensions().width;
|
|
6612
6834
|
isDragging = dragging;
|
|
6613
6835
|
if (!dragging && pendingRender) {
|
|
6614
6836
|
pendingRender = false;
|
|
6615
6837
|
render();
|
|
6616
6838
|
}
|
|
6617
6839
|
};
|
|
6618
|
-
const dragAnnotations =
|
|
6619
|
-
if (!editSuppressed && (options?.editable ?? Boolean(options?.onAnnotationEdit || options?.onEdit))) {
|
|
6840
|
+
const dragAnnotations = getSpecAnnotations();
|
|
6841
|
+
if (!editSuppressed && (options?.editable ?? Boolean(options?.onAnnotationEdit || options?.onEdit || options?.onEditTransaction))) {
|
|
6620
6842
|
cleanupAnnotationDrag = wireAnnotationDrag(
|
|
6621
6843
|
svgElement,
|
|
6622
6844
|
dragAnnotations,
|
|
6623
6845
|
options?.onAnnotationEdit,
|
|
6624
|
-
|
|
6625
|
-
setDragging
|
|
6846
|
+
emitEdit,
|
|
6847
|
+
setDragging,
|
|
6848
|
+
currentLayout,
|
|
6849
|
+
currentSpec
|
|
6626
6850
|
);
|
|
6627
6851
|
}
|
|
6628
|
-
if (!editSuppressed && (options?.editable ?? Boolean(options?.onEdit))) {
|
|
6852
|
+
if (!editSuppressed && (options?.editable ?? Boolean(options?.onEdit || options?.onEditTransaction))) {
|
|
6629
6853
|
const editCleanups = [];
|
|
6630
|
-
const onEditFn =
|
|
6631
|
-
});
|
|
6854
|
+
const onEditFn = emitEdit;
|
|
6632
6855
|
editCleanups.push(
|
|
6633
|
-
wireConnectorEndpointDrag(
|
|
6856
|
+
wireConnectorEndpointDrag(
|
|
6857
|
+
svgElement,
|
|
6858
|
+
dragAnnotations,
|
|
6859
|
+
onEditFn,
|
|
6860
|
+
setDragging,
|
|
6861
|
+
currentLayout,
|
|
6862
|
+
currentSpec
|
|
6863
|
+
)
|
|
6634
6864
|
);
|
|
6635
6865
|
editCleanups.push(
|
|
6636
6866
|
wireAnnotationLabelDrag(svgElement, dragAnnotations, onEditFn, setDragging)
|
|
@@ -6750,6 +6980,17 @@ function createChart(container, spec, options) {
|
|
|
6750
6980
|
}
|
|
6751
6981
|
function doUpdate(newSpec, updateOpts, manual, layoutOnly = false) {
|
|
6752
6982
|
if (destroyed) return null;
|
|
6983
|
+
annotationNudges.flush();
|
|
6984
|
+
if (destroyed) return null;
|
|
6985
|
+
if (layoutOnly) newSpec = currentSpec;
|
|
6986
|
+
if (isDragging) {
|
|
6987
|
+
if (layoutOnly && getContainerDimensions().width === gestureWidth) {
|
|
6988
|
+
pendingRender = true;
|
|
6989
|
+
return null;
|
|
6990
|
+
}
|
|
6991
|
+
pendingRender = false;
|
|
6992
|
+
svgElement?.dispatchEvent(new Event("oc-cancel-drag"));
|
|
6993
|
+
}
|
|
6753
6994
|
const prevSpec = currentSpec;
|
|
6754
6995
|
const prevLayout = currentLayout;
|
|
6755
6996
|
const entranceWasRunning = cleanupAnimations != null;
|
|
@@ -6757,6 +6998,7 @@ function createChart(container, spec, options) {
|
|
|
6757
6998
|
const nextMeasure = nextFont === fontFamily ? measureText : createMeasureText(nextFont);
|
|
6758
6999
|
const nextLayout = compile(newSpec, nextMeasure, layoutOnly);
|
|
6759
7000
|
const snapshot = transitionHandle?.running ? transitionHandle.snapshot() : void 0;
|
|
7001
|
+
if (updateOpts && Object.hasOwn(updateOpts, "revision")) editRevision = updateOpts.revision;
|
|
6760
7002
|
currentSpec = newSpec;
|
|
6761
7003
|
fontFamily = nextFont;
|
|
6762
7004
|
measureText = nextMeasure;
|
|
@@ -6880,6 +7122,7 @@ function createChart(container, spec, options) {
|
|
|
6880
7122
|
}
|
|
6881
7123
|
}
|
|
6882
7124
|
function destroy() {
|
|
7125
|
+
annotationNudges.flush();
|
|
6883
7126
|
if (destroyed) return;
|
|
6884
7127
|
destroyed = true;
|
|
6885
7128
|
if (transitionHandle) {
|
|
@@ -6965,7 +7208,7 @@ function createChart(container, spec, options) {
|
|
|
6965
7208
|
}
|
|
6966
7209
|
);
|
|
6967
7210
|
container.dataset.ocFontsState = pending ? "pending" : "ready";
|
|
6968
|
-
if (options?.responsive !== false) {
|
|
7211
|
+
if ((options?.autoResize ?? options?.responsive) !== false) {
|
|
6969
7212
|
disconnectResize = observeResize(container, (width, height) => {
|
|
6970
7213
|
const widthChanged = lastRenderedWidth === null || Math.abs(width - lastRenderedWidth) >= 0.5;
|
|
6971
7214
|
const selfInducedHeight = lastRenderedSvgHeight !== null && Math.abs(height - lastRenderedSvgHeight) < 0.5;
|
|
@@ -7748,13 +7991,7 @@ function snapTween(tw) {
|
|
|
7748
7991
|
tw.el.setAttribute(tw.attr, String(tw.to));
|
|
7749
7992
|
}
|
|
7750
7993
|
}
|
|
7751
|
-
function runMapFillTransition(svg,
|
|
7752
|
-
const snapshot = prev instanceof Map ? {
|
|
7753
|
-
fills: prev,
|
|
7754
|
-
points: new Map(
|
|
7755
|
-
Array.from(prev).filter(([k]) => k.startsWith("pt:")).map(([k, fill]) => [k.slice(3), { fill }])
|
|
7756
|
-
)
|
|
7757
|
-
} : prev;
|
|
7994
|
+
function runMapFillTransition(svg, snapshot, opts) {
|
|
7758
7995
|
const tweens = [];
|
|
7759
7996
|
const features = svg.querySelectorAll(".oc-map-feature[data-key]");
|
|
7760
7997
|
for (const p of features) {
|
|
@@ -8351,7 +8588,12 @@ function createGeoMap(container, spec, options) {
|
|
|
8351
8588
|
theme: options?.theme,
|
|
8352
8589
|
darkMode,
|
|
8353
8590
|
watermark: options?.watermark,
|
|
8354
|
-
measureText: measure
|
|
8591
|
+
measureText: measure,
|
|
8592
|
+
// An unconstrained host must grow around wrapped titles and sources.
|
|
8593
|
+
// Treating the aspect-ratio budget as the entire SVG can leave only a
|
|
8594
|
+
// few pixels for geography on phones. Explicit heights remain a hard
|
|
8595
|
+
// budget, as they are in dashboards and scrollytelling panels.
|
|
8596
|
+
chromeLayout: isAutoHeight === true ? "grow" : "subtract"
|
|
8355
8597
|
};
|
|
8356
8598
|
return compileGeoMap(candidate, compileOpts);
|
|
8357
8599
|
}
|
|
@@ -8936,10 +9178,10 @@ export {
|
|
|
8936
9178
|
scheduleFontReload,
|
|
8937
9179
|
VECTOR_EXPORT_MAX_POINTS,
|
|
8938
9180
|
materializeCanvasModeSVG,
|
|
9181
|
+
applySrOnlyStyles,
|
|
8939
9182
|
DEFAULT_UPDATE_MAX_MARKS,
|
|
8940
9183
|
CANVAS_DEFAULT_UPDATE_MAX_MARKS,
|
|
8941
9184
|
canTransitionSpecShape,
|
|
8942
|
-
applySrOnlyStyles,
|
|
8943
9185
|
createTextEditOverlay,
|
|
8944
9186
|
createYouDrawIt,
|
|
8945
9187
|
createChart,
|
|
@@ -8956,4 +9198,4 @@ export {
|
|
|
8956
9198
|
scrubCamera,
|
|
8957
9199
|
createGeoMap
|
|
8958
9200
|
};
|
|
8959
|
-
//# sourceMappingURL=chunk-
|
|
9201
|
+
//# sourceMappingURL=chunk-CCRFYGHU.js.map
|