@opendata-ai/openchart-vanilla 6.4.1 → 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 +9 -2
- package/dist/index.js +327 -174
- 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 +14 -14
- 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/mount.ts +71 -37
- package/src/renderers/table-cells.ts +11 -9
- package/src/svg-renderer.ts +161 -54
- package/src/table-keyboard.ts +5 -5
- package/src/table-mount.ts +34 -11
- package/src/table-renderer.ts +70 -39
- 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();
|
|
@@ -3258,9 +3258,59 @@ function escapeHtml(str) {
|
|
|
3258
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,14 +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));
|
|
3850
3921
|
if (annotation.id) {
|
|
3851
3922
|
g.setAttribute("data-annotation-id", annotation.id);
|
|
3852
3923
|
}
|
|
3853
3924
|
if (annotation.rect) {
|
|
3854
3925
|
const rect = createSVGElement("rect");
|
|
3855
|
-
rect.setAttribute("class", "
|
|
3926
|
+
rect.setAttribute("class", "oc-annotation-range");
|
|
3856
3927
|
setAttrs(rect, {
|
|
3857
3928
|
x: annotation.rect.x,
|
|
3858
3929
|
y: annotation.rect.y,
|
|
@@ -3867,7 +3938,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3867
3938
|
}
|
|
3868
3939
|
if (annotation.line) {
|
|
3869
3940
|
const line = createSVGElement("line");
|
|
3870
|
-
line.setAttribute("class", "
|
|
3941
|
+
line.setAttribute("class", "oc-annotation-line");
|
|
3871
3942
|
setAttrs(line, {
|
|
3872
3943
|
x1: annotation.line.start.x,
|
|
3873
3944
|
y1: annotation.line.start.y,
|
|
@@ -3898,7 +3969,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3898
3969
|
const tipY = pointsDown ? midY + caretSize / 2 : midY - caretSize / 2;
|
|
3899
3970
|
const baseY = pointsDown ? tipY - caretSize : tipY + caretSize;
|
|
3900
3971
|
const path = createSVGElement("path");
|
|
3901
|
-
path.setAttribute("class", "
|
|
3972
|
+
path.setAttribute("class", "oc-annotation-connector");
|
|
3902
3973
|
setAttrs(path, {
|
|
3903
3974
|
d: `M${tipX - caretSize},${baseY} L${tipX},${tipY} L${tipX + caretSize},${baseY}`,
|
|
3904
3975
|
fill: "none",
|
|
@@ -3913,7 +3984,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3913
3984
|
renderCurvedArrow(g, c2.from, c2.to, c2.stroke);
|
|
3914
3985
|
} else {
|
|
3915
3986
|
const connector = createSVGElement("line");
|
|
3916
|
-
connector.setAttribute("class", "
|
|
3987
|
+
connector.setAttribute("class", "oc-annotation-connector");
|
|
3917
3988
|
setAttrs(connector, {
|
|
3918
3989
|
x1: c2.from.x,
|
|
3919
3990
|
y1: c2.from.y,
|
|
@@ -3927,7 +3998,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3927
3998
|
}
|
|
3928
3999
|
}
|
|
3929
4000
|
const text = createSVGElement("text");
|
|
3930
|
-
text.setAttribute("class", "
|
|
4001
|
+
text.setAttribute("class", "oc-annotation-label");
|
|
3931
4002
|
setAttrs(text, { x: annotation.label.x, y: annotation.label.y });
|
|
3932
4003
|
applyTextStyle(text, annotation.label.style);
|
|
3933
4004
|
const lines = annotation.label.text.split("\n");
|
|
@@ -3952,7 +4023,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3952
4023
|
const pad = 3;
|
|
3953
4024
|
const bgX = isMultiLine ? annotation.label.x - maxLineWidth / 2 - pad : annotation.label.x - pad;
|
|
3954
4025
|
const bgRect = createSVGElement("rect");
|
|
3955
|
-
bgRect.setAttribute("class", "
|
|
4026
|
+
bgRect.setAttribute("class", "oc-annotation-bg");
|
|
3956
4027
|
setAttrs(bgRect, {
|
|
3957
4028
|
x: bgX,
|
|
3958
4029
|
y: annotation.label.y - fontSize + (lineHeight - fontSize) / 2 - pad,
|
|
@@ -3970,7 +4041,7 @@ function renderAnnotation(parent, annotation, index2) {
|
|
|
3970
4041
|
function renderLegend(parent, legend) {
|
|
3971
4042
|
if (legend.entries.length === 0) return;
|
|
3972
4043
|
const g = createSVGElement("g");
|
|
3973
|
-
g.setAttribute("class", "
|
|
4044
|
+
g.setAttribute("class", "oc-legend");
|
|
3974
4045
|
g.setAttribute("role", "list");
|
|
3975
4046
|
g.setAttribute("aria-label", "Chart legend");
|
|
3976
4047
|
const isHorizontal = legend.position === "top" || legend.position === "bottom";
|
|
@@ -3991,7 +4062,7 @@ function renderLegend(parent, legend) {
|
|
|
3991
4062
|
}
|
|
3992
4063
|
}
|
|
3993
4064
|
const entryG = createSVGElement("g");
|
|
3994
|
-
entryG.setAttribute("class", "
|
|
4065
|
+
entryG.setAttribute("class", "oc-legend-entry");
|
|
3995
4066
|
entryG.setAttribute("role", "listitem");
|
|
3996
4067
|
entryG.setAttribute("data-legend-index", String(i));
|
|
3997
4068
|
entryG.setAttribute("data-legend-label", entry.label);
|
|
@@ -4091,7 +4162,7 @@ function renderBrand(parent, layout) {
|
|
|
4091
4162
|
a2.setAttributeNS(XLINK_NS, "xlink:href", BRAND_URL);
|
|
4092
4163
|
a2.setAttribute("target", "_blank");
|
|
4093
4164
|
a2.setAttribute("rel", "noopener");
|
|
4094
|
-
a2.setAttribute("class", "
|
|
4165
|
+
a2.setAttribute("class", "oc-chrome-ref");
|
|
4095
4166
|
const text = createSVGElement("text");
|
|
4096
4167
|
setAttrs(text, {
|
|
4097
4168
|
x: rightEdge,
|
|
@@ -4114,8 +4185,10 @@ function renderBrand(parent, layout) {
|
|
|
4114
4185
|
a2.appendChild(text);
|
|
4115
4186
|
parent.appendChild(a2);
|
|
4116
4187
|
}
|
|
4117
|
-
function renderChartSVG(layout, container) {
|
|
4188
|
+
function renderChartSVG(layout, container, opts) {
|
|
4118
4189
|
const { width, height } = layout.dimensions;
|
|
4190
|
+
const animation = layout.animation;
|
|
4191
|
+
currentAnimation = animation;
|
|
4119
4192
|
const svg = createSVGElement("svg");
|
|
4120
4193
|
setAttrs(svg, {
|
|
4121
4194
|
viewBox: `0 0 ${width} ${height}`,
|
|
@@ -4130,7 +4203,30 @@ function renderChartSVG(layout, container) {
|
|
|
4130
4203
|
svg.style.height = `${height}px`;
|
|
4131
4204
|
svg.setAttribute("role", layout.a11y.role);
|
|
4132
4205
|
svg.setAttribute("aria-label", layout.a11y.altText);
|
|
4133
|
-
|
|
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
|
+
}
|
|
4134
4230
|
const bg = createSVGElement("rect");
|
|
4135
4231
|
setAttrs(bg, {
|
|
4136
4232
|
x: 0,
|
|
@@ -4140,7 +4236,7 @@ function renderChartSVG(layout, container) {
|
|
|
4140
4236
|
fill: layout.theme.colors.background
|
|
4141
4237
|
});
|
|
4142
4238
|
svg.appendChild(bg);
|
|
4143
|
-
const clipId = `
|
|
4239
|
+
const clipId = `oc-clip-${Math.random().toString(36).slice(2, 8)}`;
|
|
4144
4240
|
const defs = createSVGElement("defs");
|
|
4145
4241
|
const clipPath = createSVGElement("clipPath");
|
|
4146
4242
|
clipPath.setAttribute("id", clipId);
|
|
@@ -4171,7 +4267,7 @@ function renderChartSVG(layout, container) {
|
|
|
4171
4267
|
height: layout.area.height,
|
|
4172
4268
|
fill: "transparent"
|
|
4173
4269
|
});
|
|
4174
|
-
overlay.setAttribute("class", "
|
|
4270
|
+
overlay.setAttribute("class", "oc-voronoi-overlay");
|
|
4175
4271
|
overlay.setAttribute("data-voronoi-overlay", "true");
|
|
4176
4272
|
clippedGroup.appendChild(overlay);
|
|
4177
4273
|
}
|
|
@@ -4180,6 +4276,7 @@ function renderChartSVG(layout, container) {
|
|
|
4180
4276
|
renderLegend(svg, layout.legend);
|
|
4181
4277
|
renderChrome(svg, layout);
|
|
4182
4278
|
renderBrand(svg, layout);
|
|
4279
|
+
currentAnimation = void 0;
|
|
4183
4280
|
container.appendChild(svg);
|
|
4184
4281
|
return svg;
|
|
4185
4282
|
}
|
|
@@ -4587,7 +4684,7 @@ function wireChartEvents(svg, layout, specAnnotations, handlers) {
|
|
|
4587
4684
|
}
|
|
4588
4685
|
}
|
|
4589
4686
|
if (handlers.onAnnotationClick) {
|
|
4590
|
-
const annotationElements = svg.querySelectorAll(".
|
|
4687
|
+
const annotationElements = svg.querySelectorAll(".oc-annotation");
|
|
4591
4688
|
for (let i = 0; i < annotationElements.length; i++) {
|
|
4592
4689
|
const el = annotationElements[i];
|
|
4593
4690
|
const specAnnotation = specAnnotations[i];
|
|
@@ -4750,7 +4847,7 @@ function createDragHandler(config) {
|
|
|
4750
4847
|
};
|
|
4751
4848
|
}
|
|
4752
4849
|
function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setDragging) {
|
|
4753
|
-
const annotationElements = svg.querySelectorAll(".
|
|
4850
|
+
const annotationElements = svg.querySelectorAll(".oc-annotation-text");
|
|
4754
4851
|
const cleanups = [];
|
|
4755
4852
|
for (const el of annotationElements) {
|
|
4756
4853
|
const indexStr = el.getAttribute("data-annotation-index");
|
|
@@ -4761,11 +4858,11 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4761
4858
|
const textAnnotation = specAnnotation;
|
|
4762
4859
|
const annotationG = el;
|
|
4763
4860
|
annotationG.style.cursor = "grab";
|
|
4764
|
-
const connectorLine = annotationG.querySelector("line.
|
|
4861
|
+
const connectorLine = annotationG.querySelector("line.oc-annotation-connector");
|
|
4765
4862
|
const origX2 = connectorLine ? Number(connectorLine.getAttribute("x2")) : 0;
|
|
4766
4863
|
const origY2 = connectorLine ? Number(connectorLine.getAttribute("y2")) : 0;
|
|
4767
|
-
const curvedPath = annotationG.querySelector("path.
|
|
4768
|
-
const arrowhead = annotationG.querySelector("polygon.
|
|
4864
|
+
const curvedPath = annotationG.querySelector("path.oc-annotation-connector");
|
|
4865
|
+
const arrowhead = annotationG.querySelector("polygon.oc-annotation-connector");
|
|
4769
4866
|
const hasCurvedConnector = curvedPath !== null;
|
|
4770
4867
|
const origDx = textAnnotation.offset?.dx ?? 0;
|
|
4771
4868
|
const origDy = textAnnotation.offset?.dy ?? 0;
|
|
@@ -4815,7 +4912,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4815
4912
|
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4816
4913
|
const SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
4817
4914
|
const cleanups = [];
|
|
4818
|
-
const annotationGroups = svg.querySelectorAll(".
|
|
4915
|
+
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4819
4916
|
for (const el of annotationGroups) {
|
|
4820
4917
|
const annotationG = el;
|
|
4821
4918
|
const indexStr = annotationG.getAttribute("data-annotation-index");
|
|
@@ -4824,8 +4921,8 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4824
4921
|
const specAnnotation = specAnnotations[index2];
|
|
4825
4922
|
if (!specAnnotation || specAnnotation.type !== "text") continue;
|
|
4826
4923
|
const textAnnotation = specAnnotation;
|
|
4827
|
-
const connectorLine = annotationG.querySelector("line.
|
|
4828
|
-
const curvedPath = annotationG.querySelector("path.
|
|
4924
|
+
const connectorLine = annotationG.querySelector("line.oc-annotation-connector");
|
|
4925
|
+
const curvedPath = annotationG.querySelector("path.oc-annotation-connector");
|
|
4829
4926
|
if (!connectorLine && !curvedPath) continue;
|
|
4830
4927
|
let fromX, fromY, toX, toY;
|
|
4831
4928
|
if (connectorLine) {
|
|
@@ -4838,7 +4935,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4838
4935
|
const mMatch = pathD.match(/M\s*([\d.e+-]+)\s+([\d.e+-]+)/);
|
|
4839
4936
|
fromX = mMatch ? Number(mMatch[1]) : 0;
|
|
4840
4937
|
fromY = mMatch ? Number(mMatch[2]) : 0;
|
|
4841
|
-
const arrowhead = annotationG.querySelector("polygon.
|
|
4938
|
+
const arrowhead = annotationG.querySelector("polygon.oc-annotation-connector");
|
|
4842
4939
|
const points = arrowhead?.getAttribute("points") ?? "";
|
|
4843
4940
|
const firstPoint = points.split(" ")[0] ?? "0,0";
|
|
4844
4941
|
const [px, py] = firstPoint.split(",");
|
|
@@ -4853,7 +4950,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4853
4950
|
for (const ep of endpoints) {
|
|
4854
4951
|
if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
|
|
4855
4952
|
const handleEl = document.createElementNS(SVG_NS2, "circle");
|
|
4856
|
-
handleEl.setAttribute("class", "
|
|
4953
|
+
handleEl.setAttribute("class", "oc-connector-handle");
|
|
4857
4954
|
handleEl.setAttribute("data-endpoint", ep.name);
|
|
4858
4955
|
handleEl.setAttribute("cx", String(ep.cx));
|
|
4859
4956
|
handleEl.setAttribute("cy", String(ep.cy));
|
|
@@ -4947,13 +5044,13 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4947
5044
|
function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4948
5045
|
const cleanups = [];
|
|
4949
5046
|
const selectors = [
|
|
4950
|
-
".
|
|
4951
|
-
".
|
|
5047
|
+
".oc-annotation-range .oc-annotation-label",
|
|
5048
|
+
".oc-annotation-refline .oc-annotation-label"
|
|
4952
5049
|
];
|
|
4953
5050
|
for (const selector of selectors) {
|
|
4954
5051
|
const labels = svg.querySelectorAll(selector);
|
|
4955
5052
|
for (const label of labels) {
|
|
4956
|
-
const annotationG = label.closest(".
|
|
5053
|
+
const annotationG = label.closest(".oc-annotation");
|
|
4957
5054
|
if (!annotationG) continue;
|
|
4958
5055
|
const indexStr = annotationG.getAttribute("data-annotation-index");
|
|
4959
5056
|
if (indexStr === null) continue;
|
|
@@ -5002,7 +5099,7 @@ function wireAnnotationLabelDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
5002
5099
|
};
|
|
5003
5100
|
}
|
|
5004
5101
|
function wireChromeDrag(svg, spec, onEdit, setDragging) {
|
|
5005
|
-
const chromeTexts = svg.querySelectorAll(".
|
|
5102
|
+
const chromeTexts = svg.querySelectorAll(".oc-chrome text[data-chrome-key]");
|
|
5006
5103
|
const cleanups = [];
|
|
5007
5104
|
const chromeConfig = "chrome" in spec ? spec.chrome : void 0;
|
|
5008
5105
|
for (const el of chromeTexts) {
|
|
@@ -5042,7 +5139,7 @@ function wireChromeDrag(svg, spec, onEdit, setDragging) {
|
|
|
5042
5139
|
};
|
|
5043
5140
|
}
|
|
5044
5141
|
function wireLegendDrag(svg, spec, onEdit, setDragging) {
|
|
5045
|
-
const legendG = svg.querySelector(".
|
|
5142
|
+
const legendG = svg.querySelector(".oc-legend");
|
|
5046
5143
|
if (!legendG) return () => {
|
|
5047
5144
|
};
|
|
5048
5145
|
const cleanups = [];
|
|
@@ -5072,7 +5169,7 @@ function wireLegendDrag(svg, spec, onEdit, setDragging) {
|
|
|
5072
5169
|
};
|
|
5073
5170
|
}
|
|
5074
5171
|
function wireSeriesLabelDrag(svg, spec, onEdit, setDragging) {
|
|
5075
|
-
const labels = svg.querySelectorAll(".
|
|
5172
|
+
const labels = svg.querySelectorAll(".oc-mark-label");
|
|
5076
5173
|
const cleanups = [];
|
|
5077
5174
|
const labelsConfig = "labels" in spec ? spec.labels : void 0;
|
|
5078
5175
|
for (const label of labels) {
|
|
@@ -5131,7 +5228,7 @@ function wireLegendInteraction(svg, _layout, onLegendToggle, onEdit) {
|
|
|
5131
5228
|
onLegendToggle?.(label, false);
|
|
5132
5229
|
onEdit?.({ type: "legend-toggle", series: label, hidden: true });
|
|
5133
5230
|
}
|
|
5134
|
-
const marks = svg.querySelectorAll(".
|
|
5231
|
+
const marks = svg.querySelectorAll(".oc-mark");
|
|
5135
5232
|
for (const mark of marks) {
|
|
5136
5233
|
const seriesName = mark.getAttribute("data-series");
|
|
5137
5234
|
if (!seriesName) continue;
|
|
@@ -5166,13 +5263,13 @@ function wireKeyboardNav(svg, container, tooltipDescriptors, tooltipManager, lay
|
|
|
5166
5263
|
let focusIndex = -1;
|
|
5167
5264
|
function highlightMark(index2) {
|
|
5168
5265
|
if (focusIndex >= 0 && focusIndex < markElements.length) {
|
|
5169
|
-
markElements[focusIndex].classList.remove("
|
|
5266
|
+
markElements[focusIndex].classList.remove("oc-mark-focused");
|
|
5170
5267
|
markElements[focusIndex].removeAttribute("aria-selected");
|
|
5171
5268
|
}
|
|
5172
5269
|
focusIndex = index2;
|
|
5173
5270
|
if (focusIndex >= 0 && focusIndex < markElements.length) {
|
|
5174
5271
|
const el = markElements[focusIndex];
|
|
5175
|
-
el.classList.add("
|
|
5272
|
+
el.classList.add("oc-mark-focused");
|
|
5176
5273
|
el.setAttribute("aria-selected", "true");
|
|
5177
5274
|
}
|
|
5178
5275
|
}
|
|
@@ -5239,7 +5336,7 @@ function createScreenReaderTable(layout, container) {
|
|
|
5239
5336
|
const data = layout.a11y.dataTableFallback;
|
|
5240
5337
|
if (!data || data.length === 0) return null;
|
|
5241
5338
|
const table = document.createElement("table");
|
|
5242
|
-
table.className = "
|
|
5339
|
+
table.className = "oc-sr-only";
|
|
5243
5340
|
table.style.position = "absolute";
|
|
5244
5341
|
table.style.width = "1px";
|
|
5245
5342
|
table.style.height = "1px";
|
|
@@ -5282,7 +5379,7 @@ function createScreenReaderTable(layout, container) {
|
|
|
5282
5379
|
return table;
|
|
5283
5380
|
}
|
|
5284
5381
|
var EDITABLE_HOVER_CSS = `
|
|
5285
|
-
.
|
|
5382
|
+
.oc-editable-hover {
|
|
5286
5383
|
outline: 1.5px solid rgba(79, 70, 229, 0.35);
|
|
5287
5384
|
outline-offset: 2px;
|
|
5288
5385
|
border-radius: 2px;
|
|
@@ -5310,9 +5407,9 @@ function findElementByRef(svg, ref) {
|
|
|
5310
5407
|
case "chrome":
|
|
5311
5408
|
return svg.querySelector(`[data-chrome-key="${ref.key}"]`);
|
|
5312
5409
|
case "series-label":
|
|
5313
|
-
return svg.querySelector(`.
|
|
5410
|
+
return svg.querySelector(`.oc-mark-label[data-series="${ref.series}"]`);
|
|
5314
5411
|
case "legend":
|
|
5315
|
-
return svg.querySelector(".
|
|
5412
|
+
return svg.querySelector(".oc-legend");
|
|
5316
5413
|
case "legend-entry":
|
|
5317
5414
|
return svg.querySelector(`[data-legend-index="${ref.index}"]`);
|
|
5318
5415
|
}
|
|
@@ -5329,7 +5426,7 @@ function buildElementRef(element, _specAnnotations) {
|
|
|
5329
5426
|
const key = chromeEl.getAttribute("data-chrome-key");
|
|
5330
5427
|
if (key) return elementRef.chrome(key);
|
|
5331
5428
|
}
|
|
5332
|
-
const seriesLabelEl = element.closest(".
|
|
5429
|
+
const seriesLabelEl = element.closest(".oc-mark-label[data-series]");
|
|
5333
5430
|
if (seriesLabelEl) {
|
|
5334
5431
|
const series = seriesLabelEl.getAttribute("data-series");
|
|
5335
5432
|
if (series) return elementRef.seriesLabel(series);
|
|
@@ -5340,7 +5437,7 @@ function buildElementRef(element, _specAnnotations) {
|
|
|
5340
5437
|
const series = legendEntryEl.getAttribute("data-legend-label") ?? "";
|
|
5341
5438
|
return elementRef.legendEntry(series, index2);
|
|
5342
5439
|
}
|
|
5343
|
-
const legendEl = element.closest(".
|
|
5440
|
+
const legendEl = element.closest(".oc-legend");
|
|
5344
5441
|
if (legendEl) return elementRef.legend();
|
|
5345
5442
|
return null;
|
|
5346
5443
|
}
|
|
@@ -5427,7 +5524,7 @@ function renderSelectionOverlay(svg, ref, layout) {
|
|
|
5427
5524
|
const padding = 4;
|
|
5428
5525
|
const accentColor = layout.theme.colors.categorical?.[0] ?? "#4f46e5";
|
|
5429
5526
|
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
5430
|
-
g.setAttribute("class", "
|
|
5527
|
+
g.setAttribute("class", "oc-selection-overlay");
|
|
5431
5528
|
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
5432
5529
|
rect.setAttribute("x", String(bbox.x - padding));
|
|
5433
5530
|
rect.setAttribute("y", String(bbox.y - padding));
|
|
@@ -5462,6 +5559,8 @@ function createChart(container, spec, options) {
|
|
|
5462
5559
|
let isDragging = false;
|
|
5463
5560
|
let pendingRender = false;
|
|
5464
5561
|
let resizeTimer = null;
|
|
5562
|
+
let isFirstRender = true;
|
|
5563
|
+
let cleanupAnimations = null;
|
|
5465
5564
|
let selectedElement = options?.selectedElement ?? null;
|
|
5466
5565
|
let overlayElement = null;
|
|
5467
5566
|
let isTextEditingActive = false;
|
|
@@ -5577,16 +5676,16 @@ function createChart(container, spec, options) {
|
|
|
5577
5676
|
cleanups.push(() => svg.removeEventListener("click", handleClick));
|
|
5578
5677
|
const handleMouseEnter = (e) => {
|
|
5579
5678
|
const target = e.target.closest(
|
|
5580
|
-
"[data-annotation-index], [data-chrome-key], .
|
|
5679
|
+
"[data-annotation-index], [data-chrome-key], .oc-mark-label[data-series], .oc-legend, [data-legend-index]"
|
|
5581
5680
|
);
|
|
5582
5681
|
if (target) {
|
|
5583
|
-
target.classList.add("
|
|
5682
|
+
target.classList.add("oc-editable-hover");
|
|
5584
5683
|
}
|
|
5585
5684
|
};
|
|
5586
5685
|
const handleMouseLeave = (e) => {
|
|
5587
|
-
const target = e.target.closest(".
|
|
5686
|
+
const target = e.target.closest(".oc-editable-hover");
|
|
5588
5687
|
if (target) {
|
|
5589
|
-
target.classList.remove("
|
|
5688
|
+
target.classList.remove("oc-editable-hover");
|
|
5590
5689
|
}
|
|
5591
5690
|
};
|
|
5592
5691
|
svg.addEventListener("mouseenter", handleMouseEnter, true);
|
|
@@ -5686,6 +5785,11 @@ function createChart(container, spec, options) {
|
|
|
5686
5785
|
pendingRender = true;
|
|
5687
5786
|
return;
|
|
5688
5787
|
}
|
|
5788
|
+
if (cleanupAnimations) {
|
|
5789
|
+
cleanupAnimations();
|
|
5790
|
+
cleanupAnimations = null;
|
|
5791
|
+
}
|
|
5792
|
+
cancelAnimations(svgElement);
|
|
5689
5793
|
if (cleanupTooltipEvents) {
|
|
5690
5794
|
cleanupTooltipEvents();
|
|
5691
5795
|
cleanupTooltipEvents = null;
|
|
@@ -5739,7 +5843,8 @@ function createChart(container, spec, options) {
|
|
|
5739
5843
|
srTable = null;
|
|
5740
5844
|
}
|
|
5741
5845
|
currentLayout = compile();
|
|
5742
|
-
|
|
5846
|
+
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enabled;
|
|
5847
|
+
svgElement = renderChartSVG(currentLayout, container, { animate: shouldAnimate });
|
|
5743
5848
|
tooltipManager = createTooltipManager(container);
|
|
5744
5849
|
cleanupTooltipEvents = wireTooltipEvents(
|
|
5745
5850
|
svgElement,
|
|
@@ -5814,12 +5919,18 @@ function createChart(container, spec, options) {
|
|
|
5814
5919
|
}
|
|
5815
5920
|
}
|
|
5816
5921
|
srTable = createScreenReaderTable(currentLayout, container);
|
|
5817
|
-
container.classList.add("
|
|
5922
|
+
container.classList.add("oc-root");
|
|
5818
5923
|
const isDark = resolveDarkMode2(options?.darkMode);
|
|
5819
5924
|
if (isDark) {
|
|
5820
|
-
container.classList.add("
|
|
5925
|
+
container.classList.add("oc-dark");
|
|
5821
5926
|
} else {
|
|
5822
|
-
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;
|
|
5823
5934
|
}
|
|
5824
5935
|
}
|
|
5825
5936
|
function update(newSpec, updateOpts) {
|
|
@@ -5832,6 +5943,7 @@ function createChart(container, spec, options) {
|
|
|
5832
5943
|
}
|
|
5833
5944
|
function resize() {
|
|
5834
5945
|
if (destroyed) return;
|
|
5946
|
+
if (cleanupAnimations) return;
|
|
5835
5947
|
render();
|
|
5836
5948
|
}
|
|
5837
5949
|
function doExport(format, exportOptions) {
|
|
@@ -5858,6 +5970,11 @@ function createChart(container, spec, options) {
|
|
|
5858
5970
|
function destroy() {
|
|
5859
5971
|
if (destroyed) return;
|
|
5860
5972
|
destroyed = true;
|
|
5973
|
+
if (cleanupAnimations) {
|
|
5974
|
+
cleanupAnimations();
|
|
5975
|
+
cleanupAnimations = null;
|
|
5976
|
+
}
|
|
5977
|
+
cancelAnimations(svgElement);
|
|
5861
5978
|
if (resizeTimer !== null) {
|
|
5862
5979
|
clearTimeout(resizeTimer);
|
|
5863
5980
|
resizeTimer = null;
|
|
@@ -5921,8 +6038,8 @@ function createChart(container, spec, options) {
|
|
|
5921
6038
|
srTable.parentNode.removeChild(srTable);
|
|
5922
6039
|
srTable = null;
|
|
5923
6040
|
}
|
|
5924
|
-
container.classList.remove("
|
|
5925
|
-
container.classList.remove("
|
|
6041
|
+
container.classList.remove("oc-dark");
|
|
6042
|
+
container.classList.remove("oc-root");
|
|
5926
6043
|
}
|
|
5927
6044
|
render();
|
|
5928
6045
|
if (options?.responsive !== false) {
|
|
@@ -6059,28 +6176,30 @@ function renderTextCell(cell) {
|
|
|
6059
6176
|
}
|
|
6060
6177
|
function renderHeatmapCell(cell) {
|
|
6061
6178
|
const td = document.createElement("td");
|
|
6179
|
+
td.className = "oc-table-heatmap";
|
|
6062
6180
|
td.textContent = cell.formattedValue;
|
|
6063
6181
|
applyCellStyle(td, cell);
|
|
6064
6182
|
return td;
|
|
6065
6183
|
}
|
|
6066
6184
|
function renderCategoryCell(cell) {
|
|
6067
6185
|
const td = document.createElement("td");
|
|
6186
|
+
td.className = "oc-table-category";
|
|
6068
6187
|
td.textContent = cell.formattedValue;
|
|
6069
6188
|
applyCellStyle(td, cell);
|
|
6070
6189
|
return td;
|
|
6071
6190
|
}
|
|
6072
6191
|
function renderBarCell(cell) {
|
|
6073
6192
|
const td = document.createElement("td");
|
|
6074
|
-
td.className = "
|
|
6193
|
+
td.className = "oc-table-bar";
|
|
6075
6194
|
applyCellStyle(td, cell);
|
|
6076
6195
|
const fill = document.createElement("div");
|
|
6077
|
-
fill.className = "
|
|
6196
|
+
fill.className = "oc-table-bar-fill";
|
|
6078
6197
|
fill.style.width = `${Math.round(cell.barWidth * 100)}%`;
|
|
6079
6198
|
fill.style.left = `${Math.round(cell.barOffset * 100)}%`;
|
|
6080
6199
|
fill.style.background = cell.barColor;
|
|
6081
6200
|
td.appendChild(fill);
|
|
6082
6201
|
const valueSpan = document.createElement("span");
|
|
6083
|
-
valueSpan.className = "
|
|
6202
|
+
valueSpan.className = "oc-table-bar-value";
|
|
6084
6203
|
valueSpan.textContent = cell.formattedValue;
|
|
6085
6204
|
td.appendChild(valueSpan);
|
|
6086
6205
|
return td;
|
|
@@ -6107,7 +6226,7 @@ function renderSparklineCell(cell) {
|
|
|
6107
6226
|
td.setAttribute("aria-label", trendDescription);
|
|
6108
6227
|
}
|
|
6109
6228
|
const wrapper = document.createElement("span");
|
|
6110
|
-
wrapper.className = "
|
|
6229
|
+
wrapper.className = "oc-table-sparkline";
|
|
6111
6230
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
6112
6231
|
if (sparklineData.type === "line") {
|
|
6113
6232
|
const svgH = 28;
|
|
@@ -6139,19 +6258,19 @@ function renderSparklineCell(cell) {
|
|
|
6139
6258
|
const lastY = yPositions[yPositions.length - 1];
|
|
6140
6259
|
const dotSize = 5;
|
|
6141
6260
|
const startDot = document.createElement("span");
|
|
6142
|
-
startDot.className = "
|
|
6261
|
+
startDot.className = "oc-table-sparkline-dot";
|
|
6143
6262
|
startDot.style.left = "0";
|
|
6144
6263
|
startDot.style.top = `${firstY - dotSize / 2}px`;
|
|
6145
6264
|
startDot.style.background = sparklineData.color;
|
|
6146
6265
|
wrapper.appendChild(startDot);
|
|
6147
6266
|
const endDot = document.createElement("span");
|
|
6148
|
-
endDot.className = "
|
|
6267
|
+
endDot.className = "oc-table-sparkline-dot";
|
|
6149
6268
|
endDot.style.right = "0";
|
|
6150
6269
|
endDot.style.top = `${lastY - dotSize / 2}px`;
|
|
6151
6270
|
endDot.style.background = sparklineData.color;
|
|
6152
6271
|
wrapper.appendChild(endDot);
|
|
6153
6272
|
const labelsRow = document.createElement("span");
|
|
6154
|
-
labelsRow.className = "
|
|
6273
|
+
labelsRow.className = "oc-table-sparkline-labels";
|
|
6155
6274
|
labelsRow.style.color = sparklineData.color;
|
|
6156
6275
|
const startLabel = document.createElement("span");
|
|
6157
6276
|
startLabel.textContent = formatSparklineValue(sparklineData.startValue);
|
|
@@ -6228,7 +6347,7 @@ function renderImageCell(cell) {
|
|
|
6228
6347
|
const td = document.createElement("td");
|
|
6229
6348
|
applyCellStyle(td, cell);
|
|
6230
6349
|
const wrapper = document.createElement("span");
|
|
6231
|
-
wrapper.className = `
|
|
6350
|
+
wrapper.className = `oc-table-image${cell.rounded ? " oc-table-image-rounded" : ""}`;
|
|
6232
6351
|
const img = document.createElement("img");
|
|
6233
6352
|
img.src = cell.src;
|
|
6234
6353
|
img.alt = cell.formattedValue || "";
|
|
@@ -6243,7 +6362,7 @@ function renderFlagCell(cell) {
|
|
|
6243
6362
|
const td = document.createElement("td");
|
|
6244
6363
|
applyCellStyle(td, cell);
|
|
6245
6364
|
const span = document.createElement("span");
|
|
6246
|
-
span.className = "
|
|
6365
|
+
span.className = "oc-table-flag";
|
|
6247
6366
|
span.setAttribute("role", "img");
|
|
6248
6367
|
if (cell.countryCode && cell.countryCode.length === 2) {
|
|
6249
6368
|
const countryName = getCountryName(cell.countryCode);
|
|
@@ -6308,9 +6427,9 @@ function attachKeyboardNav(options) {
|
|
|
6308
6427
|
return getCellsInRow(rows[0]).length;
|
|
6309
6428
|
}
|
|
6310
6429
|
function clearFocusHighlight() {
|
|
6311
|
-
const prev = wrapper.querySelector(".
|
|
6430
|
+
const prev = wrapper.querySelector(".oc-table-cell-focus");
|
|
6312
6431
|
if (prev) {
|
|
6313
|
-
prev.classList.remove("
|
|
6432
|
+
prev.classList.remove("oc-table-cell-focus");
|
|
6314
6433
|
prev.removeAttribute("id");
|
|
6315
6434
|
}
|
|
6316
6435
|
}
|
|
@@ -6327,9 +6446,9 @@ function attachKeyboardNav(options) {
|
|
|
6327
6446
|
const cells = getCellsInRow(tr);
|
|
6328
6447
|
const cell = cells[col];
|
|
6329
6448
|
if (!cell) return;
|
|
6330
|
-
const cellId = `
|
|
6449
|
+
const cellId = `oc-cell-${row}-${col}`;
|
|
6331
6450
|
cell.id = cellId;
|
|
6332
|
-
cell.classList.add("
|
|
6451
|
+
cell.classList.add("oc-table-cell-focus");
|
|
6333
6452
|
cell.setAttribute("data-row", String(row));
|
|
6334
6453
|
cell.setAttribute("data-col", String(col));
|
|
6335
6454
|
if (tbody) {
|
|
@@ -6430,7 +6549,7 @@ function attachKeyboardNav(options) {
|
|
|
6430
6549
|
}
|
|
6431
6550
|
}
|
|
6432
6551
|
}
|
|
6433
|
-
const searchInput = wrapper.querySelector(".
|
|
6552
|
+
const searchInput = wrapper.querySelector(".oc-table-search input");
|
|
6434
6553
|
function handleSearchKeydown(e) {
|
|
6435
6554
|
if (e.key === "Escape") {
|
|
6436
6555
|
e.preventDefault();
|
|
@@ -6470,22 +6589,27 @@ import { compileTable } from "@opendata-ai/openchart-engine";
|
|
|
6470
6589
|
|
|
6471
6590
|
// src/table-renderer.ts
|
|
6472
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
|
+
};
|
|
6473
6597
|
var BRAND_URL2 = "https://tryopendata.ai";
|
|
6474
6598
|
function renderChromeBlock(layout, position) {
|
|
6475
6599
|
const chrome = layout.chrome;
|
|
6476
6600
|
if (position === "header") {
|
|
6477
6601
|
if (!chrome.title && !chrome.subtitle) return null;
|
|
6478
6602
|
const div2 = document.createElement("div");
|
|
6479
|
-
div2.className = "
|
|
6603
|
+
div2.className = "oc-chrome";
|
|
6480
6604
|
if (chrome.title) {
|
|
6481
6605
|
const h = document.createElement("div");
|
|
6482
|
-
h.className = "
|
|
6606
|
+
h.className = "oc-table-title";
|
|
6483
6607
|
h.textContent = chrome.title.text;
|
|
6484
6608
|
div2.appendChild(h);
|
|
6485
6609
|
}
|
|
6486
6610
|
if (chrome.subtitle) {
|
|
6487
6611
|
const sub = document.createElement("div");
|
|
6488
|
-
sub.className = "
|
|
6612
|
+
sub.className = "oc-table-subtitle";
|
|
6489
6613
|
sub.textContent = chrome.subtitle.text;
|
|
6490
6614
|
div2.appendChild(sub);
|
|
6491
6615
|
}
|
|
@@ -6493,16 +6617,16 @@ function renderChromeBlock(layout, position) {
|
|
|
6493
6617
|
}
|
|
6494
6618
|
if (!chrome.source && !chrome.footer) return null;
|
|
6495
6619
|
const div = document.createElement("div");
|
|
6496
|
-
div.className = "
|
|
6620
|
+
div.className = "oc-chrome oc-chrome-footer";
|
|
6497
6621
|
if (chrome.source) {
|
|
6498
6622
|
const src = document.createElement("div");
|
|
6499
|
-
src.className = "
|
|
6623
|
+
src.className = "oc-table-source";
|
|
6500
6624
|
src.textContent = chrome.source.text;
|
|
6501
6625
|
div.appendChild(src);
|
|
6502
6626
|
}
|
|
6503
6627
|
if (chrome.footer) {
|
|
6504
6628
|
const foot = document.createElement("div");
|
|
6505
|
-
foot.className = "
|
|
6629
|
+
foot.className = "oc-table-footer-text";
|
|
6506
6630
|
foot.textContent = chrome.footer.text;
|
|
6507
6631
|
div.appendChild(foot);
|
|
6508
6632
|
}
|
|
@@ -6528,7 +6652,7 @@ function renderThead(columns, sort) {
|
|
|
6528
6652
|
th.appendChild(labelSpan);
|
|
6529
6653
|
if (col.sortable) {
|
|
6530
6654
|
const btn = document.createElement("button");
|
|
6531
|
-
btn.className = "
|
|
6655
|
+
btn.className = "oc-table-sort-btn";
|
|
6532
6656
|
btn.setAttribute("aria-label", `Sort by ${col.label}`);
|
|
6533
6657
|
btn.setAttribute("data-sort-column", col.key);
|
|
6534
6658
|
btn.type = "button";
|
|
@@ -6546,6 +6670,7 @@ function renderTbody(rows, columns) {
|
|
|
6546
6670
|
const tr = document.createElement("tr");
|
|
6547
6671
|
tr.setAttribute("role", "row");
|
|
6548
6672
|
tr.setAttribute("data-row-id", row.id);
|
|
6673
|
+
tr.style.setProperty("--oc-row-index", String(r));
|
|
6549
6674
|
for (let c2 = 0; c2 < columns.length; c2++) {
|
|
6550
6675
|
const cell = row.cells[c2];
|
|
6551
6676
|
if (!cell) continue;
|
|
@@ -6561,7 +6686,7 @@ function renderTbody(rows, columns) {
|
|
|
6561
6686
|
function renderSearchBar(layout) {
|
|
6562
6687
|
if (!layout.search.enabled) return null;
|
|
6563
6688
|
const div = document.createElement("div");
|
|
6564
|
-
div.className = "
|
|
6689
|
+
div.className = "oc-table-search";
|
|
6565
6690
|
const input = document.createElement("input");
|
|
6566
6691
|
input.type = "search";
|
|
6567
6692
|
input.placeholder = layout.search.placeholder;
|
|
@@ -6574,9 +6699,9 @@ function renderPagination(layout) {
|
|
|
6574
6699
|
if (!layout.pagination) return null;
|
|
6575
6700
|
const { page, pageSize, totalRows, totalPages } = layout.pagination;
|
|
6576
6701
|
const div = document.createElement("div");
|
|
6577
|
-
div.className = "
|
|
6702
|
+
div.className = "oc-table-pagination";
|
|
6578
6703
|
const info = document.createElement("span");
|
|
6579
|
-
info.className = "
|
|
6704
|
+
info.className = "oc-table-pagination-info";
|
|
6580
6705
|
if (totalRows === 0) {
|
|
6581
6706
|
info.textContent = "No results";
|
|
6582
6707
|
} else {
|
|
@@ -6586,7 +6711,7 @@ function renderPagination(layout) {
|
|
|
6586
6711
|
}
|
|
6587
6712
|
div.appendChild(info);
|
|
6588
6713
|
const btnGroup = document.createElement("span");
|
|
6589
|
-
btnGroup.className = "
|
|
6714
|
+
btnGroup.className = "oc-table-pagination-btns";
|
|
6590
6715
|
const prevBtn = document.createElement("button");
|
|
6591
6716
|
prevBtn.setAttribute("aria-label", "Previous page");
|
|
6592
6717
|
prevBtn.setAttribute("data-page-action", "prev");
|
|
@@ -6604,49 +6729,49 @@ function renderPagination(layout) {
|
|
|
6604
6729
|
}
|
|
6605
6730
|
function renderEmptyState(message) {
|
|
6606
6731
|
const div = document.createElement("div");
|
|
6607
|
-
div.className = "
|
|
6732
|
+
div.className = "oc-table-empty";
|
|
6608
6733
|
div.setAttribute("aria-live", "polite");
|
|
6609
6734
|
div.textContent = message;
|
|
6610
6735
|
return div;
|
|
6611
6736
|
}
|
|
6612
|
-
function renderTable(layout, container) {
|
|
6737
|
+
function renderTable(layout, container, opts) {
|
|
6613
6738
|
const wrapper = document.createElement("div");
|
|
6614
|
-
wrapper.className = "
|
|
6739
|
+
wrapper.className = "oc-table-wrapper";
|
|
6615
6740
|
const { theme, chrome } = layout;
|
|
6616
6741
|
if (theme) {
|
|
6617
6742
|
const s = wrapper.style;
|
|
6618
|
-
s.setProperty("--
|
|
6619
|
-
s.setProperty("--
|
|
6620
|
-
s.setProperty("--
|
|
6621
|
-
s.setProperty("--
|
|
6622
|
-
s.setProperty("--
|
|
6623
|
-
s.setProperty("--
|
|
6624
|
-
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);
|
|
6625
6750
|
s.fontFamily = theme.fonts.family;
|
|
6626
6751
|
}
|
|
6627
6752
|
{
|
|
6628
6753
|
const s = wrapper.style;
|
|
6629
6754
|
if (chrome.title) {
|
|
6630
|
-
s.setProperty("--
|
|
6631
|
-
s.setProperty("--
|
|
6632
|
-
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);
|
|
6633
6758
|
}
|
|
6634
6759
|
if (chrome.subtitle) {
|
|
6635
|
-
s.setProperty("--
|
|
6636
|
-
s.setProperty("--
|
|
6637
|
-
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);
|
|
6638
6763
|
}
|
|
6639
6764
|
if (chrome.source) {
|
|
6640
|
-
s.setProperty("--
|
|
6641
|
-
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);
|
|
6642
6767
|
}
|
|
6643
6768
|
if (chrome.footer) {
|
|
6644
|
-
s.setProperty("--
|
|
6645
|
-
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);
|
|
6646
6771
|
}
|
|
6647
6772
|
}
|
|
6648
6773
|
if (layout.compact) {
|
|
6649
|
-
wrapper.classList.add("
|
|
6774
|
+
wrapper.classList.add("oc-table--compact");
|
|
6650
6775
|
}
|
|
6651
6776
|
const headerChrome = renderChromeBlock(layout, "header");
|
|
6652
6777
|
if (headerChrome) {
|
|
@@ -6661,15 +6786,15 @@ function renderTable(layout, container) {
|
|
|
6661
6786
|
wrapper.appendChild(renderEmptyState(message));
|
|
6662
6787
|
} else {
|
|
6663
6788
|
const scroll = document.createElement("div");
|
|
6664
|
-
scroll.className = "
|
|
6789
|
+
scroll.className = "oc-table-scroll";
|
|
6665
6790
|
const table = document.createElement("table");
|
|
6666
6791
|
table.setAttribute("role", "grid");
|
|
6667
6792
|
table.setAttribute("aria-label", layout.a11y.caption);
|
|
6668
6793
|
if (layout.stickyFirstColumn) {
|
|
6669
|
-
table.classList.add("
|
|
6794
|
+
table.classList.add("oc-table--sticky");
|
|
6670
6795
|
}
|
|
6671
6796
|
const caption = document.createElement("caption");
|
|
6672
|
-
caption.className = "
|
|
6797
|
+
caption.className = "oc-sr-only";
|
|
6673
6798
|
caption.style.position = "absolute";
|
|
6674
6799
|
caption.style.width = "1px";
|
|
6675
6800
|
caption.style.height = "1px";
|
|
@@ -6695,7 +6820,7 @@ function renderTable(layout, container) {
|
|
|
6695
6820
|
wrapper.appendChild(footerChrome);
|
|
6696
6821
|
}
|
|
6697
6822
|
const liveRegion = document.createElement("div");
|
|
6698
|
-
liveRegion.className = "
|
|
6823
|
+
liveRegion.className = "oc-table-live-region oc-sr-only";
|
|
6699
6824
|
liveRegion.style.position = "absolute";
|
|
6700
6825
|
liveRegion.style.width = "1px";
|
|
6701
6826
|
liveRegion.style.height = "1px";
|
|
@@ -6711,7 +6836,7 @@ function renderTable(layout, container) {
|
|
|
6711
6836
|
wrapper.appendChild(liveRegion);
|
|
6712
6837
|
const brandColor = theme ? theme.colors.axis : "#999999";
|
|
6713
6838
|
const brand = document.createElement("div");
|
|
6714
|
-
brand.className = "
|
|
6839
|
+
brand.className = "oc-table-ref";
|
|
6715
6840
|
brand.style.cssText = "text-align: right; padding: 4px 8px;";
|
|
6716
6841
|
const brandLink = document.createElement("a");
|
|
6717
6842
|
brandLink.href = BRAND_URL2;
|
|
@@ -6721,6 +6846,16 @@ function renderTable(layout, container) {
|
|
|
6721
6846
|
brandLink.textContent = "OpenData";
|
|
6722
6847
|
brand.appendChild(brandLink);
|
|
6723
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
|
+
}
|
|
6724
6859
|
container.appendChild(wrapper);
|
|
6725
6860
|
return wrapper;
|
|
6726
6861
|
}
|
|
@@ -6755,6 +6890,8 @@ function createTable(container, spec, options) {
|
|
|
6755
6890
|
let wrapperElement = null;
|
|
6756
6891
|
let disconnectResize = null;
|
|
6757
6892
|
let cleanupKeyboard = null;
|
|
6893
|
+
let cleanupAnimations = null;
|
|
6894
|
+
let isFirstRender = true;
|
|
6758
6895
|
let destroyed = false;
|
|
6759
6896
|
const internalState = {
|
|
6760
6897
|
sort: null,
|
|
@@ -6814,7 +6951,7 @@ function createTable(container, spec, options) {
|
|
|
6814
6951
|
}
|
|
6815
6952
|
function announce(message) {
|
|
6816
6953
|
if (!wrapperElement) return;
|
|
6817
|
-
const liveRegion = wrapperElement.querySelector(".
|
|
6954
|
+
const liveRegion = wrapperElement.querySelector(".oc-table-live-region");
|
|
6818
6955
|
if (liveRegion) {
|
|
6819
6956
|
liveRegion.textContent = message;
|
|
6820
6957
|
}
|
|
@@ -6824,14 +6961,18 @@ function createTable(container, spec, options) {
|
|
|
6824
6961
|
const { width } = getContainerDimensions();
|
|
6825
6962
|
const bp = getBreakpoint(width);
|
|
6826
6963
|
if (bp === "compact" || bp === "medium") {
|
|
6827
|
-
wrapperElement.classList.add("
|
|
6964
|
+
wrapperElement.classList.add("oc-table--compact");
|
|
6828
6965
|
} else if (!currentLayout?.compact) {
|
|
6829
|
-
wrapperElement.classList.remove("
|
|
6966
|
+
wrapperElement.classList.remove("oc-table--compact");
|
|
6830
6967
|
}
|
|
6831
6968
|
}
|
|
6832
6969
|
function render() {
|
|
6833
6970
|
if (destroyed) return;
|
|
6834
6971
|
try {
|
|
6972
|
+
if (cleanupAnimations) {
|
|
6973
|
+
cleanupAnimations();
|
|
6974
|
+
cleanupAnimations = null;
|
|
6975
|
+
}
|
|
6835
6976
|
if (cleanupKeyboard) {
|
|
6836
6977
|
cleanupKeyboard();
|
|
6837
6978
|
cleanupKeyboard = null;
|
|
@@ -6841,16 +6982,23 @@ function createTable(container, spec, options) {
|
|
|
6841
6982
|
wrapperElement = null;
|
|
6842
6983
|
}
|
|
6843
6984
|
currentLayout = compile();
|
|
6844
|
-
|
|
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
|
+
}
|
|
6845
6993
|
const isDark = resolveDarkMode3(options?.darkMode);
|
|
6846
6994
|
if (isDark) {
|
|
6847
|
-
container.classList.add("
|
|
6995
|
+
container.classList.add("oc-dark");
|
|
6848
6996
|
} else {
|
|
6849
|
-
container.classList.remove("
|
|
6997
|
+
container.classList.remove("oc-dark");
|
|
6850
6998
|
}
|
|
6851
6999
|
applyBreakpointClass();
|
|
6852
7000
|
if (options?.onRowClick) {
|
|
6853
|
-
wrapperElement.classList.add("
|
|
7001
|
+
wrapperElement.classList.add("oc-table--clickable");
|
|
6854
7002
|
}
|
|
6855
7003
|
wireEvents();
|
|
6856
7004
|
if (wrapperElement) {
|
|
@@ -6890,7 +7038,7 @@ function createTable(container, spec, options) {
|
|
|
6890
7038
|
btn.addEventListener("click", handleSortClick);
|
|
6891
7039
|
}
|
|
6892
7040
|
const searchInput = wrapperElement.querySelector(
|
|
6893
|
-
".
|
|
7041
|
+
".oc-table-search input"
|
|
6894
7042
|
);
|
|
6895
7043
|
if (searchInput) {
|
|
6896
7044
|
searchInput.addEventListener("input", handleSearchInput);
|
|
@@ -6966,7 +7114,7 @@ function createTable(container, spec, options) {
|
|
|
6966
7114
|
function rerender() {
|
|
6967
7115
|
if (destroyed) return;
|
|
6968
7116
|
const searchInput = wrapperElement?.querySelector(
|
|
6969
|
-
".
|
|
7117
|
+
".oc-table-search input"
|
|
6970
7118
|
);
|
|
6971
7119
|
const hadFocus = searchInput && document.activeElement === searchInput;
|
|
6972
7120
|
const selectionStart = searchInput?.selectionStart ?? 0;
|
|
@@ -6974,7 +7122,7 @@ function createTable(container, spec, options) {
|
|
|
6974
7122
|
render();
|
|
6975
7123
|
if (hadFocus) {
|
|
6976
7124
|
const newInput = wrapperElement?.querySelector(
|
|
6977
|
-
".
|
|
7125
|
+
".oc-table-search input"
|
|
6978
7126
|
);
|
|
6979
7127
|
if (newInput) {
|
|
6980
7128
|
newInput.focus();
|
|
@@ -6989,6 +7137,7 @@ function createTable(container, spec, options) {
|
|
|
6989
7137
|
}
|
|
6990
7138
|
function resize() {
|
|
6991
7139
|
if (destroyed) return;
|
|
7140
|
+
if (cleanupAnimations) return;
|
|
6992
7141
|
render();
|
|
6993
7142
|
}
|
|
6994
7143
|
function doExport(format) {
|
|
@@ -7025,6 +7174,10 @@ function createTable(container, spec, options) {
|
|
|
7025
7174
|
function destroy() {
|
|
7026
7175
|
if (destroyed) return;
|
|
7027
7176
|
destroyed = true;
|
|
7177
|
+
if (cleanupAnimations) {
|
|
7178
|
+
cleanupAnimations();
|
|
7179
|
+
cleanupAnimations = null;
|
|
7180
|
+
}
|
|
7028
7181
|
if (cleanupKeyboard) {
|
|
7029
7182
|
cleanupKeyboard();
|
|
7030
7183
|
cleanupKeyboard = null;
|
|
@@ -7045,7 +7198,7 @@ function createTable(container, spec, options) {
|
|
|
7045
7198
|
wrapperElement.parentNode.removeChild(wrapperElement);
|
|
7046
7199
|
wrapperElement = null;
|
|
7047
7200
|
}
|
|
7048
|
-
container.classList.remove("
|
|
7201
|
+
container.classList.remove("oc-dark");
|
|
7049
7202
|
}
|
|
7050
7203
|
render();
|
|
7051
7204
|
if (options?.responsive !== false) {
|