@opendata-ai/openchart-vanilla 7.7.0 → 7.9.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/{chunk-PSMDJEXK.js → chunk-QRE4FQQM.js} +380 -14
- package/dist/chunk-QRE4FQQM.js.map +1 -0
- package/dist/index.d.ts +26 -19
- package/dist/index.js +1755 -378
- package/dist/index.js.map +1 -1
- package/dist/static.d.ts +2 -2
- package/dist/static.js +37 -27
- package/dist/static.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/animation.test.ts +1 -1
- package/src/__tests__/transition.test.ts +1592 -0
- package/src/barlist-mount.ts +1 -1
- package/src/barlist-renderer.ts +4 -4
- package/src/index.ts +2 -0
- package/src/mount.ts +64 -1
- package/src/renderers/axes.ts +18 -0
- package/src/renderers/marks.ts +21 -7
- package/src/sankey-mount.ts +2 -2
- package/src/sankey-renderer.ts +9 -8
- package/src/static.ts +42 -32
- package/src/svg-renderer.ts +8 -7
- package/src/table-mount.ts +1 -1
- package/src/table-renderer.ts +5 -5
- package/src/tilemap-mount.ts +1 -1
- package/src/tilemap-renderer.ts +8 -9
- package/src/transition.ts +2473 -0
- package/dist/chunk-PSMDJEXK.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildGradientDefs,
|
|
3
|
+
rectPathWithCorners,
|
|
2
4
|
registerMarkRenderer,
|
|
3
5
|
renderChartSVG,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
renderSingleMark,
|
|
7
|
+
renderTileMapSVG,
|
|
8
|
+
resolveMarkFill
|
|
9
|
+
} from "./chunk-QRE4FQQM.js";
|
|
6
10
|
|
|
7
11
|
// src/barlist-mount.ts
|
|
8
12
|
import { compileBarList } from "@opendata-ai/openchart-engine";
|
|
@@ -134,7 +138,7 @@ function renderRows(parent, rows, animation) {
|
|
|
134
138
|
if (row.aria?.label) {
|
|
135
139
|
rowGroup.setAttribute("aria-label", row.aria.label);
|
|
136
140
|
}
|
|
137
|
-
if (animation?.
|
|
141
|
+
if (animation?.enter) {
|
|
138
142
|
rowGroup.setAttribute("data-animation-index", String(row.animationIndex));
|
|
139
143
|
const style = rowGroup.style;
|
|
140
144
|
style.setProperty("--oc-mark-index", String(row.animationIndex));
|
|
@@ -216,7 +220,7 @@ function renderRows(parent, rows, animation) {
|
|
|
216
220
|
}
|
|
217
221
|
function renderBarListSVG(layout, opts) {
|
|
218
222
|
const { width, height, rows, a11y, watermark, animation } = layout;
|
|
219
|
-
const animate = opts?.animate && animation?.
|
|
223
|
+
const animate = opts?.animate && !!animation?.enter;
|
|
220
224
|
const svg = createSVGElement("svg");
|
|
221
225
|
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
222
226
|
svg.setAttribute("role", "list");
|
|
@@ -226,8 +230,8 @@ function renderBarListSVG(layout, opts) {
|
|
|
226
230
|
}
|
|
227
231
|
const classes = animate ? "oc-barlist oc-animate" : "oc-barlist";
|
|
228
232
|
svg.setAttribute("class", classes);
|
|
229
|
-
if (animate && animation) {
|
|
230
|
-
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
233
|
+
if (animate && animation?.enter) {
|
|
234
|
+
svg.style.setProperty("--oc-animation-duration", `${animation.enter.duration}ms`);
|
|
231
235
|
svg.style.setProperty("--oc-animation-stagger", "40ms");
|
|
232
236
|
}
|
|
233
237
|
renderChrome(svg, layout);
|
|
@@ -805,7 +809,7 @@ function createBarList(container, spec, options) {
|
|
|
805
809
|
tooltipManager = createTooltipManager(container);
|
|
806
810
|
}
|
|
807
811
|
}
|
|
808
|
-
if (currentLayout.animation?.
|
|
812
|
+
if (currentLayout.animation?.enter) {
|
|
809
813
|
animationCleanup = setupAnimationCleanup(newSvg, () => {
|
|
810
814
|
if (pendingResize && !destroyed) {
|
|
811
815
|
pendingResize = false;
|
|
@@ -3370,7 +3374,7 @@ function createGraph(container, spec, options) {
|
|
|
3370
3374
|
}
|
|
3371
3375
|
chromeEl = document.createElement("div");
|
|
3372
3376
|
chromeEl.className = "oc-graph-chrome";
|
|
3373
|
-
|
|
3377
|
+
renderChrome3();
|
|
3374
3378
|
wrapper.appendChild(chromeEl);
|
|
3375
3379
|
canvas = document.createElement("canvas");
|
|
3376
3380
|
canvas.className = "oc-graph-canvas";
|
|
@@ -3382,7 +3386,7 @@ function createGraph(container, spec, options) {
|
|
|
3382
3386
|
if (options?.legend !== false) {
|
|
3383
3387
|
legendEl = document.createElement("div");
|
|
3384
3388
|
legendEl.className = "oc-graph-legend";
|
|
3385
|
-
|
|
3389
|
+
renderLegend2();
|
|
3386
3390
|
wrapper.appendChild(legendEl);
|
|
3387
3391
|
}
|
|
3388
3392
|
container.appendChild(wrapper);
|
|
@@ -3390,7 +3394,7 @@ function createGraph(container, spec, options) {
|
|
|
3390
3394
|
renderer = new GraphCanvasRenderer(canvas);
|
|
3391
3395
|
renderer.resize(width, canvasHeight);
|
|
3392
3396
|
}
|
|
3393
|
-
function
|
|
3397
|
+
function renderChrome3() {
|
|
3394
3398
|
if (!chromeEl) return;
|
|
3395
3399
|
let html = "";
|
|
3396
3400
|
if (compilation.chrome.title) {
|
|
@@ -3406,7 +3410,7 @@ function createGraph(container, spec, options) {
|
|
|
3406
3410
|
chromeEl.style.display = "";
|
|
3407
3411
|
}
|
|
3408
3412
|
}
|
|
3409
|
-
function
|
|
3413
|
+
function renderLegend2() {
|
|
3410
3414
|
if (!legendEl) return;
|
|
3411
3415
|
const entries = "entries" in compilation.legend ? compilation.legend.entries : [];
|
|
3412
3416
|
if (entries.length === 0) {
|
|
@@ -3704,8 +3708,8 @@ function createGraph(container, spec, options) {
|
|
|
3704
3708
|
compilation = compile();
|
|
3705
3709
|
adjacencyMap = buildAdjacencyMap(compilation.edges);
|
|
3706
3710
|
buildDataMaps();
|
|
3707
|
-
|
|
3708
|
-
|
|
3711
|
+
renderChrome3();
|
|
3712
|
+
renderLegend2();
|
|
3709
3713
|
initSimulation();
|
|
3710
3714
|
initInteraction();
|
|
3711
3715
|
hoveredNodeId = null;
|
|
@@ -3739,8 +3743,8 @@ function createGraph(container, spec, options) {
|
|
|
3739
3743
|
};
|
|
3740
3744
|
});
|
|
3741
3745
|
spatialIndex.rebuild(positionedNodes);
|
|
3742
|
-
|
|
3743
|
-
|
|
3746
|
+
renderChrome3();
|
|
3747
|
+
renderLegend2();
|
|
3744
3748
|
needsRender = true;
|
|
3745
3749
|
scheduleRender();
|
|
3746
3750
|
}
|
|
@@ -4335,7 +4339,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4335
4339
|
};
|
|
4336
4340
|
}
|
|
4337
4341
|
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4338
|
-
const
|
|
4342
|
+
const SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
4339
4343
|
const cleanups = [];
|
|
4340
4344
|
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4341
4345
|
for (const el of annotationGroups) {
|
|
@@ -4374,7 +4378,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4374
4378
|
const createdHandles = [];
|
|
4375
4379
|
for (const ep of endpoints) {
|
|
4376
4380
|
if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
|
|
4377
|
-
const handleEl = document.createElementNS(
|
|
4381
|
+
const handleEl = document.createElementNS(SVG_NS3, "circle");
|
|
4378
4382
|
handleEl.setAttribute("class", "oc-connector-handle");
|
|
4379
4383
|
handleEl.setAttribute("data-endpoint", ep.name);
|
|
4380
4384
|
handleEl.setAttribute("cx", String(ep.cx));
|
|
@@ -5177,6 +5181,1680 @@ function stampThemeProperties(el, theme) {
|
|
|
5177
5181
|
}
|
|
5178
5182
|
}
|
|
5179
5183
|
|
|
5184
|
+
// src/transition.ts
|
|
5185
|
+
import { isGradientDef } from "@opendata-ai/openchart-core";
|
|
5186
|
+
import {
|
|
5187
|
+
buildAreaPath,
|
|
5188
|
+
buildLinePath,
|
|
5189
|
+
EXIT_DEFAULTS,
|
|
5190
|
+
serializeKeyValue
|
|
5191
|
+
} from "@opendata-ai/openchart-engine";
|
|
5192
|
+
function cubicOut(t) {
|
|
5193
|
+
const f = 1 - t;
|
|
5194
|
+
return 1 - f * f * f;
|
|
5195
|
+
}
|
|
5196
|
+
var TRANSITIONABLE_MARKS = /* @__PURE__ */ new Set(["bar", "line", "area", "point"]);
|
|
5197
|
+
function canTransition(args) {
|
|
5198
|
+
const { prevLayout, nextLayout, prevSpec, nextSpec, isFirstRender, entranceInFlight } = args;
|
|
5199
|
+
if (!prevLayout || !prevSpec || isFirstRender) return false;
|
|
5200
|
+
if (!nextLayout.animation?.update) return false;
|
|
5201
|
+
const prev = prevSpec;
|
|
5202
|
+
const next = nextSpec;
|
|
5203
|
+
if (!("mark" in prev) || !("mark" in next)) return false;
|
|
5204
|
+
const prevMark = typeof prev.mark === "string" ? prev.mark : prev.mark?.type;
|
|
5205
|
+
const nextMark = typeof next.mark === "string" ? next.mark : next.mark?.type;
|
|
5206
|
+
if (prevMark !== nextMark) return false;
|
|
5207
|
+
if (!TRANSITIONABLE_MARKS.has(prevMark)) return false;
|
|
5208
|
+
const prevEnc = prev.encoding;
|
|
5209
|
+
const nextEnc = next.encoding;
|
|
5210
|
+
if (!prevEnc || !nextEnc) return false;
|
|
5211
|
+
if (prevEnc.x?.field !== nextEnc.x?.field || prevEnc.x?.type !== nextEnc.x?.type || prevEnc.y?.field !== nextEnc.y?.field || prevEnc.y?.type !== nextEnc.y?.type || prevEnc.color?.field !== nextEnc.color?.field) {
|
|
5212
|
+
return false;
|
|
5213
|
+
}
|
|
5214
|
+
if (nextLayout.display === "sparkline") return false;
|
|
5215
|
+
if (entranceInFlight) return false;
|
|
5216
|
+
if (prevLayout.dimensions.width !== nextLayout.dimensions.width || prevLayout.dimensions.height !== nextLayout.dimensions.height) {
|
|
5217
|
+
return false;
|
|
5218
|
+
}
|
|
5219
|
+
if (nextLayout.marks.length > 500) return false;
|
|
5220
|
+
if (!hasGeometryChanged(prevLayout, nextLayout)) return false;
|
|
5221
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
5222
|
+
try {
|
|
5223
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
|
|
5224
|
+
} catch {
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
return true;
|
|
5228
|
+
}
|
|
5229
|
+
function hasGeometryChanged(prevLayout, nextLayout) {
|
|
5230
|
+
const prevMarks = prevLayout.marks;
|
|
5231
|
+
const nextMarks = nextLayout.marks;
|
|
5232
|
+
if (prevMarks.length !== nextMarks.length) return true;
|
|
5233
|
+
for (let i = 0; i < prevMarks.length; i++) {
|
|
5234
|
+
const p = prevMarks[i];
|
|
5235
|
+
const n = nextMarks[i];
|
|
5236
|
+
if (p.type !== n.type || p.key !== n.key) return true;
|
|
5237
|
+
if (p.type === "rect" && n.type === "rect") {
|
|
5238
|
+
if (p.x !== n.x || p.y !== n.y || p.width !== n.width || p.height !== n.height) return true;
|
|
5239
|
+
} else if (p.type === "line" && n.type === "line") {
|
|
5240
|
+
if (p.path !== n.path || p.points.length !== n.points.length) return true;
|
|
5241
|
+
for (let j = 0; j < p.points.length; j++) {
|
|
5242
|
+
if (p.points[j].x !== n.points[j].x || p.points[j].y !== n.points[j].y) return true;
|
|
5243
|
+
}
|
|
5244
|
+
} else if (p.type === "area" && n.type === "area") {
|
|
5245
|
+
if (p.path !== n.path) return true;
|
|
5246
|
+
} else if (p.type === "point" && n.type === "point") {
|
|
5247
|
+
if (p.cx !== n.cx || p.cy !== n.cy || p.r !== n.r) return true;
|
|
5248
|
+
} else if (p.type === "rule" && n.type === "rule") {
|
|
5249
|
+
if (p.x1 !== n.x1 || p.y1 !== n.y1 || p.x2 !== n.x2 || p.y2 !== n.y2) return true;
|
|
5250
|
+
} else if (p.type === "tick" && n.type === "tick") {
|
|
5251
|
+
if (p.x !== n.x || p.y !== n.y) return true;
|
|
5252
|
+
} else if (p.type === "textMark" && n.type === "textMark") {
|
|
5253
|
+
if (p.x !== n.x || p.y !== n.y) return true;
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
return false;
|
|
5257
|
+
}
|
|
5258
|
+
function geomFromMark(m2) {
|
|
5259
|
+
return { x: m2.x, y: m2.y, width: m2.width, height: m2.height };
|
|
5260
|
+
}
|
|
5261
|
+
function applyGeomToElement(el, geom, mark) {
|
|
5262
|
+
const shapeEl = el.querySelector("rect, path");
|
|
5263
|
+
if (!shapeEl) return;
|
|
5264
|
+
const sides = mark.cornerRadiusSides;
|
|
5265
|
+
const partialCorners = !!sides && (!sides.tl || !sides.tr || !sides.br || !sides.bl) && !!mark.cornerRadius;
|
|
5266
|
+
if (partialCorners && shapeEl.tagName === "path") {
|
|
5267
|
+
const tempMark = { ...mark, ...geom };
|
|
5268
|
+
shapeEl.setAttribute("d", rectPathWithCorners(tempMark, sides));
|
|
5269
|
+
} else {
|
|
5270
|
+
shapeEl.setAttribute("x", String(geom.x));
|
|
5271
|
+
shapeEl.setAttribute("y", String(geom.y));
|
|
5272
|
+
shapeEl.setAttribute("width", String(geom.width));
|
|
5273
|
+
shapeEl.setAttribute("height", String(geom.height));
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
function lerpGeom(from, to, t) {
|
|
5277
|
+
return {
|
|
5278
|
+
x: from.x + (to.x - from.x) * t,
|
|
5279
|
+
y: from.y + (to.y - from.y) * t,
|
|
5280
|
+
width: from.width + (to.width - from.width) * t,
|
|
5281
|
+
height: from.height + (to.height - from.height) * t
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
function lerpPoint(a2, b, t) {
|
|
5285
|
+
return { x: a2.x + (b.x - a2.x) * t, y: a2.y + (b.y - a2.y) * t };
|
|
5286
|
+
}
|
|
5287
|
+
function normalizePointArrays(prevPoints, nextPoints, prevKeys, nextKeys) {
|
|
5288
|
+
const nextKeySet = new Set(nextKeys);
|
|
5289
|
+
const survivors = prevKeys.filter((k) => nextKeySet.has(k));
|
|
5290
|
+
if (survivors.length === 0) {
|
|
5291
|
+
return [prevPoints, nextPoints];
|
|
5292
|
+
}
|
|
5293
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
5294
|
+
for (let i = 0; i < prevKeys.length; i++) {
|
|
5295
|
+
prevByKey.set(prevKeys[i], prevPoints[i]);
|
|
5296
|
+
}
|
|
5297
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
5298
|
+
for (let i = 0; i < nextKeys.length; i++) {
|
|
5299
|
+
nextByKey.set(nextKeys[i], nextPoints[i]);
|
|
5300
|
+
}
|
|
5301
|
+
const merged = mergeKeyOrder(prevKeys, nextKeys);
|
|
5302
|
+
const mergedIndex = /* @__PURE__ */ new Map();
|
|
5303
|
+
for (let i = 0; i < merged.length; i++) {
|
|
5304
|
+
mergedIndex.set(merged[i], i);
|
|
5305
|
+
}
|
|
5306
|
+
const fromPts = [];
|
|
5307
|
+
const toPts = [];
|
|
5308
|
+
for (const key of merged) {
|
|
5309
|
+
const inPrev = prevByKey.has(key);
|
|
5310
|
+
const inNext = nextByKey.has(key);
|
|
5311
|
+
if (inPrev && inNext) {
|
|
5312
|
+
fromPts.push(prevByKey.get(key));
|
|
5313
|
+
toPts.push(nextByKey.get(key));
|
|
5314
|
+
} else if (inNext && !inPrev) {
|
|
5315
|
+
const nextPt = nextByKey.get(key);
|
|
5316
|
+
toPts.push(nextPt);
|
|
5317
|
+
fromPts.push(findInsertionPosition(key, merged, mergedIndex, prevByKey, nextByKey, nextPt));
|
|
5318
|
+
} else if (inPrev && !inNext) {
|
|
5319
|
+
const prevPt = prevByKey.get(key);
|
|
5320
|
+
fromPts.push(prevPt);
|
|
5321
|
+
toPts.push(findRemovalPosition(key, merged, mergedIndex, prevByKey, nextByKey, prevPt));
|
|
5322
|
+
}
|
|
5323
|
+
}
|
|
5324
|
+
return [fromPts, toPts];
|
|
5325
|
+
}
|
|
5326
|
+
function mergeKeyOrder(prevKeys, nextKeys) {
|
|
5327
|
+
const nextIndex = /* @__PURE__ */ new Map();
|
|
5328
|
+
for (let i = 0; i < nextKeys.length; i++) {
|
|
5329
|
+
if (!nextIndex.has(nextKeys[i])) nextIndex.set(nextKeys[i], i);
|
|
5330
|
+
}
|
|
5331
|
+
const prevIndex = /* @__PURE__ */ new Map();
|
|
5332
|
+
for (let i = 0; i < prevKeys.length; i++) {
|
|
5333
|
+
if (!prevIndex.has(prevKeys[i])) prevIndex.set(prevKeys[i], i);
|
|
5334
|
+
}
|
|
5335
|
+
const result = [];
|
|
5336
|
+
const added = /* @__PURE__ */ new Set();
|
|
5337
|
+
let pi = 0;
|
|
5338
|
+
let ni = 0;
|
|
5339
|
+
while (pi < prevKeys.length && ni < nextKeys.length) {
|
|
5340
|
+
const pk = prevKeys[pi];
|
|
5341
|
+
const nk = nextKeys[ni];
|
|
5342
|
+
if (added.has(pk)) {
|
|
5343
|
+
pi++;
|
|
5344
|
+
continue;
|
|
5345
|
+
}
|
|
5346
|
+
if (added.has(nk)) {
|
|
5347
|
+
ni++;
|
|
5348
|
+
continue;
|
|
5349
|
+
}
|
|
5350
|
+
if (pk === nk) {
|
|
5351
|
+
result.push(pk);
|
|
5352
|
+
added.add(pk);
|
|
5353
|
+
pi++;
|
|
5354
|
+
ni++;
|
|
5355
|
+
} else {
|
|
5356
|
+
const pkInNext = nextIndex.has(pk) && nextIndex.get(pk) >= ni ? nextIndex.get(pk) : -1;
|
|
5357
|
+
const nkInPrev = prevIndex.has(nk) && prevIndex.get(nk) >= pi ? prevIndex.get(nk) : -1;
|
|
5358
|
+
if (pkInNext === -1 && nkInPrev === -1) {
|
|
5359
|
+
result.push(pk);
|
|
5360
|
+
added.add(pk);
|
|
5361
|
+
pi++;
|
|
5362
|
+
} else if (pkInNext === -1) {
|
|
5363
|
+
result.push(pk);
|
|
5364
|
+
added.add(pk);
|
|
5365
|
+
pi++;
|
|
5366
|
+
} else if (nkInPrev === -1) {
|
|
5367
|
+
result.push(nk);
|
|
5368
|
+
added.add(nk);
|
|
5369
|
+
ni++;
|
|
5370
|
+
} else {
|
|
5371
|
+
if (pkInNext <= nkInPrev) {
|
|
5372
|
+
result.push(pk);
|
|
5373
|
+
added.add(pk);
|
|
5374
|
+
pi++;
|
|
5375
|
+
} else {
|
|
5376
|
+
result.push(nk);
|
|
5377
|
+
added.add(nk);
|
|
5378
|
+
ni++;
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
while (pi < prevKeys.length) {
|
|
5384
|
+
if (!added.has(prevKeys[pi])) {
|
|
5385
|
+
result.push(prevKeys[pi]);
|
|
5386
|
+
added.add(prevKeys[pi]);
|
|
5387
|
+
}
|
|
5388
|
+
pi++;
|
|
5389
|
+
}
|
|
5390
|
+
while (ni < nextKeys.length) {
|
|
5391
|
+
if (!added.has(nextKeys[ni])) {
|
|
5392
|
+
result.push(nextKeys[ni]);
|
|
5393
|
+
added.add(nextKeys[ni]);
|
|
5394
|
+
}
|
|
5395
|
+
ni++;
|
|
5396
|
+
}
|
|
5397
|
+
return result;
|
|
5398
|
+
}
|
|
5399
|
+
function findInsertionPosition(key, merged, mergedIndex, prevByKey, nextByKey, nextPt) {
|
|
5400
|
+
const idx = mergedIndex.get(key) ?? -1;
|
|
5401
|
+
let beforeKey = null;
|
|
5402
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
5403
|
+
if (prevByKey.has(merged[i]) && nextByKey.has(merged[i])) {
|
|
5404
|
+
beforeKey = merged[i];
|
|
5405
|
+
break;
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
let afterKey = null;
|
|
5409
|
+
for (let i = idx + 1; i < merged.length; i++) {
|
|
5410
|
+
if (prevByKey.has(merged[i]) && nextByKey.has(merged[i])) {
|
|
5411
|
+
afterKey = merged[i];
|
|
5412
|
+
break;
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
if (beforeKey && afterKey) {
|
|
5416
|
+
const beforeNext = nextByKey.get(beforeKey);
|
|
5417
|
+
const afterNext = nextByKey.get(afterKey);
|
|
5418
|
+
const beforePrev = prevByKey.get(beforeKey);
|
|
5419
|
+
const afterPrev = prevByKey.get(afterKey);
|
|
5420
|
+
const range = afterNext.x - beforeNext.x;
|
|
5421
|
+
const t = range === 0 ? 0.5 : (nextPt.x - beforeNext.x) / range;
|
|
5422
|
+
return lerpPoint(beforePrev, afterPrev, Math.max(0, Math.min(1, t)));
|
|
5423
|
+
}
|
|
5424
|
+
if (beforeKey) {
|
|
5425
|
+
return { ...prevByKey.get(beforeKey) };
|
|
5426
|
+
}
|
|
5427
|
+
if (afterKey) {
|
|
5428
|
+
return { ...prevByKey.get(afterKey) };
|
|
5429
|
+
}
|
|
5430
|
+
return nextPt;
|
|
5431
|
+
}
|
|
5432
|
+
function findRemovalPosition(key, merged, mergedIndex, prevByKey, nextByKey, prevPt) {
|
|
5433
|
+
const idx = mergedIndex.get(key) ?? -1;
|
|
5434
|
+
let beforeKey = null;
|
|
5435
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
5436
|
+
if (prevByKey.has(merged[i]) && nextByKey.has(merged[i])) {
|
|
5437
|
+
beforeKey = merged[i];
|
|
5438
|
+
break;
|
|
5439
|
+
}
|
|
5440
|
+
}
|
|
5441
|
+
let afterKey = null;
|
|
5442
|
+
for (let i = idx + 1; i < merged.length; i++) {
|
|
5443
|
+
if (prevByKey.has(merged[i]) && nextByKey.has(merged[i])) {
|
|
5444
|
+
afterKey = merged[i];
|
|
5445
|
+
break;
|
|
5446
|
+
}
|
|
5447
|
+
}
|
|
5448
|
+
if (beforeKey && afterKey) {
|
|
5449
|
+
const beforePrev = prevByKey.get(beforeKey);
|
|
5450
|
+
const afterPrev = prevByKey.get(afterKey);
|
|
5451
|
+
const beforeNext = nextByKey.get(beforeKey);
|
|
5452
|
+
const afterNext = nextByKey.get(afterKey);
|
|
5453
|
+
const range = afterPrev.x - beforePrev.x;
|
|
5454
|
+
const t = range === 0 ? 0.5 : (prevPt.x - beforePrev.x) / range;
|
|
5455
|
+
return lerpPoint(beforeNext, afterNext, Math.max(0, Math.min(1, t)));
|
|
5456
|
+
}
|
|
5457
|
+
if (beforeKey) {
|
|
5458
|
+
return { ...nextByKey.get(beforeKey) };
|
|
5459
|
+
}
|
|
5460
|
+
if (afterKey) {
|
|
5461
|
+
return { ...nextByKey.get(afterKey) };
|
|
5462
|
+
}
|
|
5463
|
+
return prevPt;
|
|
5464
|
+
}
|
|
5465
|
+
function interpolatePoints(fromPts, toPts, t) {
|
|
5466
|
+
const len = Math.min(fromPts.length, toPts.length);
|
|
5467
|
+
const result = new Array(len);
|
|
5468
|
+
for (let i = 0; i < len; i++) {
|
|
5469
|
+
result[i] = lerpPoint(fromPts[i], toPts[i], t);
|
|
5470
|
+
}
|
|
5471
|
+
return result;
|
|
5472
|
+
}
|
|
5473
|
+
function applyLinePath(el, points, interpolate) {
|
|
5474
|
+
const pathEl = el.querySelector("path");
|
|
5475
|
+
if (!pathEl) return;
|
|
5476
|
+
pathEl.setAttribute("d", buildLinePath(points, interpolate));
|
|
5477
|
+
}
|
|
5478
|
+
function applyAreaPaths(el, topPoints, bottomPoints, interpolate, hasStroke) {
|
|
5479
|
+
const paths = el.querySelectorAll("path");
|
|
5480
|
+
if (paths[0]) {
|
|
5481
|
+
paths[0].setAttribute("d", buildAreaPath(topPoints, bottomPoints, interpolate));
|
|
5482
|
+
}
|
|
5483
|
+
if (hasStroke && paths[1]) {
|
|
5484
|
+
paths[1].setAttribute("d", buildLinePath(topPoints, interpolate));
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
function applyFinalLinePath(el, mark) {
|
|
5488
|
+
const pathEl = el.querySelector("path");
|
|
5489
|
+
if (!pathEl || !mark.path) return;
|
|
5490
|
+
pathEl.setAttribute("d", mark.path);
|
|
5491
|
+
}
|
|
5492
|
+
function applyFinalAreaPaths(el, mark) {
|
|
5493
|
+
const paths = el.querySelectorAll("path");
|
|
5494
|
+
if (paths[0]) {
|
|
5495
|
+
paths[0].setAttribute("d", mark.path);
|
|
5496
|
+
}
|
|
5497
|
+
if (paths[1] && mark.topPath) {
|
|
5498
|
+
paths[1].setAttribute("d", mark.topPath);
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
function runTransition(args) {
|
|
5502
|
+
const { svg, prevLayout, nextLayout, animation, onComplete, fromSnapshot } = args;
|
|
5503
|
+
const update = animation.update;
|
|
5504
|
+
const exit = animation.exit ?? { ...EXIT_DEFAULTS };
|
|
5505
|
+
const totalMs = Math.max(update.duration, exit.duration);
|
|
5506
|
+
const enterDelay = 0.4 * update.duration;
|
|
5507
|
+
const enterDuration = update.duration - enterDelay;
|
|
5508
|
+
const marksContainer = svg.querySelector(".oc-marks");
|
|
5509
|
+
if (!marksContainer) {
|
|
5510
|
+
onComplete();
|
|
5511
|
+
return {
|
|
5512
|
+
cancel() {
|
|
5513
|
+
},
|
|
5514
|
+
get running() {
|
|
5515
|
+
return false;
|
|
5516
|
+
},
|
|
5517
|
+
snapshot() {
|
|
5518
|
+
return /* @__PURE__ */ new Map();
|
|
5519
|
+
}
|
|
5520
|
+
};
|
|
5521
|
+
}
|
|
5522
|
+
const tweens = [];
|
|
5523
|
+
const ghosts = [];
|
|
5524
|
+
const defs = svg.querySelector("defs");
|
|
5525
|
+
let ghostGradientMap = /* @__PURE__ */ new Map();
|
|
5526
|
+
if (defs) {
|
|
5527
|
+
const nextKeySet = new Set(nextLayout.marks.filter((m2) => m2.key).map((m2) => m2.key));
|
|
5528
|
+
const exitingMarks = prevLayout.marks.filter(
|
|
5529
|
+
(m2) => m2.key && !nextKeySet.has(m2.key) && "fill" in m2
|
|
5530
|
+
);
|
|
5531
|
+
const gradientMarks = exitingMarks.filter(
|
|
5532
|
+
(m2) => "fill" in m2 && isGradientDef(m2.fill)
|
|
5533
|
+
);
|
|
5534
|
+
if (gradientMarks.length > 0) {
|
|
5535
|
+
ghostGradientMap = buildGradientDefs(gradientMarks, defs);
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
const hasRects = prevLayout.marks.some((m2) => m2.type === "rect") || nextLayout.marks.some((m2) => m2.type === "rect");
|
|
5539
|
+
const hasLines = prevLayout.marks.some((m2) => m2.type === "line") || nextLayout.marks.some((m2) => m2.type === "line");
|
|
5540
|
+
const hasAreas = prevLayout.marks.some((m2) => m2.type === "area") || nextLayout.marks.some((m2) => m2.type === "area");
|
|
5541
|
+
const hasPoints = prevLayout.marks.some((m2) => m2.type === "point") || nextLayout.marks.some((m2) => m2.type === "point");
|
|
5542
|
+
const hasRules = prevLayout.marks.some((m2) => m2.type === "rule") || nextLayout.marks.some((m2) => m2.type === "rule");
|
|
5543
|
+
const hasTicks = prevLayout.marks.some((m2) => m2.type === "tick") || nextLayout.marks.some((m2) => m2.type === "tick");
|
|
5544
|
+
const hasTextMarks = prevLayout.marks.some((m2) => m2.type === "textMark") || nextLayout.marks.some((m2) => m2.type === "textMark");
|
|
5545
|
+
if (hasRects) {
|
|
5546
|
+
buildRectTweens(
|
|
5547
|
+
prevLayout,
|
|
5548
|
+
nextLayout,
|
|
5549
|
+
marksContainer,
|
|
5550
|
+
tweens,
|
|
5551
|
+
ghosts,
|
|
5552
|
+
ghostGradientMap,
|
|
5553
|
+
fromSnapshot
|
|
5554
|
+
);
|
|
5555
|
+
}
|
|
5556
|
+
if (hasLines) {
|
|
5557
|
+
buildLineTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot);
|
|
5558
|
+
}
|
|
5559
|
+
if (hasAreas) {
|
|
5560
|
+
buildAreaTweens(
|
|
5561
|
+
prevLayout,
|
|
5562
|
+
nextLayout,
|
|
5563
|
+
marksContainer,
|
|
5564
|
+
tweens,
|
|
5565
|
+
ghosts,
|
|
5566
|
+
ghostGradientMap,
|
|
5567
|
+
fromSnapshot
|
|
5568
|
+
);
|
|
5569
|
+
}
|
|
5570
|
+
if (hasPoints) {
|
|
5571
|
+
buildPointTweens(
|
|
5572
|
+
prevLayout,
|
|
5573
|
+
nextLayout,
|
|
5574
|
+
marksContainer,
|
|
5575
|
+
tweens,
|
|
5576
|
+
ghosts,
|
|
5577
|
+
ghostGradientMap,
|
|
5578
|
+
fromSnapshot
|
|
5579
|
+
);
|
|
5580
|
+
}
|
|
5581
|
+
if (hasRules) {
|
|
5582
|
+
buildRuleTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot);
|
|
5583
|
+
}
|
|
5584
|
+
if (hasTicks) {
|
|
5585
|
+
buildTickTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot);
|
|
5586
|
+
}
|
|
5587
|
+
if (hasTextMarks) {
|
|
5588
|
+
buildTextMarkTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot);
|
|
5589
|
+
}
|
|
5590
|
+
buildAxisTweens(svg, prevLayout, nextLayout, tweens, ghosts);
|
|
5591
|
+
const secondaryEls = collectSecondaryElements(svg);
|
|
5592
|
+
const secondaryOriginalOpacity = secondaryEls.map((el) => el.style.opacity ?? "");
|
|
5593
|
+
for (const tw of tweens) {
|
|
5594
|
+
applyTweenState(tw, 0, update, exit, enterDelay, enterDuration);
|
|
5595
|
+
}
|
|
5596
|
+
for (const el of secondaryEls) {
|
|
5597
|
+
el.style.opacity = "0";
|
|
5598
|
+
}
|
|
5599
|
+
let rafId = null;
|
|
5600
|
+
let running = true;
|
|
5601
|
+
let startTime = null;
|
|
5602
|
+
let lastElapsed = 0;
|
|
5603
|
+
const nextLineMarks = nextLayout.marks.filter((m2) => m2.type === "line");
|
|
5604
|
+
const nextAreaMarks = nextLayout.marks.filter((m2) => m2.type === "area");
|
|
5605
|
+
function tick(now2) {
|
|
5606
|
+
if (!running) return;
|
|
5607
|
+
if (startTime === null) startTime = now2;
|
|
5608
|
+
const elapsed = now2 - startTime;
|
|
5609
|
+
lastElapsed = elapsed;
|
|
5610
|
+
const tGlobal = Math.min(elapsed / totalMs, 1);
|
|
5611
|
+
for (const tw of tweens) {
|
|
5612
|
+
applyTweenState(tw, elapsed, update, exit, enterDelay, enterDuration);
|
|
5613
|
+
}
|
|
5614
|
+
applySecondaryOpacity(secondaryEls, elapsed, enterDelay, enterDuration);
|
|
5615
|
+
if (tGlobal >= 1) {
|
|
5616
|
+
finish();
|
|
5617
|
+
} else {
|
|
5618
|
+
rafId = requestAnimationFrame(tick);
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
function finish() {
|
|
5622
|
+
if (!running) return;
|
|
5623
|
+
running = false;
|
|
5624
|
+
rafId = null;
|
|
5625
|
+
snapToFinal();
|
|
5626
|
+
removeGhosts();
|
|
5627
|
+
for (let i = 0; i < secondaryEls.length; i++) {
|
|
5628
|
+
secondaryEls[i].style.opacity = secondaryOriginalOpacity[i];
|
|
5629
|
+
}
|
|
5630
|
+
onComplete();
|
|
5631
|
+
}
|
|
5632
|
+
function snapToFinal() {
|
|
5633
|
+
for (const tw of tweens) {
|
|
5634
|
+
snapTweenToFinal(tw);
|
|
5635
|
+
}
|
|
5636
|
+
for (const mark of nextLineMarks) {
|
|
5637
|
+
if (!mark.key) continue;
|
|
5638
|
+
const el = marksContainer.querySelector(`[data-key="${mark.key}"]`);
|
|
5639
|
+
if (el) applyFinalLinePath(el, mark);
|
|
5640
|
+
}
|
|
5641
|
+
for (const mark of nextAreaMarks) {
|
|
5642
|
+
if (!mark.key) continue;
|
|
5643
|
+
const el = marksContainer.querySelector(`[data-key="${mark.key}"]`);
|
|
5644
|
+
if (el) applyFinalAreaPaths(el, mark);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
function removeGhosts() {
|
|
5648
|
+
for (const ghost of ghosts) {
|
|
5649
|
+
ghost.parentNode?.removeChild(ghost);
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
function captureSnapshot() {
|
|
5653
|
+
const snap = /* @__PURE__ */ new Map();
|
|
5654
|
+
const tUpdate = Math.min(lastElapsed / update.duration, 1);
|
|
5655
|
+
const easedUpdate = cubicOut(tUpdate);
|
|
5656
|
+
for (const tw of tweens) {
|
|
5657
|
+
if (tw.kind !== "update") continue;
|
|
5658
|
+
const key = getKeyForTween(tw);
|
|
5659
|
+
if (!key) continue;
|
|
5660
|
+
switch (tw.tweenType) {
|
|
5661
|
+
case "rect": {
|
|
5662
|
+
const g = lerpGeom(tw.from, tw.to, easedUpdate);
|
|
5663
|
+
snap.set(key, { type: "rect", ...g });
|
|
5664
|
+
break;
|
|
5665
|
+
}
|
|
5666
|
+
case "point": {
|
|
5667
|
+
const cx = tw.fromCx + (tw.toCx - tw.fromCx) * easedUpdate;
|
|
5668
|
+
const cy = tw.fromCy + (tw.toCy - tw.fromCy) * easedUpdate;
|
|
5669
|
+
const entry = { type: "point", cx, cy };
|
|
5670
|
+
if (tw.fromR !== void 0 && tw.toR !== void 0) {
|
|
5671
|
+
entry.r = tw.fromR + (tw.toR - tw.fromR) * easedUpdate;
|
|
5672
|
+
}
|
|
5673
|
+
snap.set(key, entry);
|
|
5674
|
+
break;
|
|
5675
|
+
}
|
|
5676
|
+
case "rule":
|
|
5677
|
+
case "tick": {
|
|
5678
|
+
snap.set(key, {
|
|
5679
|
+
type: tw.tweenType,
|
|
5680
|
+
x1: tw.fromX1 + (tw.toX1 - tw.fromX1) * easedUpdate,
|
|
5681
|
+
y1: tw.fromY1 + (tw.toY1 - tw.fromY1) * easedUpdate,
|
|
5682
|
+
x2: tw.fromX2 + (tw.toX2 - tw.fromX2) * easedUpdate,
|
|
5683
|
+
y2: tw.fromY2 + (tw.toY2 - tw.fromY2) * easedUpdate
|
|
5684
|
+
});
|
|
5685
|
+
break;
|
|
5686
|
+
}
|
|
5687
|
+
case "textMark": {
|
|
5688
|
+
snap.set(key, {
|
|
5689
|
+
type: "textMark",
|
|
5690
|
+
x: tw.fromX + (tw.toX - tw.fromX) * easedUpdate,
|
|
5691
|
+
y: tw.fromY + (tw.toY - tw.fromY) * easedUpdate
|
|
5692
|
+
});
|
|
5693
|
+
break;
|
|
5694
|
+
}
|
|
5695
|
+
case "line": {
|
|
5696
|
+
const pts = interpolatePoints(tw.fromPts, tw.toPts, easedUpdate);
|
|
5697
|
+
const d = buildLinePath(pts, tw.interpolate);
|
|
5698
|
+
snap.set(key, { type: "line", d });
|
|
5699
|
+
break;
|
|
5700
|
+
}
|
|
5701
|
+
case "area": {
|
|
5702
|
+
const top = interpolatePoints(tw.fromTop, tw.toTop, easedUpdate);
|
|
5703
|
+
const bottom = interpolatePoints(tw.fromBottom, tw.toBottom, easedUpdate);
|
|
5704
|
+
const d = buildAreaPath(top, bottom, tw.interpolate);
|
|
5705
|
+
let topD;
|
|
5706
|
+
if (tw.hasStroke) {
|
|
5707
|
+
topD = buildLinePath(top, tw.interpolate);
|
|
5708
|
+
}
|
|
5709
|
+
snap.set(key, { type: "area", d, topD });
|
|
5710
|
+
break;
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
5714
|
+
return snap;
|
|
5715
|
+
}
|
|
5716
|
+
rafId = requestAnimationFrame(tick);
|
|
5717
|
+
return {
|
|
5718
|
+
cancel() {
|
|
5719
|
+
if (!running) return;
|
|
5720
|
+
running = false;
|
|
5721
|
+
if (rafId !== null) {
|
|
5722
|
+
cancelAnimationFrame(rafId);
|
|
5723
|
+
rafId = null;
|
|
5724
|
+
}
|
|
5725
|
+
snapToFinal();
|
|
5726
|
+
removeGhosts();
|
|
5727
|
+
for (let i = 0; i < secondaryEls.length; i++) {
|
|
5728
|
+
secondaryEls[i].style.opacity = secondaryOriginalOpacity[i];
|
|
5729
|
+
}
|
|
5730
|
+
},
|
|
5731
|
+
get running() {
|
|
5732
|
+
return running;
|
|
5733
|
+
},
|
|
5734
|
+
snapshot() {
|
|
5735
|
+
return captureSnapshot();
|
|
5736
|
+
}
|
|
5737
|
+
};
|
|
5738
|
+
}
|
|
5739
|
+
function collectSecondaryElements(svg) {
|
|
5740
|
+
const els = [];
|
|
5741
|
+
for (const el of svg.querySelectorAll(".oc-annotation")) {
|
|
5742
|
+
els.push(el);
|
|
5743
|
+
}
|
|
5744
|
+
const epLabels = svg.querySelector(".oc-endpoint-labels");
|
|
5745
|
+
if (epLabels) els.push(epLabels);
|
|
5746
|
+
const markLabels = svg.querySelector(".oc-mark-labels");
|
|
5747
|
+
if (markLabels) els.push(markLabels);
|
|
5748
|
+
return els;
|
|
5749
|
+
}
|
|
5750
|
+
function applySecondaryOpacity(els, elapsed, enterDelay, enterDuration) {
|
|
5751
|
+
if (els.length === 0) return;
|
|
5752
|
+
let t;
|
|
5753
|
+
if (elapsed < enterDelay) {
|
|
5754
|
+
t = 0;
|
|
5755
|
+
} else {
|
|
5756
|
+
t = Math.min((elapsed - enterDelay) / enterDuration, 1);
|
|
5757
|
+
}
|
|
5758
|
+
const opacity = String(cubicOut(t));
|
|
5759
|
+
for (const el of els) {
|
|
5760
|
+
el.style.opacity = opacity;
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
function getKeyForTween(tw) {
|
|
5764
|
+
if ("ghost" in tw && tw.ghost) return null;
|
|
5765
|
+
return tw.el.getAttribute("data-key");
|
|
5766
|
+
}
|
|
5767
|
+
function resolveLineElement(el) {
|
|
5768
|
+
return el.tagName === "line" ? el : el.querySelector("line") ?? el;
|
|
5769
|
+
}
|
|
5770
|
+
function applyTweenState(tw, elapsed, update, exit, enterDelay, enterDuration) {
|
|
5771
|
+
let tLocal;
|
|
5772
|
+
if (tw.kind === "exit") {
|
|
5773
|
+
tLocal = Math.min(elapsed / exit.duration, 1);
|
|
5774
|
+
} else if (tw.kind === "enter") {
|
|
5775
|
+
if (elapsed < enterDelay) {
|
|
5776
|
+
tLocal = 0;
|
|
5777
|
+
} else {
|
|
5778
|
+
tLocal = Math.min((elapsed - enterDelay) / enterDuration, 1);
|
|
5779
|
+
}
|
|
5780
|
+
} else {
|
|
5781
|
+
tLocal = Math.min(elapsed / update.duration, 1);
|
|
5782
|
+
}
|
|
5783
|
+
const eased = cubicOut(tLocal);
|
|
5784
|
+
switch (tw.tweenType) {
|
|
5785
|
+
case "rect": {
|
|
5786
|
+
const geom = lerpGeom(tw.from, tw.to, eased);
|
|
5787
|
+
applyGeomToElement(tw.el, geom, tw.mark);
|
|
5788
|
+
break;
|
|
5789
|
+
}
|
|
5790
|
+
case "line": {
|
|
5791
|
+
const pts = interpolatePoints(tw.fromPts, tw.toPts, eased);
|
|
5792
|
+
applyLinePath(tw.el, pts, tw.interpolate);
|
|
5793
|
+
break;
|
|
5794
|
+
}
|
|
5795
|
+
case "area": {
|
|
5796
|
+
const top = interpolatePoints(tw.fromTop, tw.toTop, eased);
|
|
5797
|
+
const bottom = interpolatePoints(tw.fromBottom, tw.toBottom, eased);
|
|
5798
|
+
applyAreaPaths(tw.el, top, bottom, tw.interpolate, tw.hasStroke);
|
|
5799
|
+
break;
|
|
5800
|
+
}
|
|
5801
|
+
case "point": {
|
|
5802
|
+
const cx = tw.fromCx + (tw.toCx - tw.fromCx) * eased;
|
|
5803
|
+
const cy = tw.fromCy + (tw.toCy - tw.fromCy) * eased;
|
|
5804
|
+
tw.el.setAttribute("cx", String(cx));
|
|
5805
|
+
tw.el.setAttribute("cy", String(cy));
|
|
5806
|
+
if (tw.fromR !== void 0 && tw.toR !== void 0) {
|
|
5807
|
+
const r = tw.fromR + (tw.toR - tw.fromR) * eased;
|
|
5808
|
+
tw.el.setAttribute("r", String(r));
|
|
5809
|
+
}
|
|
5810
|
+
break;
|
|
5811
|
+
}
|
|
5812
|
+
case "rule":
|
|
5813
|
+
case "tick": {
|
|
5814
|
+
const lineEl = resolveLineElement(tw.el);
|
|
5815
|
+
lineEl.setAttribute("x1", String(tw.fromX1 + (tw.toX1 - tw.fromX1) * eased));
|
|
5816
|
+
lineEl.setAttribute("y1", String(tw.fromY1 + (tw.toY1 - tw.fromY1) * eased));
|
|
5817
|
+
lineEl.setAttribute("x2", String(tw.fromX2 + (tw.toX2 - tw.fromX2) * eased));
|
|
5818
|
+
lineEl.setAttribute("y2", String(tw.fromY2 + (tw.toY2 - tw.fromY2) * eased));
|
|
5819
|
+
break;
|
|
5820
|
+
}
|
|
5821
|
+
case "textMark": {
|
|
5822
|
+
const x3 = tw.fromX + (tw.toX - tw.fromX) * eased;
|
|
5823
|
+
const y3 = tw.fromY + (tw.toY - tw.fromY) * eased;
|
|
5824
|
+
tw.el.setAttribute("x", String(x3));
|
|
5825
|
+
tw.el.setAttribute("y", String(y3));
|
|
5826
|
+
break;
|
|
5827
|
+
}
|
|
5828
|
+
case "axisTick": {
|
|
5829
|
+
if (tw.crossfade) {
|
|
5830
|
+
break;
|
|
5831
|
+
}
|
|
5832
|
+
const pos = tw.fromPos + (tw.toPos - tw.fromPos) * eased;
|
|
5833
|
+
if (tw.posAttr === "x") {
|
|
5834
|
+
tw.el.setAttribute("x", String(pos));
|
|
5835
|
+
} else {
|
|
5836
|
+
tw.el.setAttribute("y", String(pos));
|
|
5837
|
+
}
|
|
5838
|
+
break;
|
|
5839
|
+
}
|
|
5840
|
+
case "gridline": {
|
|
5841
|
+
const pos = tw.fromPos + (tw.toPos - tw.fromPos) * eased;
|
|
5842
|
+
if (tw.orientation === "y") {
|
|
5843
|
+
tw.el.setAttribute("y1", String(pos));
|
|
5844
|
+
tw.el.setAttribute("y2", String(pos));
|
|
5845
|
+
} else {
|
|
5846
|
+
tw.el.setAttribute("x1", String(pos));
|
|
5847
|
+
tw.el.setAttribute("x2", String(pos));
|
|
5848
|
+
}
|
|
5849
|
+
break;
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
if (tw.fromOpacity !== void 0 && tw.toOpacity !== void 0) {
|
|
5853
|
+
const opacity = tw.fromOpacity + (tw.toOpacity - tw.fromOpacity) * eased;
|
|
5854
|
+
tw.el.style.opacity = String(opacity);
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
function snapTweenToFinal(tw) {
|
|
5858
|
+
switch (tw.tweenType) {
|
|
5859
|
+
case "rect":
|
|
5860
|
+
applyGeomToElement(tw.el, tw.to, tw.mark);
|
|
5861
|
+
break;
|
|
5862
|
+
case "line":
|
|
5863
|
+
applyLinePath(tw.el, tw.toPts, tw.interpolate);
|
|
5864
|
+
break;
|
|
5865
|
+
case "area":
|
|
5866
|
+
applyAreaPaths(tw.el, tw.toTop, tw.toBottom, tw.interpolate, tw.hasStroke);
|
|
5867
|
+
break;
|
|
5868
|
+
case "point":
|
|
5869
|
+
tw.el.setAttribute("cx", String(tw.toCx));
|
|
5870
|
+
tw.el.setAttribute("cy", String(tw.toCy));
|
|
5871
|
+
if (tw.toR !== void 0) {
|
|
5872
|
+
tw.el.setAttribute("r", String(tw.toR));
|
|
5873
|
+
}
|
|
5874
|
+
break;
|
|
5875
|
+
case "rule":
|
|
5876
|
+
case "tick": {
|
|
5877
|
+
const lineEl = resolveLineElement(tw.el);
|
|
5878
|
+
lineEl.setAttribute("x1", String(tw.toX1));
|
|
5879
|
+
lineEl.setAttribute("y1", String(tw.toY1));
|
|
5880
|
+
lineEl.setAttribute("x2", String(tw.toX2));
|
|
5881
|
+
lineEl.setAttribute("y2", String(tw.toY2));
|
|
5882
|
+
break;
|
|
5883
|
+
}
|
|
5884
|
+
case "textMark":
|
|
5885
|
+
tw.el.setAttribute("x", String(tw.toX));
|
|
5886
|
+
tw.el.setAttribute("y", String(tw.toY));
|
|
5887
|
+
break;
|
|
5888
|
+
case "axisTick":
|
|
5889
|
+
if (!tw.crossfade) {
|
|
5890
|
+
if (tw.posAttr === "x") {
|
|
5891
|
+
tw.el.setAttribute("x", String(tw.toPos));
|
|
5892
|
+
} else {
|
|
5893
|
+
tw.el.setAttribute("y", String(tw.toPos));
|
|
5894
|
+
}
|
|
5895
|
+
}
|
|
5896
|
+
break;
|
|
5897
|
+
case "gridline":
|
|
5898
|
+
if (tw.orientation === "y") {
|
|
5899
|
+
tw.el.setAttribute("y1", String(tw.toPos));
|
|
5900
|
+
tw.el.setAttribute("y2", String(tw.toPos));
|
|
5901
|
+
} else {
|
|
5902
|
+
tw.el.setAttribute("x1", String(tw.toPos));
|
|
5903
|
+
tw.el.setAttribute("x2", String(tw.toPos));
|
|
5904
|
+
}
|
|
5905
|
+
break;
|
|
5906
|
+
}
|
|
5907
|
+
if (tw.toOpacity !== void 0) {
|
|
5908
|
+
tw.el.style.opacity = String(tw.toOpacity);
|
|
5909
|
+
}
|
|
5910
|
+
if (tw.tweenType === "point" && tw.suppressedOpacity !== void 0) {
|
|
5911
|
+
tw.el.setAttribute("opacity", tw.suppressedOpacity);
|
|
5912
|
+
}
|
|
5913
|
+
}
|
|
5914
|
+
function resolveGhostGradientFill(mark, ghostGradientMap) {
|
|
5915
|
+
if (!mark.fill || typeof mark.fill === "string" || ghostGradientMap.size === 0) return mark;
|
|
5916
|
+
const resolved = resolveMarkFill(mark.fill, ghostGradientMap);
|
|
5917
|
+
return { ...mark, fill: resolved };
|
|
5918
|
+
}
|
|
5919
|
+
function buildRectTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, ghostGradientMap, fromSnapshot) {
|
|
5920
|
+
const prevRects = prevLayout.marks.filter((m2) => m2.type === "rect");
|
|
5921
|
+
const nextRects = nextLayout.marks.filter((m2) => m2.type === "rect");
|
|
5922
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
5923
|
+
for (const m2 of prevRects) {
|
|
5924
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
5925
|
+
}
|
|
5926
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
5927
|
+
for (const m2 of nextRects) {
|
|
5928
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
5929
|
+
}
|
|
5930
|
+
for (const [key, next] of nextByKey) {
|
|
5931
|
+
const prev = prevByKey.get(key);
|
|
5932
|
+
if (!prev) continue;
|
|
5933
|
+
const el = marksContainer.querySelector(`[data-key="${key}"]`);
|
|
5934
|
+
if (!el) continue;
|
|
5935
|
+
let fromGeom = geomFromMark(prev);
|
|
5936
|
+
const snap = fromSnapshot?.get(key);
|
|
5937
|
+
if (snap && snap.type === "rect") {
|
|
5938
|
+
fromGeom = { x: snap.x, y: snap.y, width: snap.width, height: snap.height };
|
|
5939
|
+
}
|
|
5940
|
+
tweens.push({
|
|
5941
|
+
tweenType: "rect",
|
|
5942
|
+
kind: "update",
|
|
5943
|
+
el,
|
|
5944
|
+
from: fromGeom,
|
|
5945
|
+
to: geomFromMark(next),
|
|
5946
|
+
mark: next
|
|
5947
|
+
});
|
|
5948
|
+
}
|
|
5949
|
+
for (const [key, next] of nextByKey) {
|
|
5950
|
+
if (prevByKey.has(key)) continue;
|
|
5951
|
+
const el = marksContainer.querySelector(`[data-key="${key}"]`);
|
|
5952
|
+
if (!el) continue;
|
|
5953
|
+
let fromGeom;
|
|
5954
|
+
if (next.orient === "horizontal") {
|
|
5955
|
+
fromGeom = { x: next.x, y: next.y, width: 0, height: next.height };
|
|
5956
|
+
} else {
|
|
5957
|
+
fromGeom = { x: next.x, y: next.y + next.height, width: next.width, height: 0 };
|
|
5958
|
+
}
|
|
5959
|
+
tweens.push({
|
|
5960
|
+
tweenType: "rect",
|
|
5961
|
+
kind: "enter",
|
|
5962
|
+
el,
|
|
5963
|
+
from: fromGeom,
|
|
5964
|
+
to: geomFromMark(next),
|
|
5965
|
+
mark: next
|
|
5966
|
+
});
|
|
5967
|
+
}
|
|
5968
|
+
for (const [key, prev] of prevByKey) {
|
|
5969
|
+
if (nextByKey.has(key)) continue;
|
|
5970
|
+
const ghostMark = resolveGhostGradientFill(prev, ghostGradientMap);
|
|
5971
|
+
const ghost = renderSingleMark(ghostMark, 0);
|
|
5972
|
+
if (!ghost) continue;
|
|
5973
|
+
ghost.classList.add("oc-ghost");
|
|
5974
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
5975
|
+
ghost.setAttribute("pointer-events", "none");
|
|
5976
|
+
ghost.removeAttribute("data-key");
|
|
5977
|
+
marksContainer.appendChild(ghost);
|
|
5978
|
+
ghosts.push(ghost);
|
|
5979
|
+
let toGeom;
|
|
5980
|
+
if (prev.orient === "horizontal") {
|
|
5981
|
+
toGeom = { x: prev.x, y: prev.y, width: 0, height: prev.height };
|
|
5982
|
+
} else {
|
|
5983
|
+
toGeom = { x: prev.x, y: prev.y + prev.height, width: prev.width, height: 0 };
|
|
5984
|
+
}
|
|
5985
|
+
tweens.push({
|
|
5986
|
+
tweenType: "rect",
|
|
5987
|
+
kind: "exit",
|
|
5988
|
+
el: ghost,
|
|
5989
|
+
from: geomFromMark(prev),
|
|
5990
|
+
to: toGeom,
|
|
5991
|
+
mark: prev,
|
|
5992
|
+
ghost,
|
|
5993
|
+
fromOpacity: 1,
|
|
5994
|
+
toOpacity: 0
|
|
5995
|
+
});
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5998
|
+
function buildLineTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot) {
|
|
5999
|
+
const prevLines = prevLayout.marks.filter((m2) => m2.type === "line");
|
|
6000
|
+
const nextLines = nextLayout.marks.filter((m2) => m2.type === "line");
|
|
6001
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
6002
|
+
for (const m2 of prevLines) {
|
|
6003
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
6004
|
+
}
|
|
6005
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
6006
|
+
for (const m2 of nextLines) {
|
|
6007
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
6008
|
+
}
|
|
6009
|
+
for (const [key, next] of nextByKey) {
|
|
6010
|
+
const prev = prevByKey.get(key);
|
|
6011
|
+
if (!prev) continue;
|
|
6012
|
+
const el = marksContainer.querySelector(`[data-key="${key}"]`);
|
|
6013
|
+
if (!el) continue;
|
|
6014
|
+
const snap = fromSnapshot?.get(key);
|
|
6015
|
+
if (snap && snap.type === "line") {
|
|
6016
|
+
const ghost = el.cloneNode(true);
|
|
6017
|
+
ghost.classList.add("oc-ghost");
|
|
6018
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6019
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6020
|
+
ghost.removeAttribute("data-key");
|
|
6021
|
+
const ghostPath = ghost.querySelector("path");
|
|
6022
|
+
if (ghostPath) ghostPath.setAttribute("d", snap.d);
|
|
6023
|
+
marksContainer.appendChild(ghost);
|
|
6024
|
+
ghosts.push(ghost);
|
|
6025
|
+
tweens.push({
|
|
6026
|
+
tweenType: "line",
|
|
6027
|
+
kind: "exit",
|
|
6028
|
+
el: ghost,
|
|
6029
|
+
fromPts: next.points,
|
|
6030
|
+
// placeholder, not used for path
|
|
6031
|
+
toPts: next.points,
|
|
6032
|
+
finalPoints: next.points,
|
|
6033
|
+
interpolate: next.interpolate,
|
|
6034
|
+
ghost,
|
|
6035
|
+
fromOpacity: 1,
|
|
6036
|
+
toOpacity: 0
|
|
6037
|
+
});
|
|
6038
|
+
tweens.push({
|
|
6039
|
+
tweenType: "line",
|
|
6040
|
+
kind: "enter",
|
|
6041
|
+
el,
|
|
6042
|
+
fromPts: next.points,
|
|
6043
|
+
toPts: next.points,
|
|
6044
|
+
finalPoints: next.points,
|
|
6045
|
+
interpolate: next.interpolate,
|
|
6046
|
+
fromOpacity: 0,
|
|
6047
|
+
toOpacity: 1
|
|
6048
|
+
});
|
|
6049
|
+
continue;
|
|
6050
|
+
}
|
|
6051
|
+
const prevPKs = prev.pointKeys ?? prev.points.map((_, i) => `${i}`);
|
|
6052
|
+
const nextPKs = next.pointKeys ?? next.points.map((_, i) => `${i}`);
|
|
6053
|
+
const prevKeySet = new Set(prevPKs);
|
|
6054
|
+
const hasSurvivors = nextPKs.some((k) => prevKeySet.has(k));
|
|
6055
|
+
if (!hasSurvivors) {
|
|
6056
|
+
const ghost = renderSingleMark(prev, 0);
|
|
6057
|
+
if (ghost) {
|
|
6058
|
+
ghost.classList.add("oc-ghost");
|
|
6059
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6060
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6061
|
+
ghost.removeAttribute("data-key");
|
|
6062
|
+
marksContainer.appendChild(ghost);
|
|
6063
|
+
ghosts.push(ghost);
|
|
6064
|
+
tweens.push({
|
|
6065
|
+
tweenType: "line",
|
|
6066
|
+
kind: "exit",
|
|
6067
|
+
el: ghost,
|
|
6068
|
+
fromPts: prev.points,
|
|
6069
|
+
toPts: prev.points,
|
|
6070
|
+
finalPoints: prev.points,
|
|
6071
|
+
interpolate: prev.interpolate,
|
|
6072
|
+
ghost,
|
|
6073
|
+
fromOpacity: 1,
|
|
6074
|
+
toOpacity: 0
|
|
6075
|
+
});
|
|
6076
|
+
}
|
|
6077
|
+
tweens.push({
|
|
6078
|
+
tweenType: "line",
|
|
6079
|
+
kind: "enter",
|
|
6080
|
+
el,
|
|
6081
|
+
fromPts: next.points,
|
|
6082
|
+
toPts: next.points,
|
|
6083
|
+
finalPoints: next.points,
|
|
6084
|
+
interpolate: next.interpolate,
|
|
6085
|
+
fromOpacity: 0,
|
|
6086
|
+
toOpacity: 1
|
|
6087
|
+
});
|
|
6088
|
+
} else {
|
|
6089
|
+
const [fromPts, toPts] = normalizePointArrays(prev.points, next.points, prevPKs, nextPKs);
|
|
6090
|
+
tweens.push({
|
|
6091
|
+
tweenType: "line",
|
|
6092
|
+
kind: "update",
|
|
6093
|
+
el,
|
|
6094
|
+
fromPts,
|
|
6095
|
+
toPts,
|
|
6096
|
+
finalPoints: next.points,
|
|
6097
|
+
interpolate: next.interpolate
|
|
6098
|
+
});
|
|
6099
|
+
}
|
|
6100
|
+
}
|
|
6101
|
+
for (const [key, next] of nextByKey) {
|
|
6102
|
+
if (prevByKey.has(key)) continue;
|
|
6103
|
+
const el = marksContainer.querySelector(`[data-key="${key}"]`);
|
|
6104
|
+
if (!el) continue;
|
|
6105
|
+
tweens.push({
|
|
6106
|
+
tweenType: "line",
|
|
6107
|
+
kind: "enter",
|
|
6108
|
+
el,
|
|
6109
|
+
fromPts: next.points,
|
|
6110
|
+
toPts: next.points,
|
|
6111
|
+
finalPoints: next.points,
|
|
6112
|
+
interpolate: next.interpolate,
|
|
6113
|
+
fromOpacity: 0,
|
|
6114
|
+
toOpacity: 1
|
|
6115
|
+
});
|
|
6116
|
+
}
|
|
6117
|
+
for (const [key, prev] of prevByKey) {
|
|
6118
|
+
if (nextByKey.has(key)) continue;
|
|
6119
|
+
const ghost = renderSingleMark(prev, 0);
|
|
6120
|
+
if (!ghost) continue;
|
|
6121
|
+
ghost.classList.add("oc-ghost");
|
|
6122
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6123
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6124
|
+
ghost.removeAttribute("data-key");
|
|
6125
|
+
marksContainer.appendChild(ghost);
|
|
6126
|
+
ghosts.push(ghost);
|
|
6127
|
+
tweens.push({
|
|
6128
|
+
tweenType: "line",
|
|
6129
|
+
kind: "exit",
|
|
6130
|
+
el: ghost,
|
|
6131
|
+
fromPts: prev.points,
|
|
6132
|
+
toPts: prev.points,
|
|
6133
|
+
finalPoints: prev.points,
|
|
6134
|
+
interpolate: prev.interpolate,
|
|
6135
|
+
ghost,
|
|
6136
|
+
fromOpacity: 1,
|
|
6137
|
+
toOpacity: 0
|
|
6138
|
+
});
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
function buildAreaTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, ghostGradientMap, fromSnapshot) {
|
|
6142
|
+
const prevAreas = prevLayout.marks.filter((m2) => m2.type === "area");
|
|
6143
|
+
const nextAreas = nextLayout.marks.filter((m2) => m2.type === "area");
|
|
6144
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
6145
|
+
for (const m2 of prevAreas) {
|
|
6146
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
6147
|
+
}
|
|
6148
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
6149
|
+
for (const m2 of nextAreas) {
|
|
6150
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
6151
|
+
}
|
|
6152
|
+
for (const [key, next] of nextByKey) {
|
|
6153
|
+
const prev = prevByKey.get(key);
|
|
6154
|
+
if (!prev) continue;
|
|
6155
|
+
const el = marksContainer.querySelector(`[data-key="${key}"]`);
|
|
6156
|
+
if (!el) continue;
|
|
6157
|
+
const snap = fromSnapshot?.get(key);
|
|
6158
|
+
if (snap && snap.type === "area") {
|
|
6159
|
+
const ghost = el.cloneNode(true);
|
|
6160
|
+
ghost.classList.add("oc-ghost");
|
|
6161
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6162
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6163
|
+
ghost.removeAttribute("data-key");
|
|
6164
|
+
const ghostPaths = ghost.querySelectorAll("path");
|
|
6165
|
+
if (ghostPaths[0]) ghostPaths[0].setAttribute("d", snap.d);
|
|
6166
|
+
if (ghostPaths[1] && snap.topD) ghostPaths[1].setAttribute("d", snap.topD);
|
|
6167
|
+
marksContainer.appendChild(ghost);
|
|
6168
|
+
ghosts.push(ghost);
|
|
6169
|
+
tweens.push({
|
|
6170
|
+
tweenType: "area",
|
|
6171
|
+
kind: "exit",
|
|
6172
|
+
el: ghost,
|
|
6173
|
+
fromTop: next.topPoints,
|
|
6174
|
+
toTop: next.topPoints,
|
|
6175
|
+
fromBottom: next.bottomPoints,
|
|
6176
|
+
toBottom: next.bottomPoints,
|
|
6177
|
+
finalTopPoints: next.topPoints,
|
|
6178
|
+
finalBottomPoints: next.bottomPoints,
|
|
6179
|
+
interpolate: next.interpolate,
|
|
6180
|
+
hasStroke: !!next.stroke && !!next.topPath,
|
|
6181
|
+
ghost,
|
|
6182
|
+
fromOpacity: 1,
|
|
6183
|
+
toOpacity: 0
|
|
6184
|
+
});
|
|
6185
|
+
tweens.push({
|
|
6186
|
+
tweenType: "area",
|
|
6187
|
+
kind: "enter",
|
|
6188
|
+
el,
|
|
6189
|
+
fromTop: next.topPoints,
|
|
6190
|
+
toTop: next.topPoints,
|
|
6191
|
+
fromBottom: next.bottomPoints,
|
|
6192
|
+
toBottom: next.bottomPoints,
|
|
6193
|
+
finalTopPoints: next.topPoints,
|
|
6194
|
+
finalBottomPoints: next.bottomPoints,
|
|
6195
|
+
interpolate: next.interpolate,
|
|
6196
|
+
hasStroke: !!next.stroke && !!next.topPath,
|
|
6197
|
+
fromOpacity: 0,
|
|
6198
|
+
toOpacity: 1
|
|
6199
|
+
});
|
|
6200
|
+
continue;
|
|
6201
|
+
}
|
|
6202
|
+
const prevPKs = prev.pointKeys ?? prev.topPoints.map((_, i) => `${i}`);
|
|
6203
|
+
const nextPKs = next.pointKeys ?? next.topPoints.map((_, i) => `${i}`);
|
|
6204
|
+
const prevKeySet = new Set(prevPKs);
|
|
6205
|
+
const hasSurvivors = nextPKs.some((k) => prevKeySet.has(k));
|
|
6206
|
+
if (!hasSurvivors) {
|
|
6207
|
+
const ghost = renderSingleMark(prev, 0);
|
|
6208
|
+
if (ghost) {
|
|
6209
|
+
ghost.classList.add("oc-ghost");
|
|
6210
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6211
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6212
|
+
ghost.removeAttribute("data-key");
|
|
6213
|
+
marksContainer.appendChild(ghost);
|
|
6214
|
+
ghosts.push(ghost);
|
|
6215
|
+
tweens.push({
|
|
6216
|
+
tweenType: "area",
|
|
6217
|
+
kind: "exit",
|
|
6218
|
+
el: ghost,
|
|
6219
|
+
fromTop: prev.topPoints,
|
|
6220
|
+
toTop: prev.topPoints,
|
|
6221
|
+
fromBottom: prev.bottomPoints,
|
|
6222
|
+
toBottom: prev.bottomPoints,
|
|
6223
|
+
finalTopPoints: prev.topPoints,
|
|
6224
|
+
finalBottomPoints: prev.bottomPoints,
|
|
6225
|
+
interpolate: prev.interpolate,
|
|
6226
|
+
hasStroke: !!prev.stroke && !!prev.topPath,
|
|
6227
|
+
ghost,
|
|
6228
|
+
fromOpacity: 1,
|
|
6229
|
+
toOpacity: 0
|
|
6230
|
+
});
|
|
6231
|
+
}
|
|
6232
|
+
tweens.push({
|
|
6233
|
+
tweenType: "area",
|
|
6234
|
+
kind: "enter",
|
|
6235
|
+
el,
|
|
6236
|
+
fromTop: next.topPoints,
|
|
6237
|
+
toTop: next.topPoints,
|
|
6238
|
+
fromBottom: next.bottomPoints,
|
|
6239
|
+
toBottom: next.bottomPoints,
|
|
6240
|
+
finalTopPoints: next.topPoints,
|
|
6241
|
+
finalBottomPoints: next.bottomPoints,
|
|
6242
|
+
interpolate: next.interpolate,
|
|
6243
|
+
hasStroke: !!next.stroke && !!next.topPath,
|
|
6244
|
+
fromOpacity: 0,
|
|
6245
|
+
toOpacity: 1
|
|
6246
|
+
});
|
|
6247
|
+
} else {
|
|
6248
|
+
const [fromTop, toTop] = normalizePointArrays(
|
|
6249
|
+
prev.topPoints,
|
|
6250
|
+
next.topPoints,
|
|
6251
|
+
prevPKs,
|
|
6252
|
+
nextPKs
|
|
6253
|
+
);
|
|
6254
|
+
const [fromBottom, toBottom] = normalizePointArrays(
|
|
6255
|
+
prev.bottomPoints,
|
|
6256
|
+
next.bottomPoints,
|
|
6257
|
+
prevPKs,
|
|
6258
|
+
nextPKs
|
|
6259
|
+
);
|
|
6260
|
+
tweens.push({
|
|
6261
|
+
tweenType: "area",
|
|
6262
|
+
kind: "update",
|
|
6263
|
+
el,
|
|
6264
|
+
fromTop,
|
|
6265
|
+
toTop,
|
|
6266
|
+
fromBottom,
|
|
6267
|
+
toBottom,
|
|
6268
|
+
finalTopPoints: next.topPoints,
|
|
6269
|
+
finalBottomPoints: next.bottomPoints,
|
|
6270
|
+
interpolate: next.interpolate,
|
|
6271
|
+
hasStroke: !!next.stroke && !!next.topPath
|
|
6272
|
+
});
|
|
6273
|
+
}
|
|
6274
|
+
}
|
|
6275
|
+
for (const [key, next] of nextByKey) {
|
|
6276
|
+
if (prevByKey.has(key)) continue;
|
|
6277
|
+
const el = marksContainer.querySelector(`[data-key="${key}"]`);
|
|
6278
|
+
if (!el) continue;
|
|
6279
|
+
tweens.push({
|
|
6280
|
+
tweenType: "area",
|
|
6281
|
+
kind: "enter",
|
|
6282
|
+
el,
|
|
6283
|
+
fromTop: next.topPoints,
|
|
6284
|
+
toTop: next.topPoints,
|
|
6285
|
+
fromBottom: next.bottomPoints,
|
|
6286
|
+
toBottom: next.bottomPoints,
|
|
6287
|
+
finalTopPoints: next.topPoints,
|
|
6288
|
+
finalBottomPoints: next.bottomPoints,
|
|
6289
|
+
interpolate: next.interpolate,
|
|
6290
|
+
hasStroke: !!next.stroke && !!next.topPath,
|
|
6291
|
+
fromOpacity: 0,
|
|
6292
|
+
toOpacity: 1
|
|
6293
|
+
});
|
|
6294
|
+
}
|
|
6295
|
+
for (const [key, prev] of prevByKey) {
|
|
6296
|
+
if (nextByKey.has(key)) continue;
|
|
6297
|
+
const ghostMark = resolveGhostGradientFill(prev, ghostGradientMap);
|
|
6298
|
+
const ghost = renderSingleMark(ghostMark, 0);
|
|
6299
|
+
if (!ghost) continue;
|
|
6300
|
+
ghost.classList.add("oc-ghost");
|
|
6301
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6302
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6303
|
+
ghost.removeAttribute("data-key");
|
|
6304
|
+
marksContainer.appendChild(ghost);
|
|
6305
|
+
ghosts.push(ghost);
|
|
6306
|
+
tweens.push({
|
|
6307
|
+
tweenType: "area",
|
|
6308
|
+
kind: "exit",
|
|
6309
|
+
el: ghost,
|
|
6310
|
+
fromTop: prev.topPoints,
|
|
6311
|
+
toTop: prev.topPoints,
|
|
6312
|
+
fromBottom: prev.bottomPoints,
|
|
6313
|
+
toBottom: prev.bottomPoints,
|
|
6314
|
+
finalTopPoints: prev.topPoints,
|
|
6315
|
+
finalBottomPoints: prev.bottomPoints,
|
|
6316
|
+
interpolate: prev.interpolate,
|
|
6317
|
+
hasStroke: !!prev.stroke && !!prev.topPath,
|
|
6318
|
+
ghost,
|
|
6319
|
+
fromOpacity: 1,
|
|
6320
|
+
toOpacity: 0
|
|
6321
|
+
});
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
function buildPointTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, ghostGradientMap, fromSnapshot) {
|
|
6325
|
+
const prevPoints = prevLayout.marks.filter((m2) => m2.type === "point");
|
|
6326
|
+
const nextPoints = nextLayout.marks.filter((m2) => m2.type === "point");
|
|
6327
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
6328
|
+
for (const m2 of prevPoints) {
|
|
6329
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
6330
|
+
}
|
|
6331
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
6332
|
+
for (const m2 of nextPoints) {
|
|
6333
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
6334
|
+
}
|
|
6335
|
+
for (const [key, next] of nextByKey) {
|
|
6336
|
+
const prev = prevByKey.get(key);
|
|
6337
|
+
if (!prev) continue;
|
|
6338
|
+
const el = marksContainer.querySelector(
|
|
6339
|
+
`circle.oc-mark-point[data-key="${key}"]`
|
|
6340
|
+
);
|
|
6341
|
+
if (!el) continue;
|
|
6342
|
+
const renderedOpacity = el.getAttribute("opacity");
|
|
6343
|
+
const isSuppressed = renderedOpacity === "0";
|
|
6344
|
+
let fromCx = prev.cx;
|
|
6345
|
+
let fromCy = prev.cy;
|
|
6346
|
+
let fromR = prev.r !== next.r ? prev.r : void 0;
|
|
6347
|
+
const snap = fromSnapshot?.get(key);
|
|
6348
|
+
if (snap && snap.type === "point") {
|
|
6349
|
+
fromCx = snap.cx;
|
|
6350
|
+
fromCy = snap.cy;
|
|
6351
|
+
if (snap.r !== void 0 && snap.r !== next.r) {
|
|
6352
|
+
fromR = snap.r;
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
tweens.push({
|
|
6356
|
+
tweenType: "point",
|
|
6357
|
+
kind: "update",
|
|
6358
|
+
el,
|
|
6359
|
+
fromCx,
|
|
6360
|
+
fromCy,
|
|
6361
|
+
toCx: next.cx,
|
|
6362
|
+
toCy: next.cy,
|
|
6363
|
+
fromR,
|
|
6364
|
+
toR: fromR !== void 0 ? next.r : void 0,
|
|
6365
|
+
// Do NOT tween opacity for updated points - preserve renderer state
|
|
6366
|
+
suppressedOpacity: isSuppressed ? "0" : void 0
|
|
6367
|
+
});
|
|
6368
|
+
}
|
|
6369
|
+
for (const [key, next] of nextByKey) {
|
|
6370
|
+
if (prevByKey.has(key)) continue;
|
|
6371
|
+
const el = marksContainer.querySelector(
|
|
6372
|
+
`circle.oc-mark-point[data-key="${key}"]`
|
|
6373
|
+
);
|
|
6374
|
+
if (!el) continue;
|
|
6375
|
+
const renderedOpacity = el.getAttribute("opacity");
|
|
6376
|
+
const targetOpacity = renderedOpacity != null ? Number(renderedOpacity) : 1;
|
|
6377
|
+
tweens.push({
|
|
6378
|
+
tweenType: "point",
|
|
6379
|
+
kind: "enter",
|
|
6380
|
+
el,
|
|
6381
|
+
fromCx: next.cx,
|
|
6382
|
+
fromCy: next.cy,
|
|
6383
|
+
toCx: next.cx,
|
|
6384
|
+
toCy: next.cy,
|
|
6385
|
+
fromOpacity: 0,
|
|
6386
|
+
toOpacity: targetOpacity,
|
|
6387
|
+
suppressedOpacity: renderedOpacity === "0" ? "0" : void 0
|
|
6388
|
+
});
|
|
6389
|
+
}
|
|
6390
|
+
for (const [key, prev] of prevByKey) {
|
|
6391
|
+
if (nextByKey.has(key)) continue;
|
|
6392
|
+
const ghostMark = resolveGhostGradientFill(prev, ghostGradientMap);
|
|
6393
|
+
const ghost = renderSingleMark(ghostMark, 0);
|
|
6394
|
+
if (!ghost) continue;
|
|
6395
|
+
ghost.classList.add("oc-ghost");
|
|
6396
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6397
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6398
|
+
ghost.removeAttribute("data-key");
|
|
6399
|
+
marksContainer.appendChild(ghost);
|
|
6400
|
+
ghosts.push(ghost);
|
|
6401
|
+
tweens.push({
|
|
6402
|
+
tweenType: "point",
|
|
6403
|
+
kind: "exit",
|
|
6404
|
+
el: ghost,
|
|
6405
|
+
fromCx: prev.cx,
|
|
6406
|
+
fromCy: prev.cy,
|
|
6407
|
+
toCx: prev.cx,
|
|
6408
|
+
toCy: prev.cy,
|
|
6409
|
+
ghost,
|
|
6410
|
+
fromOpacity: 1,
|
|
6411
|
+
toOpacity: 0
|
|
6412
|
+
});
|
|
6413
|
+
}
|
|
6414
|
+
}
|
|
6415
|
+
function buildRuleTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot) {
|
|
6416
|
+
const prevRules = prevLayout.marks.filter((m2) => m2.type === "rule");
|
|
6417
|
+
const nextRules = nextLayout.marks.filter((m2) => m2.type === "rule");
|
|
6418
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
6419
|
+
for (const m2 of prevRules) {
|
|
6420
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
6421
|
+
}
|
|
6422
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
6423
|
+
for (const m2 of nextRules) {
|
|
6424
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
6425
|
+
}
|
|
6426
|
+
for (const [key, next] of nextByKey) {
|
|
6427
|
+
const prev = prevByKey.get(key);
|
|
6428
|
+
if (!prev) continue;
|
|
6429
|
+
const el = marksContainer.querySelector(
|
|
6430
|
+
`.oc-mark-rule[data-key="${key}"]`
|
|
6431
|
+
);
|
|
6432
|
+
if (!el) continue;
|
|
6433
|
+
let fromX1 = prev.x1;
|
|
6434
|
+
let fromY1 = prev.y1;
|
|
6435
|
+
let fromX2 = prev.x2;
|
|
6436
|
+
let fromY2 = prev.y2;
|
|
6437
|
+
const snap = fromSnapshot?.get(key);
|
|
6438
|
+
if (snap && snap.type === "rule") {
|
|
6439
|
+
fromX1 = snap.x1;
|
|
6440
|
+
fromY1 = snap.y1;
|
|
6441
|
+
fromX2 = snap.x2;
|
|
6442
|
+
fromY2 = snap.y2;
|
|
6443
|
+
}
|
|
6444
|
+
tweens.push({
|
|
6445
|
+
tweenType: "rule",
|
|
6446
|
+
kind: "update",
|
|
6447
|
+
el,
|
|
6448
|
+
fromX1,
|
|
6449
|
+
fromY1,
|
|
6450
|
+
fromX2,
|
|
6451
|
+
fromY2,
|
|
6452
|
+
toX1: next.x1,
|
|
6453
|
+
toY1: next.y1,
|
|
6454
|
+
toX2: next.x2,
|
|
6455
|
+
toY2: next.y2
|
|
6456
|
+
});
|
|
6457
|
+
}
|
|
6458
|
+
for (const [key, next] of nextByKey) {
|
|
6459
|
+
if (prevByKey.has(key)) continue;
|
|
6460
|
+
const el = marksContainer.querySelector(
|
|
6461
|
+
`.oc-mark-rule[data-key="${key}"]`
|
|
6462
|
+
);
|
|
6463
|
+
if (!el) continue;
|
|
6464
|
+
tweens.push({
|
|
6465
|
+
tweenType: "rule",
|
|
6466
|
+
kind: "enter",
|
|
6467
|
+
el,
|
|
6468
|
+
fromX1: next.x1,
|
|
6469
|
+
fromY1: next.y1,
|
|
6470
|
+
fromX2: next.x2,
|
|
6471
|
+
fromY2: next.y2,
|
|
6472
|
+
toX1: next.x1,
|
|
6473
|
+
toY1: next.y1,
|
|
6474
|
+
toX2: next.x2,
|
|
6475
|
+
toY2: next.y2,
|
|
6476
|
+
fromOpacity: 0,
|
|
6477
|
+
toOpacity: 1
|
|
6478
|
+
});
|
|
6479
|
+
}
|
|
6480
|
+
for (const [key, prev] of prevByKey) {
|
|
6481
|
+
if (nextByKey.has(key)) continue;
|
|
6482
|
+
const ghost = renderSingleMark(prev, 0);
|
|
6483
|
+
if (!ghost) continue;
|
|
6484
|
+
ghost.classList.add("oc-ghost");
|
|
6485
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6486
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6487
|
+
ghost.removeAttribute("data-key");
|
|
6488
|
+
marksContainer.appendChild(ghost);
|
|
6489
|
+
ghosts.push(ghost);
|
|
6490
|
+
tweens.push({
|
|
6491
|
+
tweenType: "rule",
|
|
6492
|
+
kind: "exit",
|
|
6493
|
+
el: ghost,
|
|
6494
|
+
fromX1: prev.x1,
|
|
6495
|
+
fromY1: prev.y1,
|
|
6496
|
+
fromX2: prev.x2,
|
|
6497
|
+
fromY2: prev.y2,
|
|
6498
|
+
toX1: prev.x1,
|
|
6499
|
+
toY1: prev.y1,
|
|
6500
|
+
toX2: prev.x2,
|
|
6501
|
+
toY2: prev.y2,
|
|
6502
|
+
ghost,
|
|
6503
|
+
fromOpacity: 1,
|
|
6504
|
+
toOpacity: 0
|
|
6505
|
+
});
|
|
6506
|
+
}
|
|
6507
|
+
}
|
|
6508
|
+
function buildTickTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot) {
|
|
6509
|
+
const prevTicks = prevLayout.marks.filter((m2) => m2.type === "tick");
|
|
6510
|
+
const nextTicks = nextLayout.marks.filter((m2) => m2.type === "tick");
|
|
6511
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
6512
|
+
for (const m2 of prevTicks) {
|
|
6513
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
6514
|
+
}
|
|
6515
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
6516
|
+
for (const m2 of nextTicks) {
|
|
6517
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
6518
|
+
}
|
|
6519
|
+
function tickEndpoints(t) {
|
|
6520
|
+
const half = t.length / 2;
|
|
6521
|
+
if (t.orient === "vertical") {
|
|
6522
|
+
return { x1: t.x, y1: t.y - half, x2: t.x, y2: t.y + half };
|
|
6523
|
+
}
|
|
6524
|
+
return { x1: t.x - half, y1: t.y, x2: t.x + half, y2: t.y };
|
|
6525
|
+
}
|
|
6526
|
+
for (const [key, next] of nextByKey) {
|
|
6527
|
+
const prev = prevByKey.get(key);
|
|
6528
|
+
if (!prev) continue;
|
|
6529
|
+
const el = marksContainer.querySelector(
|
|
6530
|
+
`.oc-mark-tick[data-key="${key}"]`
|
|
6531
|
+
);
|
|
6532
|
+
if (!el) continue;
|
|
6533
|
+
let pEnd = tickEndpoints(prev);
|
|
6534
|
+
const nEnd = tickEndpoints(next);
|
|
6535
|
+
const snap = fromSnapshot?.get(key);
|
|
6536
|
+
if (snap && snap.type === "tick") {
|
|
6537
|
+
pEnd = { x1: snap.x1, y1: snap.y1, x2: snap.x2, y2: snap.y2 };
|
|
6538
|
+
}
|
|
6539
|
+
tweens.push({
|
|
6540
|
+
tweenType: "tick",
|
|
6541
|
+
kind: "update",
|
|
6542
|
+
el,
|
|
6543
|
+
fromX1: pEnd.x1,
|
|
6544
|
+
fromY1: pEnd.y1,
|
|
6545
|
+
fromX2: pEnd.x2,
|
|
6546
|
+
fromY2: pEnd.y2,
|
|
6547
|
+
toX1: nEnd.x1,
|
|
6548
|
+
toY1: nEnd.y1,
|
|
6549
|
+
toX2: nEnd.x2,
|
|
6550
|
+
toY2: nEnd.y2
|
|
6551
|
+
});
|
|
6552
|
+
}
|
|
6553
|
+
for (const [key, next] of nextByKey) {
|
|
6554
|
+
if (prevByKey.has(key)) continue;
|
|
6555
|
+
const el = marksContainer.querySelector(
|
|
6556
|
+
`.oc-mark-tick[data-key="${key}"]`
|
|
6557
|
+
);
|
|
6558
|
+
if (!el) continue;
|
|
6559
|
+
const nEnd = tickEndpoints(next);
|
|
6560
|
+
tweens.push({
|
|
6561
|
+
tweenType: "tick",
|
|
6562
|
+
kind: "enter",
|
|
6563
|
+
el,
|
|
6564
|
+
fromX1: nEnd.x1,
|
|
6565
|
+
fromY1: nEnd.y1,
|
|
6566
|
+
fromX2: nEnd.x2,
|
|
6567
|
+
fromY2: nEnd.y2,
|
|
6568
|
+
toX1: nEnd.x1,
|
|
6569
|
+
toY1: nEnd.y1,
|
|
6570
|
+
toX2: nEnd.x2,
|
|
6571
|
+
toY2: nEnd.y2,
|
|
6572
|
+
fromOpacity: 0,
|
|
6573
|
+
toOpacity: 1
|
|
6574
|
+
});
|
|
6575
|
+
}
|
|
6576
|
+
for (const [key, prev] of prevByKey) {
|
|
6577
|
+
if (nextByKey.has(key)) continue;
|
|
6578
|
+
const ghost = renderSingleMark(prev, 0);
|
|
6579
|
+
if (!ghost) continue;
|
|
6580
|
+
ghost.classList.add("oc-ghost");
|
|
6581
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6582
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6583
|
+
ghost.removeAttribute("data-key");
|
|
6584
|
+
marksContainer.appendChild(ghost);
|
|
6585
|
+
ghosts.push(ghost);
|
|
6586
|
+
const pEnd = tickEndpoints(prev);
|
|
6587
|
+
tweens.push({
|
|
6588
|
+
tweenType: "tick",
|
|
6589
|
+
kind: "exit",
|
|
6590
|
+
el: ghost,
|
|
6591
|
+
fromX1: pEnd.x1,
|
|
6592
|
+
fromY1: pEnd.y1,
|
|
6593
|
+
fromX2: pEnd.x2,
|
|
6594
|
+
fromY2: pEnd.y2,
|
|
6595
|
+
toX1: pEnd.x1,
|
|
6596
|
+
toY1: pEnd.y1,
|
|
6597
|
+
toX2: pEnd.x2,
|
|
6598
|
+
toY2: pEnd.y2,
|
|
6599
|
+
ghost,
|
|
6600
|
+
fromOpacity: 1,
|
|
6601
|
+
toOpacity: 0
|
|
6602
|
+
});
|
|
6603
|
+
}
|
|
6604
|
+
}
|
|
6605
|
+
function buildTextMarkTweens(prevLayout, nextLayout, marksContainer, tweens, ghosts, fromSnapshot) {
|
|
6606
|
+
const prevTexts = prevLayout.marks.filter((m2) => m2.type === "textMark");
|
|
6607
|
+
const nextTexts = nextLayout.marks.filter((m2) => m2.type === "textMark");
|
|
6608
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
6609
|
+
for (const m2 of prevTexts) {
|
|
6610
|
+
if (m2.key) prevByKey.set(m2.key, m2);
|
|
6611
|
+
}
|
|
6612
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
6613
|
+
for (const m2 of nextTexts) {
|
|
6614
|
+
if (m2.key) nextByKey.set(m2.key, m2);
|
|
6615
|
+
}
|
|
6616
|
+
for (const [key, next] of nextByKey) {
|
|
6617
|
+
const prev = prevByKey.get(key);
|
|
6618
|
+
if (!prev) continue;
|
|
6619
|
+
const el = marksContainer.querySelector(
|
|
6620
|
+
`.oc-mark-text[data-key="${key}"]`
|
|
6621
|
+
);
|
|
6622
|
+
if (!el) continue;
|
|
6623
|
+
let fromX = prev.x;
|
|
6624
|
+
let fromY = prev.y;
|
|
6625
|
+
const snap = fromSnapshot?.get(key);
|
|
6626
|
+
if (snap && snap.type === "textMark") {
|
|
6627
|
+
fromX = snap.x;
|
|
6628
|
+
fromY = snap.y;
|
|
6629
|
+
}
|
|
6630
|
+
tweens.push({
|
|
6631
|
+
tweenType: "textMark",
|
|
6632
|
+
kind: "update",
|
|
6633
|
+
el,
|
|
6634
|
+
fromX,
|
|
6635
|
+
fromY,
|
|
6636
|
+
toX: next.x,
|
|
6637
|
+
toY: next.y
|
|
6638
|
+
});
|
|
6639
|
+
}
|
|
6640
|
+
for (const [key, next] of nextByKey) {
|
|
6641
|
+
if (prevByKey.has(key)) continue;
|
|
6642
|
+
const el = marksContainer.querySelector(
|
|
6643
|
+
`.oc-mark-text[data-key="${key}"]`
|
|
6644
|
+
);
|
|
6645
|
+
if (!el) continue;
|
|
6646
|
+
tweens.push({
|
|
6647
|
+
tweenType: "textMark",
|
|
6648
|
+
kind: "enter",
|
|
6649
|
+
el,
|
|
6650
|
+
fromX: next.x,
|
|
6651
|
+
fromY: next.y,
|
|
6652
|
+
toX: next.x,
|
|
6653
|
+
toY: next.y,
|
|
6654
|
+
fromOpacity: 0,
|
|
6655
|
+
toOpacity: 1
|
|
6656
|
+
});
|
|
6657
|
+
}
|
|
6658
|
+
for (const [key, prev] of prevByKey) {
|
|
6659
|
+
if (nextByKey.has(key)) continue;
|
|
6660
|
+
const ghost = renderSingleMark(prev, 0);
|
|
6661
|
+
if (!ghost) continue;
|
|
6662
|
+
ghost.classList.add("oc-ghost");
|
|
6663
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6664
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6665
|
+
ghost.removeAttribute("data-key");
|
|
6666
|
+
marksContainer.appendChild(ghost);
|
|
6667
|
+
ghosts.push(ghost);
|
|
6668
|
+
tweens.push({
|
|
6669
|
+
tweenType: "textMark",
|
|
6670
|
+
kind: "exit",
|
|
6671
|
+
el: ghost,
|
|
6672
|
+
fromX: prev.x,
|
|
6673
|
+
fromY: prev.y,
|
|
6674
|
+
toX: prev.x,
|
|
6675
|
+
toY: prev.y,
|
|
6676
|
+
ghost,
|
|
6677
|
+
fromOpacity: 1,
|
|
6678
|
+
toOpacity: 0
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
function buildAxisTweens(svg, prevLayout, nextLayout, tweens, ghosts) {
|
|
6683
|
+
buildSingleAxisTweens(svg, prevLayout.axes.x, nextLayout.axes.x, "x", nextLayout, tweens, ghosts);
|
|
6684
|
+
buildSingleAxisTweens(svg, prevLayout.axes.y, nextLayout.axes.y, "y", nextLayout, tweens, ghosts);
|
|
6685
|
+
}
|
|
6686
|
+
function buildSingleAxisTweens(svg, prevAxis, nextAxis, orientation, layout, tweens, ghosts) {
|
|
6687
|
+
if (!prevAxis || !nextAxis) return;
|
|
6688
|
+
const axisClass = `oc-axis-${orientation}`;
|
|
6689
|
+
const axisGroup = svg.querySelector(`.${axisClass}`);
|
|
6690
|
+
if (!axisGroup) return;
|
|
6691
|
+
const isRotated = !!(nextAxis.tickAngle && Math.abs(nextAxis.tickAngle) > 10);
|
|
6692
|
+
const prevTickMap = /* @__PURE__ */ new Map();
|
|
6693
|
+
for (const tick of prevAxis.ticks) {
|
|
6694
|
+
prevTickMap.set(serializeKeyValue(tick.value), tick.position);
|
|
6695
|
+
}
|
|
6696
|
+
const nextTickMap = /* @__PURE__ */ new Map();
|
|
6697
|
+
for (const tick of nextAxis.ticks) {
|
|
6698
|
+
nextTickMap.set(serializeKeyValue(tick.value), tick.position);
|
|
6699
|
+
}
|
|
6700
|
+
const posAttr = orientation === "x" ? "x" : "y";
|
|
6701
|
+
const tickLabels = axisGroup.querySelectorAll(".oc-axis-tick[data-tick-key]");
|
|
6702
|
+
const matchedNextKeys = /* @__PURE__ */ new Set();
|
|
6703
|
+
for (const labelEl of tickLabels) {
|
|
6704
|
+
const key = labelEl.getAttribute("data-tick-key");
|
|
6705
|
+
if (!key) continue;
|
|
6706
|
+
const nextPos = nextTickMap.get(key);
|
|
6707
|
+
const prevPos = prevTickMap.get(key);
|
|
6708
|
+
if (prevPos !== void 0 && nextPos !== void 0) {
|
|
6709
|
+
matchedNextKeys.add(key);
|
|
6710
|
+
tweens.push({
|
|
6711
|
+
tweenType: "axisTick",
|
|
6712
|
+
kind: "update",
|
|
6713
|
+
el: labelEl,
|
|
6714
|
+
posAttr,
|
|
6715
|
+
fromPos: prevPos,
|
|
6716
|
+
toPos: nextPos,
|
|
6717
|
+
crossfade: isRotated
|
|
6718
|
+
});
|
|
6719
|
+
} else if (prevPos === void 0 && nextPos !== void 0) {
|
|
6720
|
+
matchedNextKeys.add(key);
|
|
6721
|
+
tweens.push({
|
|
6722
|
+
tweenType: "axisTick",
|
|
6723
|
+
kind: "enter",
|
|
6724
|
+
el: labelEl,
|
|
6725
|
+
posAttr,
|
|
6726
|
+
fromPos: nextPos,
|
|
6727
|
+
toPos: nextPos,
|
|
6728
|
+
crossfade: false,
|
|
6729
|
+
fromOpacity: 0,
|
|
6730
|
+
toOpacity: 1
|
|
6731
|
+
});
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
for (const [key, prevPos] of prevTickMap) {
|
|
6735
|
+
if (nextTickMap.has(key)) continue;
|
|
6736
|
+
const prevTick = prevAxis.ticks.find((t) => serializeKeyValue(t.value) === key);
|
|
6737
|
+
if (!prevTick) continue;
|
|
6738
|
+
const ghost = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
6739
|
+
ghost.setAttribute("class", "oc-axis-tick oc-ghost");
|
|
6740
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6741
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6742
|
+
ghost.textContent = prevTick.label;
|
|
6743
|
+
const area = layout.area;
|
|
6744
|
+
if (orientation === "x") {
|
|
6745
|
+
ghost.setAttribute("x", String(prevPos));
|
|
6746
|
+
const xLabelPad = nextAxis.labelPadding ?? layout.theme.spacing.xAxisLabelPadding;
|
|
6747
|
+
const fontSize = nextAxis.tickLabelStyle.fontSize;
|
|
6748
|
+
ghost.setAttribute("y", String(area.y + area.height + xLabelPad + fontSize * 0.8));
|
|
6749
|
+
ghost.setAttribute("text-anchor", "middle");
|
|
6750
|
+
} else {
|
|
6751
|
+
const TICK_LABEL_OFFSET = 8;
|
|
6752
|
+
ghost.setAttribute("x", String(area.x - TICK_LABEL_OFFSET));
|
|
6753
|
+
ghost.setAttribute("y", String(prevPos));
|
|
6754
|
+
ghost.setAttribute("text-anchor", "end");
|
|
6755
|
+
ghost.setAttribute("dominant-baseline", "central");
|
|
6756
|
+
}
|
|
6757
|
+
axisGroup.appendChild(ghost);
|
|
6758
|
+
ghosts.push(ghost);
|
|
6759
|
+
tweens.push({
|
|
6760
|
+
tweenType: "axisTick",
|
|
6761
|
+
kind: "exit",
|
|
6762
|
+
el: ghost,
|
|
6763
|
+
posAttr,
|
|
6764
|
+
fromPos: prevPos,
|
|
6765
|
+
toPos: prevPos,
|
|
6766
|
+
crossfade: false,
|
|
6767
|
+
ghost,
|
|
6768
|
+
fromOpacity: 1,
|
|
6769
|
+
toOpacity: 0
|
|
6770
|
+
});
|
|
6771
|
+
}
|
|
6772
|
+
const prevGridMap = /* @__PURE__ */ new Map();
|
|
6773
|
+
for (const gridline of prevAxis.gridlines) {
|
|
6774
|
+
prevGridMap.set(String(gridline.position), gridline.position);
|
|
6775
|
+
}
|
|
6776
|
+
const prevGridByValue = /* @__PURE__ */ new Map();
|
|
6777
|
+
for (const tick of prevAxis.ticks) {
|
|
6778
|
+
const key = serializeKeyValue(tick.value);
|
|
6779
|
+
const matchingGridline = prevAxis.gridlines.find((g) => g.position === tick.position);
|
|
6780
|
+
if (matchingGridline) {
|
|
6781
|
+
prevGridByValue.set(key, matchingGridline.position);
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
const nextGridByValue = /* @__PURE__ */ new Map();
|
|
6785
|
+
for (const tick of nextAxis.ticks) {
|
|
6786
|
+
const key = serializeKeyValue(tick.value);
|
|
6787
|
+
const matchingGridline = nextAxis.gridlines.find((g) => g.position === tick.position);
|
|
6788
|
+
if (matchingGridline) {
|
|
6789
|
+
nextGridByValue.set(key, matchingGridline.position);
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6792
|
+
const gridlines = axisGroup.querySelectorAll(".oc-gridline[data-tick-key]");
|
|
6793
|
+
const matchedGridKeys = /* @__PURE__ */ new Set();
|
|
6794
|
+
for (const glEl of gridlines) {
|
|
6795
|
+
const key = glEl.getAttribute("data-tick-key");
|
|
6796
|
+
if (!key) continue;
|
|
6797
|
+
const nextPos = nextGridByValue.get(key);
|
|
6798
|
+
const prevPos = prevGridByValue.get(key);
|
|
6799
|
+
if (prevPos !== void 0 && nextPos !== void 0) {
|
|
6800
|
+
matchedGridKeys.add(key);
|
|
6801
|
+
tweens.push({
|
|
6802
|
+
tweenType: "gridline",
|
|
6803
|
+
kind: "update",
|
|
6804
|
+
el: glEl,
|
|
6805
|
+
orientation,
|
|
6806
|
+
fromPos: prevPos,
|
|
6807
|
+
toPos: nextPos
|
|
6808
|
+
});
|
|
6809
|
+
} else if (prevPos === void 0 && nextPos !== void 0) {
|
|
6810
|
+
matchedGridKeys.add(key);
|
|
6811
|
+
tweens.push({
|
|
6812
|
+
tweenType: "gridline",
|
|
6813
|
+
kind: "enter",
|
|
6814
|
+
el: glEl,
|
|
6815
|
+
orientation,
|
|
6816
|
+
fromPos: nextPos,
|
|
6817
|
+
toPos: nextPos,
|
|
6818
|
+
fromOpacity: 0,
|
|
6819
|
+
toOpacity: 1
|
|
6820
|
+
});
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
for (const [key, prevPos] of prevGridByValue) {
|
|
6824
|
+
if (nextGridByValue.has(key)) continue;
|
|
6825
|
+
const ghost = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
6826
|
+
ghost.setAttribute("class", "oc-gridline oc-ghost");
|
|
6827
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
6828
|
+
ghost.setAttribute("pointer-events", "none");
|
|
6829
|
+
if (orientation === "y") {
|
|
6830
|
+
const area = layout.area;
|
|
6831
|
+
ghost.setAttribute("x1", String(area.x));
|
|
6832
|
+
ghost.setAttribute("y1", String(prevPos));
|
|
6833
|
+
ghost.setAttribute("x2", String(area.x + area.width));
|
|
6834
|
+
ghost.setAttribute("y2", String(prevPos));
|
|
6835
|
+
} else {
|
|
6836
|
+
const area = layout.area;
|
|
6837
|
+
ghost.setAttribute("x1", String(prevPos));
|
|
6838
|
+
ghost.setAttribute("y1", String(area.y));
|
|
6839
|
+
ghost.setAttribute("x2", String(prevPos));
|
|
6840
|
+
ghost.setAttribute("y2", String(area.y + area.height));
|
|
6841
|
+
}
|
|
6842
|
+
axisGroup.appendChild(ghost);
|
|
6843
|
+
ghosts.push(ghost);
|
|
6844
|
+
tweens.push({
|
|
6845
|
+
tweenType: "gridline",
|
|
6846
|
+
kind: "exit",
|
|
6847
|
+
el: ghost,
|
|
6848
|
+
orientation,
|
|
6849
|
+
fromPos: prevPos,
|
|
6850
|
+
toPos: prevPos,
|
|
6851
|
+
ghost,
|
|
6852
|
+
fromOpacity: 1,
|
|
6853
|
+
toOpacity: 0
|
|
6854
|
+
});
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
|
|
5180
6858
|
// src/mount.ts
|
|
5181
6859
|
function resolveDarkMode3(mode) {
|
|
5182
6860
|
if (mode === "force") return true;
|
|
@@ -5225,6 +6903,8 @@ function createChart(container, spec, options) {
|
|
|
5225
6903
|
let isFirstRender = true;
|
|
5226
6904
|
let cleanupAnimations = null;
|
|
5227
6905
|
let pendingResize = false;
|
|
6906
|
+
let transitionHandle = null;
|
|
6907
|
+
let transitionSnapshot = null;
|
|
5228
6908
|
let fontsReloadPending = false;
|
|
5229
6909
|
let selectedElement = options?.selectedElement ?? null;
|
|
5230
6910
|
let overlayElement = null;
|
|
@@ -5549,6 +7229,10 @@ function createChart(container, spec, options) {
|
|
|
5549
7229
|
pendingRender = true;
|
|
5550
7230
|
return;
|
|
5551
7231
|
}
|
|
7232
|
+
if (transitionHandle) {
|
|
7233
|
+
transitionHandle.cancel();
|
|
7234
|
+
transitionHandle = null;
|
|
7235
|
+
}
|
|
5552
7236
|
if (cleanupAnimations) {
|
|
5553
7237
|
cleanupAnimations();
|
|
5554
7238
|
cleanupAnimations = null;
|
|
@@ -5589,7 +7273,7 @@ function createChart(container, spec, options) {
|
|
|
5589
7273
|
srTable = null;
|
|
5590
7274
|
}
|
|
5591
7275
|
currentLayout = compile();
|
|
5592
|
-
const shouldAnimate = isFirstRender && !!currentLayout.animation?.
|
|
7276
|
+
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enter;
|
|
5593
7277
|
const crosshair = !!currentLayout.crosshair;
|
|
5594
7278
|
svgElement = renderChartSVG(currentLayout, container, {
|
|
5595
7279
|
animate: shouldAnimate,
|
|
@@ -5699,6 +7383,12 @@ function createChart(container, spec, options) {
|
|
|
5699
7383
|
}
|
|
5700
7384
|
function update(newSpec, updateOpts) {
|
|
5701
7385
|
if (destroyed) return;
|
|
7386
|
+
const prevSpec = currentSpec;
|
|
7387
|
+
const prevLayout = currentLayout;
|
|
7388
|
+
const entranceWasRunning = cleanupAnimations != null;
|
|
7389
|
+
if (transitionHandle?.running) {
|
|
7390
|
+
transitionSnapshot = transitionHandle.snapshot();
|
|
7391
|
+
}
|
|
5702
7392
|
currentSpec = newSpec;
|
|
5703
7393
|
const nextFont = resolveEffectiveFont();
|
|
5704
7394
|
if (nextFont !== fontFamily) {
|
|
@@ -5711,6 +7401,29 @@ function createChart(container, spec, options) {
|
|
|
5711
7401
|
selectedElement = updateOpts.selectedElement ?? null;
|
|
5712
7402
|
}
|
|
5713
7403
|
render();
|
|
7404
|
+
if (svgElement && canTransition({
|
|
7405
|
+
prevLayout,
|
|
7406
|
+
nextLayout: currentLayout,
|
|
7407
|
+
prevSpec,
|
|
7408
|
+
nextSpec: newSpec,
|
|
7409
|
+
isFirstRender: false,
|
|
7410
|
+
entranceInFlight: entranceWasRunning
|
|
7411
|
+
})) {
|
|
7412
|
+
const snapshot = transitionSnapshot;
|
|
7413
|
+
transitionSnapshot = null;
|
|
7414
|
+
transitionHandle = runTransition({
|
|
7415
|
+
svg: svgElement,
|
|
7416
|
+
prevLayout,
|
|
7417
|
+
nextLayout: currentLayout,
|
|
7418
|
+
animation: currentLayout.animation,
|
|
7419
|
+
fromSnapshot: snapshot ?? void 0,
|
|
7420
|
+
onComplete: () => {
|
|
7421
|
+
transitionHandle = null;
|
|
7422
|
+
}
|
|
7423
|
+
});
|
|
7424
|
+
} else {
|
|
7425
|
+
transitionSnapshot = null;
|
|
7426
|
+
}
|
|
5714
7427
|
}
|
|
5715
7428
|
function resize() {
|
|
5716
7429
|
if (destroyed) return;
|
|
@@ -5765,6 +7478,11 @@ function createChart(container, spec, options) {
|
|
|
5765
7478
|
function destroy() {
|
|
5766
7479
|
if (destroyed) return;
|
|
5767
7480
|
destroyed = true;
|
|
7481
|
+
if (transitionHandle) {
|
|
7482
|
+
transitionHandle.cancel();
|
|
7483
|
+
transitionHandle = null;
|
|
7484
|
+
}
|
|
7485
|
+
transitionSnapshot = null;
|
|
5768
7486
|
if (cleanupAnimations) {
|
|
5769
7487
|
cleanupAnimations();
|
|
5770
7488
|
cleanupAnimations = null;
|
|
@@ -6221,7 +7939,7 @@ function applyTextStyle(el, style) {
|
|
|
6221
7939
|
}
|
|
6222
7940
|
}
|
|
6223
7941
|
function stampAnimationAttrs(el, mark, fallbackIndex, animation) {
|
|
6224
|
-
if (!animation?.
|
|
7942
|
+
if (!animation?.enter) return;
|
|
6225
7943
|
const idx = mark.animationIndex ?? fallbackIndex;
|
|
6226
7944
|
el.setAttribute("data-animation-index", String(idx));
|
|
6227
7945
|
el.style.setProperty("--oc-mark-index", String(idx));
|
|
@@ -6335,7 +8053,7 @@ function renderBrand(parent, layout) {
|
|
|
6335
8053
|
a2.appendChild(text);
|
|
6336
8054
|
parent.appendChild(a2);
|
|
6337
8055
|
}
|
|
6338
|
-
function
|
|
8056
|
+
function renderLegend(parent, legendLayout) {
|
|
6339
8057
|
if (!("entries" in legendLayout) || legendLayout.entries.length === 0) return;
|
|
6340
8058
|
const legend = legendLayout;
|
|
6341
8059
|
const g = createSVGElement2("g");
|
|
@@ -6553,16 +8271,17 @@ function renderSankeySVG(layout, animation) {
|
|
|
6553
8271
|
svg.style.height = `${height}px`;
|
|
6554
8272
|
svg.setAttribute("role", layout.a11y.role);
|
|
6555
8273
|
svg.setAttribute("aria-label", layout.a11y.altText);
|
|
6556
|
-
const
|
|
8274
|
+
const enterPhase = animation?.enter;
|
|
8275
|
+
const animate = !!enterPhase;
|
|
6557
8276
|
const classes = animate ? "oc-chart oc-sankey oc-animate" : "oc-chart oc-sankey";
|
|
6558
8277
|
svg.setAttribute("class", classes);
|
|
6559
|
-
if (
|
|
8278
|
+
if (enterPhase) {
|
|
6560
8279
|
const totalMarks = layout.nodes.length + layout.links.length;
|
|
6561
|
-
const stagger = clampStaggerDelay(
|
|
6562
|
-
svg.style.setProperty("--oc-animation-duration", `${
|
|
8280
|
+
const stagger = clampStaggerDelay(enterPhase.staggerDelay, totalMarks);
|
|
8281
|
+
svg.style.setProperty("--oc-animation-duration", `${enterPhase.duration}ms`);
|
|
6563
8282
|
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
6564
8283
|
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
6565
|
-
const easeVar = EASE_VAR_MAP[
|
|
8284
|
+
const easeVar = EASE_VAR_MAP[enterPhase.ease] || EASE_VAR_MAP.smooth;
|
|
6566
8285
|
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
6567
8286
|
}
|
|
6568
8287
|
const bg = createSVGElement2("rect");
|
|
@@ -6582,7 +8301,7 @@ function renderSankeySVG(layout, animation) {
|
|
|
6582
8301
|
renderLinks(svg, layout.links, nodePositions, animation);
|
|
6583
8302
|
renderNodes(svg, layout.nodes, animation);
|
|
6584
8303
|
renderLabels(svg, layout.nodes, layout.measureText);
|
|
6585
|
-
|
|
8304
|
+
renderLegend(svg, layout.legend);
|
|
6586
8305
|
renderChrome2(svg, layout);
|
|
6587
8306
|
if (layout.watermark) {
|
|
6588
8307
|
renderBrand(svg, layout);
|
|
@@ -6800,7 +8519,7 @@ function createSankey(container, spec, options) {
|
|
|
6800
8519
|
svgElement.parentNode.removeChild(svgElement);
|
|
6801
8520
|
}
|
|
6802
8521
|
currentLayout = compile();
|
|
6803
|
-
const shouldAnimate = isFirstRender && currentLayout.animation?.
|
|
8522
|
+
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enter;
|
|
6804
8523
|
isFirstRender = false;
|
|
6805
8524
|
const animation = shouldAnimate ? currentLayout.animation : void 0;
|
|
6806
8525
|
svgElement = renderSankeySVG(currentLayout, animation);
|
|
@@ -6901,7 +8620,7 @@ function createSankey(container, spec, options) {
|
|
|
6901
8620
|
}
|
|
6902
8621
|
try {
|
|
6903
8622
|
currentLayout = compile();
|
|
6904
|
-
const shouldAnimate = currentLayout.animation?.
|
|
8623
|
+
const shouldAnimate = !!currentLayout.animation?.enter;
|
|
6905
8624
|
isFirstRender = false;
|
|
6906
8625
|
const animation = shouldAnimate ? currentLayout.animation : void 0;
|
|
6907
8626
|
svgElement = renderSankeySVG(currentLayout, animation);
|
|
@@ -7412,14 +9131,14 @@ function renderTable(layout, container, opts) {
|
|
|
7412
9131
|
if (footerRow.childElementCount > 0) {
|
|
7413
9132
|
wrapper.appendChild(footerRow);
|
|
7414
9133
|
}
|
|
7415
|
-
if (opts?.animate && layout.animation?.
|
|
7416
|
-
const
|
|
9134
|
+
if (opts?.animate && layout.animation?.enter) {
|
|
9135
|
+
const enterPhase = layout.animation.enter;
|
|
7417
9136
|
const rowCount = layout.rows.length;
|
|
7418
|
-
const stagger = clampStaggerDelay2(
|
|
9137
|
+
const stagger = clampStaggerDelay2(enterPhase.staggerDelay, rowCount);
|
|
7419
9138
|
const s = wrapper.style;
|
|
7420
|
-
s.setProperty("--oc-animation-duration", `${
|
|
9139
|
+
s.setProperty("--oc-animation-duration", `${enterPhase.duration}ms`);
|
|
7421
9140
|
s.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
7422
|
-
s.setProperty("--oc-animation-ease", EASE_VAR_MAP2[
|
|
9141
|
+
s.setProperty("--oc-animation-ease", EASE_VAR_MAP2[enterPhase.ease] || EASE_VAR_MAP2.smooth);
|
|
7423
9142
|
wrapper.classList.add("oc-animate");
|
|
7424
9143
|
}
|
|
7425
9144
|
container.appendChild(wrapper);
|
|
@@ -7549,7 +9268,7 @@ function createTable(container, spec, options) {
|
|
|
7549
9268
|
wrapperElement = null;
|
|
7550
9269
|
}
|
|
7551
9270
|
currentLayout = compile();
|
|
7552
|
-
const shouldAnimate = isFirstRender && !!currentLayout.animation?.
|
|
9271
|
+
const shouldAnimate = isFirstRender && !!currentLayout.animation?.enter;
|
|
7553
9272
|
wrapperElement = renderTable(currentLayout, container, { animate: shouldAnimate });
|
|
7554
9273
|
if (shouldAnimate && wrapperElement) {
|
|
7555
9274
|
cleanupAnimations = setupTableAnimationCleanup(wrapperElement);
|
|
@@ -7792,349 +9511,6 @@ function createTable(container, spec, options) {
|
|
|
7792
9511
|
|
|
7793
9512
|
// src/tilemap-mount.ts
|
|
7794
9513
|
import { compileTileMap } from "@opendata-ai/openchart-engine";
|
|
7795
|
-
|
|
7796
|
-
// src/tilemap-renderer.ts
|
|
7797
|
-
import { textAscent as textAscent3 } from "@opendata-ai/openchart-core";
|
|
7798
|
-
var SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
7799
|
-
var XLINK_NS3 = "http://www.w3.org/1999/xlink";
|
|
7800
|
-
var BRAND_URL4 = "https://tryopendata.ai";
|
|
7801
|
-
var EASE_VAR_MAP3 = {
|
|
7802
|
-
smooth: "var(--oc-ease-smooth)",
|
|
7803
|
-
snappy: "var(--oc-ease-snappy)"
|
|
7804
|
-
};
|
|
7805
|
-
var gradientIdCounter = 0;
|
|
7806
|
-
function createSVGElement3(tag) {
|
|
7807
|
-
return document.createElementNS(SVG_NS3, tag);
|
|
7808
|
-
}
|
|
7809
|
-
function setAttrs3(el, attrs) {
|
|
7810
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
7811
|
-
el.setAttribute(key, String(value));
|
|
7812
|
-
}
|
|
7813
|
-
}
|
|
7814
|
-
function renderChrome3(parent, layout) {
|
|
7815
|
-
const g = createSVGElement3("g");
|
|
7816
|
-
g.setAttribute("class", "oc-chrome");
|
|
7817
|
-
const { chrome } = layout;
|
|
7818
|
-
const bottomOffset = layout.area.y + layout.area.height;
|
|
7819
|
-
if (chrome.title) {
|
|
7820
|
-
const text = createSVGElement3("text");
|
|
7821
|
-
setAttrs3(text, {
|
|
7822
|
-
x: chrome.title.x,
|
|
7823
|
-
y: chrome.title.y + textAscent3(chrome.title.style.fontSize)
|
|
7824
|
-
});
|
|
7825
|
-
text.setAttribute("class", "oc-title");
|
|
7826
|
-
text.setAttribute("font-family", chrome.title.style.fontFamily);
|
|
7827
|
-
text.setAttribute("font-size", String(chrome.title.style.fontSize));
|
|
7828
|
-
text.setAttribute("font-weight", String(chrome.title.style.fontWeight));
|
|
7829
|
-
text.style.setProperty("fill", chrome.title.style.fill);
|
|
7830
|
-
text.textContent = chrome.title.text;
|
|
7831
|
-
g.appendChild(text);
|
|
7832
|
-
}
|
|
7833
|
-
if (chrome.subtitle) {
|
|
7834
|
-
const text = createSVGElement3("text");
|
|
7835
|
-
setAttrs3(text, {
|
|
7836
|
-
x: chrome.subtitle.x,
|
|
7837
|
-
y: chrome.subtitle.y + textAscent3(chrome.subtitle.style.fontSize)
|
|
7838
|
-
});
|
|
7839
|
-
text.setAttribute("class", "oc-subtitle");
|
|
7840
|
-
text.setAttribute("font-family", chrome.subtitle.style.fontFamily);
|
|
7841
|
-
text.setAttribute("font-size", String(chrome.subtitle.style.fontSize));
|
|
7842
|
-
text.setAttribute("font-weight", String(chrome.subtitle.style.fontWeight));
|
|
7843
|
-
text.style.setProperty(
|
|
7844
|
-
"fill",
|
|
7845
|
-
chrome.subtitle.style.fill
|
|
7846
|
-
);
|
|
7847
|
-
text.textContent = chrome.subtitle.text;
|
|
7848
|
-
g.appendChild(text);
|
|
7849
|
-
}
|
|
7850
|
-
if (chrome.source) {
|
|
7851
|
-
const text = createSVGElement3("text");
|
|
7852
|
-
setAttrs3(text, {
|
|
7853
|
-
x: chrome.source.x,
|
|
7854
|
-
y: bottomOffset + chrome.source.y + textAscent3(chrome.source.style.fontSize)
|
|
7855
|
-
});
|
|
7856
|
-
text.setAttribute("class", "oc-source");
|
|
7857
|
-
text.setAttribute("font-family", chrome.source.style.fontFamily);
|
|
7858
|
-
text.setAttribute("font-size", String(chrome.source.style.fontSize));
|
|
7859
|
-
text.setAttribute("font-weight", String(chrome.source.style.fontWeight));
|
|
7860
|
-
text.style.setProperty(
|
|
7861
|
-
"fill",
|
|
7862
|
-
chrome.source.style.fill
|
|
7863
|
-
);
|
|
7864
|
-
text.textContent = chrome.source.text;
|
|
7865
|
-
g.appendChild(text);
|
|
7866
|
-
}
|
|
7867
|
-
if (chrome.byline) {
|
|
7868
|
-
const text = createSVGElement3("text");
|
|
7869
|
-
setAttrs3(text, {
|
|
7870
|
-
x: chrome.byline.x,
|
|
7871
|
-
y: bottomOffset + chrome.byline.y + textAscent3(chrome.byline.style.fontSize)
|
|
7872
|
-
});
|
|
7873
|
-
text.setAttribute("class", "oc-byline");
|
|
7874
|
-
text.setAttribute("font-family", chrome.byline.style.fontFamily);
|
|
7875
|
-
text.setAttribute("font-size", String(chrome.byline.style.fontSize));
|
|
7876
|
-
text.setAttribute("font-weight", String(chrome.byline.style.fontWeight));
|
|
7877
|
-
text.style.setProperty(
|
|
7878
|
-
"fill",
|
|
7879
|
-
chrome.byline.style.fill
|
|
7880
|
-
);
|
|
7881
|
-
text.textContent = chrome.byline.text;
|
|
7882
|
-
g.appendChild(text);
|
|
7883
|
-
}
|
|
7884
|
-
if (chrome.footer) {
|
|
7885
|
-
const text = createSVGElement3("text");
|
|
7886
|
-
setAttrs3(text, {
|
|
7887
|
-
x: chrome.footer.x,
|
|
7888
|
-
y: bottomOffset + chrome.footer.y + textAscent3(chrome.footer.style.fontSize)
|
|
7889
|
-
});
|
|
7890
|
-
text.setAttribute("class", "oc-footer");
|
|
7891
|
-
text.setAttribute("font-family", chrome.footer.style.fontFamily);
|
|
7892
|
-
text.setAttribute("font-size", String(chrome.footer.style.fontSize));
|
|
7893
|
-
text.setAttribute("font-weight", String(chrome.footer.style.fontWeight));
|
|
7894
|
-
text.style.setProperty(
|
|
7895
|
-
"fill",
|
|
7896
|
-
chrome.footer.style.fill
|
|
7897
|
-
);
|
|
7898
|
-
text.textContent = chrome.footer.text;
|
|
7899
|
-
g.appendChild(text);
|
|
7900
|
-
}
|
|
7901
|
-
parent.appendChild(g);
|
|
7902
|
-
}
|
|
7903
|
-
function renderWatermark2(parent, layout) {
|
|
7904
|
-
if (layout.width < 480) return;
|
|
7905
|
-
const { width, height } = layout;
|
|
7906
|
-
const { theme } = layout;
|
|
7907
|
-
const padding = theme.spacing.padding;
|
|
7908
|
-
const rightEdge = width - padding;
|
|
7909
|
-
const bottomEdge = height - padding;
|
|
7910
|
-
const fill = theme.colors.axis;
|
|
7911
|
-
const a2 = createSVGElement3("a");
|
|
7912
|
-
a2.setAttribute("href", BRAND_URL4);
|
|
7913
|
-
a2.setAttributeNS(XLINK_NS3, "xlink:href", BRAND_URL4);
|
|
7914
|
-
a2.setAttribute("target", "_blank");
|
|
7915
|
-
a2.setAttribute("rel", "noopener");
|
|
7916
|
-
a2.setAttribute("class", "oc-chrome-ref");
|
|
7917
|
-
const text = createSVGElement3("text");
|
|
7918
|
-
setAttrs3(text, {
|
|
7919
|
-
x: rightEdge,
|
|
7920
|
-
y: bottomEdge,
|
|
7921
|
-
"dominant-baseline": "alphabetic",
|
|
7922
|
-
"text-anchor": "end",
|
|
7923
|
-
"font-family": theme.fonts.family,
|
|
7924
|
-
"font-size": 12,
|
|
7925
|
-
"fill-opacity": 0.55
|
|
7926
|
-
});
|
|
7927
|
-
text.style.setProperty("fill", fill);
|
|
7928
|
-
const trySpan = createSVGElement3("tspan");
|
|
7929
|
-
setAttrs3(trySpan, { "font-weight": 500 });
|
|
7930
|
-
trySpan.textContent = "try";
|
|
7931
|
-
const openDataSpan = createSVGElement3("tspan");
|
|
7932
|
-
setAttrs3(openDataSpan, { "font-weight": 600, "font-size": 16 });
|
|
7933
|
-
openDataSpan.textContent = "OpenData";
|
|
7934
|
-
const aiSpan = createSVGElement3("tspan");
|
|
7935
|
-
setAttrs3(aiSpan, { "font-weight": 500 });
|
|
7936
|
-
aiSpan.textContent = ".ai";
|
|
7937
|
-
text.appendChild(trySpan);
|
|
7938
|
-
text.appendChild(openDataSpan);
|
|
7939
|
-
text.appendChild(aiSpan);
|
|
7940
|
-
a2.appendChild(text);
|
|
7941
|
-
parent.appendChild(a2);
|
|
7942
|
-
}
|
|
7943
|
-
function renderTiles(parent, tiles, animation) {
|
|
7944
|
-
const g = createSVGElement3("g");
|
|
7945
|
-
g.setAttribute("class", "oc-tilemap-tiles");
|
|
7946
|
-
g.setAttribute("role", "list");
|
|
7947
|
-
const tileDelays = [];
|
|
7948
|
-
if (animation?.enabled) {
|
|
7949
|
-
const baseStagger = 800 / Math.max(tiles.length, 1);
|
|
7950
|
-
let seed = 17;
|
|
7951
|
-
for (let i = 0; i < tiles.length; i++) {
|
|
7952
|
-
const idx = tiles[i].animationIndex ?? i;
|
|
7953
|
-
seed = seed * 1103515245 + 12345 & 2147483647;
|
|
7954
|
-
const jitter = (seed % 1e3 / 1e3 - 0.5) * 0.8;
|
|
7955
|
-
tileDelays.push(Math.max(0, Math.round(idx * baseStagger * (1 + jitter))));
|
|
7956
|
-
}
|
|
7957
|
-
}
|
|
7958
|
-
for (let i = 0; i < tiles.length; i++) {
|
|
7959
|
-
const tile = tiles[i];
|
|
7960
|
-
const tileGroup = createSVGElement3("g");
|
|
7961
|
-
tileGroup.setAttribute("class", "oc-tilemap-tile");
|
|
7962
|
-
tileGroup.setAttribute("data-state", tile.stateCode);
|
|
7963
|
-
tileGroup.setAttribute("role", "listitem");
|
|
7964
|
-
if (tile.aria?.label) {
|
|
7965
|
-
tileGroup.setAttribute("aria-label", tile.aria.label);
|
|
7966
|
-
}
|
|
7967
|
-
if (animation?.enabled) {
|
|
7968
|
-
const idx = tile.animationIndex ?? i;
|
|
7969
|
-
tileGroup.setAttribute("data-animation-index", String(idx));
|
|
7970
|
-
tileGroup.style.setProperty(
|
|
7971
|
-
"--oc-mark-index",
|
|
7972
|
-
String(idx)
|
|
7973
|
-
);
|
|
7974
|
-
tileGroup.style.setProperty(
|
|
7975
|
-
"--oc-tile-delay",
|
|
7976
|
-
`${tileDelays[i]}ms`
|
|
7977
|
-
);
|
|
7978
|
-
}
|
|
7979
|
-
const rect = createSVGElement3("rect");
|
|
7980
|
-
setAttrs3(rect, {
|
|
7981
|
-
x: tile.x,
|
|
7982
|
-
y: tile.y,
|
|
7983
|
-
width: tile.size,
|
|
7984
|
-
height: tile.size,
|
|
7985
|
-
rx: tile.cornerRadius,
|
|
7986
|
-
fill: tile.fill,
|
|
7987
|
-
"fill-opacity": tile.fillOpacity ?? 1,
|
|
7988
|
-
stroke: tile.stroke,
|
|
7989
|
-
"stroke-width": tile.strokeWidth
|
|
7990
|
-
});
|
|
7991
|
-
tileGroup.appendChild(rect);
|
|
7992
|
-
const codeLabel = createSVGElement3("text");
|
|
7993
|
-
setAttrs3(codeLabel, {
|
|
7994
|
-
x: tile.label.x,
|
|
7995
|
-
y: tile.label.y,
|
|
7996
|
-
"text-anchor": "middle",
|
|
7997
|
-
"dominant-baseline": "central",
|
|
7998
|
-
"font-family": tile.label.style.fontFamily,
|
|
7999
|
-
"font-size": tile.label.style.fontSize,
|
|
8000
|
-
"font-weight": tile.label.style.fontWeight
|
|
8001
|
-
});
|
|
8002
|
-
codeLabel.style.setProperty(
|
|
8003
|
-
"fill",
|
|
8004
|
-
tile.label.style.fill
|
|
8005
|
-
);
|
|
8006
|
-
codeLabel.textContent = tile.label.text;
|
|
8007
|
-
tileGroup.appendChild(codeLabel);
|
|
8008
|
-
if (tile.valueLabel.visible && tile.valueLabel.text) {
|
|
8009
|
-
const valueLabel = createSVGElement3("text");
|
|
8010
|
-
setAttrs3(valueLabel, {
|
|
8011
|
-
x: tile.valueLabel.x,
|
|
8012
|
-
y: tile.valueLabel.y,
|
|
8013
|
-
"text-anchor": "middle",
|
|
8014
|
-
"dominant-baseline": "central",
|
|
8015
|
-
"font-family": tile.valueLabel.style.fontFamily,
|
|
8016
|
-
"font-size": tile.valueLabel.style.fontSize,
|
|
8017
|
-
"font-weight": tile.valueLabel.style.fontWeight
|
|
8018
|
-
});
|
|
8019
|
-
valueLabel.style.setProperty(
|
|
8020
|
-
"fill",
|
|
8021
|
-
tile.valueLabel.style.fill
|
|
8022
|
-
);
|
|
8023
|
-
valueLabel.textContent = tile.valueLabel.text;
|
|
8024
|
-
tileGroup.appendChild(valueLabel);
|
|
8025
|
-
}
|
|
8026
|
-
g.appendChild(tileGroup);
|
|
8027
|
-
}
|
|
8028
|
-
parent.appendChild(g);
|
|
8029
|
-
}
|
|
8030
|
-
function renderGradientLegend(parent, layout) {
|
|
8031
|
-
if (!layout.gradientLegend) return;
|
|
8032
|
-
const { gradientLegend } = layout;
|
|
8033
|
-
const g = createSVGElement3("g");
|
|
8034
|
-
g.setAttribute("class", "oc-tilemap-legend");
|
|
8035
|
-
const defs = parent.querySelector("defs") || createSVGElement3("defs");
|
|
8036
|
-
const exists = parent.querySelector("defs");
|
|
8037
|
-
if (!exists) {
|
|
8038
|
-
parent.insertBefore(defs, parent.firstChild);
|
|
8039
|
-
}
|
|
8040
|
-
const gradientId = `oc-tilemap-legend-gradient-${gradientIdCounter++}`;
|
|
8041
|
-
const grad = createSVGElement3("linearGradient");
|
|
8042
|
-
grad.id = gradientId;
|
|
8043
|
-
grad.setAttribute("x1", "0%");
|
|
8044
|
-
grad.setAttribute("y1", "0%");
|
|
8045
|
-
grad.setAttribute("x2", "100%");
|
|
8046
|
-
grad.setAttribute("y2", "0%");
|
|
8047
|
-
for (const stop of gradientLegend.colorStops) {
|
|
8048
|
-
const s = createSVGElement3("stop");
|
|
8049
|
-
const attrs = {
|
|
8050
|
-
offset: `${stop.offset * 100}%`,
|
|
8051
|
-
"stop-color": stop.color
|
|
8052
|
-
};
|
|
8053
|
-
if (stop.opacity !== void 0) {
|
|
8054
|
-
attrs["stop-opacity"] = stop.opacity;
|
|
8055
|
-
}
|
|
8056
|
-
setAttrs3(s, attrs);
|
|
8057
|
-
grad.appendChild(s);
|
|
8058
|
-
}
|
|
8059
|
-
defs.appendChild(grad);
|
|
8060
|
-
const barHeight = gradientLegend.bounds.height;
|
|
8061
|
-
const bar = createSVGElement3("rect");
|
|
8062
|
-
setAttrs3(bar, {
|
|
8063
|
-
x: gradientLegend.bounds.x,
|
|
8064
|
-
y: gradientLegend.bounds.y,
|
|
8065
|
-
width: gradientLegend.bounds.width,
|
|
8066
|
-
height: barHeight,
|
|
8067
|
-
rx: barHeight / 2,
|
|
8068
|
-
fill: `url(#${gradientId})`
|
|
8069
|
-
});
|
|
8070
|
-
g.appendChild(bar);
|
|
8071
|
-
const minText = createSVGElement3("text");
|
|
8072
|
-
setAttrs3(minText, {
|
|
8073
|
-
x: gradientLegend.bounds.x,
|
|
8074
|
-
y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,
|
|
8075
|
-
"text-anchor": "start",
|
|
8076
|
-
"font-family": gradientLegend.labelStyle.fontFamily,
|
|
8077
|
-
"font-size": gradientLegend.labelStyle.fontSize,
|
|
8078
|
-
"font-weight": gradientLegend.labelStyle.fontWeight
|
|
8079
|
-
});
|
|
8080
|
-
minText.style.setProperty(
|
|
8081
|
-
"fill",
|
|
8082
|
-
gradientLegend.labelStyle.fill
|
|
8083
|
-
);
|
|
8084
|
-
minText.textContent = gradientLegend.minLabel;
|
|
8085
|
-
g.appendChild(minText);
|
|
8086
|
-
const maxText = createSVGElement3("text");
|
|
8087
|
-
setAttrs3(maxText, {
|
|
8088
|
-
x: gradientLegend.bounds.x + gradientLegend.bounds.width,
|
|
8089
|
-
y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,
|
|
8090
|
-
"text-anchor": "end",
|
|
8091
|
-
"font-family": gradientLegend.labelStyle.fontFamily,
|
|
8092
|
-
"font-size": gradientLegend.labelStyle.fontSize,
|
|
8093
|
-
"font-weight": gradientLegend.labelStyle.fontWeight
|
|
8094
|
-
});
|
|
8095
|
-
maxText.style.setProperty(
|
|
8096
|
-
"fill",
|
|
8097
|
-
gradientLegend.labelStyle.fill
|
|
8098
|
-
);
|
|
8099
|
-
maxText.textContent = gradientLegend.maxLabel;
|
|
8100
|
-
g.appendChild(maxText);
|
|
8101
|
-
parent.appendChild(g);
|
|
8102
|
-
}
|
|
8103
|
-
function renderTileMapSVG(layout, opts) {
|
|
8104
|
-
const { width, height, tiles, a11y, watermark, animation } = layout;
|
|
8105
|
-
const animate = opts?.animate && animation?.enabled;
|
|
8106
|
-
const svg = createSVGElement3("svg");
|
|
8107
|
-
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
8108
|
-
svg.setAttribute("role", "img");
|
|
8109
|
-
if (a11y.altText) {
|
|
8110
|
-
svg.setAttribute("aria-label", a11y.altText);
|
|
8111
|
-
}
|
|
8112
|
-
const classes = animate ? "oc-tilemap oc-animate" : "oc-tilemap";
|
|
8113
|
-
svg.setAttribute("class", classes);
|
|
8114
|
-
if (animate && animation) {
|
|
8115
|
-
const stagger = Math.max(5, Math.round(800 / Math.max(tiles.length, 1)));
|
|
8116
|
-
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
8117
|
-
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
8118
|
-
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
8119
|
-
const easeVar = EASE_VAR_MAP3[animation.ease] || EASE_VAR_MAP3.smooth;
|
|
8120
|
-
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
8121
|
-
}
|
|
8122
|
-
const defs = createSVGElement3("defs");
|
|
8123
|
-
svg.appendChild(defs);
|
|
8124
|
-
renderChrome3(svg, layout);
|
|
8125
|
-
renderTiles(svg, tiles, animate ? animation : void 0);
|
|
8126
|
-
if (layout.categoricalLegend) {
|
|
8127
|
-
renderLegend(svg, layout.categoricalLegend);
|
|
8128
|
-
} else {
|
|
8129
|
-
renderGradientLegend(svg, layout);
|
|
8130
|
-
}
|
|
8131
|
-
if (watermark) {
|
|
8132
|
-
renderWatermark2(svg, layout);
|
|
8133
|
-
}
|
|
8134
|
-
return svg;
|
|
8135
|
-
}
|
|
8136
|
-
|
|
8137
|
-
// src/tilemap-mount.ts
|
|
8138
9514
|
function resolveDarkMode6(mode) {
|
|
8139
9515
|
if (mode === "force") return true;
|
|
8140
9516
|
if (mode === "off" || mode === void 0) return false;
|
|
@@ -8265,7 +9641,7 @@ function createTileMap(container, spec, options) {
|
|
|
8265
9641
|
tooltipManager = createTooltipManager(container);
|
|
8266
9642
|
}
|
|
8267
9643
|
}
|
|
8268
|
-
if (animate && currentLayout.animation?.
|
|
9644
|
+
if (animate && currentLayout.animation?.enter) {
|
|
8269
9645
|
animationCleanup = setupAnimationCleanup(newSvg, () => {
|
|
8270
9646
|
if (pendingResize && !destroyed) {
|
|
8271
9647
|
pendingResize = false;
|
|
@@ -8386,6 +9762,7 @@ export {
|
|
|
8386
9762
|
exportSVG,
|
|
8387
9763
|
exportSVGWithFonts,
|
|
8388
9764
|
observeResize,
|
|
9765
|
+
rectPathWithCorners,
|
|
8389
9766
|
registerMarkRenderer,
|
|
8390
9767
|
renderBarCell,
|
|
8391
9768
|
renderCategoryCell,
|