@opendata-ai/openchart-vanilla 6.7.0 → 6.8.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.js +62 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/svg-renderer.test.ts +80 -0
- package/src/export.ts +24 -4
- package/src/sankey-mount.ts +25 -2
- package/src/sankey-renderer.ts +28 -16
- package/src/svg-renderer.ts +13 -2
package/dist/index.js
CHANGED
|
@@ -12,6 +12,10 @@ function getSVGDimensions(svg) {
|
|
|
12
12
|
}
|
|
13
13
|
return { width: 600, height: 400 };
|
|
14
14
|
}
|
|
15
|
+
function ensureSVGDimensions(svgString, width, height) {
|
|
16
|
+
if (/^<svg[^>]*\swidth\s*=/.test(svgString)) return svgString;
|
|
17
|
+
return svgString.replace(/^(<svg)/, `$1 width="${width}" height="${height}"`);
|
|
18
|
+
}
|
|
15
19
|
function collectUsedFonts(svgElement) {
|
|
16
20
|
const fonts = /* @__PURE__ */ new Map();
|
|
17
21
|
const textElements = svgElement.querySelectorAll("text");
|
|
@@ -135,8 +139,8 @@ async function exportPNG(svgElement, options) {
|
|
|
135
139
|
if (shouldEmbed) {
|
|
136
140
|
await embedFonts(svgElement);
|
|
137
141
|
}
|
|
138
|
-
const svgString = exportSVG(svgElement);
|
|
139
142
|
const { width, height } = getSVGDimensions(svgElement);
|
|
143
|
+
const svgString = ensureSVGDimensions(exportSVG(svgElement), width, height);
|
|
140
144
|
const canvas = document.createElement("canvas");
|
|
141
145
|
canvas.width = width * dpi;
|
|
142
146
|
canvas.height = height * dpi;
|
|
@@ -150,7 +154,7 @@ async function exportPNG(svgElement, options) {
|
|
|
150
154
|
const url = URL.createObjectURL(blob);
|
|
151
155
|
return new Promise((resolve, reject) => {
|
|
152
156
|
img.onload = () => {
|
|
153
|
-
ctx.drawImage(img, 0, 0);
|
|
157
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
154
158
|
URL.revokeObjectURL(url);
|
|
155
159
|
canvas.toBlob((result) => {
|
|
156
160
|
if (result) {
|
|
@@ -174,8 +178,8 @@ async function exportJPG(svgElement, options) {
|
|
|
174
178
|
if (shouldEmbed) {
|
|
175
179
|
await embedFonts(svgElement);
|
|
176
180
|
}
|
|
177
|
-
const svgString = exportSVG(svgElement);
|
|
178
181
|
const { width, height } = getSVGDimensions(svgElement);
|
|
182
|
+
const svgString = ensureSVGDimensions(exportSVG(svgElement), width, height);
|
|
179
183
|
const canvas = document.createElement("canvas");
|
|
180
184
|
canvas.width = width * dpi;
|
|
181
185
|
canvas.height = height * dpi;
|
|
@@ -191,7 +195,7 @@ async function exportJPG(svgElement, options) {
|
|
|
191
195
|
const url = URL.createObjectURL(blob);
|
|
192
196
|
return new Promise((resolve, reject) => {
|
|
193
197
|
img.onload = () => {
|
|
194
|
-
ctx.drawImage(img, 0, 0);
|
|
198
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
195
199
|
URL.revokeObjectURL(url);
|
|
196
200
|
canvas.toBlob(
|
|
197
201
|
(result) => {
|
|
@@ -3356,6 +3360,12 @@ function applyTextStyle(el, style) {
|
|
|
3356
3360
|
}
|
|
3357
3361
|
function wrapText(text, fontSize, fontWeight, maxWidth) {
|
|
3358
3362
|
if (maxWidth <= 0) return [text];
|
|
3363
|
+
const segments = text.split("\n");
|
|
3364
|
+
if (segments.length > 1) {
|
|
3365
|
+
return segments.flatMap(
|
|
3366
|
+
(segment) => segment.length === 0 ? [""] : wrapText(segment, fontSize, fontWeight, maxWidth)
|
|
3367
|
+
);
|
|
3368
|
+
}
|
|
3359
3369
|
const AVG_CHAR_WIDTH = 0.55;
|
|
3360
3370
|
const WEIGHT_FACTORS = {
|
|
3361
3371
|
100: 0.9,
|
|
@@ -4139,11 +4149,12 @@ function renderBrand(parent, layout) {
|
|
|
4139
4149
|
a2.setAttribute("target", "_blank");
|
|
4140
4150
|
a2.setAttribute("rel", "noopener");
|
|
4141
4151
|
a2.setAttribute("class", "oc-chrome-ref");
|
|
4152
|
+
const BRAND_LARGE = 16;
|
|
4142
4153
|
const text = createSVGElement("text");
|
|
4143
4154
|
setAttrs(text, {
|
|
4144
4155
|
x: rightEdge,
|
|
4145
|
-
y: chromeY,
|
|
4146
|
-
"dominant-baseline": "
|
|
4156
|
+
y: chromeY + BRAND_LARGE,
|
|
4157
|
+
"dominant-baseline": "alphabetic",
|
|
4147
4158
|
"font-family": layout.theme.fonts.family,
|
|
4148
4159
|
"font-size": BRAND_FONT_SIZE2,
|
|
4149
4160
|
"text-anchor": "end",
|
|
@@ -4156,6 +4167,7 @@ function renderBrand(parent, layout) {
|
|
|
4156
4167
|
text.appendChild(trySpan);
|
|
4157
4168
|
const openDataSpan = createSVGElement("tspan");
|
|
4158
4169
|
openDataSpan.setAttribute("font-weight", "600");
|
|
4170
|
+
openDataSpan.setAttribute("font-size", String(BRAND_LARGE));
|
|
4159
4171
|
openDataSpan.textContent = "OpenData";
|
|
4160
4172
|
text.appendChild(openDataSpan);
|
|
4161
4173
|
const aiSpan = createSVGElement("tspan");
|
|
@@ -6391,11 +6403,11 @@ function renderCell(cell) {
|
|
|
6391
6403
|
import { compileSankey } from "@opendata-ai/openchart-engine";
|
|
6392
6404
|
|
|
6393
6405
|
// src/sankey-renderer.ts
|
|
6394
|
-
import { BRAND_MIN_WIDTH as BRAND_MIN_WIDTH3, estimateTextWidth as estimateTextWidth2 } from "@opendata-ai/openchart-core";
|
|
6406
|
+
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE3, BRAND_MIN_WIDTH as BRAND_MIN_WIDTH3, estimateTextWidth as estimateTextWidth2 } from "@opendata-ai/openchart-core";
|
|
6395
6407
|
import { clampStaggerDelay as clampStaggerDelay2 } from "@opendata-ai/openchart-engine";
|
|
6396
6408
|
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
6397
6409
|
var XLINK_NS2 = "http://www.w3.org/1999/xlink";
|
|
6398
|
-
var BRAND_URL2 = "https://
|
|
6410
|
+
var BRAND_URL2 = "https://tryopendata.ai";
|
|
6399
6411
|
var EASE_VAR_MAP2 = {
|
|
6400
6412
|
smooth: "var(--oc-ease-smooth)",
|
|
6401
6413
|
snappy: "var(--oc-ease-snappy)"
|
|
@@ -6542,25 +6554,30 @@ function renderBrand2(parent, layout) {
|
|
|
6542
6554
|
a2.setAttribute("target", "_blank");
|
|
6543
6555
|
a2.setAttribute("rel", "noopener");
|
|
6544
6556
|
a2.setAttribute("class", "oc-chrome-ref");
|
|
6557
|
+
const BRAND_LARGE = 16;
|
|
6545
6558
|
const text = createSVGElement2("text");
|
|
6546
6559
|
setAttrs2(text, {
|
|
6547
6560
|
x: rightEdge,
|
|
6548
|
-
y: chromeY,
|
|
6549
|
-
"dominant-baseline": "
|
|
6561
|
+
y: chromeY + BRAND_LARGE,
|
|
6562
|
+
"dominant-baseline": "alphabetic",
|
|
6550
6563
|
"text-anchor": "end",
|
|
6551
6564
|
"font-family": layout.theme.fonts.family,
|
|
6552
|
-
"font-size":
|
|
6565
|
+
"font-size": BRAND_FONT_SIZE3,
|
|
6553
6566
|
"fill-opacity": 0.55
|
|
6554
6567
|
});
|
|
6555
6568
|
text.style.setProperty("fill", fill);
|
|
6556
|
-
const
|
|
6557
|
-
setAttrs2(
|
|
6558
|
-
|
|
6559
|
-
const
|
|
6560
|
-
setAttrs2(
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6569
|
+
const trySpan = createSVGElement2("tspan");
|
|
6570
|
+
setAttrs2(trySpan, { "font-weight": 500 });
|
|
6571
|
+
trySpan.textContent = "try";
|
|
6572
|
+
const openDataSpan = createSVGElement2("tspan");
|
|
6573
|
+
setAttrs2(openDataSpan, { "font-weight": 600, "font-size": BRAND_LARGE });
|
|
6574
|
+
openDataSpan.textContent = "OpenData";
|
|
6575
|
+
const aiSpan = createSVGElement2("tspan");
|
|
6576
|
+
setAttrs2(aiSpan, { "font-weight": 500 });
|
|
6577
|
+
aiSpan.textContent = ".ai";
|
|
6578
|
+
text.appendChild(trySpan);
|
|
6579
|
+
text.appendChild(openDataSpan);
|
|
6580
|
+
text.appendChild(aiSpan);
|
|
6564
6581
|
a2.appendChild(text);
|
|
6565
6582
|
parent.appendChild(a2);
|
|
6566
6583
|
}
|
|
@@ -6647,11 +6664,14 @@ function buildNodePositionMap(nodes) {
|
|
|
6647
6664
|
}
|
|
6648
6665
|
return map;
|
|
6649
6666
|
}
|
|
6667
|
+
function sanitizeId(s) {
|
|
6668
|
+
return s.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
6669
|
+
}
|
|
6650
6670
|
function renderGradientDefs(defs, links, nodePositions) {
|
|
6651
6671
|
for (let i = 0; i < links.length; i++) {
|
|
6652
6672
|
const link = links[i];
|
|
6653
6673
|
if (link.sourceColor === link.targetColor) continue;
|
|
6654
|
-
const gradId = `oc-sg-${link.sourceId}-${link.targetId}-${i}`;
|
|
6674
|
+
const gradId = `oc-sg-${sanitizeId(link.sourceId)}-${sanitizeId(link.targetId)}-${i}`;
|
|
6655
6675
|
const gradient = createSVGElement2("linearGradient");
|
|
6656
6676
|
gradient.setAttribute("id", gradId);
|
|
6657
6677
|
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
|
|
@@ -6677,7 +6697,7 @@ function renderLinks(parent, links, _nodePositions, animation) {
|
|
|
6677
6697
|
const link = links[i];
|
|
6678
6698
|
const linkG = createSVGElement2("g");
|
|
6679
6699
|
linkG.setAttribute("class", "oc-sankey-link");
|
|
6680
|
-
linkG.setAttribute("data-mark-id", `link-${link.sourceId}-${link.targetId}`);
|
|
6700
|
+
linkG.setAttribute("data-mark-id", `link-${link.sourceId}-${link.targetId}-${i}`);
|
|
6681
6701
|
linkG.setAttribute("data-source", link.sourceId);
|
|
6682
6702
|
linkG.setAttribute("data-target", link.targetId);
|
|
6683
6703
|
if (link.aria?.label) {
|
|
@@ -6689,7 +6709,7 @@ function renderLinks(parent, links, _nodePositions, animation) {
|
|
|
6689
6709
|
path.setAttribute("stroke", "none");
|
|
6690
6710
|
path.setAttribute("fill-opacity", String(link.fillOpacity));
|
|
6691
6711
|
if (link.sourceColor !== link.targetColor) {
|
|
6692
|
-
const gradId = `oc-sg-${link.sourceId}-${link.targetId}-${i}`;
|
|
6712
|
+
const gradId = `oc-sg-${sanitizeId(link.sourceId)}-${sanitizeId(link.targetId)}-${i}`;
|
|
6693
6713
|
path.setAttribute("fill", `url(#${gradId})`);
|
|
6694
6714
|
} else {
|
|
6695
6715
|
path.setAttribute("fill", link.sourceColor);
|
|
@@ -6804,6 +6824,7 @@ function resolveDarkMode3(mode) {
|
|
|
6804
6824
|
}
|
|
6805
6825
|
var HIGHLIGHT_OPACITY = 0.7;
|
|
6806
6826
|
var DIM_OPACITY = 0.15;
|
|
6827
|
+
var NODE_DIM_OPACITY = 0.2;
|
|
6807
6828
|
function createSankey(container, spec, options) {
|
|
6808
6829
|
let currentSpec = spec;
|
|
6809
6830
|
let currentLayout;
|
|
@@ -6938,6 +6959,7 @@ function createSankey(container, spec, options) {
|
|
|
6938
6959
|
return link?.data ?? {};
|
|
6939
6960
|
}
|
|
6940
6961
|
function highlightConnectedLinks(svg, nodeId, _layout) {
|
|
6962
|
+
const connectedNodeIds = /* @__PURE__ */ new Set([nodeId]);
|
|
6941
6963
|
const linkElements = svg.querySelectorAll(".oc-sankey-link");
|
|
6942
6964
|
for (const el of linkElements) {
|
|
6943
6965
|
const source = el.getAttribute("data-source");
|
|
@@ -6946,6 +6968,17 @@ function createSankey(container, spec, options) {
|
|
|
6946
6968
|
if (!path) continue;
|
|
6947
6969
|
const isConnected = source === nodeId || target === nodeId;
|
|
6948
6970
|
path.setAttribute("fill-opacity", String(isConnected ? HIGHLIGHT_OPACITY : DIM_OPACITY));
|
|
6971
|
+
if (isConnected) {
|
|
6972
|
+
if (source) connectedNodeIds.add(source);
|
|
6973
|
+
if (target) connectedNodeIds.add(target);
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
const nodeElements = svg.querySelectorAll(".oc-sankey-node");
|
|
6977
|
+
for (const el of nodeElements) {
|
|
6978
|
+
const nid = el.getAttribute("data-node-id");
|
|
6979
|
+
if (!nid) continue;
|
|
6980
|
+
const isConnected = connectedNodeIds.has(nid);
|
|
6981
|
+
el.style.opacity = isConnected ? "1" : String(NODE_DIM_OPACITY);
|
|
6949
6982
|
}
|
|
6950
6983
|
}
|
|
6951
6984
|
function resetLinkOpacity(svg, layout) {
|
|
@@ -6956,7 +6989,11 @@ function createSankey(container, spec, options) {
|
|
|
6956
6989
|
const source = el.getAttribute("data-source");
|
|
6957
6990
|
const target = el.getAttribute("data-target");
|
|
6958
6991
|
const link = layout.links.find((l) => l.sourceId === source && l.targetId === target);
|
|
6959
|
-
path.setAttribute("fill-opacity", String(link?.fillOpacity ?? 0.
|
|
6992
|
+
path.setAttribute("fill-opacity", String(link?.fillOpacity ?? 0.5));
|
|
6993
|
+
}
|
|
6994
|
+
const nodeElements = svg.querySelectorAll(".oc-sankey-node");
|
|
6995
|
+
for (const el of nodeElements) {
|
|
6996
|
+
el.style.opacity = "1";
|
|
6960
6997
|
}
|
|
6961
6998
|
}
|
|
6962
6999
|
function render() {
|
|
@@ -7301,7 +7338,7 @@ import { getBreakpoint } from "@opendata-ai/openchart-core";
|
|
|
7301
7338
|
import { compileTable } from "@opendata-ai/openchart-engine";
|
|
7302
7339
|
|
|
7303
7340
|
// src/table-renderer.ts
|
|
7304
|
-
import { BRAND_FONT_SIZE as
|
|
7341
|
+
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE4 } from "@opendata-ai/openchart-core";
|
|
7305
7342
|
import { clampStaggerDelay as clampStaggerDelay3 } from "@opendata-ai/openchart-engine";
|
|
7306
7343
|
var EASE_VAR_MAP3 = {
|
|
7307
7344
|
smooth: "var(--oc-ease-smooth)",
|
|
@@ -7555,7 +7592,7 @@ function renderTable(layout, container, opts) {
|
|
|
7555
7592
|
brandLink.href = BRAND_URL3;
|
|
7556
7593
|
brandLink.target = "_blank";
|
|
7557
7594
|
brandLink.rel = "noopener";
|
|
7558
|
-
brandLink.style.cssText = `font-size: ${
|
|
7595
|
+
brandLink.style.cssText = `font-size: ${BRAND_FONT_SIZE4}px; font-weight: 600; color: ${brandColor}; opacity: 0.55; text-decoration: none; font-family: ${theme ? theme.fonts.family : "sans-serif"};`;
|
|
7559
7596
|
brandLink.textContent = "tryOpenData.ai";
|
|
7560
7597
|
brand.appendChild(brandLink);
|
|
7561
7598
|
wrapper.appendChild(brand);
|