@opendata-ai/openchart-vanilla 6.5.2 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +45 -2
- package/dist/index.js +757 -30
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -3
- package/src/__tests__/sankey.test.ts +133 -0
- package/src/__tests__/svg-renderer.test.ts +6 -5
- package/src/graph/canvas-renderer.ts +1 -1
- package/src/index.ts +3 -0
- package/src/sankey-mount.ts +532 -0
- package/src/sankey-renderer.ts +602 -0
- package/src/svg-renderer.ts +15 -10
- package/src/table-renderer.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -390,7 +390,7 @@ var GraphCanvasRenderer = class {
|
|
|
390
390
|
ctx.globalAlpha = 0.55;
|
|
391
391
|
ctx.textAlign = "right";
|
|
392
392
|
ctx.textBaseline = "alphabetic";
|
|
393
|
-
ctx.fillText("
|
|
393
|
+
ctx.fillText("tryOpenData.ai", x3, y3);
|
|
394
394
|
ctx.restore();
|
|
395
395
|
}
|
|
396
396
|
// -------------------------------------------------------------------------
|
|
@@ -2788,7 +2788,7 @@ function createGraph(container, spec, options) {
|
|
|
2788
2788
|
}
|
|
2789
2789
|
chromeEl = document.createElement("div");
|
|
2790
2790
|
chromeEl.className = "oc-graph-chrome";
|
|
2791
|
-
|
|
2791
|
+
renderChrome3();
|
|
2792
2792
|
wrapper.appendChild(chromeEl);
|
|
2793
2793
|
canvas = document.createElement("canvas");
|
|
2794
2794
|
canvas.className = "oc-graph-canvas";
|
|
@@ -2800,7 +2800,7 @@ function createGraph(container, spec, options) {
|
|
|
2800
2800
|
if (options?.legend !== false) {
|
|
2801
2801
|
legendEl = document.createElement("div");
|
|
2802
2802
|
legendEl.className = "oc-graph-legend";
|
|
2803
|
-
|
|
2803
|
+
renderLegend3();
|
|
2804
2804
|
wrapper.appendChild(legendEl);
|
|
2805
2805
|
}
|
|
2806
2806
|
container.appendChild(wrapper);
|
|
@@ -2809,7 +2809,7 @@ function createGraph(container, spec, options) {
|
|
|
2809
2809
|
renderer = new GraphCanvasRenderer(canvas);
|
|
2810
2810
|
renderer.resize(width, canvasHeight);
|
|
2811
2811
|
}
|
|
2812
|
-
function
|
|
2812
|
+
function renderChrome3() {
|
|
2813
2813
|
if (!chromeEl) return;
|
|
2814
2814
|
let html = "";
|
|
2815
2815
|
if (compilation.chrome.title) {
|
|
@@ -2825,7 +2825,7 @@ function createGraph(container, spec, options) {
|
|
|
2825
2825
|
chromeEl.style.display = "";
|
|
2826
2826
|
}
|
|
2827
2827
|
}
|
|
2828
|
-
function
|
|
2828
|
+
function renderLegend3() {
|
|
2829
2829
|
if (!legendEl) return;
|
|
2830
2830
|
const entries = compilation.legend.entries;
|
|
2831
2831
|
if (entries.length === 0) {
|
|
@@ -3123,8 +3123,8 @@ function createGraph(container, spec, options) {
|
|
|
3123
3123
|
compilation = compile();
|
|
3124
3124
|
adjacencyMap = buildAdjacencyMap(compilation.edges);
|
|
3125
3125
|
buildDataMaps();
|
|
3126
|
-
|
|
3127
|
-
|
|
3126
|
+
renderChrome3();
|
|
3127
|
+
renderLegend3();
|
|
3128
3128
|
initSimulation();
|
|
3129
3129
|
initInteraction();
|
|
3130
3130
|
hoveredNodeId = null;
|
|
@@ -3158,8 +3158,8 @@ function createGraph(container, spec, options) {
|
|
|
3158
3158
|
};
|
|
3159
3159
|
});
|
|
3160
3160
|
spatialIndex.rebuild(positionedNodes);
|
|
3161
|
-
|
|
3162
|
-
|
|
3161
|
+
renderChrome3();
|
|
3162
|
+
renderLegend3();
|
|
3163
3163
|
needsRender = true;
|
|
3164
3164
|
scheduleRender();
|
|
3165
3165
|
}
|
|
@@ -4150,14 +4150,18 @@ function renderBrand(parent, layout) {
|
|
|
4150
4150
|
"fill-opacity": 0.55
|
|
4151
4151
|
});
|
|
4152
4152
|
text.style.setProperty("fill", fill);
|
|
4153
|
-
const
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
text.appendChild(
|
|
4157
|
-
const
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
text.appendChild(
|
|
4153
|
+
const trySpan = createSVGElement("tspan");
|
|
4154
|
+
trySpan.setAttribute("font-weight", "500");
|
|
4155
|
+
trySpan.textContent = "try";
|
|
4156
|
+
text.appendChild(trySpan);
|
|
4157
|
+
const openDataSpan = createSVGElement("tspan");
|
|
4158
|
+
openDataSpan.setAttribute("font-weight", "600");
|
|
4159
|
+
openDataSpan.textContent = "OpenData";
|
|
4160
|
+
text.appendChild(openDataSpan);
|
|
4161
|
+
const aiSpan = createSVGElement("tspan");
|
|
4162
|
+
aiSpan.setAttribute("font-weight", "500");
|
|
4163
|
+
aiSpan.textContent = ".ai";
|
|
4164
|
+
text.appendChild(aiSpan);
|
|
4161
4165
|
a2.appendChild(text);
|
|
4162
4166
|
parent.appendChild(a2);
|
|
4163
4167
|
}
|
|
@@ -4886,7 +4890,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4886
4890
|
};
|
|
4887
4891
|
}
|
|
4888
4892
|
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4889
|
-
const
|
|
4893
|
+
const SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
4890
4894
|
const cleanups = [];
|
|
4891
4895
|
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4892
4896
|
for (const el of annotationGroups) {
|
|
@@ -4925,7 +4929,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4925
4929
|
const createdHandles = [];
|
|
4926
4930
|
for (const ep of endpoints) {
|
|
4927
4931
|
if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
|
|
4928
|
-
const handleEl = document.createElementNS(
|
|
4932
|
+
const handleEl = document.createElementNS(SVG_NS3, "circle");
|
|
4929
4933
|
handleEl.setAttribute("class", "oc-connector-handle");
|
|
4930
4934
|
handleEl.setAttribute("data-endpoint", ep.name);
|
|
4931
4935
|
handleEl.setAttribute("cx", String(ep.cx));
|
|
@@ -6383,6 +6387,728 @@ function renderCell(cell) {
|
|
|
6383
6387
|
}
|
|
6384
6388
|
}
|
|
6385
6389
|
|
|
6390
|
+
// src/sankey-mount.ts
|
|
6391
|
+
import { compileSankey } from "@opendata-ai/openchart-engine";
|
|
6392
|
+
|
|
6393
|
+
// src/sankey-renderer.ts
|
|
6394
|
+
import { BRAND_MIN_WIDTH as BRAND_MIN_WIDTH3, estimateTextWidth as estimateTextWidth2 } from "@opendata-ai/openchart-core";
|
|
6395
|
+
import { clampStaggerDelay as clampStaggerDelay2 } from "@opendata-ai/openchart-engine";
|
|
6396
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
6397
|
+
var XLINK_NS2 = "http://www.w3.org/1999/xlink";
|
|
6398
|
+
var BRAND_URL2 = "https://opendata.ai";
|
|
6399
|
+
var EASE_VAR_MAP2 = {
|
|
6400
|
+
smooth: "var(--oc-ease-smooth)",
|
|
6401
|
+
snappy: "var(--oc-ease-snappy)"
|
|
6402
|
+
};
|
|
6403
|
+
function createSVGElement2(tag) {
|
|
6404
|
+
return document.createElementNS(SVG_NS2, tag);
|
|
6405
|
+
}
|
|
6406
|
+
function setAttrs2(el, attrs) {
|
|
6407
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
6408
|
+
el.setAttribute(key, String(value));
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
function applyTextStyle2(el, style) {
|
|
6412
|
+
setAttrs2(el, {
|
|
6413
|
+
"font-family": style.fontFamily,
|
|
6414
|
+
"font-size": style.fontSize,
|
|
6415
|
+
"font-weight": style.fontWeight
|
|
6416
|
+
});
|
|
6417
|
+
el.style.setProperty("fill", style.fill);
|
|
6418
|
+
if (style.textAnchor) {
|
|
6419
|
+
el.setAttribute("text-anchor", style.textAnchor);
|
|
6420
|
+
}
|
|
6421
|
+
if (style.dominantBaseline) {
|
|
6422
|
+
el.setAttribute("dominant-baseline", style.dominantBaseline);
|
|
6423
|
+
}
|
|
6424
|
+
if (style.fontVariant) {
|
|
6425
|
+
el.setAttribute("font-variant", style.fontVariant);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
function stampAnimationAttrs2(el, mark, fallbackIndex, animation) {
|
|
6429
|
+
if (!animation?.enabled) return;
|
|
6430
|
+
const idx = mark.animationIndex ?? fallbackIndex;
|
|
6431
|
+
el.setAttribute("data-animation-index", String(idx));
|
|
6432
|
+
el.style.setProperty("--oc-mark-index", String(idx));
|
|
6433
|
+
}
|
|
6434
|
+
function wrapText2(text, fontSize, fontWeight, maxWidth) {
|
|
6435
|
+
if (maxWidth <= 0) return [text];
|
|
6436
|
+
const AVG_CHAR_WIDTH = 0.55;
|
|
6437
|
+
const WEIGHT_FACTORS = {
|
|
6438
|
+
100: 0.9,
|
|
6439
|
+
200: 0.92,
|
|
6440
|
+
300: 0.95,
|
|
6441
|
+
400: 1,
|
|
6442
|
+
500: 1.02,
|
|
6443
|
+
600: 1.05,
|
|
6444
|
+
700: 1.08,
|
|
6445
|
+
800: 1.1,
|
|
6446
|
+
900: 1.12
|
|
6447
|
+
};
|
|
6448
|
+
const weightFactor = WEIGHT_FACTORS[fontWeight] ?? 1;
|
|
6449
|
+
const charWidth = fontSize * AVG_CHAR_WIDTH * weightFactor;
|
|
6450
|
+
const maxChars = Math.floor(maxWidth / charWidth);
|
|
6451
|
+
if (text.length <= maxChars) return [text];
|
|
6452
|
+
const words = text.split(" ");
|
|
6453
|
+
const lines = [];
|
|
6454
|
+
let current = "";
|
|
6455
|
+
for (const word of words) {
|
|
6456
|
+
const candidate = current ? `${current} ${word}` : word;
|
|
6457
|
+
if (candidate.length > maxChars && current) {
|
|
6458
|
+
lines.push(current);
|
|
6459
|
+
current = word;
|
|
6460
|
+
} else {
|
|
6461
|
+
current = candidate;
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
if (current) lines.push(current);
|
|
6465
|
+
return lines;
|
|
6466
|
+
}
|
|
6467
|
+
function renderChromeElement2(parent, element, className, chromeKey) {
|
|
6468
|
+
const text = createSVGElement2("text");
|
|
6469
|
+
setAttrs2(text, { x: element.x, y: element.y });
|
|
6470
|
+
applyTextStyle2(text, element.style);
|
|
6471
|
+
text.setAttribute("class", className);
|
|
6472
|
+
text.setAttribute("data-chrome-key", chromeKey);
|
|
6473
|
+
const lines = wrapText2(
|
|
6474
|
+
element.text,
|
|
6475
|
+
element.style.fontSize,
|
|
6476
|
+
element.style.fontWeight,
|
|
6477
|
+
element.maxWidth
|
|
6478
|
+
);
|
|
6479
|
+
if (lines.length === 1) {
|
|
6480
|
+
text.textContent = element.text;
|
|
6481
|
+
} else {
|
|
6482
|
+
const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
|
|
6483
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6484
|
+
const tspan = createSVGElement2("tspan");
|
|
6485
|
+
setAttrs2(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
|
|
6486
|
+
tspan.textContent = lines[i];
|
|
6487
|
+
text.appendChild(tspan);
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
parent.appendChild(text);
|
|
6491
|
+
}
|
|
6492
|
+
function renderChrome2(parent, layout) {
|
|
6493
|
+
const g = createSVGElement2("g");
|
|
6494
|
+
g.setAttribute("class", "oc-chrome");
|
|
6495
|
+
const { chrome } = layout;
|
|
6496
|
+
if (chrome.title) {
|
|
6497
|
+
renderChromeElement2(g, chrome.title, "oc-title", "title");
|
|
6498
|
+
}
|
|
6499
|
+
if (chrome.subtitle) {
|
|
6500
|
+
renderChromeElement2(g, chrome.subtitle, "oc-subtitle", "subtitle");
|
|
6501
|
+
}
|
|
6502
|
+
const bottomOffset = layout.area.y + layout.area.height;
|
|
6503
|
+
if (chrome.source) {
|
|
6504
|
+
renderChromeElement2(
|
|
6505
|
+
g,
|
|
6506
|
+
{ ...chrome.source, y: bottomOffset + chrome.source.y },
|
|
6507
|
+
"oc-source",
|
|
6508
|
+
"source"
|
|
6509
|
+
);
|
|
6510
|
+
}
|
|
6511
|
+
if (chrome.byline) {
|
|
6512
|
+
renderChromeElement2(
|
|
6513
|
+
g,
|
|
6514
|
+
{ ...chrome.byline, y: bottomOffset + chrome.byline.y },
|
|
6515
|
+
"oc-byline",
|
|
6516
|
+
"byline"
|
|
6517
|
+
);
|
|
6518
|
+
}
|
|
6519
|
+
if (chrome.footer) {
|
|
6520
|
+
renderChromeElement2(
|
|
6521
|
+
g,
|
|
6522
|
+
{ ...chrome.footer, y: bottomOffset + chrome.footer.y },
|
|
6523
|
+
"oc-footer",
|
|
6524
|
+
"footer"
|
|
6525
|
+
);
|
|
6526
|
+
}
|
|
6527
|
+
parent.appendChild(g);
|
|
6528
|
+
}
|
|
6529
|
+
function renderBrand2(parent, layout) {
|
|
6530
|
+
if (layout.dimensions.width < BRAND_MIN_WIDTH3) return;
|
|
6531
|
+
const { width } = layout.dimensions;
|
|
6532
|
+
const padding = layout.theme.spacing.padding;
|
|
6533
|
+
const rightEdge = width - padding;
|
|
6534
|
+
const fill = layout.theme.colors.axis;
|
|
6535
|
+
const bottomOffset = layout.area.y + layout.area.height;
|
|
6536
|
+
const { chrome } = layout;
|
|
6537
|
+
const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;
|
|
6538
|
+
const chromeY = firstBottom ? bottomOffset + firstBottom.y : bottomOffset + layout.theme.spacing.chartToFooter;
|
|
6539
|
+
const a2 = createSVGElement2("a");
|
|
6540
|
+
a2.setAttribute("href", BRAND_URL2);
|
|
6541
|
+
a2.setAttributeNS(XLINK_NS2, "xlink:href", BRAND_URL2);
|
|
6542
|
+
a2.setAttribute("target", "_blank");
|
|
6543
|
+
a2.setAttribute("rel", "noopener");
|
|
6544
|
+
a2.setAttribute("class", "oc-chrome-ref");
|
|
6545
|
+
const text = createSVGElement2("text");
|
|
6546
|
+
setAttrs2(text, {
|
|
6547
|
+
x: rightEdge,
|
|
6548
|
+
y: chromeY,
|
|
6549
|
+
"dominant-baseline": "hanging",
|
|
6550
|
+
"text-anchor": "end",
|
|
6551
|
+
"font-family": layout.theme.fonts.family,
|
|
6552
|
+
"font-size": 20,
|
|
6553
|
+
"fill-opacity": 0.55
|
|
6554
|
+
});
|
|
6555
|
+
text.style.setProperty("fill", fill);
|
|
6556
|
+
const openSpan = createSVGElement2("tspan");
|
|
6557
|
+
setAttrs2(openSpan, { "font-weight": 500 });
|
|
6558
|
+
openSpan.textContent = "Open";
|
|
6559
|
+
const dataSpan = createSVGElement2("tspan");
|
|
6560
|
+
setAttrs2(dataSpan, { "font-weight": 600 });
|
|
6561
|
+
dataSpan.textContent = "Data";
|
|
6562
|
+
text.appendChild(openSpan);
|
|
6563
|
+
text.appendChild(dataSpan);
|
|
6564
|
+
a2.appendChild(text);
|
|
6565
|
+
parent.appendChild(a2);
|
|
6566
|
+
}
|
|
6567
|
+
function renderLegend2(parent, legend) {
|
|
6568
|
+
if (legend.entries.length === 0) return;
|
|
6569
|
+
const g = createSVGElement2("g");
|
|
6570
|
+
g.setAttribute("class", "oc-legend");
|
|
6571
|
+
g.setAttribute("role", "list");
|
|
6572
|
+
g.setAttribute("aria-label", "Chart legend");
|
|
6573
|
+
const isHorizontal = legend.position === "top" || legend.position === "bottom";
|
|
6574
|
+
let offsetX = legend.bounds.x;
|
|
6575
|
+
let offsetY = legend.bounds.y;
|
|
6576
|
+
for (let i = 0; i < legend.entries.length; i++) {
|
|
6577
|
+
const entry = legend.entries[i];
|
|
6578
|
+
if (isHorizontal && i > 0) {
|
|
6579
|
+
const labelWidth = estimateTextWidth2(
|
|
6580
|
+
entry.label,
|
|
6581
|
+
legend.labelStyle.fontSize,
|
|
6582
|
+
legend.labelStyle.fontWeight
|
|
6583
|
+
);
|
|
6584
|
+
const entryWidth = legend.swatchSize + legend.swatchGap + labelWidth + legend.entryGap;
|
|
6585
|
+
if (offsetX + entryWidth > legend.bounds.x + legend.bounds.width) {
|
|
6586
|
+
offsetX = legend.bounds.x;
|
|
6587
|
+
offsetY += legend.swatchSize + 6;
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
const entryG = createSVGElement2("g");
|
|
6591
|
+
entryG.setAttribute("class", "oc-legend-entry");
|
|
6592
|
+
entryG.setAttribute("role", "listitem");
|
|
6593
|
+
entryG.setAttribute("data-legend-index", String(i));
|
|
6594
|
+
entryG.setAttribute("data-legend-label", entry.label);
|
|
6595
|
+
if (entry.overflow) {
|
|
6596
|
+
entryG.setAttribute("data-legend-overflow", "true");
|
|
6597
|
+
entryG.setAttribute("aria-label", entry.label);
|
|
6598
|
+
entryG.setAttribute("opacity", "0.5");
|
|
6599
|
+
} else {
|
|
6600
|
+
entryG.setAttribute(
|
|
6601
|
+
"aria-label",
|
|
6602
|
+
`${entry.label}: ${entry.active !== false ? "visible" : "hidden"}`
|
|
6603
|
+
);
|
|
6604
|
+
entryG.setAttribute("style", "cursor: pointer");
|
|
6605
|
+
if (entry.active === false) {
|
|
6606
|
+
entryG.setAttribute("opacity", "0.3");
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6609
|
+
const rect = createSVGElement2("rect");
|
|
6610
|
+
setAttrs2(rect, {
|
|
6611
|
+
x: offsetX,
|
|
6612
|
+
y: offsetY,
|
|
6613
|
+
width: legend.swatchSize,
|
|
6614
|
+
height: legend.swatchSize,
|
|
6615
|
+
fill: entry.color,
|
|
6616
|
+
rx: 2
|
|
6617
|
+
});
|
|
6618
|
+
entryG.appendChild(rect);
|
|
6619
|
+
const label = createSVGElement2("text");
|
|
6620
|
+
setAttrs2(label, {
|
|
6621
|
+
x: offsetX + legend.swatchSize + legend.swatchGap,
|
|
6622
|
+
y: offsetY + legend.swatchSize / 2,
|
|
6623
|
+
"dominant-baseline": "central"
|
|
6624
|
+
});
|
|
6625
|
+
applyTextStyle2(label, legend.labelStyle);
|
|
6626
|
+
label.textContent = entry.label;
|
|
6627
|
+
entryG.appendChild(label);
|
|
6628
|
+
g.appendChild(entryG);
|
|
6629
|
+
if (isHorizontal) {
|
|
6630
|
+
const labelWidth = estimateTextWidth2(
|
|
6631
|
+
entry.label,
|
|
6632
|
+
legend.labelStyle.fontSize,
|
|
6633
|
+
legend.labelStyle.fontWeight
|
|
6634
|
+
);
|
|
6635
|
+
const entryWidth = legend.swatchSize + legend.swatchGap + labelWidth + legend.entryGap;
|
|
6636
|
+
offsetX += entryWidth;
|
|
6637
|
+
} else {
|
|
6638
|
+
offsetY += legend.swatchSize + legend.entryGap;
|
|
6639
|
+
}
|
|
6640
|
+
}
|
|
6641
|
+
parent.appendChild(g);
|
|
6642
|
+
}
|
|
6643
|
+
function buildNodePositionMap(nodes) {
|
|
6644
|
+
const map = /* @__PURE__ */ new Map();
|
|
6645
|
+
for (const node of nodes) {
|
|
6646
|
+
map.set(node.nodeId, { x: node.x, width: node.width });
|
|
6647
|
+
}
|
|
6648
|
+
return map;
|
|
6649
|
+
}
|
|
6650
|
+
function renderGradientDefs(defs, links, nodePositions) {
|
|
6651
|
+
for (let i = 0; i < links.length; i++) {
|
|
6652
|
+
const link = links[i];
|
|
6653
|
+
if (link.sourceColor === link.targetColor) continue;
|
|
6654
|
+
const gradId = `oc-sg-${link.sourceId}-${link.targetId}-${i}`;
|
|
6655
|
+
const gradient = createSVGElement2("linearGradient");
|
|
6656
|
+
gradient.setAttribute("id", gradId);
|
|
6657
|
+
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
|
|
6658
|
+
const sourcePos = nodePositions.get(link.sourceId);
|
|
6659
|
+
const targetPos = nodePositions.get(link.targetId);
|
|
6660
|
+
const x1 = sourcePos ? sourcePos.x + sourcePos.width : 0;
|
|
6661
|
+
const x22 = targetPos ? targetPos.x : 0;
|
|
6662
|
+
gradient.setAttribute("x1", String(x1));
|
|
6663
|
+
gradient.setAttribute("x2", String(x22));
|
|
6664
|
+
const stop0 = createSVGElement2("stop");
|
|
6665
|
+
setAttrs2(stop0, { offset: "0%", "stop-color": link.sourceColor });
|
|
6666
|
+
gradient.appendChild(stop0);
|
|
6667
|
+
const stop1 = createSVGElement2("stop");
|
|
6668
|
+
setAttrs2(stop1, { offset: "100%", "stop-color": link.targetColor });
|
|
6669
|
+
gradient.appendChild(stop1);
|
|
6670
|
+
defs.appendChild(gradient);
|
|
6671
|
+
}
|
|
6672
|
+
}
|
|
6673
|
+
function renderLinks(parent, links, _nodePositions, animation) {
|
|
6674
|
+
const g = createSVGElement2("g");
|
|
6675
|
+
g.setAttribute("class", "oc-sankey-links");
|
|
6676
|
+
for (let i = 0; i < links.length; i++) {
|
|
6677
|
+
const link = links[i];
|
|
6678
|
+
const linkG = createSVGElement2("g");
|
|
6679
|
+
linkG.setAttribute("class", "oc-sankey-link");
|
|
6680
|
+
linkG.setAttribute("data-mark-id", `link-${link.sourceId}-${link.targetId}`);
|
|
6681
|
+
linkG.setAttribute("data-source", link.sourceId);
|
|
6682
|
+
linkG.setAttribute("data-target", link.targetId);
|
|
6683
|
+
if (link.aria?.label) {
|
|
6684
|
+
linkG.setAttribute("aria-label", link.aria.label);
|
|
6685
|
+
}
|
|
6686
|
+
stampAnimationAttrs2(linkG, link, i, animation);
|
|
6687
|
+
const path = createSVGElement2("path");
|
|
6688
|
+
path.setAttribute("d", link.path);
|
|
6689
|
+
path.setAttribute("stroke", "none");
|
|
6690
|
+
path.setAttribute("fill-opacity", String(link.fillOpacity));
|
|
6691
|
+
if (link.sourceColor !== link.targetColor) {
|
|
6692
|
+
const gradId = `oc-sg-${link.sourceId}-${link.targetId}-${i}`;
|
|
6693
|
+
path.setAttribute("fill", `url(#${gradId})`);
|
|
6694
|
+
} else {
|
|
6695
|
+
path.setAttribute("fill", link.sourceColor);
|
|
6696
|
+
}
|
|
6697
|
+
linkG.appendChild(path);
|
|
6698
|
+
g.appendChild(linkG);
|
|
6699
|
+
}
|
|
6700
|
+
parent.appendChild(g);
|
|
6701
|
+
}
|
|
6702
|
+
function renderNodes(parent, nodes, animation) {
|
|
6703
|
+
const g = createSVGElement2("g");
|
|
6704
|
+
g.setAttribute("class", "oc-sankey-nodes");
|
|
6705
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
6706
|
+
const node = nodes[i];
|
|
6707
|
+
const nodeG = createSVGElement2("g");
|
|
6708
|
+
nodeG.setAttribute("class", "oc-sankey-node");
|
|
6709
|
+
nodeG.setAttribute("data-mark-id", `node-${node.nodeId}`);
|
|
6710
|
+
nodeG.setAttribute("data-node-id", node.nodeId);
|
|
6711
|
+
if (node.aria?.label) {
|
|
6712
|
+
nodeG.setAttribute("aria-label", node.aria.label);
|
|
6713
|
+
}
|
|
6714
|
+
stampAnimationAttrs2(nodeG, node, i, animation);
|
|
6715
|
+
const rect = createSVGElement2("rect");
|
|
6716
|
+
setAttrs2(rect, {
|
|
6717
|
+
x: node.x,
|
|
6718
|
+
y: node.y,
|
|
6719
|
+
width: node.width,
|
|
6720
|
+
height: Math.max(node.height, 1),
|
|
6721
|
+
// Ensure at least 1px visibility
|
|
6722
|
+
fill: node.fill,
|
|
6723
|
+
rx: node.cornerRadius
|
|
6724
|
+
});
|
|
6725
|
+
if (node.stroke) {
|
|
6726
|
+
rect.setAttribute("stroke", node.stroke);
|
|
6727
|
+
}
|
|
6728
|
+
if (node.strokeWidth) {
|
|
6729
|
+
rect.setAttribute("stroke-width", String(node.strokeWidth));
|
|
6730
|
+
}
|
|
6731
|
+
nodeG.appendChild(rect);
|
|
6732
|
+
g.appendChild(nodeG);
|
|
6733
|
+
}
|
|
6734
|
+
parent.appendChild(g);
|
|
6735
|
+
}
|
|
6736
|
+
function renderLabels(parent, nodes) {
|
|
6737
|
+
const g = createSVGElement2("g");
|
|
6738
|
+
g.setAttribute("class", "oc-sankey-labels");
|
|
6739
|
+
for (const node of nodes) {
|
|
6740
|
+
const { label } = node;
|
|
6741
|
+
if (!label.visible) continue;
|
|
6742
|
+
const text = createSVGElement2("text");
|
|
6743
|
+
setAttrs2(text, { x: label.x, y: label.y });
|
|
6744
|
+
applyTextStyle2(text, label.style);
|
|
6745
|
+
text.textContent = label.text;
|
|
6746
|
+
g.appendChild(text);
|
|
6747
|
+
}
|
|
6748
|
+
parent.appendChild(g);
|
|
6749
|
+
}
|
|
6750
|
+
function renderSankeySVG(layout, animation) {
|
|
6751
|
+
const { width, height } = layout.dimensions;
|
|
6752
|
+
const svg = createSVGElement2("svg");
|
|
6753
|
+
setAttrs2(svg, {
|
|
6754
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
6755
|
+
xmlns: SVG_NS2,
|
|
6756
|
+
overflow: "visible"
|
|
6757
|
+
});
|
|
6758
|
+
svg.style.height = `${height}px`;
|
|
6759
|
+
svg.setAttribute("role", layout.a11y.role);
|
|
6760
|
+
svg.setAttribute("aria-label", layout.a11y.altText);
|
|
6761
|
+
const animate = animation?.enabled;
|
|
6762
|
+
const classes = animate ? "oc-chart oc-sankey oc-animate" : "oc-chart oc-sankey";
|
|
6763
|
+
svg.setAttribute("class", classes);
|
|
6764
|
+
if (animation?.enabled) {
|
|
6765
|
+
const totalMarks = layout.nodes.length + layout.links.length;
|
|
6766
|
+
const stagger = clampStaggerDelay2(animation.staggerDelay, totalMarks);
|
|
6767
|
+
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
6768
|
+
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
6769
|
+
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
6770
|
+
const easeVar = EASE_VAR_MAP2[animation.ease] || EASE_VAR_MAP2.smooth;
|
|
6771
|
+
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
6772
|
+
}
|
|
6773
|
+
const bg = createSVGElement2("rect");
|
|
6774
|
+
bg.setAttribute("class", "oc-background");
|
|
6775
|
+
setAttrs2(bg, {
|
|
6776
|
+
x: 0,
|
|
6777
|
+
y: 0,
|
|
6778
|
+
width,
|
|
6779
|
+
height,
|
|
6780
|
+
fill: layout.theme.colors.background
|
|
6781
|
+
});
|
|
6782
|
+
svg.appendChild(bg);
|
|
6783
|
+
const nodePositions = buildNodePositionMap(layout.nodes);
|
|
6784
|
+
const defs = createSVGElement2("defs");
|
|
6785
|
+
renderGradientDefs(defs, layout.links, nodePositions);
|
|
6786
|
+
svg.appendChild(defs);
|
|
6787
|
+
renderLinks(svg, layout.links, nodePositions, animation);
|
|
6788
|
+
renderNodes(svg, layout.nodes, animation);
|
|
6789
|
+
renderLabels(svg, layout.nodes);
|
|
6790
|
+
renderLegend2(svg, layout.legend);
|
|
6791
|
+
renderChrome2(svg, layout);
|
|
6792
|
+
renderBrand2(svg, layout);
|
|
6793
|
+
return svg;
|
|
6794
|
+
}
|
|
6795
|
+
|
|
6796
|
+
// src/sankey-mount.ts
|
|
6797
|
+
function resolveDarkMode3(mode) {
|
|
6798
|
+
if (mode === "force") return true;
|
|
6799
|
+
if (mode === "off" || mode === void 0) return false;
|
|
6800
|
+
if (typeof window !== "undefined" && window.matchMedia) {
|
|
6801
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
6802
|
+
}
|
|
6803
|
+
return false;
|
|
6804
|
+
}
|
|
6805
|
+
var HIGHLIGHT_OPACITY = 0.7;
|
|
6806
|
+
var DIM_OPACITY = 0.15;
|
|
6807
|
+
function createSankey(container, spec, options) {
|
|
6808
|
+
let currentSpec = spec;
|
|
6809
|
+
let currentLayout;
|
|
6810
|
+
let destroyed = false;
|
|
6811
|
+
let svgElement = null;
|
|
6812
|
+
let tooltipManager = null;
|
|
6813
|
+
let cleanupTooltipEvents = null;
|
|
6814
|
+
let disconnectResize = null;
|
|
6815
|
+
let isFirstRender = true;
|
|
6816
|
+
let animationCleanup = null;
|
|
6817
|
+
let pendingResize = false;
|
|
6818
|
+
function getContainerDimensions() {
|
|
6819
|
+
const rect = container.getBoundingClientRect();
|
|
6820
|
+
return {
|
|
6821
|
+
width: Math.max(rect.width || 600, 100),
|
|
6822
|
+
height: Math.max(rect.height || 400, 100)
|
|
6823
|
+
};
|
|
6824
|
+
}
|
|
6825
|
+
function compile() {
|
|
6826
|
+
const { width, height } = getContainerDimensions();
|
|
6827
|
+
const darkMode = resolveDarkMode3(options?.darkMode);
|
|
6828
|
+
const compileOpts = {
|
|
6829
|
+
width,
|
|
6830
|
+
height,
|
|
6831
|
+
theme: options?.theme,
|
|
6832
|
+
darkMode
|
|
6833
|
+
};
|
|
6834
|
+
return compileSankey(currentSpec, compileOpts);
|
|
6835
|
+
}
|
|
6836
|
+
function wireTooltipAndInteraction(svg, layout) {
|
|
6837
|
+
const cleanups = [];
|
|
6838
|
+
const nodeElements = svg.querySelectorAll(".oc-sankey-node");
|
|
6839
|
+
for (const el of nodeElements) {
|
|
6840
|
+
const markId = el.getAttribute("data-mark-id");
|
|
6841
|
+
if (!markId) continue;
|
|
6842
|
+
const content = layout.tooltipDescriptors.get(markId);
|
|
6843
|
+
const nodeId = el.getAttribute("data-node-id");
|
|
6844
|
+
const nodeData = nodeId ? layout.nodes.find((n) => n.nodeId === nodeId)?.data ?? {} : {};
|
|
6845
|
+
const handleMouseEnter = (e) => {
|
|
6846
|
+
const mouseEvent = e;
|
|
6847
|
+
if (content && tooltipManager) {
|
|
6848
|
+
const svgRect = svg.getBoundingClientRect();
|
|
6849
|
+
const x3 = mouseEvent.clientX - svgRect.left;
|
|
6850
|
+
const y3 = mouseEvent.clientY - svgRect.top;
|
|
6851
|
+
tooltipManager.show(content, x3, y3);
|
|
6852
|
+
}
|
|
6853
|
+
options?.onNodeHover?.(nodeData);
|
|
6854
|
+
if (nodeId) highlightConnectedLinks(svg, nodeId, layout);
|
|
6855
|
+
};
|
|
6856
|
+
const handleMouseMove = (e) => {
|
|
6857
|
+
if (content && tooltipManager) {
|
|
6858
|
+
const mouseEvent = e;
|
|
6859
|
+
const svgRect = svg.getBoundingClientRect();
|
|
6860
|
+
const x3 = mouseEvent.clientX - svgRect.left;
|
|
6861
|
+
const y3 = mouseEvent.clientY - svgRect.top;
|
|
6862
|
+
tooltipManager.show(content, x3, y3);
|
|
6863
|
+
}
|
|
6864
|
+
};
|
|
6865
|
+
const handleMouseLeave = () => {
|
|
6866
|
+
tooltipManager?.hide();
|
|
6867
|
+
options?.onNodeHover?.(null);
|
|
6868
|
+
resetLinkOpacity(svg, layout);
|
|
6869
|
+
};
|
|
6870
|
+
const handleClick = () => {
|
|
6871
|
+
options?.onNodeClick?.(nodeData);
|
|
6872
|
+
};
|
|
6873
|
+
el.addEventListener("mouseenter", handleMouseEnter);
|
|
6874
|
+
el.addEventListener("mousemove", handleMouseMove);
|
|
6875
|
+
el.addEventListener("mouseleave", handleMouseLeave);
|
|
6876
|
+
el.addEventListener("click", handleClick);
|
|
6877
|
+
cleanups.push(() => {
|
|
6878
|
+
el.removeEventListener("mouseenter", handleMouseEnter);
|
|
6879
|
+
el.removeEventListener("mousemove", handleMouseMove);
|
|
6880
|
+
el.removeEventListener("mouseleave", handleMouseLeave);
|
|
6881
|
+
el.removeEventListener("click", handleClick);
|
|
6882
|
+
});
|
|
6883
|
+
}
|
|
6884
|
+
const linkElements = svg.querySelectorAll(".oc-sankey-link");
|
|
6885
|
+
for (const el of linkElements) {
|
|
6886
|
+
const markId = el.getAttribute("data-mark-id");
|
|
6887
|
+
if (!markId) continue;
|
|
6888
|
+
const content = layout.tooltipDescriptors.get(markId);
|
|
6889
|
+
const sourceId = el.getAttribute("data-source");
|
|
6890
|
+
const targetId = el.getAttribute("data-target");
|
|
6891
|
+
const linkData = findLinkData(layout, sourceId, targetId);
|
|
6892
|
+
const handleMouseEnter = (e) => {
|
|
6893
|
+
const mouseEvent = e;
|
|
6894
|
+
if (content && tooltipManager) {
|
|
6895
|
+
const svgRect = svg.getBoundingClientRect();
|
|
6896
|
+
const x3 = mouseEvent.clientX - svgRect.left;
|
|
6897
|
+
const y3 = mouseEvent.clientY - svgRect.top;
|
|
6898
|
+
tooltipManager.show(content, x3, y3);
|
|
6899
|
+
}
|
|
6900
|
+
options?.onLinkHover?.(linkData);
|
|
6901
|
+
};
|
|
6902
|
+
const handleMouseMove = (e) => {
|
|
6903
|
+
if (content && tooltipManager) {
|
|
6904
|
+
const mouseEvent = e;
|
|
6905
|
+
const svgRect = svg.getBoundingClientRect();
|
|
6906
|
+
const x3 = mouseEvent.clientX - svgRect.left;
|
|
6907
|
+
const y3 = mouseEvent.clientY - svgRect.top;
|
|
6908
|
+
tooltipManager.show(content, x3, y3);
|
|
6909
|
+
}
|
|
6910
|
+
};
|
|
6911
|
+
const handleMouseLeave = () => {
|
|
6912
|
+
tooltipManager?.hide();
|
|
6913
|
+
options?.onLinkHover?.(null);
|
|
6914
|
+
};
|
|
6915
|
+
const handleClick = () => {
|
|
6916
|
+
options?.onLinkClick?.(linkData);
|
|
6917
|
+
};
|
|
6918
|
+
el.addEventListener("mouseenter", handleMouseEnter);
|
|
6919
|
+
el.addEventListener("mousemove", handleMouseMove);
|
|
6920
|
+
el.addEventListener("mouseleave", handleMouseLeave);
|
|
6921
|
+
el.addEventListener("click", handleClick);
|
|
6922
|
+
cleanups.push(() => {
|
|
6923
|
+
el.removeEventListener("mouseenter", handleMouseEnter);
|
|
6924
|
+
el.removeEventListener("mousemove", handleMouseMove);
|
|
6925
|
+
el.removeEventListener("mouseleave", handleMouseLeave);
|
|
6926
|
+
el.removeEventListener("click", handleClick);
|
|
6927
|
+
});
|
|
6928
|
+
}
|
|
6929
|
+
return () => {
|
|
6930
|
+
for (const cleanup of cleanups) {
|
|
6931
|
+
cleanup();
|
|
6932
|
+
}
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
function findLinkData(layout, sourceId, targetId) {
|
|
6936
|
+
if (!sourceId || !targetId) return {};
|
|
6937
|
+
const link = layout.links.find((l) => l.sourceId === sourceId && l.targetId === targetId);
|
|
6938
|
+
return link?.data ?? {};
|
|
6939
|
+
}
|
|
6940
|
+
function highlightConnectedLinks(svg, nodeId, _layout) {
|
|
6941
|
+
const linkElements = svg.querySelectorAll(".oc-sankey-link");
|
|
6942
|
+
for (const el of linkElements) {
|
|
6943
|
+
const source = el.getAttribute("data-source");
|
|
6944
|
+
const target = el.getAttribute("data-target");
|
|
6945
|
+
const path = el.querySelector("path");
|
|
6946
|
+
if (!path) continue;
|
|
6947
|
+
const isConnected = source === nodeId || target === nodeId;
|
|
6948
|
+
path.setAttribute("fill-opacity", String(isConnected ? HIGHLIGHT_OPACITY : DIM_OPACITY));
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
function resetLinkOpacity(svg, layout) {
|
|
6952
|
+
const linkElements = svg.querySelectorAll(".oc-sankey-link");
|
|
6953
|
+
for (const el of linkElements) {
|
|
6954
|
+
const path = el.querySelector("path");
|
|
6955
|
+
if (!path) continue;
|
|
6956
|
+
const source = el.getAttribute("data-source");
|
|
6957
|
+
const target = el.getAttribute("data-target");
|
|
6958
|
+
const link = layout.links.find((l) => l.sourceId === source && l.targetId === target);
|
|
6959
|
+
path.setAttribute("fill-opacity", String(link?.fillOpacity ?? 0.35));
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
function render() {
|
|
6963
|
+
if (destroyed) return;
|
|
6964
|
+
if (animationCleanup) {
|
|
6965
|
+
animationCleanup();
|
|
6966
|
+
animationCleanup = null;
|
|
6967
|
+
}
|
|
6968
|
+
if (svgElement) {
|
|
6969
|
+
cancelAnimations(svgElement);
|
|
6970
|
+
}
|
|
6971
|
+
if (cleanupTooltipEvents) {
|
|
6972
|
+
cleanupTooltipEvents();
|
|
6973
|
+
cleanupTooltipEvents = null;
|
|
6974
|
+
}
|
|
6975
|
+
if (svgElement?.parentNode) {
|
|
6976
|
+
svgElement.parentNode.removeChild(svgElement);
|
|
6977
|
+
}
|
|
6978
|
+
currentLayout = compile();
|
|
6979
|
+
const shouldAnimate = isFirstRender && currentLayout.animation?.enabled;
|
|
6980
|
+
isFirstRender = false;
|
|
6981
|
+
const animation = shouldAnimate ? currentLayout.animation : void 0;
|
|
6982
|
+
svgElement = renderSankeySVG(currentLayout, animation);
|
|
6983
|
+
container.appendChild(svgElement);
|
|
6984
|
+
const isDark = resolveDarkMode3(options?.darkMode);
|
|
6985
|
+
if (isDark) {
|
|
6986
|
+
container.classList.add("oc-dark");
|
|
6987
|
+
} else {
|
|
6988
|
+
container.classList.remove("oc-dark");
|
|
6989
|
+
}
|
|
6990
|
+
if (options?.tooltip !== false && svgElement) {
|
|
6991
|
+
if (!tooltipManager) {
|
|
6992
|
+
tooltipManager = createTooltipManager(container);
|
|
6993
|
+
}
|
|
6994
|
+
cleanupTooltipEvents = wireTooltipAndInteraction(svgElement, currentLayout);
|
|
6995
|
+
}
|
|
6996
|
+
if (shouldAnimate && svgElement) {
|
|
6997
|
+
animationCleanup = setupAnimationCleanup(svgElement, () => {
|
|
6998
|
+
animationCleanup = null;
|
|
6999
|
+
if (pendingResize) {
|
|
7000
|
+
pendingResize = false;
|
|
7001
|
+
resize();
|
|
7002
|
+
}
|
|
7003
|
+
});
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
7006
|
+
function update(newSpec) {
|
|
7007
|
+
if (destroyed) return;
|
|
7008
|
+
currentSpec = newSpec;
|
|
7009
|
+
isFirstRender = true;
|
|
7010
|
+
render();
|
|
7011
|
+
}
|
|
7012
|
+
function resize() {
|
|
7013
|
+
if (destroyed) return;
|
|
7014
|
+
if (animationCleanup) {
|
|
7015
|
+
pendingResize = true;
|
|
7016
|
+
return;
|
|
7017
|
+
}
|
|
7018
|
+
render();
|
|
7019
|
+
}
|
|
7020
|
+
function doExport(format, exportOptions) {
|
|
7021
|
+
if (!svgElement) {
|
|
7022
|
+
throw new Error("Sankey is not rendered yet");
|
|
7023
|
+
}
|
|
7024
|
+
switch (format) {
|
|
7025
|
+
case "svg":
|
|
7026
|
+
return exportSVG(svgElement);
|
|
7027
|
+
case "svg-with-fonts":
|
|
7028
|
+
return exportSVGWithFonts(svgElement, exportOptions);
|
|
7029
|
+
case "png":
|
|
7030
|
+
return exportPNG(svgElement, exportOptions);
|
|
7031
|
+
case "jpg":
|
|
7032
|
+
return exportJPG(svgElement, exportOptions);
|
|
7033
|
+
default:
|
|
7034
|
+
throw new Error(`Unsupported export format: ${format}`);
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
function destroy() {
|
|
7038
|
+
if (destroyed) return;
|
|
7039
|
+
destroyed = true;
|
|
7040
|
+
if (animationCleanup) {
|
|
7041
|
+
animationCleanup();
|
|
7042
|
+
animationCleanup = null;
|
|
7043
|
+
pendingResize = false;
|
|
7044
|
+
}
|
|
7045
|
+
if (svgElement) {
|
|
7046
|
+
cancelAnimations(svgElement);
|
|
7047
|
+
}
|
|
7048
|
+
if (disconnectResize) {
|
|
7049
|
+
disconnectResize();
|
|
7050
|
+
disconnectResize = null;
|
|
7051
|
+
}
|
|
7052
|
+
if (cleanupTooltipEvents) {
|
|
7053
|
+
cleanupTooltipEvents();
|
|
7054
|
+
cleanupTooltipEvents = null;
|
|
7055
|
+
}
|
|
7056
|
+
if (tooltipManager) {
|
|
7057
|
+
tooltipManager.destroy();
|
|
7058
|
+
tooltipManager = null;
|
|
7059
|
+
}
|
|
7060
|
+
if (svgElement?.parentNode) {
|
|
7061
|
+
svgElement.parentNode.removeChild(svgElement);
|
|
7062
|
+
}
|
|
7063
|
+
svgElement = null;
|
|
7064
|
+
container.classList.remove("oc-dark");
|
|
7065
|
+
}
|
|
7066
|
+
try {
|
|
7067
|
+
currentLayout = compile();
|
|
7068
|
+
const shouldAnimate = currentLayout.animation?.enabled;
|
|
7069
|
+
isFirstRender = false;
|
|
7070
|
+
const animation = shouldAnimate ? currentLayout.animation : void 0;
|
|
7071
|
+
svgElement = renderSankeySVG(currentLayout, animation);
|
|
7072
|
+
container.appendChild(svgElement);
|
|
7073
|
+
const isDark = resolveDarkMode3(options?.darkMode);
|
|
7074
|
+
if (isDark) {
|
|
7075
|
+
container.classList.add("oc-dark");
|
|
7076
|
+
} else {
|
|
7077
|
+
container.classList.remove("oc-dark");
|
|
7078
|
+
}
|
|
7079
|
+
if (options?.tooltip !== false && svgElement) {
|
|
7080
|
+
tooltipManager = createTooltipManager(container);
|
|
7081
|
+
cleanupTooltipEvents = wireTooltipAndInteraction(svgElement, currentLayout);
|
|
7082
|
+
}
|
|
7083
|
+
if (shouldAnimate && svgElement) {
|
|
7084
|
+
animationCleanup = setupAnimationCleanup(svgElement, () => {
|
|
7085
|
+
animationCleanup = null;
|
|
7086
|
+
if (pendingResize) {
|
|
7087
|
+
pendingResize = false;
|
|
7088
|
+
resize();
|
|
7089
|
+
}
|
|
7090
|
+
});
|
|
7091
|
+
}
|
|
7092
|
+
} catch (err) {
|
|
7093
|
+
console.error("[viz] Sankey mount failed:", err);
|
|
7094
|
+
throw err;
|
|
7095
|
+
}
|
|
7096
|
+
if (options?.responsive !== false) {
|
|
7097
|
+
disconnectResize = observeResize(container, () => {
|
|
7098
|
+
resize();
|
|
7099
|
+
});
|
|
7100
|
+
}
|
|
7101
|
+
return {
|
|
7102
|
+
update,
|
|
7103
|
+
resize,
|
|
7104
|
+
export: doExport,
|
|
7105
|
+
destroy,
|
|
7106
|
+
get layout() {
|
|
7107
|
+
return currentLayout;
|
|
7108
|
+
}
|
|
7109
|
+
};
|
|
7110
|
+
}
|
|
7111
|
+
|
|
6386
7112
|
// src/table-keyboard.ts
|
|
6387
7113
|
function attachKeyboardNav(options) {
|
|
6388
7114
|
const { wrapper, onSort, onClearSearch, onAnnounce } = options;
|
|
@@ -6576,12 +7302,12 @@ import { compileTable } from "@opendata-ai/openchart-engine";
|
|
|
6576
7302
|
|
|
6577
7303
|
// src/table-renderer.ts
|
|
6578
7304
|
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE3 } from "@opendata-ai/openchart-core";
|
|
6579
|
-
import { clampStaggerDelay as
|
|
6580
|
-
var
|
|
7305
|
+
import { clampStaggerDelay as clampStaggerDelay3 } from "@opendata-ai/openchart-engine";
|
|
7306
|
+
var EASE_VAR_MAP3 = {
|
|
6581
7307
|
smooth: "var(--oc-ease-smooth)",
|
|
6582
7308
|
snappy: "var(--oc-ease-snappy)"
|
|
6583
7309
|
};
|
|
6584
|
-
var
|
|
7310
|
+
var BRAND_URL3 = "https://tryopendata.ai";
|
|
6585
7311
|
function renderChromeBlock(layout, position) {
|
|
6586
7312
|
const chrome = layout.chrome;
|
|
6587
7313
|
if (position === "header") {
|
|
@@ -6826,21 +7552,21 @@ function renderTable(layout, container, opts) {
|
|
|
6826
7552
|
brand.className = "oc-table-ref";
|
|
6827
7553
|
brand.style.cssText = "text-align: right; padding: 4px 8px;";
|
|
6828
7554
|
const brandLink = document.createElement("a");
|
|
6829
|
-
brandLink.href =
|
|
7555
|
+
brandLink.href = BRAND_URL3;
|
|
6830
7556
|
brandLink.target = "_blank";
|
|
6831
7557
|
brandLink.rel = "noopener";
|
|
6832
7558
|
brandLink.style.cssText = `font-size: ${BRAND_FONT_SIZE3}px; font-weight: 600; color: ${brandColor}; opacity: 0.55; text-decoration: none; font-family: ${theme ? theme.fonts.family : "sans-serif"};`;
|
|
6833
|
-
brandLink.textContent = "
|
|
7559
|
+
brandLink.textContent = "tryOpenData.ai";
|
|
6834
7560
|
brand.appendChild(brandLink);
|
|
6835
7561
|
wrapper.appendChild(brand);
|
|
6836
7562
|
if (opts?.animate && layout.animation?.enabled) {
|
|
6837
7563
|
const anim = layout.animation;
|
|
6838
7564
|
const rowCount = layout.rows.length;
|
|
6839
|
-
const stagger =
|
|
7565
|
+
const stagger = clampStaggerDelay3(anim.staggerDelay, rowCount);
|
|
6840
7566
|
const s = wrapper.style;
|
|
6841
7567
|
s.setProperty("--oc-animation-duration", `${anim.duration}ms`);
|
|
6842
7568
|
s.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
6843
|
-
s.setProperty("--oc-animation-ease",
|
|
7569
|
+
s.setProperty("--oc-animation-ease", EASE_VAR_MAP3[anim.ease] || EASE_VAR_MAP3.smooth);
|
|
6844
7570
|
wrapper.classList.add("oc-animate");
|
|
6845
7571
|
}
|
|
6846
7572
|
container.appendChild(wrapper);
|
|
@@ -6848,7 +7574,7 @@ function renderTable(layout, container, opts) {
|
|
|
6848
7574
|
}
|
|
6849
7575
|
|
|
6850
7576
|
// src/table-mount.ts
|
|
6851
|
-
function
|
|
7577
|
+
function resolveDarkMode4(mode) {
|
|
6852
7578
|
if (mode === "force") return true;
|
|
6853
7579
|
if (mode === "off" || mode === void 0) return false;
|
|
6854
7580
|
if (typeof window !== "undefined" && window.matchMedia) {
|
|
@@ -6916,7 +7642,7 @@ function createTable(container, spec, options) {
|
|
|
6916
7642
|
}
|
|
6917
7643
|
function compile() {
|
|
6918
7644
|
const state = getState();
|
|
6919
|
-
const darkMode =
|
|
7645
|
+
const darkMode = resolveDarkMode4(options?.darkMode);
|
|
6920
7646
|
const { width } = getContainerDimensions();
|
|
6921
7647
|
const compileOpts = {
|
|
6922
7648
|
width,
|
|
@@ -6977,7 +7703,7 @@ function createTable(container, spec, options) {
|
|
|
6977
7703
|
if (isFirstRender) {
|
|
6978
7704
|
isFirstRender = false;
|
|
6979
7705
|
}
|
|
6980
|
-
const isDark =
|
|
7706
|
+
const isDark = resolveDarkMode4(options?.darkMode);
|
|
6981
7707
|
if (isDark) {
|
|
6982
7708
|
container.classList.add("oc-dark");
|
|
6983
7709
|
} else {
|
|
@@ -7132,7 +7858,7 @@ function createTable(container, spec, options) {
|
|
|
7132
7858
|
throw new Error(`Unsupported export format: ${format}`);
|
|
7133
7859
|
}
|
|
7134
7860
|
const state = getState();
|
|
7135
|
-
const darkMode =
|
|
7861
|
+
const darkMode = resolveDarkMode4(options?.darkMode);
|
|
7136
7862
|
const { width } = getContainerDimensions();
|
|
7137
7863
|
const fullLayout = compileTable(currentSpec, {
|
|
7138
7864
|
width,
|
|
@@ -7213,6 +7939,7 @@ export {
|
|
|
7213
7939
|
attachKeyboardNav,
|
|
7214
7940
|
createChart,
|
|
7215
7941
|
createGraph,
|
|
7942
|
+
createSankey,
|
|
7216
7943
|
createSimulationWorker,
|
|
7217
7944
|
createTable,
|
|
7218
7945
|
createTextEditOverlay,
|