@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 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": "hanging",
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://opendata.ai";
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": "hanging",
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": 20,
6565
+ "font-size": BRAND_FONT_SIZE3,
6553
6566
  "fill-opacity": 0.55
6554
6567
  });
6555
6568
  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);
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.35));
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 BRAND_FONT_SIZE3 } from "@opendata-ai/openchart-core";
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: ${BRAND_FONT_SIZE3}px; font-weight: 600; color: ${brandColor}; opacity: 0.55; text-decoration: none; font-family: ${theme ? theme.fonts.family : "sans-serif"};`;
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);