@opendata-ai/openchart-vanilla 6.10.0 → 6.12.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 CHANGED
@@ -100,6 +100,8 @@ interface GraphMountOptions {
100
100
  theme?: ThemeConfig;
101
101
  darkMode?: DarkMode;
102
102
  responsive?: boolean;
103
+ /** Show the tryOpenData.ai watermark. Defaults to true. */
104
+ watermark?: boolean;
103
105
  /** Show the built-in tooltip on node/edge hover. Defaults to true. */
104
106
  tooltip?: boolean;
105
107
  /** Show the built-in legend. Defaults to true. */
@@ -151,6 +153,8 @@ interface MountOptions extends ChartEventHandlers {
151
153
  onDataPointClick?: (data: Record<string, unknown>) => void;
152
154
  /** Enable responsive resizing. Defaults to true. */
153
155
  responsive?: boolean;
156
+ /** Show the tryOpenData.ai watermark. Defaults to true. */
157
+ watermark?: boolean;
154
158
  /** Initial selected element. */
155
159
  selectedElement?: ElementRef;
156
160
  }
@@ -250,6 +254,8 @@ interface SankeyMountOptions {
250
254
  darkMode?: DarkMode;
251
255
  /** Enable responsive resizing. Defaults to true. */
252
256
  responsive?: boolean;
257
+ /** Show the tryOpenData.ai watermark. Defaults to true. */
258
+ watermark?: boolean;
253
259
  /** Show tooltips on hover. Defaults to true. */
254
260
  tooltip?: boolean;
255
261
  /** Callback when a node is clicked. */
@@ -351,6 +357,8 @@ interface TableMountOptions {
351
357
  theme?: ThemeConfig;
352
358
  darkMode?: DarkMode;
353
359
  responsive?: boolean;
360
+ /** Show the tryOpenData.ai watermark. Defaults to true. */
361
+ watermark?: boolean;
354
362
  onRowClick?: (row: Record<string, unknown>) => void;
355
363
  onStateChange?: (state: TableState) => void;
356
364
  externalState?: {
package/dist/index.js CHANGED
@@ -376,7 +376,9 @@ var GraphCanvasRenderer = class {
376
376
  );
377
377
  }
378
378
  ctx.restore();
379
- this.drawBrand(ctx, cssWidth, cssHeight, theme);
379
+ if (state.watermark) {
380
+ this.drawBrand(ctx, cssWidth, cssHeight, theme);
381
+ }
380
382
  }
381
383
  // -------------------------------------------------------------------------
382
384
  // Brand rendering
@@ -2707,7 +2709,8 @@ function createGraph(container, spec, options) {
2707
2709
  width,
2708
2710
  height,
2709
2711
  theme: options?.theme,
2710
- darkMode
2712
+ darkMode,
2713
+ watermark: options?.watermark
2711
2714
  };
2712
2715
  return compileGraph(currentSpec, compileOpts);
2713
2716
  }
@@ -2927,7 +2930,8 @@ function createGraph(container, spec, options) {
2927
2930
  adjacencyMap,
2928
2931
  theme: compilation.theme,
2929
2932
  searchMatches: searchManager.getMatches(),
2930
- isGesturing
2933
+ isGesturing,
2934
+ watermark: compilation.watermark
2931
2935
  };
2932
2936
  renderer.render(state);
2933
2937
  }
@@ -3259,7 +3263,7 @@ function escapeHtml(str) {
3259
3263
  }
3260
3264
 
3261
3265
  // src/mount.ts
3262
- import { elementRef, isLayerSpec } from "@opendata-ai/openchart-core";
3266
+ import { elementRef, getRepresentativeColor, isLayerSpec } from "@opendata-ai/openchart-core";
3263
3267
  import { compileChart, compileLayer } from "@opendata-ai/openchart-engine";
3264
3268
 
3265
3269
  // src/animation.ts
@@ -3304,8 +3308,93 @@ function setupTableAnimationCleanup(wrapper) {
3304
3308
  // src/svg-renderer.ts
3305
3309
  import { BRAND_FONT_SIZE as BRAND_FONT_SIZE2, BRAND_MIN_WIDTH as BRAND_MIN_WIDTH2, estimateTextWidth } from "@opendata-ai/openchart-core";
3306
3310
  import { clampStaggerDelay } from "@opendata-ai/openchart-engine";
3311
+
3312
+ // src/gradient-utils.ts
3313
+ import { isGradientDef } from "@opendata-ai/openchart-core";
3307
3314
  var SVG_NS = "http://www.w3.org/2000/svg";
3315
+ function gradientKey(def) {
3316
+ return sortedStringify(def);
3317
+ }
3318
+ function sortedStringify(value) {
3319
+ if (value === null || value === void 0) return String(value);
3320
+ if (Array.isArray(value)) return `[${value.map(sortedStringify).join(",")}]`;
3321
+ if (typeof value === "object") {
3322
+ const sorted = Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${sortedStringify(value[k])}`);
3323
+ return `{${sorted.join(",")}}`;
3324
+ }
3325
+ return JSON.stringify(value);
3326
+ }
3327
+ function createGradientElement(def, id) {
3328
+ if (def.gradient === "linear") {
3329
+ return createLinearGradient(def, id);
3330
+ }
3331
+ return createRadialGradient(def, id);
3332
+ }
3333
+ function createLinearGradient(def, id) {
3334
+ const el = document.createElementNS(SVG_NS, "linearGradient");
3335
+ el.setAttribute("id", id);
3336
+ el.setAttribute("gradientUnits", "objectBoundingBox");
3337
+ el.setAttribute("x1", String(def.x1 ?? 0));
3338
+ el.setAttribute("y1", String(def.y1 ?? 0));
3339
+ el.setAttribute("x2", String(def.x2 ?? 0));
3340
+ el.setAttribute("y2", String(def.y2 ?? 1));
3341
+ for (const stop of def.stops) {
3342
+ appendStop(el, stop);
3343
+ }
3344
+ return el;
3345
+ }
3346
+ function createRadialGradient(def, id) {
3347
+ const el = document.createElementNS(SVG_NS, "radialGradient");
3348
+ el.setAttribute("id", id);
3349
+ el.setAttribute("gradientUnits", "objectBoundingBox");
3350
+ el.setAttribute("cx", String(def.x2 ?? 0.5));
3351
+ el.setAttribute("cy", String(def.y2 ?? 0.5));
3352
+ el.setAttribute("r", String(def.r2 ?? 0.5));
3353
+ el.setAttribute("fx", String(def.x1 ?? 0.5));
3354
+ el.setAttribute("fy", String(def.y1 ?? 0.5));
3355
+ el.setAttribute("fr", String(def.r1 ?? 0));
3356
+ for (const stop of def.stops) {
3357
+ appendStop(el, stop);
3358
+ }
3359
+ return el;
3360
+ }
3361
+ function appendStop(parent, stop) {
3362
+ const stopEl = document.createElementNS(SVG_NS, "stop");
3363
+ stopEl.setAttribute("offset", String(stop.offset));
3364
+ stopEl.setAttribute("stop-color", stop.color);
3365
+ if (stop.opacity !== void 0) {
3366
+ stopEl.setAttribute("stop-opacity", String(stop.opacity));
3367
+ }
3368
+ parent.appendChild(stopEl);
3369
+ }
3370
+ function buildGradientDefs(marks, defs) {
3371
+ const map = /* @__PURE__ */ new Map();
3372
+ let counter = 0;
3373
+ for (const mark of marks) {
3374
+ const fill = mark.fill;
3375
+ if (fill && isGradientDef(fill)) {
3376
+ const key = gradientKey(fill);
3377
+ if (!map.has(key)) {
3378
+ const id = `oc-grad-${counter++}`;
3379
+ const el = createGradientElement(fill, id);
3380
+ defs.appendChild(el);
3381
+ map.set(key, id);
3382
+ }
3383
+ }
3384
+ }
3385
+ return map;
3386
+ }
3387
+ function resolveMarkFill(fill, gradientMap) {
3388
+ if (typeof fill === "string") return fill;
3389
+ const key = gradientKey(fill);
3390
+ const id = gradientMap.get(key);
3391
+ return id ? `url(#${id})` : "#000000";
3392
+ }
3393
+
3394
+ // src/svg-renderer.ts
3395
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
3308
3396
  var currentAnimation;
3397
+ var currentGradientMap = /* @__PURE__ */ new Map();
3309
3398
  function stampAnimationAttrs(el, mark, fallbackIndex) {
3310
3399
  if (!currentAnimation?.enabled) return;
3311
3400
  const idx = mark.animationIndex ?? fallbackIndex;
@@ -3334,7 +3423,7 @@ function computeXAxisExtent(layout) {
3334
3423
  return xAxis.label ? 48 : 26;
3335
3424
  }
3336
3425
  function createSVGElement(tag) {
3337
- return document.createElementNS(SVG_NS, tag);
3426
+ return document.createElementNS(SVG_NS2, tag);
3338
3427
  }
3339
3428
  function setAttrs(el, attrs) {
3340
3429
  for (const [key, value] of Object.entries(attrs)) {
@@ -3649,7 +3738,7 @@ function renderAreaMark(mark, index2) {
3649
3738
  const fill = createSVGElement("path");
3650
3739
  setAttrs(fill, {
3651
3740
  d: mark.path,
3652
- fill: mark.fill,
3741
+ fill: resolveMarkFill(mark.fill, currentGradientMap),
3653
3742
  "fill-opacity": mark.fillOpacity,
3654
3743
  stroke: "none"
3655
3744
  });
@@ -3682,7 +3771,7 @@ function renderRectMark(mark, index2) {
3682
3771
  y: mark.y,
3683
3772
  width: mark.width,
3684
3773
  height: mark.height,
3685
- fill: mark.fill
3774
+ fill: resolveMarkFill(mark.fill, currentGradientMap)
3686
3775
  });
3687
3776
  if (mark.stroke) {
3688
3777
  rect.setAttribute("stroke", mark.stroke);
@@ -3713,7 +3802,7 @@ function renderArcMark(mark, index2) {
3713
3802
  const path = createSVGElement("path");
3714
3803
  setAttrs(path, {
3715
3804
  d: mark.path,
3716
- fill: mark.fill,
3805
+ fill: resolveMarkFill(mark.fill, currentGradientMap),
3717
3806
  stroke: mark.stroke,
3718
3807
  "stroke-width": mark.strokeWidth
3719
3808
  });
@@ -3740,7 +3829,7 @@ function renderPointMark(mark, index2) {
3740
3829
  cx: mark.cx,
3741
3830
  cy: mark.cy,
3742
3831
  r: mark.r,
3743
- fill: mark.fill,
3832
+ fill: resolveMarkFill(mark.fill, currentGradientMap),
3744
3833
  stroke: mark.stroke,
3745
3834
  "stroke-width": mark.strokeWidth
3746
3835
  });
@@ -4184,7 +4273,7 @@ function renderChartSVG(layout, container, opts) {
4184
4273
  const svg = createSVGElement("svg");
4185
4274
  setAttrs(svg, {
4186
4275
  viewBox: `0 0 ${width} ${height}`,
4187
- xmlns: SVG_NS,
4276
+ xmlns: SVG_NS2,
4188
4277
  // WebKit/iOS Safari getBBox() bug: text with dominant-baseline:hanging
4189
4278
  // reports bounding boxes extending above y=0. The SVG spec default
4190
4279
  // overflow is "hidden", which clips this phantom extent. Setting
@@ -4241,6 +4330,7 @@ function renderChartSVG(layout, container, opts) {
4241
4330
  });
4242
4331
  clipPath.appendChild(clipRect);
4243
4332
  defs.appendChild(clipPath);
4333
+ currentGradientMap = buildGradientDefs(layout.marks, defs);
4244
4334
  svg.appendChild(defs);
4245
4335
  renderAxes(svg, layout);
4246
4336
  const clippedGroup = createSVGElement("g");
@@ -4267,8 +4357,11 @@ function renderChartSVG(layout, container, opts) {
4267
4357
  renderAnnotations(svg, layout);
4268
4358
  renderLegend(svg, layout.legend);
4269
4359
  renderChrome(svg, layout);
4270
- renderBrand(svg, layout);
4360
+ if (layout.watermark) {
4361
+ renderBrand(svg, layout);
4362
+ }
4271
4363
  currentAnimation = void 0;
4364
+ currentGradientMap = /* @__PURE__ */ new Map();
4272
4365
  container.appendChild(svg);
4273
4366
  return svg;
4274
4367
  }
@@ -4511,7 +4604,7 @@ function collectVoronoiPoints(layout) {
4511
4604
  const points = [];
4512
4605
  for (const mark of layout.marks) {
4513
4606
  if ((mark.type === "line" || mark.type === "area") && mark.dataPoints) {
4514
- const color = mark.type === "line" ? mark.stroke : mark.fill;
4607
+ const color = mark.type === "line" ? mark.stroke : getRepresentativeColor(mark.fill);
4515
4608
  for (const dp of mark.dataPoints) {
4516
4609
  points.push({ ...dp, color });
4517
4610
  }
@@ -4902,7 +4995,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
4902
4995
  };
4903
4996
  }
4904
4997
  function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
4905
- const SVG_NS3 = "http://www.w3.org/2000/svg";
4998
+ const SVG_NS4 = "http://www.w3.org/2000/svg";
4906
4999
  const cleanups = [];
4907
5000
  const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
4908
5001
  for (const el of annotationGroups) {
@@ -4941,7 +5034,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
4941
5034
  const createdHandles = [];
4942
5035
  for (const ep of endpoints) {
4943
5036
  if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
4944
- const handleEl = document.createElementNS(SVG_NS3, "circle");
5037
+ const handleEl = document.createElementNS(SVG_NS4, "circle");
4945
5038
  handleEl.setAttribute("class", "oc-connector-handle");
4946
5039
  handleEl.setAttribute("data-endpoint", ep.name);
4947
5040
  handleEl.setAttribute("cx", String(ep.cx));
@@ -5567,6 +5660,7 @@ function createChart(container, spec, options) {
5567
5660
  height,
5568
5661
  theme: options?.theme,
5569
5662
  darkMode,
5663
+ watermark: options?.watermark,
5570
5664
  measureText
5571
5665
  };
5572
5666
  if (isLayerSpec(currentSpec)) {
@@ -6405,7 +6499,7 @@ import { compileSankey } from "@opendata-ai/openchart-engine";
6405
6499
  // src/sankey-renderer.ts
6406
6500
  import { BRAND_FONT_SIZE as BRAND_FONT_SIZE3, BRAND_MIN_WIDTH as BRAND_MIN_WIDTH3, estimateTextWidth as estimateTextWidth2 } from "@opendata-ai/openchart-core";
6407
6501
  import { clampStaggerDelay as clampStaggerDelay2 } from "@opendata-ai/openchart-engine";
6408
- var SVG_NS2 = "http://www.w3.org/2000/svg";
6502
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
6409
6503
  var XLINK_NS2 = "http://www.w3.org/1999/xlink";
6410
6504
  var BRAND_URL2 = "https://tryopendata.ai";
6411
6505
  var EASE_VAR_MAP2 = {
@@ -6413,7 +6507,7 @@ var EASE_VAR_MAP2 = {
6413
6507
  snappy: "var(--oc-ease-snappy)"
6414
6508
  };
6415
6509
  function createSVGElement2(tag) {
6416
- return document.createElementNS(SVG_NS2, tag);
6510
+ return document.createElementNS(SVG_NS3, tag);
6417
6511
  }
6418
6512
  function setAttrs2(el, attrs) {
6419
6513
  for (const [key, value] of Object.entries(attrs)) {
@@ -6772,7 +6866,7 @@ function renderSankeySVG(layout, animation) {
6772
6866
  const svg = createSVGElement2("svg");
6773
6867
  setAttrs2(svg, {
6774
6868
  viewBox: `0 0 ${width} ${height}`,
6775
- xmlns: SVG_NS2,
6869
+ xmlns: SVG_NS3,
6776
6870
  overflow: "visible"
6777
6871
  });
6778
6872
  svg.style.height = `${height}px`;
@@ -6809,7 +6903,9 @@ function renderSankeySVG(layout, animation) {
6809
6903
  renderLabels(svg, layout.nodes);
6810
6904
  renderLegend2(svg, layout.legend);
6811
6905
  renderChrome2(svg, layout);
6812
- renderBrand2(svg, layout);
6906
+ if (layout.watermark) {
6907
+ renderBrand2(svg, layout);
6908
+ }
6813
6909
  return svg;
6814
6910
  }
6815
6911
 
@@ -6850,7 +6946,8 @@ function createSankey(container, spec, options) {
6850
6946
  width,
6851
6947
  height,
6852
6948
  theme: options?.theme,
6853
- darkMode
6949
+ darkMode,
6950
+ watermark: options?.watermark
6854
6951
  };
6855
6952
  return compileSankey(currentSpec, compileOpts);
6856
6953
  }
@@ -7584,18 +7681,20 @@ function renderTable(layout, container, opts) {
7584
7681
  liveRegion.setAttribute("aria-atomic", "true");
7585
7682
  liveRegion.setAttribute("role", "status");
7586
7683
  wrapper.appendChild(liveRegion);
7587
- const brandColor = theme ? theme.colors.axis : "#999999";
7588
- const brand = document.createElement("div");
7589
- brand.className = "oc-table-ref";
7590
- brand.style.cssText = "text-align: right; padding: 4px 8px;";
7591
- const brandLink = document.createElement("a");
7592
- brandLink.href = BRAND_URL3;
7593
- brandLink.target = "_blank";
7594
- brandLink.rel = "noopener";
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"};`;
7596
- brandLink.textContent = "tryOpenData.ai";
7597
- brand.appendChild(brandLink);
7598
- wrapper.appendChild(brand);
7684
+ if (layout.watermark) {
7685
+ const brandColor = theme ? theme.colors.axis : "#999999";
7686
+ const brand = document.createElement("div");
7687
+ brand.className = "oc-table-ref";
7688
+ brand.style.cssText = "text-align: right; padding: 4px 8px;";
7689
+ const brandLink = document.createElement("a");
7690
+ brandLink.href = BRAND_URL3;
7691
+ brandLink.target = "_blank";
7692
+ brandLink.rel = "noopener";
7693
+ 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"};`;
7694
+ brandLink.textContent = "tryOpenData.ai";
7695
+ brand.appendChild(brandLink);
7696
+ wrapper.appendChild(brand);
7697
+ }
7599
7698
  if (opts?.animate && layout.animation?.enabled) {
7600
7699
  const anim = layout.animation;
7601
7700
  const rowCount = layout.rows.length;
@@ -7686,6 +7785,7 @@ function createTable(container, spec, options) {
7686
7785
  height: 600,
7687
7786
  theme: options?.theme,
7688
7787
  darkMode,
7788
+ watermark: options?.watermark,
7689
7789
  sort: state.sort ?? void 0,
7690
7790
  search: state.search || void 0,
7691
7791
  page: state.page