@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.
@@ -11,7 +11,7 @@ import {
11
11
  resolveMarkFill,
12
12
  setAttrs,
13
13
  stampAnimationVars
14
- } from "./chunk-5EOV5JQZ.js";
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: contentCenterY - bgHeight / 2,
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
- for (const p of paths) {
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 && currentLayout.focus) {
1813
- const target = currentLayout.focus.target;
1814
- currentFocusIds = [...currentLayout.focus.ids];
1815
- const cam = cameraForTarget(currentLayout, target);
1816
- currentCamera = cam;
1817
- if (svgElement) applyMapCamera(svgElement, cam, currentLayout);
1818
- if (!isAnimating) applyFocusDim(currentFocusIds);
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?.({ type: "annotation", annotation: textAnnotation, offset: newOffset });
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 elementRef.annotation(index, id);
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 elementRef.chrome(key);
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) return elementRef.seriesLabel(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 elementRef.legendEntry(series, index);
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")) return elementRef.legend();
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(elementRef.chrome(key));
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(elementRef.annotation(i, annotations[i].id));
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(elementRef.seriesLabel(series));
3764
+ refs.push(elementRef2.seriesLabel(series));
3482
3765
  }
3483
3766
  if ("entries" in layout.legend && layout.legend.entries.length > 0) {
3484
- refs.push(elementRef.legend());
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, options.onEdit, setDragging)
6645
+ wireConnectorEndpointDrag(svgElement, dragAnnotations, onEditFn, setDragging)
6328
6646
  );
6329
6647
  editCleanups.push(
6330
- wireAnnotationLabelDrag(svgElement, dragAnnotations, options.onEdit, setDragging)
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, options.onEdit, setDragging));
6334
- editCleanups.push(wireLegendDrag(svgElement, editSpec, options.onEdit, setDragging));
6335
- editCleanups.push(wireSeriesLabelDrag(svgElement, editSpec, options.onEdit, setDragging));
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-WLAVOZKD.js.map
7006
+ //# sourceMappingURL=chunk-IZQBT6QM.js.map