@opendata-ai/openchart-vanilla 6.3.0 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +53 -5
- package/dist/index.js +897 -168
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -764
- package/package.json +3 -3
- package/src/__tests__/animation.test.ts +358 -0
- package/src/__tests__/edit-events.test.ts +35 -35
- package/src/__tests__/events.test.ts +7 -7
- package/src/__tests__/export.test.ts +1 -1
- package/src/__tests__/mount.test.ts +10 -10
- package/src/__tests__/selection-events.test.ts +869 -0
- package/src/__tests__/svg-renderer.test.ts +67 -67
- package/src/__tests__/table-keyboard.test.ts +18 -18
- package/src/__tests__/table-mount.test.ts +138 -17
- package/src/__tests__/tooltip.test.ts +12 -12
- package/src/animation.ts +75 -0
- package/src/graph/__tests__/graph-mount.test.ts +16 -16
- package/src/graph-mount.ts +18 -18
- package/src/index.ts +3 -1
- package/src/mount.ts +668 -30
- package/src/renderers/table-cells.ts +11 -9
- package/src/svg-renderer.ts +164 -54
- package/src/table-keyboard.ts +5 -5
- package/src/table-mount.ts +34 -11
- package/src/table-renderer.ts +70 -39
- package/src/text-edit-overlay.ts +255 -0
- package/src/tooltip.ts +8 -8
package/dist/index.js
CHANGED
|
@@ -2561,7 +2561,7 @@ import { computePosition, flip, offset, shift } from "@floating-ui/dom";
|
|
|
2561
2561
|
var TOOLTIP_OFFSET = 12;
|
|
2562
2562
|
function createTooltipManager(container) {
|
|
2563
2563
|
const tooltip = document.createElement("div");
|
|
2564
|
-
tooltip.className = "
|
|
2564
|
+
tooltip.className = "oc-tooltip";
|
|
2565
2565
|
tooltip.setAttribute("role", "tooltip");
|
|
2566
2566
|
container.style.position = container.style.position || "relative";
|
|
2567
2567
|
container.appendChild(tooltip);
|
|
@@ -2580,19 +2580,19 @@ function createTooltipManager(container) {
|
|
|
2580
2580
|
let html = "";
|
|
2581
2581
|
if (content.title) {
|
|
2582
2582
|
const titleColor = content.fields.find((f) => f.color)?.color;
|
|
2583
|
-
html += '<div class="
|
|
2583
|
+
html += '<div class="oc-tooltip-header">';
|
|
2584
2584
|
if (titleColor) {
|
|
2585
|
-
html += `<span class="
|
|
2585
|
+
html += `<span class="oc-tooltip-dot" style="background:${esc(titleColor)}"></span>`;
|
|
2586
2586
|
}
|
|
2587
|
-
html += `<span class="
|
|
2587
|
+
html += `<span class="oc-tooltip-title">${esc(content.title)}</span>`;
|
|
2588
2588
|
html += "</div>";
|
|
2589
2589
|
}
|
|
2590
2590
|
if (content.fields.length > 0) {
|
|
2591
|
-
html += '<div class="
|
|
2591
|
+
html += '<div class="oc-tooltip-body">';
|
|
2592
2592
|
for (const field of content.fields) {
|
|
2593
|
-
html += '<div class="
|
|
2594
|
-
html += `<span class="
|
|
2595
|
-
html += `<span class="
|
|
2593
|
+
html += '<div class="oc-tooltip-row">';
|
|
2594
|
+
html += `<span class="oc-tooltip-label">${esc(field.label)}</span>`;
|
|
2595
|
+
html += `<span class="oc-tooltip-value">${esc(field.value)}</span>`;
|
|
2596
2596
|
html += "</div>";
|
|
2597
2597
|
}
|
|
2598
2598
|
html += "</div>";
|
|
@@ -2771,27 +2771,27 @@ function createGraph(container, spec, options) {
|
|
|
2771
2771
|
const { width, height } = getContainerDimensions();
|
|
2772
2772
|
const isDark = resolveDarkMode(options?.darkMode);
|
|
2773
2773
|
wrapper = document.createElement("div");
|
|
2774
|
-
wrapper.className = isDark ? "
|
|
2774
|
+
wrapper.className = isDark ? "oc-graph-wrapper oc-dark" : "oc-graph-wrapper";
|
|
2775
2775
|
if (isDark) {
|
|
2776
|
-
container.classList.add("
|
|
2776
|
+
container.classList.add("oc-dark");
|
|
2777
2777
|
} else {
|
|
2778
|
-
container.classList.remove("
|
|
2778
|
+
container.classList.remove("oc-dark");
|
|
2779
2779
|
}
|
|
2780
2780
|
const resolvedTheme = compilation.theme;
|
|
2781
2781
|
if (resolvedTheme) {
|
|
2782
2782
|
const s = wrapper.style;
|
|
2783
|
-
s.setProperty("--
|
|
2784
|
-
s.setProperty("--
|
|
2785
|
-
s.setProperty("--
|
|
2786
|
-
s.setProperty("--
|
|
2783
|
+
s.setProperty("--oc-bg", resolvedTheme.colors.background);
|
|
2784
|
+
s.setProperty("--oc-text", resolvedTheme.colors.text);
|
|
2785
|
+
s.setProperty("--oc-text-secondary", resolvedTheme.colors.axis ?? resolvedTheme.colors.text);
|
|
2786
|
+
s.setProperty("--oc-font-family", resolvedTheme.fonts.family);
|
|
2787
2787
|
s.fontFamily = resolvedTheme.fonts.family;
|
|
2788
2788
|
}
|
|
2789
2789
|
chromeEl = document.createElement("div");
|
|
2790
|
-
chromeEl.className = "
|
|
2790
|
+
chromeEl.className = "oc-graph-chrome";
|
|
2791
2791
|
renderChrome2();
|
|
2792
2792
|
wrapper.appendChild(chromeEl);
|
|
2793
2793
|
canvas = document.createElement("canvas");
|
|
2794
|
-
canvas.className = "
|
|
2794
|
+
canvas.className = "oc-graph-canvas";
|
|
2795
2795
|
canvas.setAttribute("role", "img");
|
|
2796
2796
|
if (compilation.a11y?.altText) {
|
|
2797
2797
|
canvas.setAttribute("aria-label", compilation.a11y.altText);
|
|
@@ -2799,7 +2799,7 @@ function createGraph(container, spec, options) {
|
|
|
2799
2799
|
wrapper.appendChild(canvas);
|
|
2800
2800
|
if (options?.legend !== false) {
|
|
2801
2801
|
legendEl = document.createElement("div");
|
|
2802
|
-
legendEl.className = "
|
|
2802
|
+
legendEl.className = "oc-graph-legend";
|
|
2803
2803
|
renderLegend2();
|
|
2804
2804
|
wrapper.appendChild(legendEl);
|
|
2805
2805
|
}
|
|
@@ -2813,10 +2813,10 @@ function createGraph(container, spec, options) {
|
|
|
2813
2813
|
if (!chromeEl) return;
|
|
2814
2814
|
let html = "";
|
|
2815
2815
|
if (compilation.chrome.title) {
|
|
2816
|
-
html += `<h2 class="
|
|
2816
|
+
html += `<h2 class="oc-title">${escapeHtml(compilation.chrome.title.text)}</h2>`;
|
|
2817
2817
|
}
|
|
2818
2818
|
if (compilation.chrome.subtitle) {
|
|
2819
|
-
html += `<p class="
|
|
2819
|
+
html += `<p class="oc-subtitle">${escapeHtml(compilation.chrome.subtitle.text)}</p>`;
|
|
2820
2820
|
}
|
|
2821
2821
|
chromeEl.innerHTML = html;
|
|
2822
2822
|
if (!html) {
|
|
@@ -2835,8 +2835,8 @@ function createGraph(container, spec, options) {
|
|
|
2835
2835
|
legendEl.style.display = "";
|
|
2836
2836
|
let html = "";
|
|
2837
2837
|
for (const entry of entries) {
|
|
2838
|
-
html += '<div class="
|
|
2839
|
-
html += `<span class="
|
|
2838
|
+
html += '<div class="oc-graph-legend-item">';
|
|
2839
|
+
html += `<span class="oc-graph-legend-swatch" style="background:${escapeHtml(entry.color)}"></span>`;
|
|
2840
2840
|
html += `<span>${escapeHtml(entry.label)}</span>`;
|
|
2841
2841
|
html += "</div>";
|
|
2842
2842
|
}
|
|
@@ -3017,14 +3017,14 @@ function createGraph(container, spec, options) {
|
|
|
3017
3017
|
const x3 = node?.x ?? 0;
|
|
3018
3018
|
const y3 = node?.y ?? 0;
|
|
3019
3019
|
simulation?.pinNode(nodeId, x3, y3);
|
|
3020
|
-
canvas?.classList.add("
|
|
3020
|
+
canvas?.classList.add("oc-graph-canvas--dragging");
|
|
3021
3021
|
},
|
|
3022
3022
|
onNodeDrag(nodeId, x3, y3) {
|
|
3023
3023
|
simulation?.dragNode(nodeId, x3, y3);
|
|
3024
3024
|
},
|
|
3025
3025
|
onNodeDragEnd(nodeId) {
|
|
3026
3026
|
simulation?.unpinNode(nodeId);
|
|
3027
|
-
canvas?.classList.remove("
|
|
3027
|
+
canvas?.classList.remove("oc-graph-canvas--dragging");
|
|
3028
3028
|
},
|
|
3029
3029
|
onDoubleClick(nodeId) {
|
|
3030
3030
|
options?.onNodeDoubleClick?.(nodeDataById(nodeId));
|
|
@@ -3199,7 +3199,7 @@ function createGraph(container, spec, options) {
|
|
|
3199
3199
|
chromeEl = null;
|
|
3200
3200
|
legendEl = null;
|
|
3201
3201
|
renderer = null;
|
|
3202
|
-
container.classList.remove("
|
|
3202
|
+
container.classList.remove("oc-dark");
|
|
3203
3203
|
}
|
|
3204
3204
|
try {
|
|
3205
3205
|
compilation = compile();
|
|
@@ -3255,12 +3255,62 @@ function escapeHtml(str) {
|
|
|
3255
3255
|
}
|
|
3256
3256
|
|
|
3257
3257
|
// src/mount.ts
|
|
3258
|
-
import { isLayerSpec } from "@opendata-ai/openchart-core";
|
|
3258
|
+
import { elementRef, isLayerSpec } from "@opendata-ai/openchart-core";
|
|
3259
3259
|
import { compileChart, compileLayer } from "@opendata-ai/openchart-engine";
|
|
3260
3260
|
|
|
3261
|
+
// src/animation.ts
|
|
3262
|
+
function cancelAnimations(svg) {
|
|
3263
|
+
if (svg) {
|
|
3264
|
+
svg.classList.remove("oc-animate");
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
function setupAnimationCleanup(svg) {
|
|
3268
|
+
const style = svg.style;
|
|
3269
|
+
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 600;
|
|
3270
|
+
const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
|
|
3271
|
+
const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) || 200;
|
|
3272
|
+
const animatedElements = svg.querySelectorAll("[data-animation-index]").length;
|
|
3273
|
+
const totalStagger = stagger * Math.max(0, animatedElements - 1);
|
|
3274
|
+
const totalTime = totalStagger + duration + annotationDelay + 500;
|
|
3275
|
+
const timer2 = setTimeout(() => {
|
|
3276
|
+
svg.classList.remove("oc-animate");
|
|
3277
|
+
}, totalTime);
|
|
3278
|
+
return () => {
|
|
3279
|
+
clearTimeout(timer2);
|
|
3280
|
+
cancelAnimations(svg);
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
function setupTableAnimationCleanup(wrapper) {
|
|
3284
|
+
const style = wrapper.style;
|
|
3285
|
+
const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 500;
|
|
3286
|
+
const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
|
|
3287
|
+
const rows = wrapper.querySelectorAll("tbody tr").length;
|
|
3288
|
+
const totalStagger = stagger * Math.max(0, rows - 1);
|
|
3289
|
+
const totalTime = totalStagger + duration + 300;
|
|
3290
|
+
const timer2 = setTimeout(() => {
|
|
3291
|
+
wrapper.classList.remove("oc-animate");
|
|
3292
|
+
}, totalTime);
|
|
3293
|
+
return () => {
|
|
3294
|
+
clearTimeout(timer2);
|
|
3295
|
+
wrapper.classList.remove("oc-animate");
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3261
3299
|
// src/svg-renderer.ts
|
|
3262
3300
|
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE2, BRAND_MIN_WIDTH as BRAND_MIN_WIDTH2, estimateTextWidth } from "@opendata-ai/openchart-core";
|
|
3301
|
+
import { clampStaggerDelay } from "@opendata-ai/openchart-engine";
|
|
3263
3302
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
3303
|
+
var currentAnimation;
|
|
3304
|
+
function stampAnimationAttrs(el, mark, fallbackIndex) {
|
|
3305
|
+
if (!currentAnimation?.enabled) return;
|
|
3306
|
+
const idx = mark.animationIndex ?? fallbackIndex;
|
|
3307
|
+
el.setAttribute("data-animation-index", String(idx));
|
|
3308
|
+
el.style.setProperty("--oc-mark-index", String(idx));
|
|
3309
|
+
}
|
|
3310
|
+
var EASE_VAR_MAP = {
|
|
3311
|
+
smooth: "var(--oc-ease-smooth)",
|
|
3312
|
+
snappy: "var(--oc-ease-snappy)"
|
|
3313
|
+
};
|
|
3264
3314
|
function computeXAxisExtent(layout) {
|
|
3265
3315
|
const xAxis = layout.axes.x;
|
|
3266
3316
|
if (!xAxis) return 0;
|
|
@@ -3363,13 +3413,13 @@ function renderChromeElement(parent, element, className, chromeKey) {
|
|
|
3363
3413
|
}
|
|
3364
3414
|
function renderChrome(parent, layout) {
|
|
3365
3415
|
const g = createSVGElement("g");
|
|
3366
|
-
g.setAttribute("class", "
|
|
3416
|
+
g.setAttribute("class", "oc-chrome");
|
|
3367
3417
|
const { chrome } = layout;
|
|
3368
3418
|
if (chrome.title) {
|
|
3369
|
-
renderChromeElement(g, chrome.title, "
|
|
3419
|
+
renderChromeElement(g, chrome.title, "oc-title", "title");
|
|
3370
3420
|
}
|
|
3371
3421
|
if (chrome.subtitle) {
|
|
3372
|
-
renderChromeElement(g, chrome.subtitle, "
|
|
3422
|
+
renderChromeElement(g, chrome.subtitle, "oc-subtitle", "subtitle");
|
|
3373
3423
|
}
|
|
3374
3424
|
const xAxisExtent = computeXAxisExtent(layout);
|
|
3375
3425
|
const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;
|
|
@@ -3377,7 +3427,7 @@ function renderChrome(parent, layout) {
|
|
|
3377
3427
|
renderChromeElement(
|
|
3378
3428
|
g,
|
|
3379
3429
|
{ ...chrome.source, y: bottomOffset + chrome.source.y },
|
|
3380
|
-
"
|
|
3430
|
+
"oc-source",
|
|
3381
3431
|
"source"
|
|
3382
3432
|
);
|
|
3383
3433
|
}
|
|
@@ -3385,7 +3435,7 @@ function renderChrome(parent, layout) {
|
|
|
3385
3435
|
renderChromeElement(
|
|
3386
3436
|
g,
|
|
3387
3437
|
{ ...chrome.byline, y: bottomOffset + chrome.byline.y },
|
|
3388
|
-
"
|
|
3438
|
+
"oc-byline",
|
|
3389
3439
|
"byline"
|
|
3390
3440
|
);
|
|
3391
3441
|
}
|
|
@@ -3393,7 +3443,7 @@ function renderChrome(parent, layout) {
|
|
|
3393
3443
|
renderChromeElement(
|
|
3394
3444
|
g,
|
|
3395
3445
|
{ ...chrome.footer, y: bottomOffset + chrome.footer.y },
|
|
3396
|
-
"
|
|
3446
|
+
"oc-footer",
|
|
3397
3447
|
"footer"
|
|
3398
3448
|
);
|
|
3399
3449
|
}
|
|
@@ -3401,11 +3451,11 @@ function renderChrome(parent, layout) {
|
|
|
3401
3451
|
}
|
|
3402
3452
|
function renderAxis(parent, axis, orientation, layout) {
|
|
3403
3453
|
const g = createSVGElement("g");
|
|
3404
|
-
g.setAttribute("class", `
|
|
3454
|
+
g.setAttribute("class", `oc-axis oc-axis-${orientation}`);
|
|
3405
3455
|
const { area } = layout;
|
|
3406
3456
|
if (orientation === "x") {
|
|
3407
3457
|
const line = createSVGElement("line");
|
|
3408
|
-
line.setAttribute("class", "
|
|
3458
|
+
line.setAttribute("class", "oc-axis-line");
|
|
3409
3459
|
setAttrs(line, {
|
|
3410
3460
|
x1: axis.start.x,
|
|
3411
3461
|
y1: axis.start.y,
|
|
@@ -3419,7 +3469,7 @@ function renderAxis(parent, axis, orientation, layout) {
|
|
|
3419
3469
|
for (const tick of axis.ticks) {
|
|
3420
3470
|
if (orientation === "x") {
|
|
3421
3471
|
const label = createSVGElement("text");
|
|
3422
|
-
label.setAttribute("class", "
|
|
3472
|
+
label.setAttribute("class", "oc-axis-tick");
|
|
3423
3473
|
if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {
|
|
3424
3474
|
const labelX = tick.position;
|
|
3425
3475
|
const labelY = area.y + area.height + 6;
|
|
@@ -3442,7 +3492,7 @@ function renderAxis(parent, axis, orientation, layout) {
|
|
|
3442
3492
|
g.appendChild(label);
|
|
3443
3493
|
} else {
|
|
3444
3494
|
const label = createSVGElement("text");
|
|
3445
|
-
label.setAttribute("class", "
|
|
3495
|
+
label.setAttribute("class", "oc-axis-tick");
|
|
3446
3496
|
setAttrs(label, {
|
|
3447
3497
|
x: area.x - 6,
|
|
3448
3498
|
y: tick.position,
|
|
@@ -3456,7 +3506,7 @@ function renderAxis(parent, axis, orientation, layout) {
|
|
|
3456
3506
|
}
|
|
3457
3507
|
for (const gridline of axis.gridlines) {
|
|
3458
3508
|
const gl = createSVGElement("line");
|
|
3459
|
-
gl.setAttribute("class", "
|
|
3509
|
+
gl.setAttribute("class", "oc-gridline");
|
|
3460
3510
|
if (orientation === "y") {
|
|
3461
3511
|
setAttrs(gl, {
|
|
3462
3512
|
x1: area.x,
|
|
@@ -3482,7 +3532,7 @@ function renderAxis(parent, axis, orientation, layout) {
|
|
|
3482
3532
|
}
|
|
3483
3533
|
if (axis.label && axis.labelStyle) {
|
|
3484
3534
|
const axisLabel = createSVGElement("text");
|
|
3485
|
-
axisLabel.setAttribute("class", "
|
|
3535
|
+
axisLabel.setAttribute("class", "oc-axis-title");
|
|
3486
3536
|
applyTextStyle(axisLabel, axis.labelStyle);
|
|
3487
3537
|
axisLabel.textContent = axis.label;
|
|
3488
3538
|
if (orientation === "x") {
|
|
@@ -3533,7 +3583,8 @@ function registerMarkRenderer(type, renderer) {
|
|
|
3533
3583
|
function renderLineMark(mark, index2) {
|
|
3534
3584
|
const g = createSVGElement("g");
|
|
3535
3585
|
g.setAttribute("data-mark-id", `line-${mark.seriesKey ?? index2}`);
|
|
3536
|
-
g.setAttribute("class", "
|
|
3586
|
+
g.setAttribute("class", "oc-mark oc-mark-line");
|
|
3587
|
+
stampAnimationAttrs(g, mark, index2);
|
|
3537
3588
|
if (mark.points.length > 1) {
|
|
3538
3589
|
const path = createSVGElement("path");
|
|
3539
3590
|
const d = mark.path ?? mark.points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ");
|
|
@@ -3553,7 +3604,7 @@ function renderLineMark(mark, index2) {
|
|
|
3553
3604
|
}
|
|
3554
3605
|
if (mark.label?.visible) {
|
|
3555
3606
|
const label = createSVGElement("text");
|
|
3556
|
-
label.setAttribute("class", "
|
|
3607
|
+
label.setAttribute("class", "oc-mark-label");
|
|
3557
3608
|
if (mark.seriesKey) {
|
|
3558
3609
|
label.setAttribute("data-series", mark.seriesKey);
|
|
3559
3610
|
}
|
|
@@ -3563,7 +3614,7 @@ function renderLineMark(mark, index2) {
|
|
|
3563
3614
|
g.appendChild(label);
|
|
3564
3615
|
if (mark.label.connector) {
|
|
3565
3616
|
const connector = createSVGElement("line");
|
|
3566
|
-
connector.setAttribute("class", "
|
|
3617
|
+
connector.setAttribute("class", "oc-mark-connector");
|
|
3567
3618
|
setAttrs(connector, {
|
|
3568
3619
|
x1: mark.label.connector.from.x,
|
|
3569
3620
|
y1: mark.label.connector.from.y,
|
|
@@ -3581,7 +3632,8 @@ function renderLineMark(mark, index2) {
|
|
|
3581
3632
|
function renderAreaMark(mark, index2) {
|
|
3582
3633
|
const g = createSVGElement("g");
|
|
3583
3634
|
g.setAttribute("data-mark-id", `area-${mark.seriesKey ?? index2}`);
|
|
3584
|
-
g.setAttribute("class", "
|
|
3635
|
+
g.setAttribute("class", "oc-mark oc-mark-area");
|
|
3636
|
+
stampAnimationAttrs(g, mark, index2);
|
|
3585
3637
|
if (mark.path) {
|
|
3586
3638
|
const fill = createSVGElement("path");
|
|
3587
3639
|
setAttrs(fill, {
|
|
@@ -3593,6 +3645,7 @@ function renderAreaMark(mark, index2) {
|
|
|
3593
3645
|
g.appendChild(fill);
|
|
3594
3646
|
if (mark.stroke && mark.topPath) {
|
|
3595
3647
|
const strokePath = createSVGElement("path");
|
|
3648
|
+
strokePath.setAttribute("class", "oc-area-top");
|
|
3596
3649
|
setAttrs(strokePath, {
|
|
3597
3650
|
d: mark.topPath,
|
|
3598
3651
|
fill: "none",
|
|
@@ -3607,7 +3660,11 @@ function renderAreaMark(mark, index2) {
|
|
|
3607
3660
|
function renderRectMark(mark, index2) {
|
|
3608
3661
|
const g = createSVGElement("g");
|
|
3609
3662
|
g.setAttribute("data-mark-id", `rect-${index2}`);
|
|
3610
|
-
g.setAttribute("class", "
|
|
3663
|
+
g.setAttribute("class", "oc-mark oc-mark-rect");
|
|
3664
|
+
stampAnimationAttrs(g, mark, index2);
|
|
3665
|
+
if (currentAnimation?.enabled && mark.orient === "horizontal") {
|
|
3666
|
+
g.setAttribute("data-orient", "horizontal");
|
|
3667
|
+
}
|
|
3611
3668
|
const rect = createSVGElement("rect");
|
|
3612
3669
|
setAttrs(rect, {
|
|
3613
3670
|
x: mark.x,
|
|
@@ -3628,7 +3685,7 @@ function renderRectMark(mark, index2) {
|
|
|
3628
3685
|
g.appendChild(rect);
|
|
3629
3686
|
if (mark.label?.visible) {
|
|
3630
3687
|
const label = createSVGElement("text");
|
|
3631
|
-
label.setAttribute("class", "
|
|
3688
|
+
label.setAttribute("class", "oc-mark-label");
|
|
3632
3689
|
setAttrs(label, { x: mark.label.x, y: mark.label.y });
|
|
3633
3690
|
applyTextStyle(label, mark.label.style);
|
|
3634
3691
|
label.textContent = mark.label.text;
|
|
@@ -3639,8 +3696,9 @@ function renderRectMark(mark, index2) {
|
|
|
3639
3696
|
function renderArcMark(mark, index2) {
|
|
3640
3697
|
const g = createSVGElement("g");
|
|
3641
3698
|
g.setAttribute("data-mark-id", `arc-${index2}`);
|
|
3642
|
-
g.setAttribute("class", "
|
|
3699
|
+
g.setAttribute("class", "oc-mark oc-mark-arc");
|
|
3643
3700
|
g.setAttribute("transform", `translate(${mark.center.x},${mark.center.y})`);
|
|
3701
|
+
stampAnimationAttrs(g, mark, index2);
|
|
3644
3702
|
const path = createSVGElement("path");
|
|
3645
3703
|
setAttrs(path, {
|
|
3646
3704
|
d: mark.path,
|
|
@@ -3651,7 +3709,7 @@ function renderArcMark(mark, index2) {
|
|
|
3651
3709
|
g.appendChild(path);
|
|
3652
3710
|
if (mark.label?.visible) {
|
|
3653
3711
|
const label = createSVGElement("text");
|
|
3654
|
-
label.setAttribute("class", "
|
|
3712
|
+
label.setAttribute("class", "oc-mark-label");
|
|
3655
3713
|
setAttrs(label, {
|
|
3656
3714
|
x: mark.label.x - mark.center.x,
|
|
3657
3715
|
y: mark.label.y - mark.center.y
|
|
@@ -3665,7 +3723,8 @@ function renderArcMark(mark, index2) {
|
|
|
3665
3723
|
function renderPointMark(mark, index2) {
|
|
3666
3724
|
const circle = createSVGElement("circle");
|
|
3667
3725
|
circle.setAttribute("data-mark-id", `point-${index2}`);
|
|
3668
|
-
circle.setAttribute("class", "
|
|
3726
|
+
circle.setAttribute("class", "oc-mark oc-mark-point");
|
|
3727
|
+
stampAnimationAttrs(circle, mark, index2);
|
|
3669
3728
|
setAttrs(circle, {
|
|
3670
3729
|
cx: mark.cx,
|
|
3671
3730
|
cy: mark.cy,
|
|
@@ -3682,7 +3741,8 @@ function renderPointMark(mark, index2) {
|
|
|
3682
3741
|
function renderTextMark(mark, index2) {
|
|
3683
3742
|
const text = createSVGElement("text");
|
|
3684
3743
|
text.setAttribute("data-mark-id", `textMark-${index2}`);
|
|
3685
|
-
text.setAttribute("class", "
|
|
3744
|
+
text.setAttribute("class", "oc-mark oc-mark-text");
|
|
3745
|
+
stampAnimationAttrs(text, mark, index2);
|
|
3686
3746
|
setAttrs(text, {
|
|
3687
3747
|
x: mark.x,
|
|
3688
3748
|
y: mark.y,
|
|
@@ -3705,7 +3765,8 @@ function renderTextMark(mark, index2) {
|
|
|
3705
3765
|
function renderRuleMark(mark, index2) {
|
|
3706
3766
|
const line = createSVGElement("line");
|
|
3707
3767
|
line.setAttribute("data-mark-id", `rule-${index2}`);
|
|
3708
|
-
line.setAttribute("class", "
|
|
3768
|
+
line.setAttribute("class", "oc-mark oc-mark-rule");
|
|
3769
|
+
stampAnimationAttrs(line, mark, index2);
|
|
3709
3770
|
setAttrs(line, {
|
|
3710
3771
|
x1: mark.x1,
|
|
3711
3772
|
y1: mark.y1,
|
|
@@ -3725,7 +3786,8 @@ function renderRuleMark(mark, index2) {
|
|
|
3725
3786
|
function renderTickMark(mark, index2) {
|
|
3726
3787
|
const line = createSVGElement("line");
|
|
3727
3788
|
line.setAttribute("data-mark-id", `tick-${index2}`);
|
|
3728
|
-
line.setAttribute("class", "
|
|
3789
|
+
line.setAttribute("class", "oc-mark oc-mark-tick");
|
|
3790
|
+
stampAnimationAttrs(line, mark, index2);
|
|
3729
3791
|
const half = mark.length / 2;
|
|
3730
3792
|
if (mark.orient === "vertical") {
|
|
3731
3793
|
setAttrs(line, {
|
|
@@ -3774,28 +3836,37 @@ function getMarkSeries(mark) {
|
|
|
3774
3836
|
}
|
|
3775
3837
|
function renderMarks(parent, layout) {
|
|
3776
3838
|
const g = createSVGElement("g");
|
|
3777
|
-
g.setAttribute("class", "
|
|
3839
|
+
g.setAttribute("class", "oc-marks");
|
|
3778
3840
|
for (let i = 0; i < layout.marks.length; i++) {
|
|
3779
3841
|
const mark = layout.marks[i];
|
|
3780
3842
|
const renderer = markRenderers[mark.type];
|
|
3781
|
-
if (renderer)
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3843
|
+
if (!renderer) continue;
|
|
3844
|
+
const el = renderer(mark, i);
|
|
3845
|
+
if (mark.aria?.label) {
|
|
3846
|
+
el.setAttribute("aria-label", mark.aria.label);
|
|
3847
|
+
}
|
|
3848
|
+
const series = getMarkSeries(mark);
|
|
3849
|
+
if (series) {
|
|
3850
|
+
el.setAttribute("data-series", series);
|
|
3851
|
+
}
|
|
3852
|
+
if (currentAnimation?.enabled && mark.type === "rect") {
|
|
3853
|
+
const rect = mark;
|
|
3854
|
+
if (rect.stackGroup && rect.stackPos !== void 0) {
|
|
3855
|
+
el.setAttribute("data-stack-pos", String(rect.stackPos));
|
|
3856
|
+
el.style.setProperty(
|
|
3857
|
+
"--oc-stack-pos",
|
|
3858
|
+
String(rect.stackPos)
|
|
3859
|
+
);
|
|
3789
3860
|
}
|
|
3790
|
-
g.appendChild(el);
|
|
3791
3861
|
}
|
|
3862
|
+
g.appendChild(el);
|
|
3792
3863
|
}
|
|
3793
3864
|
parent.appendChild(g);
|
|
3794
3865
|
}
|
|
3795
3866
|
function renderAnnotations(parent, layout) {
|
|
3796
3867
|
if (layout.annotations.length === 0) return;
|
|
3797
3868
|
const g = createSVGElement("g");
|
|
3798
|
-
g.setAttribute("class", "
|
|
3869
|
+
g.setAttribute("class", "oc-annotations");
|
|
3799
3870
|
for (let i = 0; i < layout.annotations.length; i++) {
|
|
3800
3871
|
renderAnnotation(g, layout.annotations[i], i);
|
|
3801
3872
|
}
|
|
@@ -3821,7 +3892,7 @@ function renderCurvedArrow(parent, from, to, stroke) {
|
|
|
3821
3892
|
const baseX = to.x - ux * arrowLen;
|
|
3822
3893
|
const baseY = tipY - uy * arrowLen;
|
|
3823
3894
|
const path = createSVGElement("path");
|
|
3824
|
-
path.setAttribute("class", "
|
|
3895
|
+
path.setAttribute("class", "oc-annotation-connector");
|
|
3825
3896
|
setAttrs(path, {
|
|
3826
3897
|
d: `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${baseX} ${baseY}`,
|
|
3827
3898
|
fill: "none",
|
|
@@ -3832,7 +3903,7 @@ function renderCurvedArrow(parent, from, to, stroke) {
|
|
|
3832
3903
|
const px = -uy;
|
|
3833
3904
|
const py = ux;
|
|
3834
3905
|
const arrow = createSVGElement("polygon");
|
|
3835
|
-
arrow.setAttribute("class", "
|
|
3906
|
+
arrow.setAttribute("class", "oc-annotation-connector");
|
|
3836
3907
|
setAttrs(arrow, {
|
|
3837
3908
|
points: [
|
|
3838
3909
|
`${to.x},${tipY}`,
|
|
@@ -3845,11 +3916,14 @@ function renderCurvedArrow(parent, from, to, stroke) {
|
|
|
3845
3916
|
}
|
|
3846
3917
|
function renderAnnotation(parent, annotation, index2) {
|
|
3847
3918
|
const g = createSVGElement("g");
|
|
3848
|
-
g.setAttribute("class", `
|
|
3919
|
+
g.setAttribute("class", `oc-annotation oc-annotation-${annotation.type}`);
|
|
3849
3920
|
g.setAttribute("data-annotation-index", String(index2));
|
|
3921
|
+
if (annotation.id) {
|
|
3922
|
+
g.setAttribute("data-annotation-id", annotation.id);
|
|
3923
|
+
}
|
|
3850
3924
|
if (annotation.rect) {
|
|
3851
3925
|
const rect = createSVGElement("rect");
|
|
3852
|
-
rect.setAttribute("class", "
|
|
3926
|
+
rect.setAttribute("class", "oc-annotation-range");
|
|
3853
3927
|
setAttrs(rect, {
|
|
3854
3928
|
x: annotation.rect.x,
|
|
3855
3929
|
y: annotation.rect.y,
|
|
@@ -3864,7 +3938,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3864
3938
|
}
|
|
3865
3939
|
if (annotation.line) {
|
|
3866
3940
|
const line = createSVGElement("line");
|
|
3867
|
-
line.setAttribute("class", "
|
|
3941
|
+
line.setAttribute("class", "oc-annotation-line");
|
|
3868
3942
|
setAttrs(line, {
|
|
3869
3943
|
x1: annotation.line.start.x,
|
|
3870
3944
|
y1: annotation.line.start.y,
|
|
@@ -3895,7 +3969,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3895
3969
|
const tipY = pointsDown ? midY + caretSize / 2 : midY - caretSize / 2;
|
|
3896
3970
|
const baseY = pointsDown ? tipY - caretSize : tipY + caretSize;
|
|
3897
3971
|
const path = createSVGElement("path");
|
|
3898
|
-
path.setAttribute("class", "
|
|
3972
|
+
path.setAttribute("class", "oc-annotation-connector");
|
|
3899
3973
|
setAttrs(path, {
|
|
3900
3974
|
d: `M${tipX - caretSize},${baseY} L${tipX},${tipY} L${tipX + caretSize},${baseY}`,
|
|
3901
3975
|
fill: "none",
|
|
@@ -3910,7 +3984,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3910
3984
|
renderCurvedArrow(g, c2.from, c2.to, c2.stroke);
|
|
3911
3985
|
} else {
|
|
3912
3986
|
const connector = createSVGElement("line");
|
|
3913
|
-
connector.setAttribute("class", "
|
|
3987
|
+
connector.setAttribute("class", "oc-annotation-connector");
|
|
3914
3988
|
setAttrs(connector, {
|
|
3915
3989
|
x1: c2.from.x,
|
|
3916
3990
|
y1: c2.from.y,
|
|
@@ -3924,7 +3998,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3924
3998
|
}
|
|
3925
3999
|
}
|
|
3926
4000
|
const text = createSVGElement("text");
|
|
3927
|
-
text.setAttribute("class", "
|
|
4001
|
+
text.setAttribute("class", "oc-annotation-label");
|
|
3928
4002
|
setAttrs(text, { x: annotation.label.x, y: annotation.label.y });
|
|
3929
4003
|
applyTextStyle(text, annotation.label.style);
|
|
3930
4004
|
const lines = annotation.label.text.split("\n");
|
|
@@ -3949,7 +4023,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3949
4023
|
const pad = 3;
|
|
3950
4024
|
const bgX = isMultiLine ? annotation.label.x - maxLineWidth / 2 - pad : annotation.label.x - pad;
|
|
3951
4025
|
const bgRect = createSVGElement("rect");
|
|
3952
|
-
bgRect.setAttribute("class", "
|
|
4026
|
+
bgRect.setAttribute("class", "oc-annotation-bg");
|
|
3953
4027
|
setAttrs(bgRect, {
|
|
3954
4028
|
x: bgX,
|
|
3955
4029
|
y: annotation.label.y - fontSize + (lineHeight - fontSize) / 2 - pad,
|
|
@@ -3967,7 +4041,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3967
4041
|
function renderLegend(parent, legend) {
|
|
3968
4042
|
if (legend.entries.length === 0) return;
|
|
3969
4043
|
const g = createSVGElement("g");
|
|
3970
|
-
g.setAttribute("class", "
|
|
4044
|
+
g.setAttribute("class", "oc-legend");
|
|
3971
4045
|
g.setAttribute("role", "list");
|
|
3972
4046
|
g.setAttribute("aria-label", "Chart legend");
|
|
3973
4047
|
const isHorizontal = legend.position === "top" || legend.position === "bottom";
|
|
@@ -3988,7 +4062,7 @@ function renderLegend(parent, legend) {
|
|
|
3988
4062
|
}
|
|
3989
4063
|
}
|
|
3990
4064
|
const entryG = createSVGElement("g");
|
|
3991
|
-
entryG.setAttribute("class", "
|
|
4065
|
+
entryG.setAttribute("class", "oc-legend-entry");
|
|
3992
4066
|
entryG.setAttribute("role", "listitem");
|
|
3993
4067
|
entryG.setAttribute("data-legend-index", String(i));
|
|
3994
4068
|
entryG.setAttribute("data-legend-label", entry.label);
|
|
@@ -4088,7 +4162,7 @@ function renderBrand(parent, layout) {
|
|
|
4088
4162
|
a2.setAttributeNS(XLINK_NS, "xlink:href", BRAND_URL);
|
|
4089
4163
|
a2.setAttribute("target", "_blank");
|
|
4090
4164
|
a2.setAttribute("rel", "noopener");
|
|
4091
|
-
a2.setAttribute("class", "
|
|
4165
|
+
a2.setAttribute("class", "oc-chrome-ref");
|
|
4092
4166
|
const text = createSVGElement("text");
|
|
4093
4167
|
setAttrs(text, {
|
|
4094
4168
|
x: rightEdge,
|
|
@@ -4111,8 +4185,10 @@ function renderBrand(parent, layout) {
|
|
|
4111
4185
|
a2.appendChild(text);
|
|
4112
4186
|
parent.appendChild(a2);
|
|
4113
4187
|
}
|
|
4114
|
-
function renderChartSVG(layout, container) {
|
|
4188
|
+
function renderChartSVG(layout, container, opts) {
|
|
4115
4189
|
const { width, height } = layout.dimensions;
|
|
4190
|
+
const animation = layout.animation;
|
|
4191
|
+
currentAnimation = animation;
|
|
4116
4192
|
const svg = createSVGElement("svg");
|
|
4117
4193
|
setAttrs(svg, {
|
|
4118
4194
|
viewBox: `0 0 ${width} ${height}`,
|
|
@@ -4127,7 +4203,30 @@ function renderChartSVG(layout, container) {
|
|
|
4127
4203
|
svg.style.height = `${height}px`;
|
|
4128
4204
|
svg.setAttribute("role", layout.a11y.role);
|
|
4129
4205
|
svg.setAttribute("aria-label", layout.a11y.altText);
|
|
4130
|
-
|
|
4206
|
+
const classes = opts?.animate ? "oc-chart oc-animate" : "oc-chart";
|
|
4207
|
+
svg.setAttribute("class", classes);
|
|
4208
|
+
if (animation?.enabled) {
|
|
4209
|
+
const markCount = layout.marks.length;
|
|
4210
|
+
const stagger = clampStaggerDelay(animation.staggerDelay, markCount);
|
|
4211
|
+
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
4212
|
+
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
4213
|
+
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
4214
|
+
const easeVar = EASE_VAR_MAP[animation.ease] || EASE_VAR_MAP.smooth;
|
|
4215
|
+
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
4216
|
+
let maxSegments = 0;
|
|
4217
|
+
for (const m2 of layout.marks) {
|
|
4218
|
+
if (m2.type === "rect") {
|
|
4219
|
+
const pos = m2.stackPos;
|
|
4220
|
+
if (pos !== void 0 && pos + 1 > maxSegments) {
|
|
4221
|
+
maxSegments = pos + 1;
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
if (maxSegments > 0) {
|
|
4226
|
+
const segDuration = Math.round(animation.duration / maxSegments);
|
|
4227
|
+
svg.style.setProperty("--oc-stack-segment-duration", `${segDuration}ms`);
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4131
4230
|
const bg = createSVGElement("rect");
|
|
4132
4231
|
setAttrs(bg, {
|
|
4133
4232
|
x: 0,
|
|
@@ -4137,7 +4236,7 @@ function renderChartSVG(layout, container) {
|
|
|
4137
4236
|
fill: layout.theme.colors.background
|
|
4138
4237
|
});
|
|
4139
4238
|
svg.appendChild(bg);
|
|
4140
|
-
const clipId = `
|
|
4239
|
+
const clipId = `oc-clip-${Math.random().toString(36).slice(2, 8)}`;
|
|
4141
4240
|
const defs = createSVGElement("defs");
|
|
4142
4241
|
const clipPath = createSVGElement("clipPath");
|
|
4143
4242
|
clipPath.setAttribute("id", clipId);
|
|
@@ -4168,7 +4267,7 @@ function renderChartSVG(layout, container) {
|
|
|
4168
4267
|
height: layout.area.height,
|
|
4169
4268
|
fill: "transparent"
|
|
4170
4269
|
});
|
|
4171
|
-
overlay.setAttribute("class", "
|
|
4270
|
+
overlay.setAttribute("class", "oc-voronoi-overlay");
|
|
4172
4271
|
overlay.setAttribute("data-voronoi-overlay", "true");
|
|
4173
4272
|
clippedGroup.appendChild(overlay);
|
|
4174
4273
|
}
|
|
@@ -4177,10 +4276,164 @@ function renderChartSVG(layout, container) {
|
|
|
4177
4276
|
renderLegend(svg, layout.legend);
|
|
4178
4277
|
renderChrome(svg, layout);
|
|
4179
4278
|
renderBrand(svg, layout);
|
|
4279
|
+
currentAnimation = void 0;
|
|
4180
4280
|
container.appendChild(svg);
|
|
4181
4281
|
return svg;
|
|
4182
4282
|
}
|
|
4183
4283
|
|
|
4284
|
+
// src/text-edit-overlay.ts
|
|
4285
|
+
function getScale(svg) {
|
|
4286
|
+
const viewBox = svg.viewBox?.baseVal;
|
|
4287
|
+
const svgRect = svg.getBoundingClientRect();
|
|
4288
|
+
return {
|
|
4289
|
+
scaleX: viewBox?.width && svgRect.width ? svgRect.width / viewBox.width : 1,
|
|
4290
|
+
scaleY: viewBox?.height && svgRect.height ? svgRect.height / viewBox.height : 1
|
|
4291
|
+
};
|
|
4292
|
+
}
|
|
4293
|
+
function getTextStyles(targetElement, scale) {
|
|
4294
|
+
const computed = window.getComputedStyle(targetElement);
|
|
4295
|
+
const svgFontSize = parseFloat(
|
|
4296
|
+
targetElement.getAttribute("font-size") ?? computed.fontSize ?? "14"
|
|
4297
|
+
);
|
|
4298
|
+
const pixelFontSize = svgFontSize * scale.scaleY;
|
|
4299
|
+
const fontFamily = targetElement.getAttribute("font-family") ?? computed.fontFamily ?? "inherit";
|
|
4300
|
+
const fontWeight = targetElement.getAttribute("font-weight") ?? computed.fontWeight ?? "400";
|
|
4301
|
+
const fill = targetElement.style.getPropertyValue("fill") || targetElement.getAttribute("fill") || computed.color || "#000";
|
|
4302
|
+
const textAnchor = targetElement.getAttribute("text-anchor") ?? "start";
|
|
4303
|
+
let textAlign = "left";
|
|
4304
|
+
if (textAnchor === "middle") textAlign = "center";
|
|
4305
|
+
else if (textAnchor === "end") textAlign = "right";
|
|
4306
|
+
return {
|
|
4307
|
+
fontFamily,
|
|
4308
|
+
fontSize: `${pixelFontSize}px`,
|
|
4309
|
+
fontWeight,
|
|
4310
|
+
color: fill,
|
|
4311
|
+
textAlign,
|
|
4312
|
+
lineHeight: "1.3"
|
|
4313
|
+
};
|
|
4314
|
+
}
|
|
4315
|
+
function computePosition2(targetElement, svg, container) {
|
|
4316
|
+
const bbox = targetElement.getBBox();
|
|
4317
|
+
const scale = getScale(svg);
|
|
4318
|
+
const svgRect = svg.getBoundingClientRect();
|
|
4319
|
+
const containerRect = container.getBoundingClientRect();
|
|
4320
|
+
const padding = 4;
|
|
4321
|
+
const left = bbox.x * scale.scaleX + (svgRect.left - containerRect.left) - padding;
|
|
4322
|
+
const top = bbox.y * scale.scaleY + (svgRect.top - containerRect.top) - padding;
|
|
4323
|
+
const width = bbox.width * scale.scaleX + padding * 2;
|
|
4324
|
+
const height = bbox.height * scale.scaleY + padding * 2;
|
|
4325
|
+
return {
|
|
4326
|
+
top: Math.max(0, top),
|
|
4327
|
+
left: Math.max(0, left),
|
|
4328
|
+
width: Math.max(width, 60),
|
|
4329
|
+
height: Math.max(height, 24)
|
|
4330
|
+
};
|
|
4331
|
+
}
|
|
4332
|
+
function createTextEditOverlay(config) {
|
|
4333
|
+
const { container, svg, targetElement, currentText, onCommit, onCancel } = config;
|
|
4334
|
+
let destroyed = false;
|
|
4335
|
+
const originalOpacity = targetElement.getAttribute("opacity");
|
|
4336
|
+
targetElement.setAttribute("opacity", "0");
|
|
4337
|
+
const scale = getScale(svg);
|
|
4338
|
+
const styles = getTextStyles(targetElement, scale);
|
|
4339
|
+
const position = computePosition2(targetElement, svg, container);
|
|
4340
|
+
const textarea = document.createElement("textarea");
|
|
4341
|
+
textarea.value = currentText;
|
|
4342
|
+
const containerPosition = window.getComputedStyle(container).position;
|
|
4343
|
+
const containerWasStatic = containerPosition === "static";
|
|
4344
|
+
if (containerWasStatic) {
|
|
4345
|
+
container.style.position = "relative";
|
|
4346
|
+
}
|
|
4347
|
+
Object.assign(textarea.style, {
|
|
4348
|
+
position: "absolute",
|
|
4349
|
+
top: `${position.top}px`,
|
|
4350
|
+
left: `${position.left}px`,
|
|
4351
|
+
width: `${position.width}px`,
|
|
4352
|
+
minHeight: `${position.height}px`,
|
|
4353
|
+
fontFamily: styles.fontFamily,
|
|
4354
|
+
fontSize: styles.fontSize,
|
|
4355
|
+
fontWeight: styles.fontWeight,
|
|
4356
|
+
color: styles.color,
|
|
4357
|
+
textAlign: styles.textAlign,
|
|
4358
|
+
lineHeight: styles.lineHeight,
|
|
4359
|
+
padding: "2px 4px",
|
|
4360
|
+
margin: "0",
|
|
4361
|
+
border: "1px solid rgba(79, 70, 229, 0.4)",
|
|
4362
|
+
borderRadius: "3px",
|
|
4363
|
+
background: "rgba(255, 255, 255, 0.95)",
|
|
4364
|
+
outline: "none",
|
|
4365
|
+
resize: "none",
|
|
4366
|
+
overflow: "hidden",
|
|
4367
|
+
boxSizing: "border-box",
|
|
4368
|
+
zIndex: "10000",
|
|
4369
|
+
// Remove default textarea appearance
|
|
4370
|
+
WebkitAppearance: "none",
|
|
4371
|
+
appearance: "none"
|
|
4372
|
+
});
|
|
4373
|
+
container.appendChild(textarea);
|
|
4374
|
+
textarea.focus();
|
|
4375
|
+
textarea.select();
|
|
4376
|
+
function destroy() {
|
|
4377
|
+
if (destroyed) return;
|
|
4378
|
+
destroyed = true;
|
|
4379
|
+
if (originalOpacity !== null) {
|
|
4380
|
+
targetElement.setAttribute("opacity", originalOpacity);
|
|
4381
|
+
} else {
|
|
4382
|
+
targetElement.removeAttribute("opacity");
|
|
4383
|
+
}
|
|
4384
|
+
textarea.removeEventListener("keydown", handleKeyDown);
|
|
4385
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
4386
|
+
resizeObserver.disconnect();
|
|
4387
|
+
if (containerWasStatic) {
|
|
4388
|
+
container.style.position = "";
|
|
4389
|
+
}
|
|
4390
|
+
if (textarea.parentNode) {
|
|
4391
|
+
textarea.parentNode.removeChild(textarea);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
function commit() {
|
|
4395
|
+
if (destroyed) return;
|
|
4396
|
+
const newText = textarea.value;
|
|
4397
|
+
destroy();
|
|
4398
|
+
onCommit(newText);
|
|
4399
|
+
}
|
|
4400
|
+
function cancel() {
|
|
4401
|
+
if (destroyed) return;
|
|
4402
|
+
destroy();
|
|
4403
|
+
onCancel();
|
|
4404
|
+
}
|
|
4405
|
+
const handleKeyDown = (e) => {
|
|
4406
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
4407
|
+
e.preventDefault();
|
|
4408
|
+
commit();
|
|
4409
|
+
} else if (e.key === "Escape") {
|
|
4410
|
+
e.preventDefault();
|
|
4411
|
+
cancel();
|
|
4412
|
+
}
|
|
4413
|
+
};
|
|
4414
|
+
textarea.addEventListener("keydown", handleKeyDown);
|
|
4415
|
+
const handleClickOutside = (e) => {
|
|
4416
|
+
if (!textarea.contains(e.target)) {
|
|
4417
|
+
commit();
|
|
4418
|
+
}
|
|
4419
|
+
};
|
|
4420
|
+
requestAnimationFrame(() => {
|
|
4421
|
+
if (!destroyed) {
|
|
4422
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
4423
|
+
}
|
|
4424
|
+
});
|
|
4425
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
4426
|
+
if (destroyed) return;
|
|
4427
|
+
const newPosition = computePosition2(targetElement, svg, container);
|
|
4428
|
+
textarea.style.top = `${newPosition.top}px`;
|
|
4429
|
+
textarea.style.left = `${newPosition.left}px`;
|
|
4430
|
+
textarea.style.width = `${newPosition.width}px`;
|
|
4431
|
+
textarea.style.minHeight = `${newPosition.height}px`;
|
|
4432
|
+
});
|
|
4433
|
+
resizeObserver.observe(container);
|
|
4434
|
+
return { destroy };
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4184
4437
|
// src/mount.ts
|
|
4185
4438
|
function resolveDarkMode2(mode) {
|
|
4186
4439
|
if (mode === "force") return true;
|
|
@@ -4431,7 +4684,7 @@ function wireChartEvents(svg, layout, specAnnotations, handlers) {
|
|
|
4431
4684
|
}
|
|
4432
4685
|
}
|
|
4433
4686
|
if (handlers.onAnnotationClick) {
|
|
4434
|
-
const annotationElements = svg.querySelectorAll(".
|
|
4687
|
+
const annotationElements = svg.querySelectorAll(".oc-annotation");
|
|
4435
4688
|
for (let i = 0; i < annotationElements.length; i++) {
|
|
4436
4689
|
const el = annotationElements[i];
|
|
4437
4690
|
const specAnnotation = specAnnotations[i];
|
|
@@ -4458,7 +4711,7 @@ function createDragHandler(config) {
|
|
|
4458
4711
|
let activeDocTouchMove = null;
|
|
4459
4712
|
let activeDocTouchEnd = null;
|
|
4460
4713
|
let activeDocTouchCancel = null;
|
|
4461
|
-
function
|
|
4714
|
+
function getScale2() {
|
|
4462
4715
|
const viewBox = svg.viewBox?.baseVal;
|
|
4463
4716
|
const svgRect = svg.getBoundingClientRect();
|
|
4464
4717
|
return {
|
|
@@ -4468,7 +4721,7 @@ function createDragHandler(config) {
|
|
|
4468
4721
|
}
|
|
4469
4722
|
function startDrag(startX, startY) {
|
|
4470
4723
|
setDragging(true);
|
|
4471
|
-
const { scaleX, scaleY } =
|
|
4724
|
+
const { scaleX, scaleY } = getScale2();
|
|
4472
4725
|
element.style.cursor = "grabbing";
|
|
4473
4726
|
svg.style.userSelect = "none";
|
|
4474
4727
|
const handleMove = (clientX, clientY) => {
|
|
@@ -4594,7 +4847,7 @@ function createDragHandler(config) {
|
|
|
4594
4847
|
};
|
|
4595
4848
|
}
|
|
4596
4849
|
function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setDragging) {
|
|
4597
|
-
const annotationElements = svg.querySelectorAll(".
|
|
4850
|
+
const annotationElements = svg.querySelectorAll(".oc-annotation-text");
|
|
4598
4851
|
const cleanups = [];
|
|
4599
4852
|
for (const el of annotationElements) {
|
|
4600
4853
|
const indexStr = el.getAttribute("data-annotation-index");
|
|
@@ -4605,11 +4858,11 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4605
4858
|
const textAnnotation = specAnnotation;
|
|
4606
4859
|
const annotationG = el;
|
|
4607
4860
|
annotationG.style.cursor = "grab";
|
|
4608
|
-
const connectorLine = annotationG.querySelector("line.
|
|
4861
|
+
const connectorLine = annotationG.querySelector("line.oc-annotation-connector");
|
|
4609
4862
|
const origX2 = connectorLine ? Number(connectorLine.getAttribute("x2")) : 0;
|
|
4610
4863
|
const origY2 = connectorLine ? Number(connectorLine.getAttribute("y2")) : 0;
|
|
4611
|
-
const curvedPath = annotationG.querySelector("path.
|
|
4612
|
-
const arrowhead = annotationG.querySelector("polygon.
|
|
4864
|
+
const curvedPath = annotationG.querySelector("path.oc-annotation-connector");
|
|
4865
|
+
const arrowhead = annotationG.querySelector("polygon.oc-annotation-connector");
|
|
4613
4866
|
const hasCurvedConnector = curvedPath !== null;
|
|
4614
4867
|
const origDx = textAnnotation.offset?.dx ?? 0;
|
|
4615
4868
|
const origDy = textAnnotation.offset?.dy ?? 0;
|
|
@@ -4659,7 +4912,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4659
4912
|
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4660
4913
|
const SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
4661
4914
|
const cleanups = [];
|
|
4662
|
-
const annotationGroups = svg.querySelectorAll(".
|
|
4915
|
+
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4663
4916
|
for (const el of annotationGroups) {
|
|
4664
4917
|
const annotationG = el;
|
|
4665
4918
|
const indexStr = annotationG.getAttribute("data-annotation-index");
|
|
@@ -4668,8 +4921,8 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4668
4921
|
const specAnnotation = specAnnotations[index2];
|
|
4669
4922
|
if (!specAnnotation || specAnnotation.type !== "text") continue;
|
|
4670
4923
|
const textAnnotation = specAnnotation;
|
|
4671
|
-
const connectorLine = annotationG.querySelector("line.
|
|
4672
|
-
const curvedPath = annotationG.querySelector("path.
|
|
4924
|
+
const connectorLine = annotationG.querySelector("line.oc-annotation-connector");
|
|
4925
|
+
const curvedPath = annotationG.querySelector("path.oc-annotation-connector");
|
|
4673
4926
|
if (!connectorLine && !curvedPath) continue;
|
|
4674
4927
|
let fromX, fromY, toX, toY;
|
|
4675
4928
|
if (connectorLine) {
|
|
@@ -4682,7 +4935,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4682
4935
|
const mMatch = pathD.match(/M\s*([\d.e+-]+)\s+([\d.e+-]+)/);
|
|
4683
4936
|
fromX = mMatch ? Number(mMatch[1]) : 0;
|
|
4684
4937
|
fromY = mMatch ? Number(mMatch[2]) : 0;
|
|
4685
|
-
const arrowhead = annotationG.querySelector("polygon.
|
|
4938
|
+
const arrowhead = annotationG.querySelector("polygon.oc-annotation-connector");
|
|
4686
4939
|
const points = arrowhead?.getAttribute("points") ?? "";
|
|
4687
4940
|
const firstPoint = points.split(" ")[0] ?? "0,0";
|
|
4688
4941
|
const [px, py] = firstPoint.split(",");
|
|
@@ -4697,7 +4950,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4697
4950
|
for (const ep of endpoints) {
|
|
4698
4951
|
if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
|
|
4699
4952
|
const handleEl = document.createElementNS(SVG_NS2, "circle");
|
|
4700
|
-
handleEl.setAttribute("class", "
|
|
4953
|
+
handleEl.setAttribute("class", "oc-connector-handle");
|
|
4701
4954
|
handleEl.setAttribute("data-endpoint", ep.name);
|
|
4702
4955
|
handleEl.setAttribute("cx", String(ep.cx));
|
|
4703
4956
|
handleEl.setAttribute("cy", String(ep.cy));
|
|
@@ -4791,13 +5044,13 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4791
5044
|
function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4792
5045
|
const cleanups = [];
|
|
4793
5046
|
const selectors = [
|
|
4794
|
-
".
|
|
4795
|
-
".
|
|
5047
|
+
".oc-annotation-range .oc-annotation-label",
|
|
5048
|
+
".oc-annotation-refline .oc-annotation-label"
|
|
4796
5049
|
];
|
|
4797
5050
|
for (const selector of selectors) {
|
|
4798
5051
|
const labels = svg.querySelectorAll(selector);
|
|
4799
5052
|
for (const label of labels) {
|
|
4800
|
-
const annotationG = label.closest(".
|
|
5053
|
+
const annotationG = label.closest(".oc-annotation");
|
|
4801
5054
|
if (!annotationG) continue;
|
|
4802
5055
|
const indexStr = annotationG.getAttribute("data-annotation-index");
|
|
4803
5056
|
if (indexStr === null) continue;
|
|
@@ -4846,7 +5099,7 @@ function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4846
5099
|
};
|
|
4847
5100
|
}
|
|
4848
5101
|
function wireChromeDrag(svg, spec, onEdit, setDragging) {
|
|
4849
|
-
const chromeTexts = svg.querySelectorAll(".
|
|
5102
|
+
const chromeTexts = svg.querySelectorAll(".oc-chrome text[data-chrome-key]");
|
|
4850
5103
|
const cleanups = [];
|
|
4851
5104
|
const chromeConfig = "chrome" in spec ? spec.chrome : void 0;
|
|
4852
5105
|
for (const el of chromeTexts) {
|
|
@@ -4886,7 +5139,7 @@ function wireChromeDrag(svg, spec, onEdit, setDragging) {
|
|
|
4886
5139
|
};
|
|
4887
5140
|
}
|
|
4888
5141
|
function wireLegendDrag(svg, spec, onEdit, setDragging) {
|
|
4889
|
-
const legendG = svg.querySelector(".
|
|
5142
|
+
const legendG = svg.querySelector(".oc-legend");
|
|
4890
5143
|
if (!legendG) return () => {
|
|
4891
5144
|
};
|
|
4892
5145
|
const cleanups = [];
|
|
@@ -4916,7 +5169,7 @@ function wireLegendDrag(svg, spec, onEdit, setDragging) {
|
|
|
4916
5169
|
};
|
|
4917
5170
|
}
|
|
4918
5171
|
function wireSeriesLabelDrag(svg, spec, onEdit, setDragging) {
|
|
4919
|
-
const labels = svg.querySelectorAll(".
|
|
5172
|
+
const labels = svg.querySelectorAll(".oc-mark-label");
|
|
4920
5173
|
const cleanups = [];
|
|
4921
5174
|
const labelsConfig = "labels" in spec ? spec.labels : void 0;
|
|
4922
5175
|
for (const label of labels) {
|
|
@@ -4975,7 +5228,7 @@ function wireLegendInteraction(svg, _layout, onLegendToggle, onEdit) {
|
|
|
4975
5228
|
onLegendToggle?.(label, false);
|
|
4976
5229
|
onEdit?.({ type: "legend-toggle", series: label, hidden: true });
|
|
4977
5230
|
}
|
|
4978
|
-
const marks = svg.querySelectorAll(".
|
|
5231
|
+
const marks = svg.querySelectorAll(".oc-mark");
|
|
4979
5232
|
for (const mark of marks) {
|
|
4980
5233
|
const seriesName = mark.getAttribute("data-series");
|
|
4981
5234
|
if (!seriesName) continue;
|
|
@@ -5010,13 +5263,13 @@ function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, lay
|
|
|
5010
5263
|
let focusIndex = -1;
|
|
5011
5264
|
function highlightMark(index2) {
|
|
5012
5265
|
if (focusIndex >= 0 && focusIndex < markElements.length) {
|
|
5013
|
-
markElements[focusIndex].classList.remove("
|
|
5266
|
+
markElements[focusIndex].classList.remove("oc-mark-focused");
|
|
5014
5267
|
markElements[focusIndex].removeAttribute("aria-selected");
|
|
5015
5268
|
}
|
|
5016
5269
|
focusIndex = index2;
|
|
5017
5270
|
if (focusIndex >= 0 && focusIndex < markElements.length) {
|
|
5018
5271
|
const el = markElements[focusIndex];
|
|
5019
|
-
el.classList.add("
|
|
5272
|
+
el.classList.add("oc-mark-focused");
|
|
5020
5273
|
el.setAttribute("aria-selected", "true");
|
|
5021
5274
|
}
|
|
5022
5275
|
}
|
|
@@ -5083,7 +5336,7 @@ function createScreenReaderTable(layout, container) {
|
|
|
5083
5336
|
const data = layout.a11y.dataTableFallback;
|
|
5084
5337
|
if (!data || data.length === 0) return null;
|
|
5085
5338
|
const table = document.createElement("table");
|
|
5086
|
-
table.className = "
|
|
5339
|
+
table.className = "oc-sr-only";
|
|
5087
5340
|
table.style.position = "absolute";
|
|
5088
5341
|
table.style.width = "1px";
|
|
5089
5342
|
table.style.height = "1px";
|
|
@@ -5125,6 +5378,167 @@ function createScreenReaderTable(layout, container) {
|
|
|
5125
5378
|
container.appendChild(table);
|
|
5126
5379
|
return table;
|
|
5127
5380
|
}
|
|
5381
|
+
var EDITABLE_HOVER_CSS = `
|
|
5382
|
+
.oc-editable-hover {
|
|
5383
|
+
outline: 1.5px solid rgba(79, 70, 229, 0.35);
|
|
5384
|
+
outline-offset: 2px;
|
|
5385
|
+
border-radius: 2px;
|
|
5386
|
+
}
|
|
5387
|
+
`;
|
|
5388
|
+
function makeEditable(svg) {
|
|
5389
|
+
svg.setAttribute("tabindex", "0");
|
|
5390
|
+
svg.style.outline = "none";
|
|
5391
|
+
const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
5392
|
+
style.textContent = EDITABLE_HOVER_CSS;
|
|
5393
|
+
svg.insertBefore(style, svg.firstChild);
|
|
5394
|
+
}
|
|
5395
|
+
function hasEditingCallbacks(opts) {
|
|
5396
|
+
return !!(opts?.onEdit || opts?.onSelect || opts?.onDeselect || opts?.onTextEdit);
|
|
5397
|
+
}
|
|
5398
|
+
function findElementByRef(svg, ref) {
|
|
5399
|
+
switch (ref.type) {
|
|
5400
|
+
case "annotation": {
|
|
5401
|
+
if (ref.id) {
|
|
5402
|
+
const byId = svg.querySelector(`[data-annotation-id="${ref.id}"]`);
|
|
5403
|
+
if (byId) return byId;
|
|
5404
|
+
}
|
|
5405
|
+
return svg.querySelector(`[data-annotation-index="${ref.index}"]`);
|
|
5406
|
+
}
|
|
5407
|
+
case "chrome":
|
|
5408
|
+
return svg.querySelector(`[data-chrome-key="${ref.key}"]`);
|
|
5409
|
+
case "series-label":
|
|
5410
|
+
return svg.querySelector(`.oc-mark-label[data-series="${ref.series}"]`);
|
|
5411
|
+
case "legend":
|
|
5412
|
+
return svg.querySelector(".oc-legend");
|
|
5413
|
+
case "legend-entry":
|
|
5414
|
+
return svg.querySelector(`[data-legend-index="${ref.index}"]`);
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
function buildElementRef(element, _specAnnotations) {
|
|
5418
|
+
const annotationEl = element.closest("[data-annotation-index]");
|
|
5419
|
+
if (annotationEl) {
|
|
5420
|
+
const index2 = Number(annotationEl.getAttribute("data-annotation-index"));
|
|
5421
|
+
const id = annotationEl.getAttribute("data-annotation-id") ?? void 0;
|
|
5422
|
+
return elementRef.annotation(index2, id);
|
|
5423
|
+
}
|
|
5424
|
+
const chromeEl = element.closest("[data-chrome-key]");
|
|
5425
|
+
if (chromeEl) {
|
|
5426
|
+
const key = chromeEl.getAttribute("data-chrome-key");
|
|
5427
|
+
if (key) return elementRef.chrome(key);
|
|
5428
|
+
}
|
|
5429
|
+
const seriesLabelEl = element.closest(".oc-mark-label[data-series]");
|
|
5430
|
+
if (seriesLabelEl) {
|
|
5431
|
+
const series = seriesLabelEl.getAttribute("data-series");
|
|
5432
|
+
if (series) return elementRef.seriesLabel(series);
|
|
5433
|
+
}
|
|
5434
|
+
const legendEntryEl = element.closest("[data-legend-index]");
|
|
5435
|
+
if (legendEntryEl) {
|
|
5436
|
+
const index2 = Number(legendEntryEl.getAttribute("data-legend-index"));
|
|
5437
|
+
const series = legendEntryEl.getAttribute("data-legend-label") ?? "";
|
|
5438
|
+
return elementRef.legendEntry(series, index2);
|
|
5439
|
+
}
|
|
5440
|
+
const legendEl = element.closest(".oc-legend");
|
|
5441
|
+
if (legendEl) return elementRef.legend();
|
|
5442
|
+
return null;
|
|
5443
|
+
}
|
|
5444
|
+
function getEditableElements(spec, layout) {
|
|
5445
|
+
const refs = [];
|
|
5446
|
+
const chromeKeys = ["title", "subtitle", "source", "byline", "footer"];
|
|
5447
|
+
for (const key of chromeKeys) {
|
|
5448
|
+
if (layout.chrome[key]) {
|
|
5449
|
+
refs.push(elementRef.chrome(key));
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
const annotations = "annotations" in spec && Array.isArray(spec.annotations) ? spec.annotations : [];
|
|
5453
|
+
for (let i = 0; i < annotations.length; i++) {
|
|
5454
|
+
refs.push(elementRef.annotation(i, annotations[i].id));
|
|
5455
|
+
}
|
|
5456
|
+
const seriesLabels = [];
|
|
5457
|
+
for (const mark of layout.marks) {
|
|
5458
|
+
if (mark.type === "line" && mark.label?.visible && mark.seriesKey) {
|
|
5459
|
+
seriesLabels.push(mark.seriesKey);
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
seriesLabels.sort();
|
|
5463
|
+
for (const series of seriesLabels) {
|
|
5464
|
+
refs.push(elementRef.seriesLabel(series));
|
|
5465
|
+
}
|
|
5466
|
+
if (layout.legend.entries.length > 0) {
|
|
5467
|
+
refs.push(elementRef.legend());
|
|
5468
|
+
}
|
|
5469
|
+
return refs;
|
|
5470
|
+
}
|
|
5471
|
+
function isTextEditable(ref, specAnnotations) {
|
|
5472
|
+
if (ref.type === "chrome") return true;
|
|
5473
|
+
if (ref.type === "annotation") {
|
|
5474
|
+
const annotation = specAnnotations[ref.index];
|
|
5475
|
+
return annotation?.type === "text";
|
|
5476
|
+
}
|
|
5477
|
+
return false;
|
|
5478
|
+
}
|
|
5479
|
+
function getElementText(ref, spec) {
|
|
5480
|
+
if (ref.type === "chrome") {
|
|
5481
|
+
const chromeConfig = "chrome" in spec ? spec.chrome : void 0;
|
|
5482
|
+
if (!chromeConfig) return null;
|
|
5483
|
+
const entry = chromeConfig[ref.key];
|
|
5484
|
+
if (typeof entry === "string") return entry;
|
|
5485
|
+
if (typeof entry === "object" && entry !== null && "text" in entry) {
|
|
5486
|
+
return entry.text;
|
|
5487
|
+
}
|
|
5488
|
+
return null;
|
|
5489
|
+
}
|
|
5490
|
+
if (ref.type === "annotation") {
|
|
5491
|
+
const annotations = "annotations" in spec && Array.isArray(spec.annotations) ? spec.annotations : [];
|
|
5492
|
+
const annotation = annotations[ref.index];
|
|
5493
|
+
if (annotation?.type === "text") return annotation.text ?? null;
|
|
5494
|
+
if (annotation?.label) return annotation.label;
|
|
5495
|
+
return null;
|
|
5496
|
+
}
|
|
5497
|
+
return null;
|
|
5498
|
+
}
|
|
5499
|
+
function refsEqual(a2, b) {
|
|
5500
|
+
if (a2 === null || b === null) return a2 === b;
|
|
5501
|
+
if (a2.type !== b.type) return false;
|
|
5502
|
+
switch (a2.type) {
|
|
5503
|
+
case "annotation": {
|
|
5504
|
+
const bAnno = b;
|
|
5505
|
+
if (a2.id && bAnno.id) return a2.id === bAnno.id;
|
|
5506
|
+
return a2.index === bAnno.index;
|
|
5507
|
+
}
|
|
5508
|
+
case "chrome":
|
|
5509
|
+
return a2.key === b.key;
|
|
5510
|
+
case "series-label":
|
|
5511
|
+
return a2.series === b.series;
|
|
5512
|
+
case "legend":
|
|
5513
|
+
return true;
|
|
5514
|
+
case "legend-entry": {
|
|
5515
|
+
const bEntry = b;
|
|
5516
|
+
return a2.index === bEntry.index && a2.series === bEntry.series;
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
function renderSelectionOverlay(svg, ref, layout) {
|
|
5521
|
+
const target = findElementByRef(svg, ref);
|
|
5522
|
+
if (!target) return null;
|
|
5523
|
+
const bbox = target.getBBox();
|
|
5524
|
+
const padding = 4;
|
|
5525
|
+
const accentColor = layout.theme.colors.categorical?.[0] ?? "#4f46e5";
|
|
5526
|
+
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
5527
|
+
g.setAttribute("class", "oc-selection-overlay");
|
|
5528
|
+
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
5529
|
+
rect.setAttribute("x", String(bbox.x - padding));
|
|
5530
|
+
rect.setAttribute("y", String(bbox.y - padding));
|
|
5531
|
+
rect.setAttribute("width", String(bbox.width + padding * 2));
|
|
5532
|
+
rect.setAttribute("height", String(bbox.height + padding * 2));
|
|
5533
|
+
rect.setAttribute("rx", "3");
|
|
5534
|
+
rect.setAttribute("fill", "transparent");
|
|
5535
|
+
rect.setAttribute("stroke", accentColor);
|
|
5536
|
+
rect.setAttribute("stroke-width", "1.5");
|
|
5537
|
+
rect.setAttribute("pointer-events", "none");
|
|
5538
|
+
g.appendChild(rect);
|
|
5539
|
+
svg.appendChild(g);
|
|
5540
|
+
return g;
|
|
5541
|
+
}
|
|
5128
5542
|
function createChart(container, spec, options) {
|
|
5129
5543
|
let currentSpec = spec;
|
|
5130
5544
|
let currentLayout;
|
|
@@ -5138,11 +5552,19 @@ function createChart(container, spec, options) {
|
|
|
5138
5552
|
let cleanupChartEvents = null;
|
|
5139
5553
|
let cleanupAnnotationDrag = null;
|
|
5140
5554
|
let cleanupEditDrags = null;
|
|
5555
|
+
let cleanupSelection = null;
|
|
5556
|
+
let cleanupKeyboardEdit = null;
|
|
5141
5557
|
let srTable = null;
|
|
5142
5558
|
let destroyed = false;
|
|
5143
5559
|
let isDragging = false;
|
|
5144
5560
|
let pendingRender = false;
|
|
5145
5561
|
let resizeTimer = null;
|
|
5562
|
+
let isFirstRender = true;
|
|
5563
|
+
let cleanupAnimations = null;
|
|
5564
|
+
let selectedElement = options?.selectedElement ?? null;
|
|
5565
|
+
let overlayElement = null;
|
|
5566
|
+
let isTextEditingActive = false;
|
|
5567
|
+
let textEditCleanup = null;
|
|
5146
5568
|
const measureText = createMeasureText();
|
|
5147
5569
|
function compile() {
|
|
5148
5570
|
const { width, height } = getContainerDimensions();
|
|
@@ -5166,11 +5588,208 @@ function createChart(container, spec, options) {
|
|
|
5166
5588
|
height: Math.max(rect.height || 400, 100)
|
|
5167
5589
|
};
|
|
5168
5590
|
}
|
|
5591
|
+
function getSpecAnnotations() {
|
|
5592
|
+
return "annotations" in currentSpec && Array.isArray(currentSpec.annotations) ? currentSpec.annotations : [];
|
|
5593
|
+
}
|
|
5594
|
+
function selectElement(ref) {
|
|
5595
|
+
if (!svgElement) return;
|
|
5596
|
+
const target = findElementByRef(svgElement, ref);
|
|
5597
|
+
if (!target) return;
|
|
5598
|
+
if (selectedElement && !refsEqual(selectedElement, ref)) {
|
|
5599
|
+
deselectElement();
|
|
5600
|
+
}
|
|
5601
|
+
selectedElement = ref;
|
|
5602
|
+
overlayElement = renderSelectionOverlay(svgElement, ref, currentLayout);
|
|
5603
|
+
options?.onSelect?.(ref);
|
|
5604
|
+
svgElement.focus();
|
|
5605
|
+
}
|
|
5606
|
+
function deselectElement() {
|
|
5607
|
+
if (!selectedElement) return;
|
|
5608
|
+
if (isTextEditingActive && textEditCleanup) {
|
|
5609
|
+
textEditCleanup();
|
|
5610
|
+
textEditCleanup = null;
|
|
5611
|
+
isTextEditingActive = false;
|
|
5612
|
+
}
|
|
5613
|
+
const prev = selectedElement;
|
|
5614
|
+
selectedElement = null;
|
|
5615
|
+
if (overlayElement?.parentNode) {
|
|
5616
|
+
overlayElement.parentNode.removeChild(overlayElement);
|
|
5617
|
+
}
|
|
5618
|
+
overlayElement = null;
|
|
5619
|
+
options?.onDeselect?.(prev);
|
|
5620
|
+
}
|
|
5621
|
+
function enterTextEditing() {
|
|
5622
|
+
if (!svgElement || !selectedElement || isTextEditingActive) return;
|
|
5623
|
+
const specAnnotations = getSpecAnnotations();
|
|
5624
|
+
if (!isTextEditable(selectedElement, specAnnotations)) return;
|
|
5625
|
+
const currentText = getElementText(selectedElement, currentSpec);
|
|
5626
|
+
if (currentText === null) return;
|
|
5627
|
+
const target = findElementByRef(svgElement, selectedElement);
|
|
5628
|
+
if (!target) return;
|
|
5629
|
+
const textEl = target.tagName === "text" ? target : target.querySelector("text");
|
|
5630
|
+
if (!textEl) return;
|
|
5631
|
+
isTextEditingActive = true;
|
|
5632
|
+
const editRef = selectedElement;
|
|
5633
|
+
const overlay = createTextEditOverlay({
|
|
5634
|
+
container,
|
|
5635
|
+
svg: svgElement,
|
|
5636
|
+
targetElement: textEl,
|
|
5637
|
+
currentText,
|
|
5638
|
+
onCommit: (newText) => {
|
|
5639
|
+
isTextEditingActive = false;
|
|
5640
|
+
textEditCleanup = null;
|
|
5641
|
+
if (newText !== currentText) {
|
|
5642
|
+
options?.onTextEdit?.(editRef, currentText, newText);
|
|
5643
|
+
options?.onEdit?.({
|
|
5644
|
+
type: "text-edit",
|
|
5645
|
+
element: editRef,
|
|
5646
|
+
oldText: currentText,
|
|
5647
|
+
newText
|
|
5648
|
+
});
|
|
5649
|
+
}
|
|
5650
|
+
},
|
|
5651
|
+
onCancel: () => {
|
|
5652
|
+
isTextEditingActive = false;
|
|
5653
|
+
textEditCleanup = null;
|
|
5654
|
+
}
|
|
5655
|
+
});
|
|
5656
|
+
textEditCleanup = overlay.destroy;
|
|
5657
|
+
}
|
|
5658
|
+
function wireSelectionEvents() {
|
|
5659
|
+
if (!svgElement) return () => {
|
|
5660
|
+
};
|
|
5661
|
+
const svg = svgElement;
|
|
5662
|
+
const cleanups = [];
|
|
5663
|
+
const handleClick = (e) => {
|
|
5664
|
+
const mouseEvent = e;
|
|
5665
|
+
const target = mouseEvent.target;
|
|
5666
|
+
if (isTextEditingActive) return;
|
|
5667
|
+
const specAnnotations = getSpecAnnotations();
|
|
5668
|
+
const ref = buildElementRef(target, specAnnotations);
|
|
5669
|
+
if (ref) {
|
|
5670
|
+
selectElement(ref);
|
|
5671
|
+
} else {
|
|
5672
|
+
deselectElement();
|
|
5673
|
+
}
|
|
5674
|
+
};
|
|
5675
|
+
svg.addEventListener("click", handleClick);
|
|
5676
|
+
cleanups.push(() => svg.removeEventListener("click", handleClick));
|
|
5677
|
+
const handleMouseEnter = (e) => {
|
|
5678
|
+
const target = e.target.closest(
|
|
5679
|
+
"[data-annotation-index], [data-chrome-key], .oc-mark-label[data-series], .oc-legend, [data-legend-index]"
|
|
5680
|
+
);
|
|
5681
|
+
if (target) {
|
|
5682
|
+
target.classList.add("oc-editable-hover");
|
|
5683
|
+
}
|
|
5684
|
+
};
|
|
5685
|
+
const handleMouseLeave = (e) => {
|
|
5686
|
+
const target = e.target.closest(".oc-editable-hover");
|
|
5687
|
+
if (target) {
|
|
5688
|
+
target.classList.remove("oc-editable-hover");
|
|
5689
|
+
}
|
|
5690
|
+
};
|
|
5691
|
+
svg.addEventListener("mouseenter", handleMouseEnter, true);
|
|
5692
|
+
svg.addEventListener("mouseleave", handleMouseLeave, true);
|
|
5693
|
+
cleanups.push(() => {
|
|
5694
|
+
svg.removeEventListener("mouseenter", handleMouseEnter, true);
|
|
5695
|
+
svg.removeEventListener("mouseleave", handleMouseLeave, true);
|
|
5696
|
+
});
|
|
5697
|
+
const handleDblClick = (e) => {
|
|
5698
|
+
const mouseEvent = e;
|
|
5699
|
+
const target = mouseEvent.target;
|
|
5700
|
+
const specAnnotations = getSpecAnnotations();
|
|
5701
|
+
const ref = buildElementRef(target, specAnnotations);
|
|
5702
|
+
if (ref && isTextEditable(ref, specAnnotations)) {
|
|
5703
|
+
if (!refsEqual(selectedElement, ref)) {
|
|
5704
|
+
selectElement(ref);
|
|
5705
|
+
}
|
|
5706
|
+
enterTextEditing();
|
|
5707
|
+
}
|
|
5708
|
+
};
|
|
5709
|
+
svg.addEventListener("dblclick", handleDblClick);
|
|
5710
|
+
cleanups.push(() => svg.removeEventListener("dblclick", handleDblClick));
|
|
5711
|
+
return () => {
|
|
5712
|
+
for (const cleanup of cleanups) {
|
|
5713
|
+
cleanup();
|
|
5714
|
+
}
|
|
5715
|
+
};
|
|
5716
|
+
}
|
|
5717
|
+
function wireKeyboardEditEvents() {
|
|
5718
|
+
if (!svgElement) return () => {
|
|
5719
|
+
};
|
|
5720
|
+
const svg = svgElement;
|
|
5721
|
+
const handleKeyDown = (e) => {
|
|
5722
|
+
const specAnnotations = getSpecAnnotations();
|
|
5723
|
+
switch (e.key) {
|
|
5724
|
+
case "Delete":
|
|
5725
|
+
case "Backspace": {
|
|
5726
|
+
if (selectedElement && !isTextEditingActive) {
|
|
5727
|
+
e.preventDefault();
|
|
5728
|
+
options?.onEdit?.({ type: "delete", element: selectedElement });
|
|
5729
|
+
}
|
|
5730
|
+
break;
|
|
5731
|
+
}
|
|
5732
|
+
case "Escape": {
|
|
5733
|
+
e.preventDefault();
|
|
5734
|
+
if (isTextEditingActive && textEditCleanup) {
|
|
5735
|
+
textEditCleanup();
|
|
5736
|
+
textEditCleanup = null;
|
|
5737
|
+
isTextEditingActive = false;
|
|
5738
|
+
} else if (selectedElement) {
|
|
5739
|
+
deselectElement();
|
|
5740
|
+
}
|
|
5741
|
+
break;
|
|
5742
|
+
}
|
|
5743
|
+
case "ArrowDown":
|
|
5744
|
+
case "ArrowRight": {
|
|
5745
|
+
if (!isTextEditingActive && selectedElement) {
|
|
5746
|
+
e.preventDefault();
|
|
5747
|
+
const editables = getEditableElements(currentSpec, currentLayout);
|
|
5748
|
+
if (editables.length === 0) break;
|
|
5749
|
+
const currentIndex = editables.findIndex((r) => refsEqual(r, selectedElement));
|
|
5750
|
+
const nextIndex = currentIndex >= editables.length - 1 ? 0 : currentIndex + 1;
|
|
5751
|
+
selectElement(editables[nextIndex]);
|
|
5752
|
+
}
|
|
5753
|
+
break;
|
|
5754
|
+
}
|
|
5755
|
+
case "ArrowUp":
|
|
5756
|
+
case "ArrowLeft": {
|
|
5757
|
+
if (!isTextEditingActive && selectedElement) {
|
|
5758
|
+
e.preventDefault();
|
|
5759
|
+
const editables = getEditableElements(currentSpec, currentLayout);
|
|
5760
|
+
if (editables.length === 0) break;
|
|
5761
|
+
const currentIndex = editables.findIndex((r) => refsEqual(r, selectedElement));
|
|
5762
|
+
const nextIndex = currentIndex <= 0 ? editables.length - 1 : currentIndex - 1;
|
|
5763
|
+
selectElement(editables[nextIndex]);
|
|
5764
|
+
}
|
|
5765
|
+
break;
|
|
5766
|
+
}
|
|
5767
|
+
case "Enter": {
|
|
5768
|
+
if (selectedElement && !isTextEditingActive) {
|
|
5769
|
+
if (isTextEditable(selectedElement, specAnnotations)) {
|
|
5770
|
+
e.preventDefault();
|
|
5771
|
+
enterTextEditing();
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
break;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
};
|
|
5778
|
+
svg.addEventListener("keydown", handleKeyDown);
|
|
5779
|
+
return () => {
|
|
5780
|
+
svg.removeEventListener("keydown", handleKeyDown);
|
|
5781
|
+
};
|
|
5782
|
+
}
|
|
5169
5783
|
function render() {
|
|
5170
5784
|
if (isDragging) {
|
|
5171
5785
|
pendingRender = true;
|
|
5172
5786
|
return;
|
|
5173
5787
|
}
|
|
5788
|
+
if (cleanupAnimations) {
|
|
5789
|
+
cleanupAnimations();
|
|
5790
|
+
cleanupAnimations = null;
|
|
5791
|
+
}
|
|
5792
|
+
cancelAnimations(svgElement);
|
|
5174
5793
|
if (cleanupTooltipEvents) {
|
|
5175
5794
|
cleanupTooltipEvents();
|
|
5176
5795
|
cleanupTooltipEvents = null;
|
|
@@ -5199,6 +5818,20 @@ function createChart(container, spec, options) {
|
|
|
5199
5818
|
cleanupEditDrags();
|
|
5200
5819
|
cleanupEditDrags = null;
|
|
5201
5820
|
}
|
|
5821
|
+
if (cleanupSelection) {
|
|
5822
|
+
cleanupSelection();
|
|
5823
|
+
cleanupSelection = null;
|
|
5824
|
+
}
|
|
5825
|
+
if (cleanupKeyboardEdit) {
|
|
5826
|
+
cleanupKeyboardEdit();
|
|
5827
|
+
cleanupKeyboardEdit = null;
|
|
5828
|
+
}
|
|
5829
|
+
if (textEditCleanup) {
|
|
5830
|
+
textEditCleanup();
|
|
5831
|
+
textEditCleanup = null;
|
|
5832
|
+
isTextEditingActive = false;
|
|
5833
|
+
}
|
|
5834
|
+
overlayElement = null;
|
|
5202
5835
|
if (svgElement?.parentNode) {
|
|
5203
5836
|
svgElement.parentNode.removeChild(svgElement);
|
|
5204
5837
|
}
|
|
@@ -5210,7 +5843,8 @@ function createChart(container, spec, options) {
|
|
|
5210
5843
|
srTable = null;
|
|
5211
5844
|
}
|
|
5212
5845
|
currentLayout = compile();
|
|
5213
|
-
|
|
5846
|
+
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enabled;
|
|
5847
|
+
svgElement = renderChartSVG(currentLayout, container, { animate: shouldAnimate });
|
|
5214
5848
|
tooltipManager = createTooltipManager(container);
|
|
5215
5849
|
cleanupTooltipEvents = wireTooltipEvents(
|
|
5216
5850
|
svgElement,
|
|
@@ -5270,22 +5904,46 @@ function createChart(container, spec, options) {
|
|
|
5270
5904
|
}
|
|
5271
5905
|
};
|
|
5272
5906
|
}
|
|
5907
|
+
if (hasEditingCallbacks(options)) {
|
|
5908
|
+
makeEditable(svgElement);
|
|
5909
|
+
cleanupSelection = wireSelectionEvents();
|
|
5910
|
+
cleanupKeyboardEdit = wireKeyboardEditEvents();
|
|
5911
|
+
if (selectedElement) {
|
|
5912
|
+
const target = findElementByRef(svgElement, selectedElement);
|
|
5913
|
+
if (target) {
|
|
5914
|
+
overlayElement = renderSelectionOverlay(svgElement, selectedElement, currentLayout);
|
|
5915
|
+
} else {
|
|
5916
|
+
selectedElement = null;
|
|
5917
|
+
overlayElement = null;
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5273
5921
|
srTable = createScreenReaderTable(currentLayout, container);
|
|
5274
|
-
container.classList.add("
|
|
5922
|
+
container.classList.add("oc-root");
|
|
5275
5923
|
const isDark = resolveDarkMode2(options?.darkMode);
|
|
5276
5924
|
if (isDark) {
|
|
5277
|
-
container.classList.add("
|
|
5925
|
+
container.classList.add("oc-dark");
|
|
5278
5926
|
} else {
|
|
5279
|
-
container.classList.remove("
|
|
5927
|
+
container.classList.remove("oc-dark");
|
|
5928
|
+
}
|
|
5929
|
+
if (shouldAnimate && svgElement) {
|
|
5930
|
+
cleanupAnimations = setupAnimationCleanup(svgElement);
|
|
5931
|
+
}
|
|
5932
|
+
if (isFirstRender) {
|
|
5933
|
+
isFirstRender = false;
|
|
5280
5934
|
}
|
|
5281
5935
|
}
|
|
5282
|
-
function update(newSpec) {
|
|
5936
|
+
function update(newSpec, updateOpts) {
|
|
5283
5937
|
if (destroyed) return;
|
|
5284
5938
|
currentSpec = newSpec;
|
|
5939
|
+
if (updateOpts && "selectedElement" in updateOpts) {
|
|
5940
|
+
selectedElement = updateOpts.selectedElement ?? null;
|
|
5941
|
+
}
|
|
5285
5942
|
render();
|
|
5286
5943
|
}
|
|
5287
5944
|
function resize() {
|
|
5288
5945
|
if (destroyed) return;
|
|
5946
|
+
if (cleanupAnimations) return;
|
|
5289
5947
|
render();
|
|
5290
5948
|
}
|
|
5291
5949
|
function doExport(format, exportOptions) {
|
|
@@ -5312,6 +5970,11 @@ function createChart(container, spec, options) {
|
|
|
5312
5970
|
function destroy() {
|
|
5313
5971
|
if (destroyed) return;
|
|
5314
5972
|
destroyed = true;
|
|
5973
|
+
if (cleanupAnimations) {
|
|
5974
|
+
cleanupAnimations();
|
|
5975
|
+
cleanupAnimations = null;
|
|
5976
|
+
}
|
|
5977
|
+
cancelAnimations(svgElement);
|
|
5315
5978
|
if (resizeTimer !== null) {
|
|
5316
5979
|
clearTimeout(resizeTimer);
|
|
5317
5980
|
resizeTimer = null;
|
|
@@ -5344,6 +6007,21 @@ function createChart(container, spec, options) {
|
|
|
5344
6007
|
cleanupEditDrags();
|
|
5345
6008
|
cleanupEditDrags = null;
|
|
5346
6009
|
}
|
|
6010
|
+
if (cleanupSelection) {
|
|
6011
|
+
cleanupSelection();
|
|
6012
|
+
cleanupSelection = null;
|
|
6013
|
+
}
|
|
6014
|
+
if (cleanupKeyboardEdit) {
|
|
6015
|
+
cleanupKeyboardEdit();
|
|
6016
|
+
cleanupKeyboardEdit = null;
|
|
6017
|
+
}
|
|
6018
|
+
if (textEditCleanup) {
|
|
6019
|
+
textEditCleanup();
|
|
6020
|
+
textEditCleanup = null;
|
|
6021
|
+
isTextEditingActive = false;
|
|
6022
|
+
}
|
|
6023
|
+
selectedElement = null;
|
|
6024
|
+
overlayElement = null;
|
|
5347
6025
|
if (disconnectResize) {
|
|
5348
6026
|
disconnectResize();
|
|
5349
6027
|
disconnectResize = null;
|
|
@@ -5360,8 +6038,8 @@ function createChart(container, spec, options) {
|
|
|
5360
6038
|
srTable.parentNode.removeChild(srTable);
|
|
5361
6039
|
srTable = null;
|
|
5362
6040
|
}
|
|
5363
|
-
container.classList.remove("
|
|
5364
|
-
container.classList.remove("
|
|
6041
|
+
container.classList.remove("oc-dark");
|
|
6042
|
+
container.classList.remove("oc-root");
|
|
5365
6043
|
}
|
|
5366
6044
|
render();
|
|
5367
6045
|
if (options?.responsive !== false) {
|
|
@@ -5380,6 +6058,20 @@ function createChart(container, spec, options) {
|
|
|
5380
6058
|
destroy,
|
|
5381
6059
|
get layout() {
|
|
5382
6060
|
return currentLayout;
|
|
6061
|
+
},
|
|
6062
|
+
getSelectedElement() {
|
|
6063
|
+
return selectedElement;
|
|
6064
|
+
},
|
|
6065
|
+
select(ref) {
|
|
6066
|
+
if (destroyed) return;
|
|
6067
|
+
selectElement(ref);
|
|
6068
|
+
},
|
|
6069
|
+
deselect() {
|
|
6070
|
+
if (destroyed) return;
|
|
6071
|
+
deselectElement();
|
|
6072
|
+
},
|
|
6073
|
+
get isEditing() {
|
|
6074
|
+
return isTextEditingActive;
|
|
5383
6075
|
}
|
|
5384
6076
|
};
|
|
5385
6077
|
}
|
|
@@ -5484,28 +6176,30 @@ function renderTextCell(cell) {
|
|
|
5484
6176
|
}
|
|
5485
6177
|
function renderHeatmapCell(cell) {
|
|
5486
6178
|
const td = document.createElement("td");
|
|
6179
|
+
td.className = "oc-table-heatmap";
|
|
5487
6180
|
td.textContent = cell.formattedValue;
|
|
5488
6181
|
applyCellStyle(td, cell);
|
|
5489
6182
|
return td;
|
|
5490
6183
|
}
|
|
5491
6184
|
function renderCategoryCell(cell) {
|
|
5492
6185
|
const td = document.createElement("td");
|
|
6186
|
+
td.className = "oc-table-category";
|
|
5493
6187
|
td.textContent = cell.formattedValue;
|
|
5494
6188
|
applyCellStyle(td, cell);
|
|
5495
6189
|
return td;
|
|
5496
6190
|
}
|
|
5497
6191
|
function renderBarCell(cell) {
|
|
5498
6192
|
const td = document.createElement("td");
|
|
5499
|
-
td.className = "
|
|
6193
|
+
td.className = "oc-table-bar";
|
|
5500
6194
|
applyCellStyle(td, cell);
|
|
5501
6195
|
const fill = document.createElement("div");
|
|
5502
|
-
fill.className = "
|
|
6196
|
+
fill.className = "oc-table-bar-fill";
|
|
5503
6197
|
fill.style.width = `${Math.round(cell.barWidth * 100)}%`;
|
|
5504
6198
|
fill.style.left = `${Math.round(cell.barOffset * 100)}%`;
|
|
5505
6199
|
fill.style.background = cell.barColor;
|
|
5506
6200
|
td.appendChild(fill);
|
|
5507
6201
|
const valueSpan = document.createElement("span");
|
|
5508
|
-
valueSpan.className = "
|
|
6202
|
+
valueSpan.className = "oc-table-bar-value";
|
|
5509
6203
|
valueSpan.textContent = cell.formattedValue;
|
|
5510
6204
|
td.appendChild(valueSpan);
|
|
5511
6205
|
return td;
|
|
@@ -5532,7 +6226,7 @@ function renderSparklineCell(cell) {
|
|
|
5532
6226
|
td.setAttribute("aria-label", trendDescription);
|
|
5533
6227
|
}
|
|
5534
6228
|
const wrapper = document.createElement("span");
|
|
5535
|
-
wrapper.className = "
|
|
6229
|
+
wrapper.className = "oc-table-sparkline";
|
|
5536
6230
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
5537
6231
|
if (sparklineData.type === "line") {
|
|
5538
6232
|
const svgH = 28;
|
|
@@ -5564,19 +6258,19 @@ function renderSparklineCell(cell) {
|
|
|
5564
6258
|
const lastY = yPositions[yPositions.length - 1];
|
|
5565
6259
|
const dotSize = 5;
|
|
5566
6260
|
const startDot = document.createElement("span");
|
|
5567
|
-
startDot.className = "
|
|
6261
|
+
startDot.className = "oc-table-sparkline-dot";
|
|
5568
6262
|
startDot.style.left = "0";
|
|
5569
6263
|
startDot.style.top = `${firstY - dotSize / 2}px`;
|
|
5570
6264
|
startDot.style.background = sparklineData.color;
|
|
5571
6265
|
wrapper.appendChild(startDot);
|
|
5572
6266
|
const endDot = document.createElement("span");
|
|
5573
|
-
endDot.className = "
|
|
6267
|
+
endDot.className = "oc-table-sparkline-dot";
|
|
5574
6268
|
endDot.style.right = "0";
|
|
5575
6269
|
endDot.style.top = `${lastY - dotSize / 2}px`;
|
|
5576
6270
|
endDot.style.background = sparklineData.color;
|
|
5577
6271
|
wrapper.appendChild(endDot);
|
|
5578
6272
|
const labelsRow = document.createElement("span");
|
|
5579
|
-
labelsRow.className = "
|
|
6273
|
+
labelsRow.className = "oc-table-sparkline-labels";
|
|
5580
6274
|
labelsRow.style.color = sparklineData.color;
|
|
5581
6275
|
const startLabel = document.createElement("span");
|
|
5582
6276
|
startLabel.textContent = formatSparklineValue(sparklineData.startValue);
|
|
@@ -5653,7 +6347,7 @@ function renderImageCell(cell) {
|
|
|
5653
6347
|
const td = document.createElement("td");
|
|
5654
6348
|
applyCellStyle(td, cell);
|
|
5655
6349
|
const wrapper = document.createElement("span");
|
|
5656
|
-
wrapper.className = `
|
|
6350
|
+
wrapper.className = `oc-table-image${cell.rounded ? " oc-table-image-rounded" : ""}`;
|
|
5657
6351
|
const img = document.createElement("img");
|
|
5658
6352
|
img.src = cell.src;
|
|
5659
6353
|
img.alt = cell.formattedValue || "";
|
|
@@ -5668,7 +6362,7 @@ function renderFlagCell(cell) {
|
|
|
5668
6362
|
const td = document.createElement("td");
|
|
5669
6363
|
applyCellStyle(td, cell);
|
|
5670
6364
|
const span = document.createElement("span");
|
|
5671
|
-
span.className = "
|
|
6365
|
+
span.className = "oc-table-flag";
|
|
5672
6366
|
span.setAttribute("role", "img");
|
|
5673
6367
|
if (cell.countryCode && cell.countryCode.length === 2) {
|
|
5674
6368
|
const countryName = getCountryName(cell.countryCode);
|
|
@@ -5733,9 +6427,9 @@ function attachKeyboardNav(options) {
|
|
|
5733
6427
|
return getCellsInRow(rows[0]).length;
|
|
5734
6428
|
}
|
|
5735
6429
|
function clearFocusHighlight() {
|
|
5736
|
-
const prev = wrapper.querySelector(".
|
|
6430
|
+
const prev = wrapper.querySelector(".oc-table-cell-focus");
|
|
5737
6431
|
if (prev) {
|
|
5738
|
-
prev.classList.remove("
|
|
6432
|
+
prev.classList.remove("oc-table-cell-focus");
|
|
5739
6433
|
prev.removeAttribute("id");
|
|
5740
6434
|
}
|
|
5741
6435
|
}
|
|
@@ -5752,9 +6446,9 @@ function attachKeyboardNav(options) {
|
|
|
5752
6446
|
const cells = getCellsInRow(tr);
|
|
5753
6447
|
const cell = cells[col];
|
|
5754
6448
|
if (!cell) return;
|
|
5755
|
-
const cellId = `
|
|
6449
|
+
const cellId = `oc-cell-${row}-${col}`;
|
|
5756
6450
|
cell.id = cellId;
|
|
5757
|
-
cell.classList.add("
|
|
6451
|
+
cell.classList.add("oc-table-cell-focus");
|
|
5758
6452
|
cell.setAttribute("data-row", String(row));
|
|
5759
6453
|
cell.setAttribute("data-col", String(col));
|
|
5760
6454
|
if (tbody) {
|
|
@@ -5855,7 +6549,7 @@ function attachKeyboardNav(options) {
|
|
|
5855
6549
|
}
|
|
5856
6550
|
}
|
|
5857
6551
|
}
|
|
5858
|
-
const searchInput = wrapper.querySelector(".
|
|
6552
|
+
const searchInput = wrapper.querySelector(".oc-table-search input");
|
|
5859
6553
|
function handleSearchKeydown(e) {
|
|
5860
6554
|
if (e.key === "Escape") {
|
|
5861
6555
|
e.preventDefault();
|
|
@@ -5895,22 +6589,27 @@ import { compileTable } from "@opendata-ai/openchart-engine";
|
|
|
5895
6589
|
|
|
5896
6590
|
// src/table-renderer.ts
|
|
5897
6591
|
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE3 } from "@opendata-ai/openchart-core";
|
|
6592
|
+
import { clampStaggerDelay as clampStaggerDelay2 } from "@opendata-ai/openchart-engine";
|
|
6593
|
+
var EASE_VAR_MAP2 = {
|
|
6594
|
+
smooth: "var(--oc-ease-smooth)",
|
|
6595
|
+
snappy: "var(--oc-ease-snappy)"
|
|
6596
|
+
};
|
|
5898
6597
|
var BRAND_URL2 = "https://tryopendata.ai";
|
|
5899
6598
|
function renderChromeBlock(layout, position) {
|
|
5900
6599
|
const chrome = layout.chrome;
|
|
5901
6600
|
if (position === "header") {
|
|
5902
6601
|
if (!chrome.title && !chrome.subtitle) return null;
|
|
5903
6602
|
const div2 = document.createElement("div");
|
|
5904
|
-
div2.className = "
|
|
6603
|
+
div2.className = "oc-chrome";
|
|
5905
6604
|
if (chrome.title) {
|
|
5906
6605
|
const h = document.createElement("div");
|
|
5907
|
-
h.className = "
|
|
6606
|
+
h.className = "oc-table-title";
|
|
5908
6607
|
h.textContent = chrome.title.text;
|
|
5909
6608
|
div2.appendChild(h);
|
|
5910
6609
|
}
|
|
5911
6610
|
if (chrome.subtitle) {
|
|
5912
6611
|
const sub = document.createElement("div");
|
|
5913
|
-
sub.className = "
|
|
6612
|
+
sub.className = "oc-table-subtitle";
|
|
5914
6613
|
sub.textContent = chrome.subtitle.text;
|
|
5915
6614
|
div2.appendChild(sub);
|
|
5916
6615
|
}
|
|
@@ -5918,16 +6617,16 @@ function renderChromeBlock(layout, position) {
|
|
|
5918
6617
|
}
|
|
5919
6618
|
if (!chrome.source && !chrome.footer) return null;
|
|
5920
6619
|
const div = document.createElement("div");
|
|
5921
|
-
div.className = "
|
|
6620
|
+
div.className = "oc-chrome oc-chrome-footer";
|
|
5922
6621
|
if (chrome.source) {
|
|
5923
6622
|
const src = document.createElement("div");
|
|
5924
|
-
src.className = "
|
|
6623
|
+
src.className = "oc-table-source";
|
|
5925
6624
|
src.textContent = chrome.source.text;
|
|
5926
6625
|
div.appendChild(src);
|
|
5927
6626
|
}
|
|
5928
6627
|
if (chrome.footer) {
|
|
5929
6628
|
const foot = document.createElement("div");
|
|
5930
|
-
foot.className = "
|
|
6629
|
+
foot.className = "oc-table-footer-text";
|
|
5931
6630
|
foot.textContent = chrome.footer.text;
|
|
5932
6631
|
div.appendChild(foot);
|
|
5933
6632
|
}
|
|
@@ -5953,7 +6652,7 @@ function renderThead(columns, sort) {
|
|
|
5953
6652
|
th.appendChild(labelSpan);
|
|
5954
6653
|
if (col.sortable) {
|
|
5955
6654
|
const btn = document.createElement("button");
|
|
5956
|
-
btn.className = "
|
|
6655
|
+
btn.className = "oc-table-sort-btn";
|
|
5957
6656
|
btn.setAttribute("aria-label", `Sort by ${col.label}`);
|
|
5958
6657
|
btn.setAttribute("data-sort-column", col.key);
|
|
5959
6658
|
btn.type = "button";
|
|
@@ -5971,6 +6670,7 @@ function renderTbody(rows, columns) {
|
|
|
5971
6670
|
const tr = document.createElement("tr");
|
|
5972
6671
|
tr.setAttribute("role", "row");
|
|
5973
6672
|
tr.setAttribute("data-row-id", row.id);
|
|
6673
|
+
tr.style.setProperty("--oc-row-index", String(r));
|
|
5974
6674
|
for (let c2 = 0; c2 < columns.length; c2++) {
|
|
5975
6675
|
const cell = row.cells[c2];
|
|
5976
6676
|
if (!cell) continue;
|
|
@@ -5986,7 +6686,7 @@ function renderTbody(rows, columns) {
|
|
|
5986
6686
|
function renderSearchBar(layout) {
|
|
5987
6687
|
if (!layout.search.enabled) return null;
|
|
5988
6688
|
const div = document.createElement("div");
|
|
5989
|
-
div.className = "
|
|
6689
|
+
div.className = "oc-table-search";
|
|
5990
6690
|
const input = document.createElement("input");
|
|
5991
6691
|
input.type = "search";
|
|
5992
6692
|
input.placeholder = layout.search.placeholder;
|
|
@@ -5999,9 +6699,9 @@ function renderPagination(layout) {
|
|
|
5999
6699
|
if (!layout.pagination) return null;
|
|
6000
6700
|
const { page, pageSize, totalRows, totalPages } = layout.pagination;
|
|
6001
6701
|
const div = document.createElement("div");
|
|
6002
|
-
div.className = "
|
|
6702
|
+
div.className = "oc-table-pagination";
|
|
6003
6703
|
const info = document.createElement("span");
|
|
6004
|
-
info.className = "
|
|
6704
|
+
info.className = "oc-table-pagination-info";
|
|
6005
6705
|
if (totalRows === 0) {
|
|
6006
6706
|
info.textContent = "No results";
|
|
6007
6707
|
} else {
|
|
@@ -6011,7 +6711,7 @@ function renderPagination(layout) {
|
|
|
6011
6711
|
}
|
|
6012
6712
|
div.appendChild(info);
|
|
6013
6713
|
const btnGroup = document.createElement("span");
|
|
6014
|
-
btnGroup.className = "
|
|
6714
|
+
btnGroup.className = "oc-table-pagination-btns";
|
|
6015
6715
|
const prevBtn = document.createElement("button");
|
|
6016
6716
|
prevBtn.setAttribute("aria-label", "Previous page");
|
|
6017
6717
|
prevBtn.setAttribute("data-page-action", "prev");
|
|
@@ -6029,49 +6729,49 @@ function renderPagination(layout) {
|
|
|
6029
6729
|
}
|
|
6030
6730
|
function renderEmptyState(message) {
|
|
6031
6731
|
const div = document.createElement("div");
|
|
6032
|
-
div.className = "
|
|
6732
|
+
div.className = "oc-table-empty";
|
|
6033
6733
|
div.setAttribute("aria-live", "polite");
|
|
6034
6734
|
div.textContent = message;
|
|
6035
6735
|
return div;
|
|
6036
6736
|
}
|
|
6037
|
-
function renderTable(layout, container) {
|
|
6737
|
+
function renderTable(layout, container, opts) {
|
|
6038
6738
|
const wrapper = document.createElement("div");
|
|
6039
|
-
wrapper.className = "
|
|
6739
|
+
wrapper.className = "oc-table-wrapper";
|
|
6040
6740
|
const { theme, chrome } = layout;
|
|
6041
6741
|
if (theme) {
|
|
6042
6742
|
const s = wrapper.style;
|
|
6043
|
-
s.setProperty("--
|
|
6044
|
-
s.setProperty("--
|
|
6045
|
-
s.setProperty("--
|
|
6046
|
-
s.setProperty("--
|
|
6047
|
-
s.setProperty("--
|
|
6048
|
-
s.setProperty("--
|
|
6049
|
-
s.setProperty("--
|
|
6743
|
+
s.setProperty("--oc-bg", theme.colors.background);
|
|
6744
|
+
s.setProperty("--oc-text", theme.colors.text);
|
|
6745
|
+
s.setProperty("--oc-text-secondary", theme.colors.axis ?? theme.colors.text);
|
|
6746
|
+
s.setProperty("--oc-text-muted", theme.colors.axis ?? theme.colors.text);
|
|
6747
|
+
s.setProperty("--oc-gridline", theme.colors.gridline);
|
|
6748
|
+
s.setProperty("--oc-border", theme.colors.gridline);
|
|
6749
|
+
s.setProperty("--oc-font-family", theme.fonts.family);
|
|
6050
6750
|
s.fontFamily = theme.fonts.family;
|
|
6051
6751
|
}
|
|
6052
6752
|
{
|
|
6053
6753
|
const s = wrapper.style;
|
|
6054
6754
|
if (chrome.title) {
|
|
6055
|
-
s.setProperty("--
|
|
6056
|
-
s.setProperty("--
|
|
6057
|
-
s.setProperty("--
|
|
6755
|
+
s.setProperty("--oc-title-computed-size", `${chrome.title.style.fontSize}px`);
|
|
6756
|
+
s.setProperty("--oc-title-computed-weight", String(chrome.title.style.fontWeight));
|
|
6757
|
+
s.setProperty("--oc-title-computed-color", chrome.title.style.fill);
|
|
6058
6758
|
}
|
|
6059
6759
|
if (chrome.subtitle) {
|
|
6060
|
-
s.setProperty("--
|
|
6061
|
-
s.setProperty("--
|
|
6062
|
-
s.setProperty("--
|
|
6760
|
+
s.setProperty("--oc-subtitle-computed-size", `${chrome.subtitle.style.fontSize}px`);
|
|
6761
|
+
s.setProperty("--oc-subtitle-computed-weight", String(chrome.subtitle.style.fontWeight));
|
|
6762
|
+
s.setProperty("--oc-subtitle-computed-color", chrome.subtitle.style.fill);
|
|
6063
6763
|
}
|
|
6064
6764
|
if (chrome.source) {
|
|
6065
|
-
s.setProperty("--
|
|
6066
|
-
s.setProperty("--
|
|
6765
|
+
s.setProperty("--oc-source-computed-size", `${chrome.source.style.fontSize}px`);
|
|
6766
|
+
s.setProperty("--oc-source-computed-color", chrome.source.style.fill);
|
|
6067
6767
|
}
|
|
6068
6768
|
if (chrome.footer) {
|
|
6069
|
-
s.setProperty("--
|
|
6070
|
-
s.setProperty("--
|
|
6769
|
+
s.setProperty("--oc-footer-computed-size", `${chrome.footer.style.fontSize}px`);
|
|
6770
|
+
s.setProperty("--oc-footer-computed-color", chrome.footer.style.fill);
|
|
6071
6771
|
}
|
|
6072
6772
|
}
|
|
6073
6773
|
if (layout.compact) {
|
|
6074
|
-
wrapper.classList.add("
|
|
6774
|
+
wrapper.classList.add("oc-table--compact");
|
|
6075
6775
|
}
|
|
6076
6776
|
const headerChrome = renderChromeBlock(layout, "header");
|
|
6077
6777
|
if (headerChrome) {
|
|
@@ -6086,15 +6786,15 @@ function renderTable(layout, container) {
|
|
|
6086
6786
|
wrapper.appendChild(renderEmptyState(message));
|
|
6087
6787
|
} else {
|
|
6088
6788
|
const scroll = document.createElement("div");
|
|
6089
|
-
scroll.className = "
|
|
6789
|
+
scroll.className = "oc-table-scroll";
|
|
6090
6790
|
const table = document.createElement("table");
|
|
6091
6791
|
table.setAttribute("role", "grid");
|
|
6092
6792
|
table.setAttribute("aria-label", layout.a11y.caption);
|
|
6093
6793
|
if (layout.stickyFirstColumn) {
|
|
6094
|
-
table.classList.add("
|
|
6794
|
+
table.classList.add("oc-table--sticky");
|
|
6095
6795
|
}
|
|
6096
6796
|
const caption = document.createElement("caption");
|
|
6097
|
-
caption.className = "
|
|
6797
|
+
caption.className = "oc-sr-only";
|
|
6098
6798
|
caption.style.position = "absolute";
|
|
6099
6799
|
caption.style.width = "1px";
|
|
6100
6800
|
caption.style.height = "1px";
|
|
@@ -6120,7 +6820,7 @@ function renderTable(layout, container) {
|
|
|
6120
6820
|
wrapper.appendChild(footerChrome);
|
|
6121
6821
|
}
|
|
6122
6822
|
const liveRegion = document.createElement("div");
|
|
6123
|
-
liveRegion.className = "
|
|
6823
|
+
liveRegion.className = "oc-table-live-region oc-sr-only";
|
|
6124
6824
|
liveRegion.style.position = "absolute";
|
|
6125
6825
|
liveRegion.style.width = "1px";
|
|
6126
6826
|
liveRegion.style.height = "1px";
|
|
@@ -6136,7 +6836,7 @@ function renderTable(layout, container) {
|
|
|
6136
6836
|
wrapper.appendChild(liveRegion);
|
|
6137
6837
|
const brandColor = theme ? theme.colors.axis : "#999999";
|
|
6138
6838
|
const brand = document.createElement("div");
|
|
6139
|
-
brand.className = "
|
|
6839
|
+
brand.className = "oc-table-ref";
|
|
6140
6840
|
brand.style.cssText = "text-align: right; padding: 4px 8px;";
|
|
6141
6841
|
const brandLink = document.createElement("a");
|
|
6142
6842
|
brandLink.href = BRAND_URL2;
|
|
@@ -6146,6 +6846,16 @@ function renderTable(layout, container) {
|
|
|
6146
6846
|
brandLink.textContent = "OpenData";
|
|
6147
6847
|
brand.appendChild(brandLink);
|
|
6148
6848
|
wrapper.appendChild(brand);
|
|
6849
|
+
if (opts?.animate && layout.animation?.enabled) {
|
|
6850
|
+
const anim = layout.animation;
|
|
6851
|
+
const rowCount = layout.rows.length;
|
|
6852
|
+
const stagger = clampStaggerDelay2(anim.staggerDelay, rowCount);
|
|
6853
|
+
const s = wrapper.style;
|
|
6854
|
+
s.setProperty("--oc-animation-duration", `${anim.duration}ms`);
|
|
6855
|
+
s.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
6856
|
+
s.setProperty("--oc-animation-ease", EASE_VAR_MAP2[anim.ease] || EASE_VAR_MAP2.smooth);
|
|
6857
|
+
wrapper.classList.add("oc-animate");
|
|
6858
|
+
}
|
|
6149
6859
|
container.appendChild(wrapper);
|
|
6150
6860
|
return wrapper;
|
|
6151
6861
|
}
|
|
@@ -6180,6 +6890,8 @@ function createTable(container, spec, options) {
|
|
|
6180
6890
|
let wrapperElement = null;
|
|
6181
6891
|
let disconnectResize = null;
|
|
6182
6892
|
let cleanupKeyboard = null;
|
|
6893
|
+
let cleanupAnimations = null;
|
|
6894
|
+
let isFirstRender = true;
|
|
6183
6895
|
let destroyed = false;
|
|
6184
6896
|
const internalState = {
|
|
6185
6897
|
sort: null,
|
|
@@ -6239,7 +6951,7 @@ function createTable(container, spec, options) {
|
|
|
6239
6951
|
}
|
|
6240
6952
|
function announce(message) {
|
|
6241
6953
|
if (!wrapperElement) return;
|
|
6242
|
-
const liveRegion = wrapperElement.querySelector(".
|
|
6954
|
+
const liveRegion = wrapperElement.querySelector(".oc-table-live-region");
|
|
6243
6955
|
if (liveRegion) {
|
|
6244
6956
|
liveRegion.textContent = message;
|
|
6245
6957
|
}
|
|
@@ -6249,14 +6961,18 @@ function createTable(container, spec, options) {
|
|
|
6249
6961
|
const { width } = getContainerDimensions();
|
|
6250
6962
|
const bp = getBreakpoint(width);
|
|
6251
6963
|
if (bp === "compact" || bp === "medium") {
|
|
6252
|
-
wrapperElement.classList.add("
|
|
6964
|
+
wrapperElement.classList.add("oc-table--compact");
|
|
6253
6965
|
} else if (!currentLayout?.compact) {
|
|
6254
|
-
wrapperElement.classList.remove("
|
|
6966
|
+
wrapperElement.classList.remove("oc-table--compact");
|
|
6255
6967
|
}
|
|
6256
6968
|
}
|
|
6257
6969
|
function render() {
|
|
6258
6970
|
if (destroyed) return;
|
|
6259
6971
|
try {
|
|
6972
|
+
if (cleanupAnimations) {
|
|
6973
|
+
cleanupAnimations();
|
|
6974
|
+
cleanupAnimations = null;
|
|
6975
|
+
}
|
|
6260
6976
|
if (cleanupKeyboard) {
|
|
6261
6977
|
cleanupKeyboard();
|
|
6262
6978
|
cleanupKeyboard = null;
|
|
@@ -6266,16 +6982,23 @@ function createTable(container, spec, options) {
|
|
|
6266
6982
|
wrapperElement = null;
|
|
6267
6983
|
}
|
|
6268
6984
|
currentLayout = compile();
|
|
6269
|
-
|
|
6985
|
+
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enabled;
|
|
6986
|
+
wrapperElement = renderTable(currentLayout, container, { animate: shouldAnimate });
|
|
6987
|
+
if (shouldAnimate && wrapperElement) {
|
|
6988
|
+
cleanupAnimations = setupTableAnimationCleanup(wrapperElement);
|
|
6989
|
+
}
|
|
6990
|
+
if (isFirstRender) {
|
|
6991
|
+
isFirstRender = false;
|
|
6992
|
+
}
|
|
6270
6993
|
const isDark = resolveDarkMode3(options?.darkMode);
|
|
6271
6994
|
if (isDark) {
|
|
6272
|
-
container.classList.add("
|
|
6995
|
+
container.classList.add("oc-dark");
|
|
6273
6996
|
} else {
|
|
6274
|
-
container.classList.remove("
|
|
6997
|
+
container.classList.remove("oc-dark");
|
|
6275
6998
|
}
|
|
6276
6999
|
applyBreakpointClass();
|
|
6277
7000
|
if (options?.onRowClick) {
|
|
6278
|
-
wrapperElement.classList.add("
|
|
7001
|
+
wrapperElement.classList.add("oc-table--clickable");
|
|
6279
7002
|
}
|
|
6280
7003
|
wireEvents();
|
|
6281
7004
|
if (wrapperElement) {
|
|
@@ -6315,7 +7038,7 @@ function createTable(container, spec, options) {
|
|
|
6315
7038
|
btn.addEventListener("click", handleSortClick);
|
|
6316
7039
|
}
|
|
6317
7040
|
const searchInput = wrapperElement.querySelector(
|
|
6318
|
-
".
|
|
7041
|
+
".oc-table-search input"
|
|
6319
7042
|
);
|
|
6320
7043
|
if (searchInput) {
|
|
6321
7044
|
searchInput.addEventListener("input", handleSearchInput);
|
|
@@ -6391,7 +7114,7 @@ function createTable(container, spec, options) {
|
|
|
6391
7114
|
function rerender() {
|
|
6392
7115
|
if (destroyed) return;
|
|
6393
7116
|
const searchInput = wrapperElement?.querySelector(
|
|
6394
|
-
".
|
|
7117
|
+
".oc-table-search input"
|
|
6395
7118
|
);
|
|
6396
7119
|
const hadFocus = searchInput && document.activeElement === searchInput;
|
|
6397
7120
|
const selectionStart = searchInput?.selectionStart ?? 0;
|
|
@@ -6399,7 +7122,7 @@ function createTable(container, spec, options) {
|
|
|
6399
7122
|
render();
|
|
6400
7123
|
if (hadFocus) {
|
|
6401
7124
|
const newInput = wrapperElement?.querySelector(
|
|
6402
|
-
".
|
|
7125
|
+
".oc-table-search input"
|
|
6403
7126
|
);
|
|
6404
7127
|
if (newInput) {
|
|
6405
7128
|
newInput.focus();
|
|
@@ -6414,6 +7137,7 @@ function createTable(container, spec, options) {
|
|
|
6414
7137
|
}
|
|
6415
7138
|
function resize() {
|
|
6416
7139
|
if (destroyed) return;
|
|
7140
|
+
if (cleanupAnimations) return;
|
|
6417
7141
|
render();
|
|
6418
7142
|
}
|
|
6419
7143
|
function doExport(format) {
|
|
@@ -6450,6 +7174,10 @@ function createTable(container, spec, options) {
|
|
|
6450
7174
|
function destroy() {
|
|
6451
7175
|
if (destroyed) return;
|
|
6452
7176
|
destroyed = true;
|
|
7177
|
+
if (cleanupAnimations) {
|
|
7178
|
+
cleanupAnimations();
|
|
7179
|
+
cleanupAnimations = null;
|
|
7180
|
+
}
|
|
6453
7181
|
if (cleanupKeyboard) {
|
|
6454
7182
|
cleanupKeyboard();
|
|
6455
7183
|
cleanupKeyboard = null;
|
|
@@ -6470,7 +7198,7 @@ function createTable(container, spec, options) {
|
|
|
6470
7198
|
wrapperElement.parentNode.removeChild(wrapperElement);
|
|
6471
7199
|
wrapperElement = null;
|
|
6472
7200
|
}
|
|
6473
|
-
container.classList.remove("
|
|
7201
|
+
container.classList.remove("oc-dark");
|
|
6474
7202
|
}
|
|
6475
7203
|
render();
|
|
6476
7204
|
if (options?.responsive !== false) {
|
|
@@ -6500,6 +7228,7 @@ export {
|
|
|
6500
7228
|
createGraph,
|
|
6501
7229
|
createSimulationWorker,
|
|
6502
7230
|
createTable,
|
|
7231
|
+
createTextEditOverlay,
|
|
6503
7232
|
createTooltipManager,
|
|
6504
7233
|
exportCSV,
|
|
6505
7234
|
exportJPG,
|