@opendata-ai/openchart-vanilla 8.0.0-rc.7 → 8.0.0-rc.9
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-WLAVOZKD.js → chunk-IZQBT6QM.js} +356 -33
- package/dist/chunk-IZQBT6QM.js.map +1 -0
- package/dist/{chunk-5EOV5JQZ.js → chunk-QW7ZB6NE.js} +2 -2
- package/dist/{chunk-5EOV5JQZ.js.map → chunk-QW7ZB6NE.js.map} +1 -1
- package/dist/{chunk-W34KN7FD.js → chunk-SPILUE5S.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/static.js +2 -2
- package/dist/story/index.js +2 -2
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/edit-events.test.ts +528 -1
- package/src/__tests__/editable-prop.test.ts +174 -0
- package/src/__tests__/invert.test.ts +187 -0
- package/src/__tests__/selection-events.test.ts +194 -1
- package/src/interactions/editing-drags.ts +145 -11
- package/src/interactions/index.ts +2 -0
- package/src/interactions/invert.ts +54 -0
- package/src/interactions/selection.ts +10 -4
- package/src/map-mount.ts +13 -8
- package/src/map-renderer.ts +20 -4
- package/src/map-transition.ts +19 -3
- package/src/mount.ts +63 -11
- package/src/renderers/legend.ts +2 -1
- package/src/text-edit-overlay.ts +134 -0
- package/dist/chunk-WLAVOZKD.js.map +0 -1
- /package/dist/{chunk-W34KN7FD.js.map → chunk-SPILUE5S.js.map} +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
resolveMarkFill,
|
|
12
12
|
setAttrs,
|
|
13
13
|
stampAnimationVars
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-QW7ZB6NE.js";
|
|
15
15
|
|
|
16
16
|
// src/export.ts
|
|
17
17
|
function getSVGDimensions(svg) {
|
|
@@ -876,6 +876,11 @@ function renderMapSVG(layout, opts) {
|
|
|
876
876
|
focusIdSet
|
|
877
877
|
);
|
|
878
878
|
renderBorders(cameraGroup, borders);
|
|
879
|
+
if (animate && animation?.enter && layout.pointMarks.length > 1) {
|
|
880
|
+
const pointStaggerBudget = animation.enter.duration * 0.5;
|
|
881
|
+
const perPoint = pointStaggerBudget / (layout.pointMarks.length - 1);
|
|
882
|
+
svg.style.setProperty("--oc-point-stagger", `${perPoint}ms`);
|
|
883
|
+
}
|
|
879
884
|
renderPointMarks(cameraGroup, layout.pointMarks, animate ? animation : void 0);
|
|
880
885
|
mapGroup.appendChild(cameraGroup);
|
|
881
886
|
svg.appendChild(mapGroup);
|
|
@@ -906,12 +911,14 @@ function renderMapSVG(layout, opts) {
|
|
|
906
911
|
contentWidth = Math.min(w, ptLegend.bounds.width);
|
|
907
912
|
}
|
|
908
913
|
}
|
|
914
|
+
const isOverlayContinuous = ptLegend.position === "top-left" && !("entries" in ptLegend && ptLegend.entries);
|
|
909
915
|
const contentCenterY = ptLegend.bounds.y + swatchSize / 2;
|
|
910
|
-
const bgHeight = swatchSize + padY * 2;
|
|
916
|
+
const bgHeight = isOverlayContinuous ? ptLegend.bounds.height + padY * 2 : swatchSize + padY * 2;
|
|
917
|
+
const bgY = isOverlayContinuous ? ptLegend.bounds.y - padY : contentCenterY - bgHeight / 2;
|
|
911
918
|
const bg = createSVGElement2("rect");
|
|
912
919
|
setAttrs2(bg, {
|
|
913
920
|
x: ptLegend.bounds.x - padX,
|
|
914
|
-
y:
|
|
921
|
+
y: bgY,
|
|
915
922
|
width: contentWidth + padX * 2,
|
|
916
923
|
height: bgHeight,
|
|
917
924
|
rx: 4,
|
|
@@ -1370,12 +1377,18 @@ function captureFeatureFills(svg) {
|
|
|
1370
1377
|
const fill = p.getAttribute("fill");
|
|
1371
1378
|
if (key && fill) fills.set(key, fill);
|
|
1372
1379
|
}
|
|
1380
|
+
const points = svg.querySelectorAll(".oc-map-point[data-point-key]");
|
|
1381
|
+
for (const p of points) {
|
|
1382
|
+
const key = `pt:${p.getAttribute("data-point-key")}`;
|
|
1383
|
+
const fill = p.getAttribute("fill");
|
|
1384
|
+
if (fill) fills.set(key, fill);
|
|
1385
|
+
}
|
|
1373
1386
|
return fills;
|
|
1374
1387
|
}
|
|
1375
1388
|
function runMapFillTransition(svg, prevFills, opts) {
|
|
1376
|
-
const paths = svg.querySelectorAll(".oc-map-feature[data-key]");
|
|
1377
1389
|
const tweens = [];
|
|
1378
|
-
|
|
1390
|
+
const features = svg.querySelectorAll(".oc-map-feature[data-key]");
|
|
1391
|
+
for (const p of features) {
|
|
1379
1392
|
const key = p.getAttribute("data-key");
|
|
1380
1393
|
const to = p.getAttribute("fill");
|
|
1381
1394
|
const from = key ? prevFills.get(key) : null;
|
|
@@ -1384,6 +1397,16 @@ function runMapFillTransition(svg, prevFills, opts) {
|
|
|
1384
1397
|
p.setAttribute("fill", from);
|
|
1385
1398
|
}
|
|
1386
1399
|
}
|
|
1400
|
+
const points = svg.querySelectorAll(".oc-map-point[data-point-key]");
|
|
1401
|
+
for (const p of points) {
|
|
1402
|
+
const key = `pt:${p.getAttribute("data-point-key")}`;
|
|
1403
|
+
const to = p.getAttribute("fill");
|
|
1404
|
+
const from = prevFills.get(key);
|
|
1405
|
+
if (from && to && from !== to) {
|
|
1406
|
+
tweens.push({ el: p, from, to, interp: rgb_default(from, to) });
|
|
1407
|
+
p.setAttribute("fill", from);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1387
1410
|
if (tweens.length === 0 || opts.duration <= 0) {
|
|
1388
1411
|
for (const tw of tweens) tw.el.setAttribute("fill", tw.to);
|
|
1389
1412
|
return { cancel: () => {
|
|
@@ -1809,13 +1832,17 @@ function createMap(container, spec, options) {
|
|
|
1809
1832
|
fontsReloadPending = false;
|
|
1810
1833
|
container.dataset.ocFontsState = "ready";
|
|
1811
1834
|
}
|
|
1812
|
-
if (isFirstRender
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1835
|
+
if (isFirstRender) {
|
|
1836
|
+
if (currentLayout.focus) {
|
|
1837
|
+
const target = currentLayout.focus.target;
|
|
1838
|
+
currentFocusIds = [...currentLayout.focus.ids];
|
|
1839
|
+
const cam = cameraForTarget(currentLayout, target);
|
|
1840
|
+
currentCamera = cam;
|
|
1841
|
+
if (svgElement) applyMapCamera(svgElement, cam, currentLayout);
|
|
1842
|
+
if (!isAnimating) applyFocusDim(currentFocusIds);
|
|
1843
|
+
} else {
|
|
1844
|
+
currentCamera = cameraForTarget(currentLayout, null);
|
|
1845
|
+
}
|
|
1819
1846
|
}
|
|
1820
1847
|
isFirstRender = false;
|
|
1821
1848
|
}
|
|
@@ -2157,6 +2184,104 @@ function createTextEditOverlay(config) {
|
|
|
2157
2184
|
resizeObserver.observe(container);
|
|
2158
2185
|
return { destroy };
|
|
2159
2186
|
}
|
|
2187
|
+
function createTextEditOverlayAtPosition(config) {
|
|
2188
|
+
const { container, svg, position, onCommit, onCancel } = config;
|
|
2189
|
+
let destroyed = false;
|
|
2190
|
+
const scale = getScale(svg);
|
|
2191
|
+
const svgRect = svg.getBoundingClientRect();
|
|
2192
|
+
const containerRect = container.getBoundingClientRect();
|
|
2193
|
+
const padding = 4;
|
|
2194
|
+
const left = position.x * scale.scaleX + (svgRect.left - containerRect.left) - padding;
|
|
2195
|
+
const top = position.y * scale.scaleY + (svgRect.top - containerRect.top) - padding;
|
|
2196
|
+
const computedStyle = window.getComputedStyle(svg);
|
|
2197
|
+
const fontFamily = computedStyle.getPropertyValue("--oc-font-family").trim() || "inherit";
|
|
2198
|
+
const textColor = computedStyle.getPropertyValue("--oc-text-color").trim() || computedStyle.color || "#333";
|
|
2199
|
+
const surfaceColor = computedStyle.getPropertyValue("--oc-surface-color").trim() || "rgba(255, 255, 255, 0.95)";
|
|
2200
|
+
const fontSize = 13 * scale.scaleY;
|
|
2201
|
+
const textarea = document.createElement("textarea");
|
|
2202
|
+
textarea.value = "";
|
|
2203
|
+
textarea.placeholder = "Type annotation text...";
|
|
2204
|
+
const containerPosition = window.getComputedStyle(container).position;
|
|
2205
|
+
const containerWasStatic = containerPosition === "static";
|
|
2206
|
+
if (containerWasStatic) {
|
|
2207
|
+
container.style.position = "relative";
|
|
2208
|
+
}
|
|
2209
|
+
Object.assign(textarea.style, {
|
|
2210
|
+
position: "absolute",
|
|
2211
|
+
top: `${Math.max(0, top)}px`,
|
|
2212
|
+
left: `${Math.max(0, left)}px`,
|
|
2213
|
+
width: "160px",
|
|
2214
|
+
minHeight: "24px",
|
|
2215
|
+
fontFamily,
|
|
2216
|
+
fontSize: `${fontSize}px`,
|
|
2217
|
+
fontWeight: "400",
|
|
2218
|
+
color: textColor,
|
|
2219
|
+
textAlign: "left",
|
|
2220
|
+
lineHeight: "1.3",
|
|
2221
|
+
padding: "2px 4px",
|
|
2222
|
+
margin: "0",
|
|
2223
|
+
border: "1px solid rgba(79, 70, 229, 0.4)",
|
|
2224
|
+
borderRadius: "3px",
|
|
2225
|
+
background: surfaceColor,
|
|
2226
|
+
outline: "none",
|
|
2227
|
+
resize: "none",
|
|
2228
|
+
overflow: "hidden",
|
|
2229
|
+
boxSizing: "border-box",
|
|
2230
|
+
zIndex: "10000",
|
|
2231
|
+
WebkitAppearance: "none",
|
|
2232
|
+
appearance: "none"
|
|
2233
|
+
});
|
|
2234
|
+
container.appendChild(textarea);
|
|
2235
|
+
textarea.focus();
|
|
2236
|
+
function destroy() {
|
|
2237
|
+
if (destroyed) return;
|
|
2238
|
+
destroyed = true;
|
|
2239
|
+
textarea.removeEventListener("keydown", handleKeyDown);
|
|
2240
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
2241
|
+
if (containerWasStatic) {
|
|
2242
|
+
container.style.position = "";
|
|
2243
|
+
}
|
|
2244
|
+
if (textarea.parentNode) {
|
|
2245
|
+
textarea.parentNode.removeChild(textarea);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
function commit() {
|
|
2249
|
+
if (destroyed) return;
|
|
2250
|
+
const text = textarea.value.trim();
|
|
2251
|
+
destroy();
|
|
2252
|
+
if (text) {
|
|
2253
|
+
onCommit(text);
|
|
2254
|
+
} else {
|
|
2255
|
+
onCancel();
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
function cancel() {
|
|
2259
|
+
if (destroyed) return;
|
|
2260
|
+
destroy();
|
|
2261
|
+
onCancel();
|
|
2262
|
+
}
|
|
2263
|
+
const handleKeyDown = (e) => {
|
|
2264
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
2265
|
+
e.preventDefault();
|
|
2266
|
+
commit();
|
|
2267
|
+
} else if (e.key === "Escape") {
|
|
2268
|
+
e.preventDefault();
|
|
2269
|
+
cancel();
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
textarea.addEventListener("keydown", handleKeyDown);
|
|
2273
|
+
const handleClickOutside = (e) => {
|
|
2274
|
+
if (!textarea.contains(e.target)) {
|
|
2275
|
+
commit();
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
requestAnimationFrame(() => {
|
|
2279
|
+
if (!destroyed) {
|
|
2280
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
return { destroy };
|
|
2284
|
+
}
|
|
2160
2285
|
|
|
2161
2286
|
// src/you-draw-it.ts
|
|
2162
2287
|
var MIN_TOUCH_TARGET = 24;
|
|
@@ -2931,6 +3056,53 @@ function createDragHandler(config) {
|
|
|
2931
3056
|
};
|
|
2932
3057
|
}
|
|
2933
3058
|
|
|
3059
|
+
// src/interactions/editing-drags.ts
|
|
3060
|
+
import {
|
|
3061
|
+
elementRef
|
|
3062
|
+
} from "@opendata-ai/openchart-core";
|
|
3063
|
+
|
|
3064
|
+
// src/interactions/invert.ts
|
|
3065
|
+
function invertScale(invert, pixel) {
|
|
3066
|
+
if (invert.domain && invert.positions && invert.domain.length > 0) {
|
|
3067
|
+
let closest = 0;
|
|
3068
|
+
let minDist = Infinity;
|
|
3069
|
+
for (let i = 0; i < invert.positions.length; i++) {
|
|
3070
|
+
const dist = Math.abs(invert.positions[i] - pixel);
|
|
3071
|
+
if (dist < minDist) {
|
|
3072
|
+
minDist = dist;
|
|
3073
|
+
closest = i;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return invert.domain[closest];
|
|
3077
|
+
}
|
|
3078
|
+
const span = invert.bottomPixel - invert.topPixel;
|
|
3079
|
+
if (span === 0) return invert.topData;
|
|
3080
|
+
const t = (pixel - invert.topPixel) / span;
|
|
3081
|
+
const { topData, bottomData, scaleType } = invert;
|
|
3082
|
+
if (scaleType === "log") {
|
|
3083
|
+
if (topData <= 0 || bottomData <= 0) return topData + t * (bottomData - topData);
|
|
3084
|
+
return Math.exp(Math.log(topData) + t * (Math.log(bottomData) - Math.log(topData)));
|
|
3085
|
+
}
|
|
3086
|
+
if (scaleType === "sqrt") {
|
|
3087
|
+
const sqrtTop = Math.sqrt(Math.abs(topData)) * Math.sign(topData);
|
|
3088
|
+
const sqrtBottom = Math.sqrt(Math.abs(bottomData)) * Math.sign(bottomData);
|
|
3089
|
+
const sqrtVal = sqrtTop + t * (sqrtBottom - sqrtTop);
|
|
3090
|
+
return sqrtVal * Math.abs(sqrtVal);
|
|
3091
|
+
}
|
|
3092
|
+
if (scaleType === "pow") {
|
|
3093
|
+
const powTop = Math.sign(topData) * Math.abs(topData) ** 0.5;
|
|
3094
|
+
const powBottom = Math.sign(bottomData) * Math.abs(bottomData) ** 0.5;
|
|
3095
|
+
const powVal = powTop + t * (powBottom - powTop);
|
|
3096
|
+
return Math.sign(powVal) * Math.abs(powVal) ** 2;
|
|
3097
|
+
}
|
|
3098
|
+
if (scaleType === "symlog") {
|
|
3099
|
+
const symlog = (x) => Math.sign(x) * Math.log1p(Math.abs(x));
|
|
3100
|
+
const symexp = (x) => Math.sign(x) * Math.expm1(Math.abs(x));
|
|
3101
|
+
return symexp(symlog(topData) + t * (symlog(bottomData) - symlog(topData)));
|
|
3102
|
+
}
|
|
3103
|
+
return topData + t * (bottomData - topData);
|
|
3104
|
+
}
|
|
3105
|
+
|
|
2934
3106
|
// src/interactions/editing-drags.ts
|
|
2935
3107
|
function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setDragging) {
|
|
2936
3108
|
const annotationElements = svg.querySelectorAll(".oc-annotation-text");
|
|
@@ -2982,7 +3154,16 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
2982
3154
|
dy: origDy + dy
|
|
2983
3155
|
};
|
|
2984
3156
|
onAnnotationEdit?.(textAnnotation, newOffset);
|
|
2985
|
-
onEdit?.({
|
|
3157
|
+
onEdit?.({
|
|
3158
|
+
type: "annotation",
|
|
3159
|
+
element: elementRef.annotation(
|
|
3160
|
+
index,
|
|
3161
|
+
textAnnotation.id,
|
|
3162
|
+
`/annotations/${index}/offset`
|
|
3163
|
+
),
|
|
3164
|
+
annotation: textAnnotation,
|
|
3165
|
+
offset: newOffset
|
|
3166
|
+
});
|
|
2986
3167
|
}
|
|
2987
3168
|
},
|
|
2988
3169
|
setDragging
|
|
@@ -3090,6 +3271,11 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
3090
3271
|
const origEndDy = existingOffset?.dy ?? 0;
|
|
3091
3272
|
onEdit({
|
|
3092
3273
|
type: "annotation-connector",
|
|
3274
|
+
element: elementRef.annotation(
|
|
3275
|
+
index,
|
|
3276
|
+
textAnnotation.id,
|
|
3277
|
+
`/annotations/${index}/connectorOffset/${ep.name}`
|
|
3278
|
+
),
|
|
3093
3279
|
annotation: textAnnotation,
|
|
3094
3280
|
endpoint: ep.name,
|
|
3095
3281
|
offset: { dx: origEndDx + dx, dy: origEndDy + dy }
|
|
@@ -3160,12 +3346,22 @@ function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
3160
3346
|
if (isRange) {
|
|
3161
3347
|
onEdit({
|
|
3162
3348
|
type: "range-label",
|
|
3349
|
+
element: elementRef.annotation(
|
|
3350
|
+
index,
|
|
3351
|
+
specAnnotation.id,
|
|
3352
|
+
`/annotations/${index}/labelOffset`
|
|
3353
|
+
),
|
|
3163
3354
|
annotation: specAnnotation,
|
|
3164
3355
|
labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy }
|
|
3165
3356
|
});
|
|
3166
3357
|
} else {
|
|
3167
3358
|
onEdit({
|
|
3168
3359
|
type: "refline-label",
|
|
3360
|
+
element: elementRef.annotation(
|
|
3361
|
+
index,
|
|
3362
|
+
specAnnotation.id,
|
|
3363
|
+
`/annotations/${index}/labelOffset`
|
|
3364
|
+
),
|
|
3169
3365
|
annotation: specAnnotation,
|
|
3170
3366
|
labelOffset: { dx: origLabelDx + dx, dy: origLabelDy + dy }
|
|
3171
3367
|
});
|
|
@@ -3292,6 +3488,88 @@ function wireSeriesLabelDrag(svg, spec, onEdit, setDragging) {
|
|
|
3292
3488
|
}
|
|
3293
3489
|
};
|
|
3294
3490
|
}
|
|
3491
|
+
function wireAnchorDrag(svg, specAnnotations, layout, onEdit, setDragging) {
|
|
3492
|
+
if (!onEdit || !layout.xInvert || !layout.yInvert) return () => {
|
|
3493
|
+
};
|
|
3494
|
+
const SVG_NS4 = "http://www.w3.org/2000/svg";
|
|
3495
|
+
const cleanups = [];
|
|
3496
|
+
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
3497
|
+
for (const el of annotationGroups) {
|
|
3498
|
+
const annotationG = el;
|
|
3499
|
+
const indexStr = annotationG.getAttribute("data-annotation-index");
|
|
3500
|
+
if (indexStr === null) continue;
|
|
3501
|
+
const index = Number(indexStr);
|
|
3502
|
+
const specAnnotation = specAnnotations[index];
|
|
3503
|
+
if (!specAnnotation || specAnnotation.type !== "text") continue;
|
|
3504
|
+
const textAnnotation = specAnnotation;
|
|
3505
|
+
const resolved = layout.annotations.find((a) => a.specIndex === index);
|
|
3506
|
+
if (!resolved?.dot) continue;
|
|
3507
|
+
const anchorX = resolved.dot.x;
|
|
3508
|
+
const anchorY = resolved.dot.y;
|
|
3509
|
+
const handleEl = document.createElementNS(SVG_NS4, "circle");
|
|
3510
|
+
handleEl.setAttribute("class", "oc-anchor-handle");
|
|
3511
|
+
handleEl.setAttribute("cx", String(anchorX));
|
|
3512
|
+
handleEl.setAttribute("cy", String(anchorY));
|
|
3513
|
+
handleEl.setAttribute("r", "4");
|
|
3514
|
+
handleEl.setAttribute("opacity", "0");
|
|
3515
|
+
handleEl.setAttribute("fill", "currentColor");
|
|
3516
|
+
handleEl.setAttribute("stroke", "currentColor");
|
|
3517
|
+
handleEl.style.cursor = "grab";
|
|
3518
|
+
annotationG.appendChild(handleEl);
|
|
3519
|
+
const stopProp = (e) => {
|
|
3520
|
+
e.stopPropagation();
|
|
3521
|
+
};
|
|
3522
|
+
handleEl.addEventListener("mousedown", stopProp);
|
|
3523
|
+
handleEl.addEventListener("touchstart", stopProp);
|
|
3524
|
+
cleanups.push(() => {
|
|
3525
|
+
handleEl.removeEventListener("mousedown", stopProp);
|
|
3526
|
+
handleEl.removeEventListener("touchstart", stopProp);
|
|
3527
|
+
});
|
|
3528
|
+
const cleanup = createDragHandler({
|
|
3529
|
+
element: handleEl,
|
|
3530
|
+
svg,
|
|
3531
|
+
onMove: (dx, dy) => {
|
|
3532
|
+
handleEl.setAttribute("cx", String(anchorX + dx));
|
|
3533
|
+
handleEl.setAttribute("cy", String(anchorY + dy));
|
|
3534
|
+
},
|
|
3535
|
+
onEnd: (dx, dy, moved) => {
|
|
3536
|
+
handleEl.setAttribute("cx", String(anchorX));
|
|
3537
|
+
handleEl.setAttribute("cy", String(anchorY));
|
|
3538
|
+
if (moved) {
|
|
3539
|
+
const newX = invertScale(layout.xInvert, anchorX + dx);
|
|
3540
|
+
const newY = invertScale(layout.yInvert, anchorY + dy);
|
|
3541
|
+
onEdit({
|
|
3542
|
+
type: "annotation-anchor",
|
|
3543
|
+
element: elementRef.annotation(index, textAnnotation.id, `/annotations/${index}`),
|
|
3544
|
+
annotation: textAnnotation,
|
|
3545
|
+
x: newX,
|
|
3546
|
+
y: newY
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
},
|
|
3550
|
+
setDragging
|
|
3551
|
+
});
|
|
3552
|
+
cleanups.push(cleanup);
|
|
3553
|
+
const showHandle = () => {
|
|
3554
|
+
handleEl.setAttribute("opacity", "0.6");
|
|
3555
|
+
};
|
|
3556
|
+
const hideHandle = () => {
|
|
3557
|
+
handleEl.setAttribute("opacity", "0");
|
|
3558
|
+
};
|
|
3559
|
+
annotationG.addEventListener("mouseenter", showHandle);
|
|
3560
|
+
annotationG.addEventListener("mouseleave", hideHandle);
|
|
3561
|
+
cleanups.push(() => {
|
|
3562
|
+
annotationG.removeEventListener("mouseenter", showHandle);
|
|
3563
|
+
annotationG.removeEventListener("mouseleave", hideHandle);
|
|
3564
|
+
handleEl.remove();
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
return () => {
|
|
3568
|
+
for (const cleanup of cleanups) {
|
|
3569
|
+
cleanup();
|
|
3570
|
+
}
|
|
3571
|
+
};
|
|
3572
|
+
}
|
|
3295
3573
|
|
|
3296
3574
|
// src/interactions/keyboard-nav.ts
|
|
3297
3575
|
function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, layout) {
|
|
@@ -3405,7 +3683,7 @@ function wireLegendInteraction(svg, _layout, toggleSeries, onLegendToggle, onEdi
|
|
|
3405
3683
|
}
|
|
3406
3684
|
|
|
3407
3685
|
// src/interactions/selection.ts
|
|
3408
|
-
import { elementRef } from "@opendata-ai/openchart-core";
|
|
3686
|
+
import { elementRef as elementRef2 } from "@opendata-ai/openchart-core";
|
|
3409
3687
|
function findElementByRef(svg, ref) {
|
|
3410
3688
|
switch (ref.type) {
|
|
3411
3689
|
case "annotation": {
|
|
@@ -3431,31 +3709,36 @@ function findElementByRef(svg, ref) {
|
|
|
3431
3709
|
);
|
|
3432
3710
|
}
|
|
3433
3711
|
}
|
|
3712
|
+
function encodePointerToken(token) {
|
|
3713
|
+
return token.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
3714
|
+
}
|
|
3434
3715
|
function buildElementRef(element, _specAnnotations) {
|
|
3435
3716
|
const annotationEl = element.closest("[data-annotation-index]");
|
|
3436
3717
|
if (annotationEl) {
|
|
3437
3718
|
const index = Number(annotationEl.getAttribute("data-annotation-index"));
|
|
3438
3719
|
const id = annotationEl.getAttribute("data-annotation-id") ?? void 0;
|
|
3439
|
-
return
|
|
3720
|
+
return elementRef2.annotation(index, id, `/annotations/${index}`);
|
|
3440
3721
|
}
|
|
3441
3722
|
const chromeEl = element.closest("[data-chrome-key]");
|
|
3442
3723
|
if (chromeEl) {
|
|
3443
3724
|
const key = chromeEl.getAttribute("data-chrome-key");
|
|
3444
|
-
if (key) return
|
|
3725
|
+
if (key) return elementRef2.chrome(key, `/chrome/${key}`);
|
|
3445
3726
|
}
|
|
3446
3727
|
const seriesLabelEl = element.closest(".oc-mark-label[data-series]");
|
|
3447
3728
|
if (seriesLabelEl) {
|
|
3448
3729
|
const series = seriesLabelEl.getAttribute("data-series");
|
|
3449
|
-
if (series)
|
|
3730
|
+
if (series)
|
|
3731
|
+
return elementRef2.seriesLabel(series, `/labels/offsets/${encodePointerToken(series)}`);
|
|
3450
3732
|
}
|
|
3451
3733
|
const legendEntryEl = element.closest("[data-legend-index]");
|
|
3452
3734
|
if (legendEntryEl) {
|
|
3453
3735
|
const index = Number(legendEntryEl.getAttribute("data-legend-index"));
|
|
3454
3736
|
const series = legendEntryEl.getAttribute("data-legend-label") ?? "";
|
|
3455
|
-
return
|
|
3737
|
+
return elementRef2.legendEntry(series, index);
|
|
3456
3738
|
}
|
|
3457
3739
|
const legendEl = element.closest(".oc-legend");
|
|
3458
|
-
if (legendEl && !legendEl.classList.contains("oc-legend--size"))
|
|
3740
|
+
if (legendEl && !legendEl.classList.contains("oc-legend--size"))
|
|
3741
|
+
return elementRef2.legend("/legend");
|
|
3459
3742
|
return null;
|
|
3460
3743
|
}
|
|
3461
3744
|
function getEditableElements(spec, layout) {
|
|
@@ -3463,12 +3746,12 @@ function getEditableElements(spec, layout) {
|
|
|
3463
3746
|
const chromeKeys = ["title", "subtitle", "source", "byline", "footer"];
|
|
3464
3747
|
for (const key of chromeKeys) {
|
|
3465
3748
|
if (layout.chrome[key]) {
|
|
3466
|
-
refs.push(
|
|
3749
|
+
refs.push(elementRef2.chrome(key));
|
|
3467
3750
|
}
|
|
3468
3751
|
}
|
|
3469
3752
|
const annotations = "annotations" in spec && Array.isArray(spec.annotations) ? spec.annotations : [];
|
|
3470
3753
|
for (let i = 0; i < annotations.length; i++) {
|
|
3471
|
-
refs.push(
|
|
3754
|
+
refs.push(elementRef2.annotation(i, annotations[i].id));
|
|
3472
3755
|
}
|
|
3473
3756
|
const seriesLabels = [];
|
|
3474
3757
|
for (const mark of layout.marks) {
|
|
@@ -3478,10 +3761,10 @@ function getEditableElements(spec, layout) {
|
|
|
3478
3761
|
}
|
|
3479
3762
|
seriesLabels.sort();
|
|
3480
3763
|
for (const series of seriesLabels) {
|
|
3481
|
-
refs.push(
|
|
3764
|
+
refs.push(elementRef2.seriesLabel(series));
|
|
3482
3765
|
}
|
|
3483
3766
|
if ("entries" in layout.legend && layout.legend.entries.length > 0) {
|
|
3484
|
-
refs.push(
|
|
3767
|
+
refs.push(elementRef2.legend());
|
|
3485
3768
|
}
|
|
3486
3769
|
return refs;
|
|
3487
3770
|
}
|
|
@@ -6137,6 +6420,39 @@ function createChart(container, spec, options) {
|
|
|
6137
6420
|
selectElement(ref);
|
|
6138
6421
|
}
|
|
6139
6422
|
enterTextEditing();
|
|
6423
|
+
return;
|
|
6424
|
+
}
|
|
6425
|
+
if (!ref && options?.onEdit && currentLayout?.xInvert && currentLayout?.yInvert) {
|
|
6426
|
+
const svgEl = svg;
|
|
6427
|
+
const viewBox = svgEl.viewBox?.baseVal;
|
|
6428
|
+
const svgRect = svgEl.getBoundingClientRect();
|
|
6429
|
+
if (!viewBox || !svgRect.width || !svgRect.height) return;
|
|
6430
|
+
const vbScaleX = viewBox.width / svgRect.width;
|
|
6431
|
+
const vbScaleY = viewBox.height / svgRect.height;
|
|
6432
|
+
const svgX = (mouseEvent.clientX - svgRect.left) * vbScaleX;
|
|
6433
|
+
const svgY = (mouseEvent.clientY - svgRect.top) * vbScaleY;
|
|
6434
|
+
const area = currentLayout.area;
|
|
6435
|
+
if (svgX < area.x || svgX > area.x + area.width) return;
|
|
6436
|
+
if (svgY < area.y || svgY > area.y + area.height) return;
|
|
6437
|
+
const dataX = invertScale(currentLayout.xInvert, svgX);
|
|
6438
|
+
const dataY = invertScale(currentLayout.yInvert, svgY);
|
|
6439
|
+
isTextEditingActive = true;
|
|
6440
|
+
const overlay = createTextEditOverlayAtPosition({
|
|
6441
|
+
container,
|
|
6442
|
+
svg: svgEl,
|
|
6443
|
+
position: { x: svgX, y: svgY },
|
|
6444
|
+
onCommit: (text) => {
|
|
6445
|
+
isTextEditingActive = false;
|
|
6446
|
+
textEditCleanup = null;
|
|
6447
|
+
const annotation = { type: "text", x: dataX, y: dataY, text };
|
|
6448
|
+
options.onEdit({ type: "add", annotation });
|
|
6449
|
+
},
|
|
6450
|
+
onCancel: () => {
|
|
6451
|
+
isTextEditingActive = false;
|
|
6452
|
+
textEditCleanup = null;
|
|
6453
|
+
}
|
|
6454
|
+
});
|
|
6455
|
+
textEditCleanup = overlay.destroy;
|
|
6140
6456
|
}
|
|
6141
6457
|
};
|
|
6142
6458
|
svg.addEventListener("dblclick", handleDblClick);
|
|
@@ -6287,7 +6603,7 @@ function createChart(container, spec, options) {
|
|
|
6287
6603
|
);
|
|
6288
6604
|
}
|
|
6289
6605
|
const editSuppressed = wantsSeriesSearch(currentSpec);
|
|
6290
|
-
if (editSuppressed && !editSuppressWarned && (hasEditingCallbacks(options) || options?.onAnnotationEdit)) {
|
|
6606
|
+
if (editSuppressed && !editSuppressWarned && (options?.editable || hasEditingCallbacks(options) || options?.onAnnotationEdit)) {
|
|
6291
6607
|
editSuppressWarned = true;
|
|
6292
6608
|
console.warn(
|
|
6293
6609
|
"[openchart] seriesSearch and edit mode are mutually exclusive; edit interactions are disabled while seriesSearch is enabled."
|
|
@@ -6312,7 +6628,7 @@ function createChart(container, spec, options) {
|
|
|
6312
6628
|
}
|
|
6313
6629
|
};
|
|
6314
6630
|
const dragAnnotations = "annotations" in currentSpec && Array.isArray(currentSpec.annotations) ? currentSpec.annotations : [];
|
|
6315
|
-
if (!editSuppressed && (options?.onAnnotationEdit || options?.onEdit)) {
|
|
6631
|
+
if (!editSuppressed && (options?.editable ?? Boolean(options?.onAnnotationEdit || options?.onEdit))) {
|
|
6316
6632
|
cleanupAnnotationDrag = wireAnnotationDrag(
|
|
6317
6633
|
svgElement,
|
|
6318
6634
|
dragAnnotations,
|
|
@@ -6321,25 +6637,32 @@ function createChart(container, spec, options) {
|
|
|
6321
6637
|
setDragging
|
|
6322
6638
|
);
|
|
6323
6639
|
}
|
|
6324
|
-
if (!editSuppressed && options?.onEdit) {
|
|
6640
|
+
if (!editSuppressed && (options?.editable ?? Boolean(options?.onEdit))) {
|
|
6325
6641
|
const editCleanups = [];
|
|
6642
|
+
const onEditFn = options?.onEdit ?? (() => {
|
|
6643
|
+
});
|
|
6326
6644
|
editCleanups.push(
|
|
6327
|
-
wireConnectorEndpointDrag(svgElement, dragAnnotations,
|
|
6645
|
+
wireConnectorEndpointDrag(svgElement, dragAnnotations, onEditFn, setDragging)
|
|
6328
6646
|
);
|
|
6329
6647
|
editCleanups.push(
|
|
6330
|
-
wireAnnotationLabelDrag(svgElement, dragAnnotations,
|
|
6648
|
+
wireAnnotationLabelDrag(svgElement, dragAnnotations, onEditFn, setDragging)
|
|
6331
6649
|
);
|
|
6650
|
+
if (currentLayout.xInvert && currentLayout.yInvert) {
|
|
6651
|
+
editCleanups.push(
|
|
6652
|
+
wireAnchorDrag(svgElement, dragAnnotations, currentLayout, onEditFn, setDragging)
|
|
6653
|
+
);
|
|
6654
|
+
}
|
|
6332
6655
|
const editSpec = currentSpec;
|
|
6333
|
-
editCleanups.push(wireChromeDrag(svgElement, editSpec,
|
|
6334
|
-
editCleanups.push(wireLegendDrag(svgElement, editSpec,
|
|
6335
|
-
editCleanups.push(wireSeriesLabelDrag(svgElement, editSpec,
|
|
6656
|
+
editCleanups.push(wireChromeDrag(svgElement, editSpec, onEditFn, setDragging));
|
|
6657
|
+
editCleanups.push(wireLegendDrag(svgElement, editSpec, onEditFn, setDragging));
|
|
6658
|
+
editCleanups.push(wireSeriesLabelDrag(svgElement, editSpec, onEditFn, setDragging));
|
|
6336
6659
|
cleanupEditDrags = () => {
|
|
6337
6660
|
for (const cleanup of editCleanups) {
|
|
6338
6661
|
cleanup();
|
|
6339
6662
|
}
|
|
6340
6663
|
};
|
|
6341
6664
|
}
|
|
6342
|
-
if (!editSuppressed && hasEditingCallbacks(options)) {
|
|
6665
|
+
if (!editSuppressed && (options?.editable ?? hasEditingCallbacks(options))) {
|
|
6343
6666
|
makeEditable(svgElement);
|
|
6344
6667
|
cleanupSelection = wireSelectionEvents();
|
|
6345
6668
|
cleanupKeyboardEdit = wireKeyboardEditEvents();
|
|
@@ -6680,4 +7003,4 @@ export {
|
|
|
6680
7003
|
createYouDrawIt,
|
|
6681
7004
|
createChart
|
|
6682
7005
|
};
|
|
6683
|
-
//# sourceMappingURL=chunk-
|
|
7006
|
+
//# sourceMappingURL=chunk-IZQBT6QM.js.map
|