@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 +8 -0
- package/dist/index.js +131 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/gradient-utils.test.ts +125 -0
- package/src/__tests__/svg-renderer.test.ts +7 -0
- package/src/gradient-utils.ts +134 -0
- package/src/graph/canvas-renderer.ts +3 -1
- package/src/graph/types.ts +2 -0
- package/src/graph-mount.ts +4 -0
- package/src/mount.ts +5 -2
- package/src/sankey-mount.ts +3 -0
- package/src/sankey-renderer.ts +3 -1
- package/src/svg-renderer.ts +20 -6
- package/src/table-mount.ts +3 -0
- package/src/table-renderer.ts +14 -12
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
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
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
|