@opendata-ai/openchart-vanilla 7.6.1 → 7.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/chunk-KYRQV6KR.js +2020 -0
- package/dist/chunk-KYRQV6KR.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +320 -2138
- package/dist/index.js.map +1 -1
- package/dist/static.d.ts +29 -0
- package/dist/static.js +187 -0
- package/dist/static.js.map +1 -0
- package/package.json +15 -3
- package/src/__tests__/static.test.ts +141 -0
- package/src/__tests__/svg-renderer.test.ts +10 -4
- package/src/mount.ts +31 -1
- package/src/renderers/annotations.ts +43 -0
- package/src/renderers/chrome.ts +35 -4
- package/src/renderers/endpoint-labels.ts +44 -38
- package/src/static.ts +246 -0
- package/src/svg-ids.ts +5 -0
- package/src/svg-renderer.ts +157 -98
- package/src/theme-tokens.ts +51 -0
- package/src/tilemap-renderer.ts +9 -5
package/dist/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerMarkRenderer,
|
|
3
|
+
renderChartSVG,
|
|
4
|
+
renderTileMapSVG
|
|
5
|
+
} from "./chunk-KYRQV6KR.js";
|
|
6
|
+
|
|
1
7
|
// src/barlist-mount.ts
|
|
2
8
|
import { compileBarList } from "@opendata-ai/openchart-engine";
|
|
3
9
|
|
|
@@ -3364,7 +3370,7 @@ function createGraph(container, spec, options) {
|
|
|
3364
3370
|
}
|
|
3365
3371
|
chromeEl = document.createElement("div");
|
|
3366
3372
|
chromeEl.className = "oc-graph-chrome";
|
|
3367
|
-
|
|
3373
|
+
renderChrome3();
|
|
3368
3374
|
wrapper.appendChild(chromeEl);
|
|
3369
3375
|
canvas = document.createElement("canvas");
|
|
3370
3376
|
canvas.className = "oc-graph-canvas";
|
|
@@ -3376,7 +3382,7 @@ function createGraph(container, spec, options) {
|
|
|
3376
3382
|
if (options?.legend !== false) {
|
|
3377
3383
|
legendEl = document.createElement("div");
|
|
3378
3384
|
legendEl.className = "oc-graph-legend";
|
|
3379
|
-
|
|
3385
|
+
renderLegend2();
|
|
3380
3386
|
wrapper.appendChild(legendEl);
|
|
3381
3387
|
}
|
|
3382
3388
|
container.appendChild(wrapper);
|
|
@@ -3384,7 +3390,7 @@ function createGraph(container, spec, options) {
|
|
|
3384
3390
|
renderer = new GraphCanvasRenderer(canvas);
|
|
3385
3391
|
renderer.resize(width, canvasHeight);
|
|
3386
3392
|
}
|
|
3387
|
-
function
|
|
3393
|
+
function renderChrome3() {
|
|
3388
3394
|
if (!chromeEl) return;
|
|
3389
3395
|
let html = "";
|
|
3390
3396
|
if (compilation.chrome.title) {
|
|
@@ -3400,7 +3406,7 @@ function createGraph(container, spec, options) {
|
|
|
3400
3406
|
chromeEl.style.display = "";
|
|
3401
3407
|
}
|
|
3402
3408
|
}
|
|
3403
|
-
function
|
|
3409
|
+
function renderLegend2() {
|
|
3404
3410
|
if (!legendEl) return;
|
|
3405
3411
|
const entries = "entries" in compilation.legend ? compilation.legend.entries : [];
|
|
3406
3412
|
if (entries.length === 0) {
|
|
@@ -3698,8 +3704,8 @@ function createGraph(container, spec, options) {
|
|
|
3698
3704
|
compilation = compile();
|
|
3699
3705
|
adjacencyMap = buildAdjacencyMap(compilation.edges);
|
|
3700
3706
|
buildDataMaps();
|
|
3701
|
-
|
|
3702
|
-
|
|
3707
|
+
renderChrome3();
|
|
3708
|
+
renderLegend2();
|
|
3703
3709
|
initSimulation();
|
|
3704
3710
|
initInteraction();
|
|
3705
3711
|
hoveredNodeId = null;
|
|
@@ -3733,8 +3739,8 @@ function createGraph(container, spec, options) {
|
|
|
3733
3739
|
};
|
|
3734
3740
|
});
|
|
3735
3741
|
spatialIndex.rebuild(positionedNodes);
|
|
3736
|
-
|
|
3737
|
-
|
|
3742
|
+
renderChrome3();
|
|
3743
|
+
renderLegend2();
|
|
3738
3744
|
needsRender = true;
|
|
3739
3745
|
scheduleRender();
|
|
3740
3746
|
}
|
|
@@ -3830,7 +3836,7 @@ function escapeHtml(str) {
|
|
|
3830
3836
|
}
|
|
3831
3837
|
|
|
3832
3838
|
// src/mount.ts
|
|
3833
|
-
import { isLayerSpec } from "@opendata-ai/openchart-core";
|
|
3839
|
+
import { isGraphSpec, isLayerSpec } from "@opendata-ai/openchart-core";
|
|
3834
3840
|
import { compileChart, compileLayer } from "@opendata-ai/openchart-engine";
|
|
3835
3841
|
|
|
3836
3842
|
// src/interactions/chart-events.ts
|
|
@@ -4329,7 +4335,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
|
|
|
4329
4335
|
};
|
|
4330
4336
|
}
|
|
4331
4337
|
function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
4332
|
-
const
|
|
4338
|
+
const SVG_NS3 = "http://www.w3.org/2000/svg";
|
|
4333
4339
|
const cleanups = [];
|
|
4334
4340
|
const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
|
|
4335
4341
|
for (const el of annotationGroups) {
|
|
@@ -4368,7 +4374,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
|
|
|
4368
4374
|
const createdHandles = [];
|
|
4369
4375
|
for (const ep of endpoints) {
|
|
4370
4376
|
if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
|
|
4371
|
-
const handleEl = document.createElementNS(
|
|
4377
|
+
const handleEl = document.createElementNS(SVG_NS3, "circle");
|
|
4372
4378
|
handleEl.setAttribute("class", "oc-connector-handle");
|
|
4373
4379
|
handleEl.setAttribute("data-endpoint", ep.name);
|
|
4374
4380
|
handleEl.setAttribute("cx", String(ep.cx));
|
|
@@ -4983,1716 +4989,208 @@ function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
|
|
|
4983
4989
|
};
|
|
4984
4990
|
}
|
|
4985
4991
|
|
|
4986
|
-
// src/
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
function nextSvgId(prefix) {
|
|
4995
|
-
return `${prefix}-${counter++}`;
|
|
4996
|
-
}
|
|
4997
|
-
|
|
4998
|
-
// src/gradient-utils.ts
|
|
4999
|
-
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
5000
|
-
function gradientKey(def) {
|
|
5001
|
-
return sortedStringify(def);
|
|
5002
|
-
}
|
|
5003
|
-
function sortedStringify(value) {
|
|
5004
|
-
if (value === null || value === void 0) return String(value);
|
|
5005
|
-
if (Array.isArray(value)) return `[${value.map(sortedStringify).join(",")}]`;
|
|
5006
|
-
if (typeof value === "object") {
|
|
5007
|
-
const sorted = Object.keys(value).sort().map((k) => `${JSON.stringify(k)}:${sortedStringify(value[k])}`);
|
|
5008
|
-
return `{${sorted.join(",")}}`;
|
|
5009
|
-
}
|
|
5010
|
-
return JSON.stringify(value);
|
|
5011
|
-
}
|
|
5012
|
-
function createGradientElement(def, id) {
|
|
5013
|
-
if (def.gradient === "linear") {
|
|
5014
|
-
return createLinearGradient(def, id);
|
|
5015
|
-
}
|
|
5016
|
-
return createRadialGradient(def, id);
|
|
5017
|
-
}
|
|
5018
|
-
function createLinearGradient(def, id) {
|
|
5019
|
-
const el = document.createElementNS(SVG_NS2, "linearGradient");
|
|
5020
|
-
el.setAttribute("id", id);
|
|
5021
|
-
el.setAttribute("gradientUnits", "objectBoundingBox");
|
|
5022
|
-
el.setAttribute("x1", String(def.x1 ?? 0));
|
|
5023
|
-
el.setAttribute("y1", String(def.y1 ?? 0));
|
|
5024
|
-
el.setAttribute("x2", String(def.x2 ?? 0));
|
|
5025
|
-
el.setAttribute("y2", String(def.y2 ?? 1));
|
|
5026
|
-
for (const stop of def.stops) {
|
|
5027
|
-
appendStop(el, stop);
|
|
5028
|
-
}
|
|
5029
|
-
return el;
|
|
5030
|
-
}
|
|
5031
|
-
function createRadialGradient(def, id) {
|
|
5032
|
-
const el = document.createElementNS(SVG_NS2, "radialGradient");
|
|
5033
|
-
el.setAttribute("id", id);
|
|
5034
|
-
el.setAttribute("gradientUnits", "objectBoundingBox");
|
|
5035
|
-
el.setAttribute("cx", String(def.x2 ?? 0.5));
|
|
5036
|
-
el.setAttribute("cy", String(def.y2 ?? 0.5));
|
|
5037
|
-
el.setAttribute("r", String(def.r2 ?? 0.5));
|
|
5038
|
-
el.setAttribute("fx", String(def.x1 ?? 0.5));
|
|
5039
|
-
el.setAttribute("fy", String(def.y1 ?? 0.5));
|
|
5040
|
-
el.setAttribute("fr", String(def.r1 ?? 0));
|
|
5041
|
-
for (const stop of def.stops) {
|
|
5042
|
-
appendStop(el, stop);
|
|
5043
|
-
}
|
|
5044
|
-
return el;
|
|
5045
|
-
}
|
|
5046
|
-
function appendStop(parent, stop) {
|
|
5047
|
-
const stopEl = document.createElementNS(SVG_NS2, "stop");
|
|
5048
|
-
stopEl.setAttribute("offset", String(stop.offset));
|
|
5049
|
-
stopEl.setAttribute("stop-color", stop.color);
|
|
5050
|
-
if (stop.opacity !== void 0) {
|
|
5051
|
-
stopEl.setAttribute("stop-opacity", String(stop.opacity));
|
|
5052
|
-
}
|
|
5053
|
-
parent.appendChild(stopEl);
|
|
5054
|
-
}
|
|
5055
|
-
function buildGradientDefs(marks, defs) {
|
|
5056
|
-
const map = /* @__PURE__ */ new Map();
|
|
5057
|
-
for (const mark of marks) {
|
|
5058
|
-
const fill = mark.fill;
|
|
5059
|
-
if (fill && isGradientDef(fill)) {
|
|
5060
|
-
const key = gradientKey(fill);
|
|
5061
|
-
if (!map.has(key)) {
|
|
5062
|
-
const id = nextSvgId("oc-grad");
|
|
5063
|
-
const el = createGradientElement(fill, id);
|
|
5064
|
-
defs.appendChild(el);
|
|
5065
|
-
map.set(key, id);
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
|
-
}
|
|
5069
|
-
return map;
|
|
5070
|
-
}
|
|
5071
|
-
function resolveMarkFill(fill, gradientMap) {
|
|
5072
|
-
if (typeof fill === "string") return fill;
|
|
5073
|
-
const key = gradientKey(fill);
|
|
5074
|
-
const id = gradientMap.get(key);
|
|
5075
|
-
return id ? `url(#${id})` : "#000000";
|
|
4992
|
+
// src/text-edit-overlay.ts
|
|
4993
|
+
function getScale(svg) {
|
|
4994
|
+
const viewBox = svg.viewBox?.baseVal;
|
|
4995
|
+
const svgRect = svg.getBoundingClientRect();
|
|
4996
|
+
return {
|
|
4997
|
+
scaleX: viewBox?.width && svgRect.width ? svgRect.width / viewBox.width : 1,
|
|
4998
|
+
scaleY: viewBox?.height && svgRect.height ? svgRect.height / viewBox.height : 1
|
|
4999
|
+
};
|
|
5076
5000
|
}
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5001
|
+
function getTextStyles(targetElement, scale) {
|
|
5002
|
+
const computed = window.getComputedStyle(targetElement);
|
|
5003
|
+
const svgFontSize = parseFloat(
|
|
5004
|
+
targetElement.getAttribute("font-size") ?? computed.fontSize ?? "14"
|
|
5005
|
+
);
|
|
5006
|
+
const pixelFontSize = svgFontSize * scale.scaleY;
|
|
5007
|
+
const fontFamily = targetElement.getAttribute("font-family") ?? computed.fontFamily ?? "inherit";
|
|
5008
|
+
const fontWeight = targetElement.getAttribute("font-weight") ?? computed.fontWeight ?? "400";
|
|
5009
|
+
const fill = targetElement.style.getPropertyValue("fill") || targetElement.getAttribute("fill") || computed.color || "#000";
|
|
5010
|
+
const textAnchor = targetElement.getAttribute("text-anchor") ?? "start";
|
|
5011
|
+
let textAlign = "left";
|
|
5012
|
+
if (textAnchor === "middle") textAlign = "center";
|
|
5013
|
+
else if (textAnchor === "end") textAlign = "right";
|
|
5014
|
+
return {
|
|
5015
|
+
fontFamily,
|
|
5016
|
+
fontSize: `${pixelFontSize}px`,
|
|
5017
|
+
fontWeight,
|
|
5018
|
+
color: fill,
|
|
5019
|
+
textAlign,
|
|
5020
|
+
lineHeight: "1.3"
|
|
5021
|
+
};
|
|
5083
5022
|
}
|
|
5084
|
-
function
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5023
|
+
function computePosition2(targetElement, svg, container) {
|
|
5024
|
+
const bbox = targetElement.getBBox();
|
|
5025
|
+
const scale = getScale(svg);
|
|
5026
|
+
const svgRect = svg.getBoundingClientRect();
|
|
5027
|
+
const containerRect = container.getBoundingClientRect();
|
|
5028
|
+
const padding = 4;
|
|
5029
|
+
const left = bbox.x * scale.scaleX + (svgRect.left - containerRect.left) - padding;
|
|
5030
|
+
const top = bbox.y * scale.scaleY + (svgRect.top - containerRect.top) - padding;
|
|
5031
|
+
const width = bbox.width * scale.scaleX + padding * 2;
|
|
5032
|
+
const height = bbox.height * scale.scaleY + padding * 2;
|
|
5033
|
+
return {
|
|
5034
|
+
top: Math.max(0, top),
|
|
5035
|
+
left: Math.max(0, left),
|
|
5036
|
+
width: Math.max(width, 60),
|
|
5037
|
+
height: Math.max(height, 24)
|
|
5038
|
+
};
|
|
5088
5039
|
}
|
|
5089
|
-
function
|
|
5090
|
-
const
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5040
|
+
function createTextEditOverlay(config) {
|
|
5041
|
+
const { container, svg, targetElement, currentText, onCommit, onCancel } = config;
|
|
5042
|
+
let destroyed = false;
|
|
5043
|
+
const originalOpacity = targetElement.getAttribute("opacity");
|
|
5044
|
+
targetElement.setAttribute("opacity", "0");
|
|
5045
|
+
const scale = getScale(svg);
|
|
5046
|
+
const styles = getTextStyles(targetElement, scale);
|
|
5047
|
+
const position = computePosition2(targetElement, svg, container);
|
|
5048
|
+
const textarea = document.createElement("textarea");
|
|
5049
|
+
textarea.value = currentText;
|
|
5050
|
+
const containerPosition = window.getComputedStyle(container).position;
|
|
5051
|
+
const containerWasStatic = containerPosition === "static";
|
|
5052
|
+
if (containerWasStatic) {
|
|
5053
|
+
container.style.position = "relative";
|
|
5097
5054
|
}
|
|
5098
|
-
|
|
5099
|
-
|
|
5055
|
+
Object.assign(textarea.style, {
|
|
5056
|
+
position: "absolute",
|
|
5057
|
+
top: `${position.top}px`,
|
|
5058
|
+
left: `${position.left}px`,
|
|
5059
|
+
width: `${position.width}px`,
|
|
5060
|
+
minHeight: `${position.height}px`,
|
|
5061
|
+
fontFamily: styles.fontFamily,
|
|
5062
|
+
fontSize: styles.fontSize,
|
|
5063
|
+
fontWeight: styles.fontWeight,
|
|
5064
|
+
color: styles.color,
|
|
5065
|
+
textAlign: styles.textAlign,
|
|
5066
|
+
lineHeight: styles.lineHeight,
|
|
5067
|
+
padding: "2px 4px",
|
|
5068
|
+
margin: "0",
|
|
5069
|
+
border: "1px solid rgba(79, 70, 229, 0.4)",
|
|
5070
|
+
borderRadius: "3px",
|
|
5071
|
+
background: "rgba(255, 255, 255, 0.95)",
|
|
5072
|
+
outline: "none",
|
|
5073
|
+
resize: "none",
|
|
5074
|
+
overflow: "hidden",
|
|
5075
|
+
boxSizing: "border-box",
|
|
5076
|
+
zIndex: "10000",
|
|
5077
|
+
// Remove default textarea appearance
|
|
5078
|
+
WebkitAppearance: "none",
|
|
5079
|
+
appearance: "none"
|
|
5080
|
+
});
|
|
5081
|
+
container.appendChild(textarea);
|
|
5082
|
+
textarea.focus();
|
|
5083
|
+
textarea.select();
|
|
5084
|
+
function destroy() {
|
|
5085
|
+
if (destroyed) return;
|
|
5086
|
+
destroyed = true;
|
|
5087
|
+
if (originalOpacity !== null) {
|
|
5088
|
+
targetElement.setAttribute("opacity", originalOpacity);
|
|
5089
|
+
} else {
|
|
5090
|
+
targetElement.removeAttribute("opacity");
|
|
5091
|
+
}
|
|
5092
|
+
textarea.removeEventListener("keydown", handleKeyDown);
|
|
5093
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
5094
|
+
resizeObserver.disconnect();
|
|
5095
|
+
if (containerWasStatic) {
|
|
5096
|
+
container.style.position = "";
|
|
5097
|
+
}
|
|
5098
|
+
if (textarea.parentNode) {
|
|
5099
|
+
textarea.parentNode.removeChild(textarea);
|
|
5100
|
+
}
|
|
5100
5101
|
}
|
|
5101
|
-
|
|
5102
|
-
|
|
5102
|
+
function commit() {
|
|
5103
|
+
if (destroyed) return;
|
|
5104
|
+
const newText = textarea.value;
|
|
5105
|
+
destroy();
|
|
5106
|
+
onCommit(newText);
|
|
5103
5107
|
}
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
const pad = 6;
|
|
5109
|
-
const tipY = to.y - pad;
|
|
5110
|
-
const dy = tipY - from.y;
|
|
5111
|
-
const dist = Math.sqrt((to.x - from.x) ** 2 + dy ** 2) || 1;
|
|
5112
|
-
const arrowLen = 8;
|
|
5113
|
-
const arrowWidth = 4;
|
|
5114
|
-
const bulge = Math.max(dist * 0.4, 35);
|
|
5115
|
-
const cp1x = from.x + bulge;
|
|
5116
|
-
const cp1y = from.y + dy * 0.35;
|
|
5117
|
-
const cp2x = to.x;
|
|
5118
|
-
const cp2y = tipY - Math.abs(dy) * 0.25;
|
|
5119
|
-
const tx = to.x - cp2x;
|
|
5120
|
-
const ty = tipY - cp2y;
|
|
5121
|
-
const tLen = Math.sqrt(tx * tx + ty * ty) || 1;
|
|
5122
|
-
const ux = tx / tLen;
|
|
5123
|
-
const uy = ty / tLen;
|
|
5124
|
-
const baseX = to.x - ux * arrowLen;
|
|
5125
|
-
const baseY = tipY - uy * arrowLen;
|
|
5126
|
-
const path = createSVGElement2("path");
|
|
5127
|
-
path.setAttribute("class", "oc-annotation-connector");
|
|
5128
|
-
setAttrs2(path, {
|
|
5129
|
-
d: `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${baseX} ${baseY}`,
|
|
5130
|
-
fill: "none",
|
|
5131
|
-
stroke,
|
|
5132
|
-
"stroke-width": 1.5
|
|
5133
|
-
});
|
|
5134
|
-
parent.appendChild(path);
|
|
5135
|
-
const px = -uy;
|
|
5136
|
-
const py = ux;
|
|
5137
|
-
const arrow = createSVGElement2("polygon");
|
|
5138
|
-
arrow.setAttribute("class", "oc-annotation-connector");
|
|
5139
|
-
setAttrs2(arrow, {
|
|
5140
|
-
points: [
|
|
5141
|
-
`${to.x},${tipY}`,
|
|
5142
|
-
`${baseX + px * arrowWidth},${baseY + py * arrowWidth}`,
|
|
5143
|
-
`${baseX - px * arrowWidth},${baseY - py * arrowWidth}`
|
|
5144
|
-
].join(" "),
|
|
5145
|
-
fill: stroke
|
|
5146
|
-
});
|
|
5147
|
-
parent.appendChild(arrow);
|
|
5148
|
-
}
|
|
5149
|
-
function renderAnnotation(parent, annotation, index2, bgColor) {
|
|
5150
|
-
const g = createSVGElement2("g");
|
|
5151
|
-
g.setAttribute("class", `oc-annotation oc-annotation-${annotation.type}`);
|
|
5152
|
-
g.setAttribute("data-annotation-index", String(index2));
|
|
5153
|
-
if (annotation.id) {
|
|
5154
|
-
g.setAttribute("data-annotation-id", annotation.id);
|
|
5108
|
+
function cancel() {
|
|
5109
|
+
if (destroyed) return;
|
|
5110
|
+
destroy();
|
|
5111
|
+
onCancel();
|
|
5155
5112
|
}
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
width: annotation.rect.width,
|
|
5164
|
-
height: annotation.rect.height
|
|
5165
|
-
});
|
|
5166
|
-
if (annotation.fill) rect.setAttribute("fill", annotation.fill);
|
|
5167
|
-
if (annotation.opacity !== void 0) {
|
|
5168
|
-
rect.setAttribute("fill-opacity", String(annotation.opacity));
|
|
5169
|
-
}
|
|
5170
|
-
g.appendChild(rect);
|
|
5171
|
-
}
|
|
5172
|
-
if (annotation.line) {
|
|
5173
|
-
const line = createSVGElement2("line");
|
|
5174
|
-
line.setAttribute("class", "oc-annotation-line");
|
|
5175
|
-
setAttrs2(line, {
|
|
5176
|
-
x1: annotation.line.start.x,
|
|
5177
|
-
y1: annotation.line.start.y,
|
|
5178
|
-
x2: annotation.line.end.x,
|
|
5179
|
-
y2: annotation.line.end.y,
|
|
5180
|
-
"stroke-width": annotation.strokeWidth ?? 1
|
|
5181
|
-
});
|
|
5182
|
-
if (annotation.stroke) line.setAttribute("stroke", annotation.stroke);
|
|
5183
|
-
if (annotation.strokeDasharray) {
|
|
5184
|
-
line.setAttribute("stroke-dasharray", annotation.strokeDasharray);
|
|
5185
|
-
}
|
|
5186
|
-
g.appendChild(line);
|
|
5187
|
-
}
|
|
5188
|
-
if (annotation.label?.visible) {
|
|
5189
|
-
if (annotation.label.connector) {
|
|
5190
|
-
const c2 = annotation.label.connector;
|
|
5191
|
-
if (c2.style === "curve") {
|
|
5192
|
-
renderCurvedArrow(g, c2.from, c2.to, c2.stroke);
|
|
5193
|
-
} else if (c2.style === "drop-line") {
|
|
5194
|
-
const connector = createSVGElement2("line");
|
|
5195
|
-
connector.setAttribute("class", "oc-annotation-connector oc-annotation-drop-line");
|
|
5196
|
-
setAttrs2(connector, {
|
|
5197
|
-
x1: c2.from.x,
|
|
5198
|
-
y1: c2.from.y,
|
|
5199
|
-
x2: c2.to.x,
|
|
5200
|
-
y2: c2.to.y,
|
|
5201
|
-
stroke: c2.stroke,
|
|
5202
|
-
"stroke-width": 1,
|
|
5203
|
-
"stroke-opacity": 0.6,
|
|
5204
|
-
"shape-rendering": "crispEdges"
|
|
5205
|
-
});
|
|
5206
|
-
g.appendChild(connector);
|
|
5207
|
-
} else {
|
|
5208
|
-
const connector = createSVGElement2("line");
|
|
5209
|
-
connector.setAttribute("class", "oc-annotation-connector");
|
|
5210
|
-
setAttrs2(connector, {
|
|
5211
|
-
x1: c2.from.x,
|
|
5212
|
-
y1: c2.from.y,
|
|
5213
|
-
x2: c2.to.x,
|
|
5214
|
-
y2: c2.to.y,
|
|
5215
|
-
stroke: c2.stroke,
|
|
5216
|
-
"stroke-width": 1,
|
|
5217
|
-
"stroke-opacity": 0.5
|
|
5218
|
-
});
|
|
5219
|
-
g.appendChild(connector);
|
|
5220
|
-
}
|
|
5221
|
-
if (c2.endpoint && c2.style !== "curve") {
|
|
5222
|
-
const ring = createSVGElement2("circle");
|
|
5223
|
-
ring.setAttribute("class", "oc-annotation-endpoint-ring");
|
|
5224
|
-
setAttrs2(ring, {
|
|
5225
|
-
cx: c2.endpoint.x,
|
|
5226
|
-
cy: c2.endpoint.y,
|
|
5227
|
-
r: 5,
|
|
5228
|
-
fill: bgColor ?? "#ffffff",
|
|
5229
|
-
stroke: c2.stroke,
|
|
5230
|
-
"stroke-width": 1.5
|
|
5231
|
-
});
|
|
5232
|
-
g.appendChild(ring);
|
|
5233
|
-
const dot = createSVGElement2("circle");
|
|
5234
|
-
dot.setAttribute("class", "oc-annotation-endpoint-dot");
|
|
5235
|
-
setAttrs2(dot, {
|
|
5236
|
-
cx: c2.endpoint.x,
|
|
5237
|
-
cy: c2.endpoint.y,
|
|
5238
|
-
r: 2,
|
|
5239
|
-
fill: c2.stroke
|
|
5240
|
-
});
|
|
5241
|
-
g.appendChild(dot);
|
|
5242
|
-
}
|
|
5243
|
-
}
|
|
5244
|
-
if (annotation.dot) {
|
|
5245
|
-
const dot = createSVGElement2("circle");
|
|
5246
|
-
dot.setAttribute("class", "oc-annotation-dot");
|
|
5247
|
-
setAttrs2(dot, {
|
|
5248
|
-
cx: annotation.dot.x,
|
|
5249
|
-
cy: annotation.dot.y,
|
|
5250
|
-
r: annotation.dot.radius,
|
|
5251
|
-
fill: annotation.dot.fill,
|
|
5252
|
-
stroke: annotation.dot.stroke,
|
|
5253
|
-
"stroke-width": annotation.dot.strokeWidth
|
|
5254
|
-
});
|
|
5255
|
-
g.appendChild(dot);
|
|
5113
|
+
const handleKeyDown = (e) => {
|
|
5114
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
5115
|
+
e.preventDefault();
|
|
5116
|
+
commit();
|
|
5117
|
+
} else if (e.key === "Escape") {
|
|
5118
|
+
e.preventDefault();
|
|
5119
|
+
cancel();
|
|
5256
5120
|
}
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
const fontSize = annotation.label.style.fontSize ?? 12;
|
|
5263
|
-
const lineHeight = fontSize * (annotation.label.style.lineHeight ?? 1.3);
|
|
5264
|
-
const isMultiLine = lines.length > 1;
|
|
5265
|
-
if (isMultiLine) {
|
|
5266
|
-
for (let i = 0; i < lines.length; i++) {
|
|
5267
|
-
const tspan = createSVGElement2("tspan");
|
|
5268
|
-
setAttrs2(tspan, { x: annotation.label.x, dy: i === 0 ? 0 : lineHeight });
|
|
5269
|
-
tspan.textContent = lines[i];
|
|
5270
|
-
text.appendChild(tspan);
|
|
5271
|
-
}
|
|
5272
|
-
} else {
|
|
5273
|
-
text.textContent = annotation.label.text;
|
|
5274
|
-
}
|
|
5275
|
-
if (annotation.label.background) {
|
|
5276
|
-
const pad = 3;
|
|
5277
|
-
let bgX;
|
|
5278
|
-
let bgY;
|
|
5279
|
-
let bgW;
|
|
5280
|
-
let bgH;
|
|
5281
|
-
if (annotation.label.bounds) {
|
|
5282
|
-
const b = annotation.label.bounds;
|
|
5283
|
-
bgX = b.x - pad;
|
|
5284
|
-
bgY = b.y - pad;
|
|
5285
|
-
bgW = b.width + pad * 2;
|
|
5286
|
-
bgH = b.height + pad * 2;
|
|
5287
|
-
} else {
|
|
5288
|
-
const charWidth = fontSize * 0.55;
|
|
5289
|
-
const maxLineWidth = Math.max(...lines.map((l) => l.length)) * charWidth;
|
|
5290
|
-
const totalHeight = lines.length * lineHeight;
|
|
5291
|
-
bgX = isMultiLine ? annotation.label.x - maxLineWidth / 2 - pad : annotation.label.x - pad;
|
|
5292
|
-
bgY = annotation.label.y - fontSize + (lineHeight - fontSize) / 2 - pad;
|
|
5293
|
-
bgW = maxLineWidth + pad * 2;
|
|
5294
|
-
bgH = totalHeight + pad * 2;
|
|
5295
|
-
}
|
|
5296
|
-
const bgRect = createSVGElement2("rect");
|
|
5297
|
-
bgRect.setAttribute("class", "oc-annotation-bg");
|
|
5298
|
-
setAttrs2(bgRect, {
|
|
5299
|
-
x: bgX,
|
|
5300
|
-
y: bgY,
|
|
5301
|
-
width: bgW,
|
|
5302
|
-
height: bgH,
|
|
5303
|
-
fill: annotation.label.background,
|
|
5304
|
-
rx: 2
|
|
5305
|
-
});
|
|
5306
|
-
g.appendChild(bgRect);
|
|
5307
|
-
} else if (bgColor && annotation.label.halo !== false) {
|
|
5308
|
-
text.style.paintOrder = "stroke";
|
|
5309
|
-
text.style.stroke = bgColor;
|
|
5310
|
-
text.style.strokeWidth = `${Math.round(fontSize * 0.3)}px`;
|
|
5311
|
-
text.style.strokeLinejoin = "round";
|
|
5121
|
+
};
|
|
5122
|
+
textarea.addEventListener("keydown", handleKeyDown);
|
|
5123
|
+
const handleClickOutside = (e) => {
|
|
5124
|
+
if (!textarea.contains(e.target)) {
|
|
5125
|
+
commit();
|
|
5312
5126
|
}
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
setAttrs2(sub, { x: annotation.subtitle.x, y: annotation.subtitle.y });
|
|
5318
|
-
applyTextStyle(sub, annotation.subtitle.style);
|
|
5319
|
-
sub.textContent = annotation.subtitle.text;
|
|
5320
|
-
g.appendChild(sub);
|
|
5127
|
+
};
|
|
5128
|
+
requestAnimationFrame(() => {
|
|
5129
|
+
if (!destroyed) {
|
|
5130
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
5321
5131
|
}
|
|
5322
|
-
}
|
|
5323
|
-
|
|
5132
|
+
});
|
|
5133
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
5134
|
+
if (destroyed) return;
|
|
5135
|
+
const newPosition = computePosition2(targetElement, svg, container);
|
|
5136
|
+
textarea.style.top = `${newPosition.top}px`;
|
|
5137
|
+
textarea.style.left = `${newPosition.left}px`;
|
|
5138
|
+
textarea.style.width = `${newPosition.width}px`;
|
|
5139
|
+
textarea.style.minHeight = `${newPosition.height}px`;
|
|
5140
|
+
});
|
|
5141
|
+
resizeObserver.observe(container);
|
|
5142
|
+
return { destroy };
|
|
5324
5143
|
}
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
const
|
|
5330
|
-
|
|
5331
|
-
|
|
5144
|
+
|
|
5145
|
+
// src/theme-tokens.ts
|
|
5146
|
+
import { adaptForLightLineStroke } from "@opendata-ai/openchart-core";
|
|
5147
|
+
function stampThemeProperties(el, theme) {
|
|
5148
|
+
const accent = theme.colors.categorical[0] ?? "#06b6d4";
|
|
5149
|
+
const bg = theme.colors.background === "transparent" ? theme.isDark ? "#09090b" : "#ffffff" : theme.colors.background;
|
|
5150
|
+
const props = [
|
|
5151
|
+
["--oc-font-family", theme.fonts.family],
|
|
5152
|
+
["--oc-font-mono", theme.fonts.mono],
|
|
5153
|
+
["--oc-title-size", `${theme.chrome.title.fontSize}px`],
|
|
5154
|
+
["--oc-title-weight", `${theme.chrome.title.fontWeight}`],
|
|
5155
|
+
["--oc-subtitle-size", `${theme.chrome.subtitle.fontSize}px`],
|
|
5156
|
+
["--oc-subtitle-weight", `${theme.chrome.subtitle.fontWeight}`],
|
|
5157
|
+
["--oc-source-size", `${theme.chrome.source.fontSize}px`],
|
|
5158
|
+
["--oc-source-weight", `${theme.chrome.source.fontWeight}`],
|
|
5159
|
+
["--oc-body-size", `${theme.fonts.sizes.body}px`],
|
|
5160
|
+
["--oc-eyebrow-size", `${theme.chrome.eyebrow.fontSize}px`],
|
|
5161
|
+
["--oc-eyebrow-weight", `${theme.chrome.eyebrow.fontWeight}`],
|
|
5162
|
+
["--oc-bg", bg],
|
|
5163
|
+
["--oc-text", theme.colors.text],
|
|
5164
|
+
["--oc-text-muted", theme.colors.axis],
|
|
5165
|
+
["--oc-gridline", theme.colors.gridline],
|
|
5166
|
+
["--oc-axis", theme.colors.axis],
|
|
5167
|
+
["--oc-border-radius", `${theme.borderRadius}px`],
|
|
5168
|
+
["--oc-accent", accent],
|
|
5169
|
+
["--oc-accent-strong", adaptForLightLineStroke(accent)],
|
|
5170
|
+
["--oc-positive", theme.colors.positive],
|
|
5171
|
+
["--oc-negative", theme.colors.negative],
|
|
5172
|
+
["--oc-space-2", `${theme.spacing.chromeGap * 2}px`],
|
|
5173
|
+
["--oc-space-4", `${theme.spacing.padding}px`]
|
|
5174
|
+
];
|
|
5175
|
+
for (const [name, value] of props) {
|
|
5176
|
+
el.style.setProperty(name, value);
|
|
5332
5177
|
}
|
|
5333
|
-
parent.appendChild(g);
|
|
5334
5178
|
}
|
|
5335
5179
|
|
|
5336
|
-
// src/
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
textAscent as textAscent2
|
|
5343
|
-
} from "@opendata-ai/openchart-core";
|
|
5344
|
-
function appendCompoundLabel(parent, primaryText, subtitle, fontWeight) {
|
|
5345
|
-
const primarySpan = createSVGElement2("tspan");
|
|
5346
|
-
primarySpan.setAttribute("font-weight", String(fontWeight));
|
|
5347
|
-
primarySpan.textContent = primaryText;
|
|
5348
|
-
parent.appendChild(primarySpan);
|
|
5349
|
-
const subtitleSpan = createSVGElement2("tspan");
|
|
5350
|
-
subtitleSpan.setAttribute("dx", "0.5em");
|
|
5351
|
-
subtitleSpan.textContent = subtitle;
|
|
5352
|
-
subtitleSpan.setAttribute("font-weight", "400");
|
|
5353
|
-
subtitleSpan.setAttribute("fill-opacity", "0.6");
|
|
5354
|
-
parent.appendChild(subtitleSpan);
|
|
5355
|
-
}
|
|
5356
|
-
function renderAxis(parent, axis, orientation, layout) {
|
|
5357
|
-
const g = createSVGElement2("g");
|
|
5358
|
-
const isRight = orientation === "y" && axis.orient === "right";
|
|
5359
|
-
const isInlineY = orientation === "y" && axis.tickPosition === "inline" && !isRight;
|
|
5360
|
-
g.setAttribute(
|
|
5361
|
-
"class",
|
|
5362
|
-
`oc-axis oc-axis-${isRight ? "y2" : orientation}${isInlineY ? " oc-axis-inline" : ""}`
|
|
5363
|
-
);
|
|
5364
|
-
const { area } = layout;
|
|
5365
|
-
if (orientation === "x" && axis.domainLine !== false) {
|
|
5366
|
-
const line = createSVGElement2("line");
|
|
5367
|
-
line.setAttribute("class", "oc-axis-line");
|
|
5368
|
-
setAttrs2(line, {
|
|
5369
|
-
x1: axis.start.x,
|
|
5370
|
-
y1: axis.start.y,
|
|
5371
|
-
x2: axis.end.x,
|
|
5372
|
-
y2: axis.end.y,
|
|
5373
|
-
stroke: layout.theme.colors.axis,
|
|
5374
|
-
"stroke-width": 1
|
|
5375
|
-
});
|
|
5376
|
-
g.appendChild(line);
|
|
5377
|
-
}
|
|
5378
|
-
for (const tick of axis.ticks) {
|
|
5379
|
-
if (orientation === "x") {
|
|
5380
|
-
const label = createSVGElement2("text");
|
|
5381
|
-
label.setAttribute("class", "oc-axis-tick");
|
|
5382
|
-
if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {
|
|
5383
|
-
const labelX = tick.position;
|
|
5384
|
-
const xLabelPad = axis.labelPadding ?? layout.theme.spacing.xAxisLabelPadding;
|
|
5385
|
-
const labelY = area.y + area.height + xLabelPad;
|
|
5386
|
-
setAttrs2(label, {
|
|
5387
|
-
x: labelX,
|
|
5388
|
-
y: labelY,
|
|
5389
|
-
"text-anchor": axis.tickAngle < 0 ? "end" : "start",
|
|
5390
|
-
"dominant-baseline": "central",
|
|
5391
|
-
transform: `rotate(${axis.tickAngle}, ${labelX}, ${labelY})`
|
|
5392
|
-
});
|
|
5393
|
-
} else {
|
|
5394
|
-
const xLabelPad = axis.labelPadding ?? layout.theme.spacing.xAxisLabelPadding;
|
|
5395
|
-
setAttrs2(label, {
|
|
5396
|
-
x: tick.position,
|
|
5397
|
-
y: area.y + area.height + xLabelPad + textAscent2(axis.tickLabelStyle.fontSize),
|
|
5398
|
-
"text-anchor": "middle"
|
|
5399
|
-
});
|
|
5400
|
-
}
|
|
5401
|
-
applyTextStyle(label, axis.tickLabelStyle);
|
|
5402
|
-
label.textContent = tick.label;
|
|
5403
|
-
g.appendChild(label);
|
|
5404
|
-
} else if (isInlineY) {
|
|
5405
|
-
const label = createSVGElement2("text");
|
|
5406
|
-
label.setAttribute("class", "oc-axis-tick oc-axis-tick-inline");
|
|
5407
|
-
setAttrs2(label, {
|
|
5408
|
-
x: area.x,
|
|
5409
|
-
y: tick.position - 6,
|
|
5410
|
-
"text-anchor": "start"
|
|
5411
|
-
});
|
|
5412
|
-
applyTextStyle(label, axis.tickLabelStyle);
|
|
5413
|
-
label.textContent = tick.label;
|
|
5414
|
-
g.appendChild(label);
|
|
5415
|
-
} else {
|
|
5416
|
-
const label = createSVGElement2("text");
|
|
5417
|
-
label.setAttribute("class", "oc-axis-tick");
|
|
5418
|
-
setAttrs2(label, {
|
|
5419
|
-
x: isRight ? area.x + area.width + TICK_LABEL_OFFSET : area.x - TICK_LABEL_OFFSET,
|
|
5420
|
-
y: tick.position,
|
|
5421
|
-
"text-anchor": isRight ? "start" : "end",
|
|
5422
|
-
"dominant-baseline": "central"
|
|
5423
|
-
});
|
|
5424
|
-
applyTextStyle(label, axis.tickLabelStyle);
|
|
5425
|
-
if (!isRight) {
|
|
5426
|
-
const availableWidth = area.x - TICK_LABEL_OFFSET;
|
|
5427
|
-
const fontSize = axis.tickLabelStyle.fontSize;
|
|
5428
|
-
const fontWeight = axis.tickLabelStyle.fontWeight;
|
|
5429
|
-
if (tick.subtitle) {
|
|
5430
|
-
const gapWidth = fontSize * 0.5;
|
|
5431
|
-
const subtitleWidth = estimateTextWidth2(tick.subtitle, fontSize, fontWeight);
|
|
5432
|
-
const primaryWidth = estimateTextWidth2(tick.label, fontSize, fontWeight);
|
|
5433
|
-
const totalWidth = primaryWidth + gapWidth + subtitleWidth;
|
|
5434
|
-
if (totalWidth > availableWidth && availableWidth > 20) {
|
|
5435
|
-
const ellipsis = "\u2026";
|
|
5436
|
-
const ellipsisWidth = estimateTextWidth2(ellipsis, fontSize, fontWeight);
|
|
5437
|
-
const budgetForPrimary = availableWidth - gapWidth - subtitleWidth - ellipsisWidth;
|
|
5438
|
-
let primaryText = tick.label;
|
|
5439
|
-
if (budgetForPrimary > 0) {
|
|
5440
|
-
let lo = 0;
|
|
5441
|
-
let hi = tick.label.length;
|
|
5442
|
-
while (lo < hi) {
|
|
5443
|
-
const mid = lo + hi + 1 >>> 1;
|
|
5444
|
-
const candidate = tick.label.slice(0, mid);
|
|
5445
|
-
if (estimateTextWidth2(candidate, fontSize, fontWeight) <= budgetForPrimary) {
|
|
5446
|
-
lo = mid;
|
|
5447
|
-
} else {
|
|
5448
|
-
hi = mid - 1;
|
|
5449
|
-
}
|
|
5450
|
-
}
|
|
5451
|
-
primaryText = lo > 0 ? tick.label.slice(0, lo).trimEnd() + ellipsis : ellipsis;
|
|
5452
|
-
} else {
|
|
5453
|
-
primaryText = ellipsis;
|
|
5454
|
-
}
|
|
5455
|
-
appendCompoundLabel(label, primaryText, tick.subtitle, fontWeight);
|
|
5456
|
-
const titleEl = createSVGElement2("title");
|
|
5457
|
-
titleEl.textContent = `${tick.label} ${tick.subtitle}`;
|
|
5458
|
-
label.appendChild(titleEl);
|
|
5459
|
-
} else {
|
|
5460
|
-
appendCompoundLabel(label, tick.label, tick.subtitle, fontWeight);
|
|
5461
|
-
}
|
|
5462
|
-
} else {
|
|
5463
|
-
const fullWidth = estimateTextWidth2(tick.label, fontSize, fontWeight);
|
|
5464
|
-
if (fullWidth > availableWidth && availableWidth > 20) {
|
|
5465
|
-
const ellipsis = "\u2026";
|
|
5466
|
-
const ellipsisWidth = estimateTextWidth2(ellipsis, fontSize, fontWeight);
|
|
5467
|
-
let lo = 0;
|
|
5468
|
-
let hi = tick.label.length;
|
|
5469
|
-
while (lo < hi) {
|
|
5470
|
-
const mid = lo + hi + 1 >>> 1;
|
|
5471
|
-
const candidate = tick.label.slice(0, mid);
|
|
5472
|
-
if (estimateTextWidth2(candidate, fontSize, fontWeight) + ellipsisWidth <= availableWidth) {
|
|
5473
|
-
lo = mid;
|
|
5474
|
-
} else {
|
|
5475
|
-
hi = mid - 1;
|
|
5476
|
-
}
|
|
5477
|
-
}
|
|
5478
|
-
label.textContent = lo > 0 ? tick.label.slice(0, lo).trimEnd() + ellipsis : ellipsis;
|
|
5479
|
-
const titleEl = createSVGElement2("title");
|
|
5480
|
-
titleEl.textContent = tick.label;
|
|
5481
|
-
label.appendChild(titleEl);
|
|
5482
|
-
} else {
|
|
5483
|
-
label.textContent = tick.label;
|
|
5484
|
-
}
|
|
5485
|
-
}
|
|
5486
|
-
} else {
|
|
5487
|
-
label.textContent = tick.label;
|
|
5488
|
-
}
|
|
5489
|
-
g.appendChild(label);
|
|
5490
|
-
}
|
|
5491
|
-
}
|
|
5492
|
-
if (!isRight) {
|
|
5493
|
-
for (const gridline of axis.gridlines) {
|
|
5494
|
-
const gl = createSVGElement2("line");
|
|
5495
|
-
gl.setAttribute("class", "oc-gridline");
|
|
5496
|
-
if (orientation === "y") {
|
|
5497
|
-
setAttrs2(gl, {
|
|
5498
|
-
x1: area.x,
|
|
5499
|
-
y1: gridline.position,
|
|
5500
|
-
x2: area.x + area.width,
|
|
5501
|
-
y2: gridline.position,
|
|
5502
|
-
stroke: layout.theme.colors.gridline,
|
|
5503
|
-
"stroke-width": 1,
|
|
5504
|
-
"stroke-opacity": 0.6
|
|
5505
|
-
});
|
|
5506
|
-
} else {
|
|
5507
|
-
setAttrs2(gl, {
|
|
5508
|
-
x1: gridline.position,
|
|
5509
|
-
y1: area.y,
|
|
5510
|
-
x2: gridline.position,
|
|
5511
|
-
y2: area.y + area.height,
|
|
5512
|
-
stroke: layout.theme.colors.gridline,
|
|
5513
|
-
"stroke-width": 1,
|
|
5514
|
-
"stroke-opacity": 0.6
|
|
5515
|
-
});
|
|
5516
|
-
}
|
|
5517
|
-
g.appendChild(gl);
|
|
5518
|
-
}
|
|
5519
|
-
}
|
|
5520
|
-
if (axis.label && axis.labelStyle) {
|
|
5521
|
-
const axisLabel = createSVGElement2("text");
|
|
5522
|
-
axisLabel.setAttribute("class", "oc-axis-title");
|
|
5523
|
-
applyTextStyle(axisLabel, axis.labelStyle);
|
|
5524
|
-
axisLabel.textContent = axis.label;
|
|
5525
|
-
const tp = axis.titlePosition;
|
|
5526
|
-
if (tp) {
|
|
5527
|
-
const attrs = {
|
|
5528
|
-
x: tp.x,
|
|
5529
|
-
y: tp.y,
|
|
5530
|
-
"text-anchor": "middle"
|
|
5531
|
-
};
|
|
5532
|
-
if (tp.angle) {
|
|
5533
|
-
attrs.transform = `rotate(${tp.angle}, ${tp.x}, ${tp.y})`;
|
|
5534
|
-
}
|
|
5535
|
-
setAttrs2(axisLabel, attrs);
|
|
5536
|
-
} else if (orientation === "x") {
|
|
5537
|
-
let titleY = area.y + area.height + 35;
|
|
5538
|
-
if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {
|
|
5539
|
-
const angleRad = Math.abs(axis.tickAngle) * (Math.PI / 180);
|
|
5540
|
-
let maxLabelWidth = 40;
|
|
5541
|
-
for (const tick of axis.ticks) {
|
|
5542
|
-
const w = estimateTextWidth2(
|
|
5543
|
-
tick.label,
|
|
5544
|
-
axis.tickLabelStyle.fontSize,
|
|
5545
|
-
axis.tickLabelStyle.fontWeight
|
|
5546
|
-
);
|
|
5547
|
-
if (w > maxLabelWidth) maxLabelWidth = w;
|
|
5548
|
-
}
|
|
5549
|
-
const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);
|
|
5550
|
-
titleY = area.y + area.height + rotatedHeight + 14;
|
|
5551
|
-
}
|
|
5552
|
-
setAttrs2(axisLabel, {
|
|
5553
|
-
x: area.x + area.width / 2,
|
|
5554
|
-
y: titleY,
|
|
5555
|
-
"text-anchor": "middle"
|
|
5556
|
-
});
|
|
5557
|
-
} else if (isRight) {
|
|
5558
|
-
const titleOffset = getAxisTitleOffset(layout.dimensions.width);
|
|
5559
|
-
const titleX = area.x + area.width + titleOffset;
|
|
5560
|
-
setAttrs2(axisLabel, {
|
|
5561
|
-
x: titleX,
|
|
5562
|
-
y: area.y + area.height / 2,
|
|
5563
|
-
"text-anchor": "middle",
|
|
5564
|
-
transform: `rotate(90, ${titleX}, ${area.y + area.height / 2})`
|
|
5565
|
-
});
|
|
5566
|
-
} else {
|
|
5567
|
-
const maxTickLabelWidth = axis.ticks.reduce((max, t) => {
|
|
5568
|
-
const w = estimateTextWidth2(
|
|
5569
|
-
t.label,
|
|
5570
|
-
axis.tickLabelStyle.fontSize,
|
|
5571
|
-
axis.tickLabelStyle.fontWeight ?? 400
|
|
5572
|
-
);
|
|
5573
|
-
return Math.max(max, w);
|
|
5574
|
-
}, 0);
|
|
5575
|
-
const titleOffset = axisTitleOffset(
|
|
5576
|
-
maxTickLabelWidth,
|
|
5577
|
-
axis.labelStyle.fontSize,
|
|
5578
|
-
layout.dimensions.width
|
|
5579
|
-
);
|
|
5580
|
-
setAttrs2(axisLabel, {
|
|
5581
|
-
x: area.x - titleOffset,
|
|
5582
|
-
y: area.y + area.height / 2,
|
|
5583
|
-
"text-anchor": "middle",
|
|
5584
|
-
transform: `rotate(-90, ${area.x - titleOffset}, ${area.y + area.height / 2})`
|
|
5585
|
-
});
|
|
5586
|
-
}
|
|
5587
|
-
g.appendChild(axisLabel);
|
|
5180
|
+
// src/mount.ts
|
|
5181
|
+
function resolveDarkMode3(mode) {
|
|
5182
|
+
if (mode === "force") return true;
|
|
5183
|
+
if (mode === "off" || mode === void 0) return false;
|
|
5184
|
+
if (typeof window !== "undefined" && window.matchMedia) {
|
|
5185
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
5588
5186
|
}
|
|
5589
|
-
|
|
5187
|
+
return false;
|
|
5590
5188
|
}
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
renderAxis(parent, layout.axes.y, "y", layout);
|
|
5597
|
-
}
|
|
5598
|
-
if (layout.axes.y2) {
|
|
5599
|
-
renderAxis(parent, layout.axes.y2, "y", layout);
|
|
5600
|
-
}
|
|
5601
|
-
}
|
|
5602
|
-
|
|
5603
|
-
// src/renderers/brand.ts
|
|
5604
|
-
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE2, BRAND_MIN_WIDTH as BRAND_MIN_WIDTH2, textAscent as textAscent3 } from "@opendata-ai/openchart-core";
|
|
5605
|
-
var BRAND_URL2 = "https://tryopendata.ai";
|
|
5606
|
-
function renderBrand(parent, layout) {
|
|
5607
|
-
if (layout.dimensions.width < BRAND_MIN_WIDTH2) return;
|
|
5608
|
-
const { width } = layout.dimensions;
|
|
5609
|
-
const padding = layout.theme.spacing.padding;
|
|
5610
|
-
const rightEdge = width - padding;
|
|
5611
|
-
const fill = layout.theme.colors.axis;
|
|
5612
|
-
const { chrome } = layout;
|
|
5613
|
-
const bottomOffset = chrome.bottomAnchorY ?? layout.area.y + layout.area.height;
|
|
5614
|
-
const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;
|
|
5615
|
-
const { legend } = layout;
|
|
5616
|
-
const bottomLegendOffset = legend.position === "bottom" && legend.bounds.height > 0 ? legend.bounds.height + 8 : 0;
|
|
5617
|
-
const chromeY = firstBottom ? bottomOffset + firstBottom.y : bottomOffset + layout.theme.spacing.chartToFooter + bottomLegendOffset;
|
|
5618
|
-
const a2 = createSVGElement2("a");
|
|
5619
|
-
a2.setAttribute("href", BRAND_URL2);
|
|
5620
|
-
a2.setAttributeNS(XLINK_NS2, "xlink:href", BRAND_URL2);
|
|
5621
|
-
a2.setAttribute("target", "_blank");
|
|
5622
|
-
a2.setAttribute("rel", "noopener");
|
|
5623
|
-
a2.setAttribute("class", "oc-chrome-ref");
|
|
5624
|
-
const BRAND_LARGE = 16;
|
|
5625
|
-
const text = createSVGElement2("text");
|
|
5626
|
-
setAttrs2(text, {
|
|
5627
|
-
x: rightEdge,
|
|
5628
|
-
y: chromeY + textAscent3(BRAND_LARGE),
|
|
5629
|
-
"font-family": layout.theme.fonts.family,
|
|
5630
|
-
"font-size": BRAND_FONT_SIZE2,
|
|
5631
|
-
"text-anchor": "end",
|
|
5632
|
-
"fill-opacity": 0.55
|
|
5633
|
-
});
|
|
5634
|
-
text.style.setProperty("fill", fill);
|
|
5635
|
-
const trySpan = createSVGElement2("tspan");
|
|
5636
|
-
trySpan.setAttribute("font-weight", "500");
|
|
5637
|
-
trySpan.textContent = "try";
|
|
5638
|
-
text.appendChild(trySpan);
|
|
5639
|
-
const openDataSpan = createSVGElement2("tspan");
|
|
5640
|
-
openDataSpan.setAttribute("font-weight", "600");
|
|
5641
|
-
openDataSpan.setAttribute("font-size", String(BRAND_LARGE));
|
|
5642
|
-
openDataSpan.textContent = "OpenData";
|
|
5643
|
-
text.appendChild(openDataSpan);
|
|
5644
|
-
const aiSpan = createSVGElement2("tspan");
|
|
5645
|
-
aiSpan.setAttribute("font-weight", "500");
|
|
5646
|
-
aiSpan.textContent = ".ai";
|
|
5647
|
-
text.appendChild(aiSpan);
|
|
5648
|
-
a2.appendChild(text);
|
|
5649
|
-
parent.appendChild(a2);
|
|
5650
|
-
}
|
|
5651
|
-
|
|
5652
|
-
// src/renderers/chrome.ts
|
|
5653
|
-
import { estimateTextWidth as estimateTextWidth3, textAscent as textAscent4, wrapText } from "@opendata-ai/openchart-core";
|
|
5654
|
-
function renderChromeElement(parent, element, className, chromeKey, measureText, uppercase = false) {
|
|
5655
|
-
const text = createSVGElement2("text");
|
|
5656
|
-
setAttrs2(text, { x: element.x, y: element.y + textAscent4(element.style.fontSize) });
|
|
5657
|
-
applyTextStyle(text, element.style);
|
|
5658
|
-
text.setAttribute("class", className);
|
|
5659
|
-
text.setAttribute("data-chrome-key", chromeKey);
|
|
5660
|
-
const renderedText = uppercase ? element.text.toUpperCase() : element.text;
|
|
5661
|
-
const lines = wrapText(
|
|
5662
|
-
renderedText,
|
|
5663
|
-
element.style.fontSize,
|
|
5664
|
-
element.style.fontWeight,
|
|
5665
|
-
element.maxWidth,
|
|
5666
|
-
measureText
|
|
5667
|
-
);
|
|
5668
|
-
if (lines.length === 1) {
|
|
5669
|
-
text.textContent = renderedText;
|
|
5670
|
-
} else {
|
|
5671
|
-
const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
|
|
5672
|
-
for (let i = 0; i < lines.length; i++) {
|
|
5673
|
-
const tspan = createSVGElement2("tspan");
|
|
5674
|
-
setAttrs2(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
|
|
5675
|
-
tspan.textContent = lines[i];
|
|
5676
|
-
text.appendChild(tspan);
|
|
5677
|
-
}
|
|
5678
|
-
}
|
|
5679
|
-
parent.appendChild(text);
|
|
5680
|
-
}
|
|
5681
|
-
function renderChrome2(parent, layout) {
|
|
5682
|
-
const g = createSVGElement2("g");
|
|
5683
|
-
g.setAttribute("class", "oc-chrome");
|
|
5684
|
-
const { chrome, measureText } = layout;
|
|
5685
|
-
if (chrome.eyebrow) {
|
|
5686
|
-
const eyebrow = chrome.eyebrow;
|
|
5687
|
-
const dotR = 3;
|
|
5688
|
-
const dotGap = 8;
|
|
5689
|
-
const dotX = eyebrow.x + dotR;
|
|
5690
|
-
const dotY = eyebrow.y + eyebrow.style.fontSize * 0.42;
|
|
5691
|
-
const dot = createSVGElement2("circle");
|
|
5692
|
-
dot.setAttribute("class", "oc-eyebrow-dot");
|
|
5693
|
-
setAttrs2(dot, { cx: dotX, cy: dotY, r: dotR });
|
|
5694
|
-
dot.setAttribute("fill", eyebrow.style.fill ?? "currentColor");
|
|
5695
|
-
g.appendChild(dot);
|
|
5696
|
-
const shifted = {
|
|
5697
|
-
...eyebrow,
|
|
5698
|
-
x: eyebrow.x + dotR * 2 + dotGap
|
|
5699
|
-
};
|
|
5700
|
-
renderChromeElement(g, shifted, "oc-eyebrow", "eyebrow", measureText, true);
|
|
5701
|
-
}
|
|
5702
|
-
if (chrome.title) {
|
|
5703
|
-
renderChromeElement(g, chrome.title, "oc-title", "title", measureText);
|
|
5704
|
-
}
|
|
5705
|
-
if (chrome.subtitle) {
|
|
5706
|
-
renderChromeElement(g, chrome.subtitle, "oc-subtitle", "subtitle", measureText);
|
|
5707
|
-
}
|
|
5708
|
-
const bottomOffset = layout.chrome.bottomAnchorY ?? layout.area.y + layout.area.height;
|
|
5709
|
-
if (chrome.source) {
|
|
5710
|
-
renderChromeElement(
|
|
5711
|
-
g,
|
|
5712
|
-
{ ...chrome.source, y: bottomOffset + chrome.source.y },
|
|
5713
|
-
"oc-source",
|
|
5714
|
-
"source",
|
|
5715
|
-
measureText
|
|
5716
|
-
);
|
|
5717
|
-
}
|
|
5718
|
-
if (chrome.byline) {
|
|
5719
|
-
renderChromeElement(
|
|
5720
|
-
g,
|
|
5721
|
-
{ ...chrome.byline, y: bottomOffset + chrome.byline.y },
|
|
5722
|
-
"oc-byline",
|
|
5723
|
-
"byline",
|
|
5724
|
-
measureText
|
|
5725
|
-
);
|
|
5726
|
-
}
|
|
5727
|
-
if (chrome.footer) {
|
|
5728
|
-
renderChromeElement(
|
|
5729
|
-
g,
|
|
5730
|
-
{ ...chrome.footer, y: bottomOffset + chrome.footer.y },
|
|
5731
|
-
"oc-footer",
|
|
5732
|
-
"footer",
|
|
5733
|
-
measureText
|
|
5734
|
-
);
|
|
5735
|
-
}
|
|
5736
|
-
if (chrome.brand) {
|
|
5737
|
-
const brandY = bottomOffset + chrome.brand.y;
|
|
5738
|
-
renderChromeElement(g, { ...chrome.brand, y: brandY }, "oc-brand", "brand", measureText);
|
|
5739
|
-
const textWidth = estimateTextWidth3(
|
|
5740
|
-
chrome.brand.text,
|
|
5741
|
-
chrome.brand.style.fontSize,
|
|
5742
|
-
chrome.brand.style.fontWeight
|
|
5743
|
-
);
|
|
5744
|
-
const dotX = chrome.brand.x - textWidth - 12;
|
|
5745
|
-
const dotY = brandY + chrome.brand.style.fontSize / 2;
|
|
5746
|
-
const dot = createSVGElement2("circle");
|
|
5747
|
-
dot.setAttribute("class", "oc-brand-dot");
|
|
5748
|
-
setAttrs2(dot, { cx: dotX, cy: dotY, r: 3 });
|
|
5749
|
-
g.appendChild(dot);
|
|
5750
|
-
}
|
|
5751
|
-
parent.appendChild(g);
|
|
5752
|
-
}
|
|
5753
|
-
|
|
5754
|
-
// src/renderers/endpoint-labels.ts
|
|
5755
|
-
var LEADER_STROKE_WIDTH = 1;
|
|
5756
|
-
var LEADER_OPACITY = 0.45;
|
|
5757
|
-
function renderEndpointLabels(parent, layout) {
|
|
5758
|
-
const ep = layout.endpointLabels;
|
|
5759
|
-
if (!ep || ep.entries.length === 0) return;
|
|
5760
|
-
const chartArea = layout.area;
|
|
5761
|
-
const chartRightX = chartArea.x + chartArea.width;
|
|
5762
|
-
const root = createSVGElement2("g");
|
|
5763
|
-
root.setAttribute("class", "oc-endpoint-labels");
|
|
5764
|
-
root.setAttribute("role", "list");
|
|
5765
|
-
root.setAttribute("aria-label", "Endpoint labels");
|
|
5766
|
-
const labelFontSize = ep.labelStyle.fontSize ?? 11;
|
|
5767
|
-
const labelLineHeight = labelFontSize * (ep.labelStyle.lineHeight ?? 1.25);
|
|
5768
|
-
const valueFontSize = ep.valueStyle.fontSize ?? 11;
|
|
5769
|
-
const chipX = ep.bounds.x;
|
|
5770
|
-
const chipWidth = ep.swatchSize;
|
|
5771
|
-
const textX = chipX + chipWidth + ep.gap;
|
|
5772
|
-
for (let i = 0; i < ep.entries.length; i++) {
|
|
5773
|
-
const entry = ep.entries[i];
|
|
5774
|
-
const entryG = createSVGElement2("g");
|
|
5775
|
-
entryG.setAttribute("class", "oc-endpoint-label-entry");
|
|
5776
|
-
entryG.setAttribute("role", "listitem");
|
|
5777
|
-
entryG.setAttribute("data-endpoint-index", String(i));
|
|
5778
|
-
entryG.setAttribute("data-endpoint-key", entry.seriesKey);
|
|
5779
|
-
entryG.setAttribute("aria-label", `${entry.seriesKey}: ${entry.value}`);
|
|
5780
|
-
if (entry.showLeader) {
|
|
5781
|
-
const leader = createSVGElement2("line");
|
|
5782
|
-
leader.setAttribute("class", "oc-endpoint-leader");
|
|
5783
|
-
setAttrs2(leader, {
|
|
5784
|
-
x1: chipX,
|
|
5785
|
-
y1: entry.labelY + labelFontSize / 2,
|
|
5786
|
-
x2: chartRightX,
|
|
5787
|
-
y2: entry.dataY,
|
|
5788
|
-
stroke: entry.color,
|
|
5789
|
-
"stroke-width": LEADER_STROKE_WIDTH,
|
|
5790
|
-
"stroke-opacity": LEADER_OPACITY
|
|
5791
|
-
});
|
|
5792
|
-
entryG.appendChild(leader);
|
|
5793
|
-
}
|
|
5794
|
-
const rowY = entry.labelY + labelFontSize / 2;
|
|
5795
|
-
const line = createSVGElement2("line");
|
|
5796
|
-
line.setAttribute("class", "oc-endpoint-swatch-line");
|
|
5797
|
-
setAttrs2(line, {
|
|
5798
|
-
x1: chipX,
|
|
5799
|
-
y1: rowY,
|
|
5800
|
-
x2: chipX + chipWidth,
|
|
5801
|
-
y2: rowY,
|
|
5802
|
-
stroke: entry.color,
|
|
5803
|
-
"stroke-width": 2,
|
|
5804
|
-
"stroke-linecap": "round"
|
|
5805
|
-
});
|
|
5806
|
-
entryG.appendChild(line);
|
|
5807
|
-
const label = createSVGElement2("text");
|
|
5808
|
-
label.setAttribute("class", "oc-endpoint-label");
|
|
5809
|
-
setAttrs2(label, { x: textX, y: entry.labelY + labelFontSize });
|
|
5810
|
-
applyTextStyle(label, ep.labelStyle);
|
|
5811
|
-
label.style.setProperty("fill", entry.color);
|
|
5812
|
-
if (entry.labelLines.length <= 1) {
|
|
5813
|
-
label.textContent = entry.labelLines[0] ?? entry.seriesKey;
|
|
5814
|
-
} else {
|
|
5815
|
-
for (let li = 0; li < entry.labelLines.length; li++) {
|
|
5816
|
-
const tspan = createSVGElement2("tspan");
|
|
5817
|
-
setAttrs2(tspan, { x: textX, dy: li === 0 ? 0 : labelLineHeight });
|
|
5818
|
-
tspan.textContent = entry.labelLines[li];
|
|
5819
|
-
label.appendChild(tspan);
|
|
5820
|
-
}
|
|
5821
|
-
}
|
|
5822
|
-
entryG.appendChild(label);
|
|
5823
|
-
const lineCount = Math.max(entry.labelLines.length, 1);
|
|
5824
|
-
const valueY = entry.labelY + labelFontSize + (lineCount - 1) * labelLineHeight + ep.valueGap + valueFontSize;
|
|
5825
|
-
const value = createSVGElement2("text");
|
|
5826
|
-
value.setAttribute("class", "oc-endpoint-value");
|
|
5827
|
-
setAttrs2(value, { x: textX, y: valueY });
|
|
5828
|
-
applyTextStyle(value, ep.valueStyle);
|
|
5829
|
-
value.textContent = entry.value;
|
|
5830
|
-
entryG.appendChild(value);
|
|
5831
|
-
if (entry.marker) {
|
|
5832
|
-
const marker = createSVGElement2("circle");
|
|
5833
|
-
marker.setAttribute("class", "oc-endpoint-marker");
|
|
5834
|
-
setAttrs2(marker, {
|
|
5835
|
-
cx: entry.marker.x,
|
|
5836
|
-
cy: entry.marker.y,
|
|
5837
|
-
r: entry.marker.radius,
|
|
5838
|
-
fill: entry.marker.fill,
|
|
5839
|
-
stroke: entry.marker.stroke,
|
|
5840
|
-
"stroke-width": entry.marker.strokeWidth
|
|
5841
|
-
});
|
|
5842
|
-
entryG.appendChild(marker);
|
|
5843
|
-
}
|
|
5844
|
-
root.appendChild(entryG);
|
|
5845
|
-
}
|
|
5846
|
-
parent.appendChild(root);
|
|
5847
|
-
}
|
|
5848
|
-
|
|
5849
|
-
// src/renderers/legend.ts
|
|
5850
|
-
import { estimateTextWidth as estimateTextWidth4 } from "@opendata-ai/openchart-core";
|
|
5851
|
-
function isCategorical(legend) {
|
|
5852
|
-
return !legend.type || legend.type === "categorical";
|
|
5853
|
-
}
|
|
5854
|
-
function renderLegend(parent, legend) {
|
|
5855
|
-
if (!isCategorical(legend) || legend.entries.length === 0) return;
|
|
5856
|
-
const g = createSVGElement2("g");
|
|
5857
|
-
g.setAttribute("class", "oc-legend");
|
|
5858
|
-
g.setAttribute("role", "list");
|
|
5859
|
-
g.setAttribute("aria-label", "Chart legend");
|
|
5860
|
-
const isHorizontal = legend.position === "top" || legend.position === "bottom";
|
|
5861
|
-
const positions = "entryPositions" in legend ? legend.entryPositions : void 0;
|
|
5862
|
-
let offsetX = legend.bounds.x;
|
|
5863
|
-
let offsetY = legend.bounds.y;
|
|
5864
|
-
for (let i = 0; i < legend.entries.length; i++) {
|
|
5865
|
-
const entry = legend.entries[i];
|
|
5866
|
-
const pos = positions?.[i];
|
|
5867
|
-
if (pos) {
|
|
5868
|
-
offsetX = pos.x;
|
|
5869
|
-
offsetY = pos.y;
|
|
5870
|
-
} else if (isHorizontal && i > 0) {
|
|
5871
|
-
const labelWidth = estimateTextWidth4(
|
|
5872
|
-
entry.label,
|
|
5873
|
-
legend.labelStyle.fontSize,
|
|
5874
|
-
legend.labelStyle.fontWeight
|
|
5875
|
-
);
|
|
5876
|
-
const entryWidth = legend.swatchSize + legend.swatchGap + labelWidth + legend.entryGap;
|
|
5877
|
-
if (offsetX + entryWidth > legend.bounds.x + legend.bounds.width) {
|
|
5878
|
-
offsetX = legend.bounds.x;
|
|
5879
|
-
offsetY += legend.swatchSize + 6;
|
|
5880
|
-
}
|
|
5881
|
-
}
|
|
5882
|
-
const entryG = createSVGElement2("g");
|
|
5883
|
-
entryG.setAttribute("class", "oc-legend-entry");
|
|
5884
|
-
entryG.setAttribute("role", "listitem");
|
|
5885
|
-
entryG.setAttribute("data-legend-index", String(i));
|
|
5886
|
-
entryG.setAttribute("data-legend-label", entry.label);
|
|
5887
|
-
if (entry.overflow) {
|
|
5888
|
-
entryG.setAttribute("data-legend-overflow", "true");
|
|
5889
|
-
entryG.setAttribute("aria-label", entry.label);
|
|
5890
|
-
entryG.setAttribute("opacity", "0.5");
|
|
5891
|
-
} else {
|
|
5892
|
-
entryG.setAttribute(
|
|
5893
|
-
"aria-label",
|
|
5894
|
-
`${entry.label}: ${entry.active !== false ? "visible" : "hidden"}`
|
|
5895
|
-
);
|
|
5896
|
-
entryG.setAttribute("style", "cursor: pointer");
|
|
5897
|
-
if (entry.active === false) {
|
|
5898
|
-
entryG.setAttribute("opacity", "0.3");
|
|
5899
|
-
}
|
|
5900
|
-
}
|
|
5901
|
-
const midX = offsetX + legend.swatchSize / 2;
|
|
5902
|
-
const midY = offsetY + legend.swatchSize / 2;
|
|
5903
|
-
if (entry.shape === "circle") {
|
|
5904
|
-
const circle = createSVGElement2("circle");
|
|
5905
|
-
setAttrs2(circle, {
|
|
5906
|
-
cx: midX,
|
|
5907
|
-
cy: midY,
|
|
5908
|
-
r: legend.swatchSize / 2,
|
|
5909
|
-
fill: entry.color
|
|
5910
|
-
});
|
|
5911
|
-
entryG.appendChild(circle);
|
|
5912
|
-
} else if (entry.shape === "line") {
|
|
5913
|
-
const lineWidth = legend.swatchSize;
|
|
5914
|
-
const line = createSVGElement2("line");
|
|
5915
|
-
line.setAttribute("class", "oc-legend-swatch-line");
|
|
5916
|
-
setAttrs2(line, {
|
|
5917
|
-
x1: offsetX,
|
|
5918
|
-
y1: midY,
|
|
5919
|
-
x2: offsetX + lineWidth,
|
|
5920
|
-
y2: midY,
|
|
5921
|
-
stroke: entry.color,
|
|
5922
|
-
"stroke-width": 2,
|
|
5923
|
-
"stroke-linecap": "round"
|
|
5924
|
-
});
|
|
5925
|
-
entryG.appendChild(line);
|
|
5926
|
-
} else {
|
|
5927
|
-
const rectSize = Math.round(legend.swatchSize * 0.6);
|
|
5928
|
-
const rect = createSVGElement2("rect");
|
|
5929
|
-
rect.setAttribute("class", "oc-legend-swatch-rect");
|
|
5930
|
-
setAttrs2(rect, {
|
|
5931
|
-
x: offsetX + (legend.swatchSize - rectSize) / 2,
|
|
5932
|
-
y: midY - rectSize / 2,
|
|
5933
|
-
width: rectSize,
|
|
5934
|
-
height: rectSize,
|
|
5935
|
-
rx: 2,
|
|
5936
|
-
ry: 2,
|
|
5937
|
-
fill: entry.color
|
|
5938
|
-
});
|
|
5939
|
-
entryG.appendChild(rect);
|
|
5940
|
-
}
|
|
5941
|
-
const label = createSVGElement2("text");
|
|
5942
|
-
setAttrs2(label, {
|
|
5943
|
-
x: offsetX + legend.swatchSize + legend.swatchGap,
|
|
5944
|
-
y: offsetY + legend.swatchSize / 2,
|
|
5945
|
-
"dominant-baseline": "central"
|
|
5946
|
-
});
|
|
5947
|
-
applyTextStyle(label, legend.labelStyle);
|
|
5948
|
-
label.textContent = entry.label;
|
|
5949
|
-
entryG.appendChild(label);
|
|
5950
|
-
g.appendChild(entryG);
|
|
5951
|
-
if (!pos) {
|
|
5952
|
-
if (isHorizontal) {
|
|
5953
|
-
const labelWidth = estimateTextWidth4(
|
|
5954
|
-
entry.label,
|
|
5955
|
-
legend.labelStyle.fontSize,
|
|
5956
|
-
legend.labelStyle.fontWeight
|
|
5957
|
-
);
|
|
5958
|
-
const entryWidth = legend.swatchSize + legend.swatchGap + labelWidth + legend.entryGap;
|
|
5959
|
-
offsetX += entryWidth;
|
|
5960
|
-
} else {
|
|
5961
|
-
offsetY += "rowHeight" in legend && legend.rowHeight ? legend.rowHeight : legend.swatchSize + legend.entryGap;
|
|
5962
|
-
}
|
|
5963
|
-
}
|
|
5964
|
-
}
|
|
5965
|
-
parent.appendChild(g);
|
|
5966
|
-
}
|
|
5967
|
-
|
|
5968
|
-
// src/renderers/marks.ts
|
|
5969
|
-
var currentAnimation;
|
|
5970
|
-
var currentGradientMap = /* @__PURE__ */ new Map();
|
|
5971
|
-
function setMarkRenderState(state) {
|
|
5972
|
-
currentAnimation = state.animation;
|
|
5973
|
-
currentGradientMap = state.gradientMap;
|
|
5974
|
-
}
|
|
5975
|
-
function resetMarkRenderState() {
|
|
5976
|
-
currentAnimation = void 0;
|
|
5977
|
-
currentGradientMap = /* @__PURE__ */ new Map();
|
|
5978
|
-
}
|
|
5979
|
-
function stampAnimationAttrs(el, mark, fallbackIndex) {
|
|
5980
|
-
if (!currentAnimation?.enabled) return;
|
|
5981
|
-
const idx = mark.animationIndex ?? fallbackIndex;
|
|
5982
|
-
el.setAttribute("data-animation-index", String(idx));
|
|
5983
|
-
el.style.setProperty("--oc-mark-index", String(idx));
|
|
5984
|
-
}
|
|
5985
|
-
var markRenderers = {};
|
|
5986
|
-
function registerMarkRenderer(type, renderer) {
|
|
5987
|
-
markRenderers[type] = renderer;
|
|
5988
|
-
}
|
|
5989
|
-
function renderLineMark(mark, index2) {
|
|
5990
|
-
const g = createSVGElement2("g");
|
|
5991
|
-
g.setAttribute("data-mark-id", `line-${mark.seriesKey ?? index2}`);
|
|
5992
|
-
g.setAttribute("class", "oc-mark oc-mark-line");
|
|
5993
|
-
stampAnimationAttrs(g, mark, index2);
|
|
5994
|
-
if (mark.points.length > 1) {
|
|
5995
|
-
const path = createSVGElement2("path");
|
|
5996
|
-
const d = mark.path ?? mark.points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ");
|
|
5997
|
-
setAttrs2(path, {
|
|
5998
|
-
d,
|
|
5999
|
-
fill: "none",
|
|
6000
|
-
stroke: mark.stroke,
|
|
6001
|
-
"stroke-width": mark.strokeWidth
|
|
6002
|
-
});
|
|
6003
|
-
if (mark.strokeDasharray) {
|
|
6004
|
-
path.setAttribute("stroke-dasharray", mark.strokeDasharray);
|
|
6005
|
-
}
|
|
6006
|
-
if (mark.opacity != null) {
|
|
6007
|
-
path.setAttribute("opacity", String(mark.opacity));
|
|
6008
|
-
}
|
|
6009
|
-
g.appendChild(path);
|
|
6010
|
-
}
|
|
6011
|
-
if (mark.label?.visible) {
|
|
6012
|
-
const label = createSVGElement2("text");
|
|
6013
|
-
label.setAttribute("class", "oc-mark-label");
|
|
6014
|
-
if (mark.seriesKey) {
|
|
6015
|
-
label.setAttribute("data-series", mark.seriesKey);
|
|
6016
|
-
}
|
|
6017
|
-
setAttrs2(label, { x: mark.label.x, y: mark.label.y });
|
|
6018
|
-
applyTextStyle(label, mark.label.style);
|
|
6019
|
-
label.textContent = mark.label.text;
|
|
6020
|
-
g.appendChild(label);
|
|
6021
|
-
if (mark.label.connector) {
|
|
6022
|
-
const connector = createSVGElement2("line");
|
|
6023
|
-
connector.setAttribute("class", "oc-mark-connector");
|
|
6024
|
-
setAttrs2(connector, {
|
|
6025
|
-
x1: mark.label.connector.from.x,
|
|
6026
|
-
y1: mark.label.connector.from.y,
|
|
6027
|
-
x2: mark.label.connector.to.x,
|
|
6028
|
-
y2: mark.label.connector.to.y,
|
|
6029
|
-
stroke: mark.label.connector.stroke,
|
|
6030
|
-
"stroke-width": 1,
|
|
6031
|
-
"stroke-opacity": 0.5
|
|
6032
|
-
});
|
|
6033
|
-
g.appendChild(connector);
|
|
6034
|
-
}
|
|
6035
|
-
}
|
|
6036
|
-
return g;
|
|
6037
|
-
}
|
|
6038
|
-
function renderAreaMark(mark, index2) {
|
|
6039
|
-
const g = createSVGElement2("g");
|
|
6040
|
-
g.setAttribute("data-mark-id", `area-${mark.seriesKey ?? index2}`);
|
|
6041
|
-
g.setAttribute("class", "oc-mark oc-mark-area");
|
|
6042
|
-
stampAnimationAttrs(g, mark, index2);
|
|
6043
|
-
if (mark.path) {
|
|
6044
|
-
const fill = createSVGElement2("path");
|
|
6045
|
-
setAttrs2(fill, {
|
|
6046
|
-
d: mark.path,
|
|
6047
|
-
fill: resolveMarkFill(mark.fill, currentGradientMap),
|
|
6048
|
-
"fill-opacity": mark.fillOpacity,
|
|
6049
|
-
stroke: "none"
|
|
6050
|
-
});
|
|
6051
|
-
g.appendChild(fill);
|
|
6052
|
-
if (mark.stroke && mark.topPath) {
|
|
6053
|
-
const strokePath = createSVGElement2("path");
|
|
6054
|
-
strokePath.setAttribute("class", "oc-area-top");
|
|
6055
|
-
setAttrs2(strokePath, {
|
|
6056
|
-
d: mark.topPath,
|
|
6057
|
-
fill: "none",
|
|
6058
|
-
stroke: mark.stroke,
|
|
6059
|
-
"stroke-width": mark.strokeWidth ?? 1
|
|
6060
|
-
});
|
|
6061
|
-
g.appendChild(strokePath);
|
|
6062
|
-
}
|
|
6063
|
-
}
|
|
6064
|
-
return g;
|
|
6065
|
-
}
|
|
6066
|
-
function _rectPathWithCorners(mark, sides) {
|
|
6067
|
-
const { x: x3, y: y3, width: w, height: h } = mark;
|
|
6068
|
-
const r = Math.max(0, Math.min(mark.cornerRadius ?? 0, w / 2, h / 2));
|
|
6069
|
-
const tl = sides.tl ? r : 0;
|
|
6070
|
-
const tr = sides.tr ? r : 0;
|
|
6071
|
-
const br = sides.br ? r : 0;
|
|
6072
|
-
const bl = sides.bl ? r : 0;
|
|
6073
|
-
return [
|
|
6074
|
-
`M${x3 + tl},${y3}`,
|
|
6075
|
-
`H${x3 + w - tr}`,
|
|
6076
|
-
tr ? `A${tr},${tr} 0 0 1 ${x3 + w},${y3 + tr}` : "",
|
|
6077
|
-
`V${y3 + h - br}`,
|
|
6078
|
-
br ? `A${br},${br} 0 0 1 ${x3 + w - br},${y3 + h}` : "",
|
|
6079
|
-
`H${x3 + bl}`,
|
|
6080
|
-
bl ? `A${bl},${bl} 0 0 1 ${x3},${y3 + h - bl}` : "",
|
|
6081
|
-
`V${y3 + tl}`,
|
|
6082
|
-
tl ? `A${tl},${tl} 0 0 1 ${x3 + tl},${y3}` : "",
|
|
6083
|
-
"Z"
|
|
6084
|
-
].filter(Boolean).join(" ");
|
|
6085
|
-
}
|
|
6086
|
-
function renderRectMark(mark, index2) {
|
|
6087
|
-
const g = createSVGElement2("g");
|
|
6088
|
-
g.setAttribute("data-mark-id", `rect-${index2}`);
|
|
6089
|
-
g.setAttribute("class", "oc-mark oc-mark-rect");
|
|
6090
|
-
stampAnimationAttrs(g, mark, index2);
|
|
6091
|
-
if (currentAnimation?.enabled && mark.orient === "horizontal") {
|
|
6092
|
-
g.setAttribute("data-orient", "horizontal");
|
|
6093
|
-
}
|
|
6094
|
-
const sides = mark.cornerRadiusSides;
|
|
6095
|
-
const partialCorners = !!sides && (!sides.tl || !sides.tr || !sides.br || !sides.bl) && !!mark.cornerRadius;
|
|
6096
|
-
const shapeEl = partialCorners ? createSVGElement2("path") : createSVGElement2("rect");
|
|
6097
|
-
if (partialCorners) {
|
|
6098
|
-
shapeEl.setAttribute("d", _rectPathWithCorners(mark, sides));
|
|
6099
|
-
} else {
|
|
6100
|
-
setAttrs2(shapeEl, {
|
|
6101
|
-
x: mark.x,
|
|
6102
|
-
y: mark.y,
|
|
6103
|
-
width: mark.width,
|
|
6104
|
-
height: mark.height
|
|
6105
|
-
});
|
|
6106
|
-
if (mark.cornerRadius) {
|
|
6107
|
-
setAttrs2(shapeEl, { rx: mark.cornerRadius, ry: mark.cornerRadius });
|
|
6108
|
-
}
|
|
6109
|
-
}
|
|
6110
|
-
shapeEl.setAttribute("fill", String(resolveMarkFill(mark.fill, currentGradientMap)));
|
|
6111
|
-
if (mark.stroke) {
|
|
6112
|
-
shapeEl.setAttribute("stroke", mark.stroke);
|
|
6113
|
-
}
|
|
6114
|
-
if (mark.strokeWidth) {
|
|
6115
|
-
shapeEl.setAttribute("stroke-width", String(mark.strokeWidth));
|
|
6116
|
-
}
|
|
6117
|
-
g.appendChild(shapeEl);
|
|
6118
|
-
return g;
|
|
6119
|
-
}
|
|
6120
|
-
function renderArcMark(mark, index2) {
|
|
6121
|
-
const g = createSVGElement2("g");
|
|
6122
|
-
g.setAttribute("data-mark-id", `arc-${index2}`);
|
|
6123
|
-
g.setAttribute("class", "oc-mark oc-mark-arc");
|
|
6124
|
-
g.setAttribute("transform", `translate(${mark.center.x},${mark.center.y})`);
|
|
6125
|
-
stampAnimationAttrs(g, mark, index2);
|
|
6126
|
-
const path = createSVGElement2("path");
|
|
6127
|
-
setAttrs2(path, {
|
|
6128
|
-
d: mark.path,
|
|
6129
|
-
fill: resolveMarkFill(mark.fill, currentGradientMap),
|
|
6130
|
-
stroke: mark.stroke,
|
|
6131
|
-
"stroke-width": mark.strokeWidth
|
|
6132
|
-
});
|
|
6133
|
-
g.appendChild(path);
|
|
6134
|
-
if (mark.label?.visible) {
|
|
6135
|
-
const label = createSVGElement2("text");
|
|
6136
|
-
label.setAttribute("class", "oc-mark-label");
|
|
6137
|
-
setAttrs2(label, {
|
|
6138
|
-
x: mark.label.x - mark.center.x,
|
|
6139
|
-
y: mark.label.y - mark.center.y
|
|
6140
|
-
});
|
|
6141
|
-
applyTextStyle(label, mark.label.style);
|
|
6142
|
-
label.textContent = mark.label.text;
|
|
6143
|
-
g.appendChild(label);
|
|
6144
|
-
}
|
|
6145
|
-
return g;
|
|
6146
|
-
}
|
|
6147
|
-
function renderPointMark(mark, index2) {
|
|
6148
|
-
const circle = createSVGElement2("circle");
|
|
6149
|
-
circle.setAttribute("data-mark-id", `point-${index2}`);
|
|
6150
|
-
circle.setAttribute("class", "oc-mark oc-mark-point");
|
|
6151
|
-
stampAnimationAttrs(circle, mark, index2);
|
|
6152
|
-
setAttrs2(circle, {
|
|
6153
|
-
cx: mark.cx,
|
|
6154
|
-
cy: mark.cy,
|
|
6155
|
-
r: mark.r,
|
|
6156
|
-
fill: resolveMarkFill(mark.fill, currentGradientMap),
|
|
6157
|
-
stroke: mark.stroke,
|
|
6158
|
-
"stroke-width": mark.strokeWidth
|
|
6159
|
-
});
|
|
6160
|
-
if (mark.fillOpacity !== void 0) {
|
|
6161
|
-
circle.setAttribute("fill-opacity", String(mark.fillOpacity));
|
|
6162
|
-
}
|
|
6163
|
-
return circle;
|
|
6164
|
-
}
|
|
6165
|
-
function renderTextMark(mark, index2) {
|
|
6166
|
-
const text = createSVGElement2("text");
|
|
6167
|
-
text.setAttribute("data-mark-id", `textMark-${index2}`);
|
|
6168
|
-
text.setAttribute("class", "oc-mark oc-mark-text");
|
|
6169
|
-
stampAnimationAttrs(text, mark, index2);
|
|
6170
|
-
setAttrs2(text, {
|
|
6171
|
-
x: mark.x,
|
|
6172
|
-
y: mark.y,
|
|
6173
|
-
"font-size": mark.fontSize,
|
|
6174
|
-
"text-anchor": mark.textAnchor
|
|
6175
|
-
});
|
|
6176
|
-
text.style.setProperty("fill", mark.fill);
|
|
6177
|
-
if (mark.fontWeight) {
|
|
6178
|
-
text.setAttribute("font-weight", String(mark.fontWeight));
|
|
6179
|
-
}
|
|
6180
|
-
if (mark.fontFamily) {
|
|
6181
|
-
text.setAttribute("font-family", mark.fontFamily);
|
|
6182
|
-
}
|
|
6183
|
-
if (mark.angle) {
|
|
6184
|
-
text.setAttribute("transform", `rotate(${mark.angle}, ${mark.x}, ${mark.y})`);
|
|
6185
|
-
}
|
|
6186
|
-
text.textContent = mark.text;
|
|
6187
|
-
return text;
|
|
6188
|
-
}
|
|
6189
|
-
function renderRuleMark(mark, index2) {
|
|
6190
|
-
const line = createSVGElement2("line");
|
|
6191
|
-
line.setAttribute("data-mark-id", `rule-${index2}`);
|
|
6192
|
-
line.setAttribute("class", "oc-mark oc-mark-rule");
|
|
6193
|
-
stampAnimationAttrs(line, mark, index2);
|
|
6194
|
-
setAttrs2(line, {
|
|
6195
|
-
x1: mark.x1,
|
|
6196
|
-
y1: mark.y1,
|
|
6197
|
-
x2: mark.x2,
|
|
6198
|
-
y2: mark.y2,
|
|
6199
|
-
stroke: mark.stroke,
|
|
6200
|
-
"stroke-width": mark.strokeWidth
|
|
6201
|
-
});
|
|
6202
|
-
if (mark.strokeDasharray) {
|
|
6203
|
-
line.setAttribute("stroke-dasharray", mark.strokeDasharray);
|
|
6204
|
-
}
|
|
6205
|
-
if (mark.opacity != null) {
|
|
6206
|
-
line.setAttribute("opacity", String(mark.opacity));
|
|
6207
|
-
}
|
|
6208
|
-
return line;
|
|
6209
|
-
}
|
|
6210
|
-
function renderTickMark(mark, index2) {
|
|
6211
|
-
const line = createSVGElement2("line");
|
|
6212
|
-
line.setAttribute("data-mark-id", `tick-${index2}`);
|
|
6213
|
-
line.setAttribute("class", "oc-mark oc-mark-tick");
|
|
6214
|
-
stampAnimationAttrs(line, mark, index2);
|
|
6215
|
-
const half = mark.length / 2;
|
|
6216
|
-
if (mark.orient === "vertical") {
|
|
6217
|
-
setAttrs2(line, {
|
|
6218
|
-
x1: mark.x,
|
|
6219
|
-
y1: mark.y - half,
|
|
6220
|
-
x2: mark.x,
|
|
6221
|
-
y2: mark.y + half,
|
|
6222
|
-
stroke: mark.stroke,
|
|
6223
|
-
"stroke-width": mark.strokeWidth
|
|
6224
|
-
});
|
|
6225
|
-
} else {
|
|
6226
|
-
setAttrs2(line, {
|
|
6227
|
-
x1: mark.x - half,
|
|
6228
|
-
y1: mark.y,
|
|
6229
|
-
x2: mark.x + half,
|
|
6230
|
-
y2: mark.y,
|
|
6231
|
-
stroke: mark.stroke,
|
|
6232
|
-
"stroke-width": mark.strokeWidth
|
|
6233
|
-
});
|
|
6234
|
-
}
|
|
6235
|
-
if (mark.opacity != null) {
|
|
6236
|
-
line.setAttribute("opacity", String(mark.opacity));
|
|
6237
|
-
}
|
|
6238
|
-
return line;
|
|
6239
|
-
}
|
|
6240
|
-
registerMarkRenderer("line", renderLineMark);
|
|
6241
|
-
registerMarkRenderer("area", renderAreaMark);
|
|
6242
|
-
registerMarkRenderer("rect", renderRectMark);
|
|
6243
|
-
registerMarkRenderer("arc", renderArcMark);
|
|
6244
|
-
registerMarkRenderer("point", renderPointMark);
|
|
6245
|
-
registerMarkRenderer("textMark", renderTextMark);
|
|
6246
|
-
registerMarkRenderer("rule", renderRuleMark);
|
|
6247
|
-
registerMarkRenderer("tick", renderTickMark);
|
|
6248
|
-
function getMarkSeries(mark) {
|
|
6249
|
-
if (mark.type === "line" || mark.type === "area") {
|
|
6250
|
-
return mark.seriesKey;
|
|
6251
|
-
}
|
|
6252
|
-
if (mark.type === "arc") {
|
|
6253
|
-
return mark.aria.label?.split(":")[0]?.trim();
|
|
6254
|
-
}
|
|
6255
|
-
if (mark.aria?.label) {
|
|
6256
|
-
const beforeColon = mark.aria.label.split(":")[0]?.trim();
|
|
6257
|
-
if (beforeColon) return beforeColon;
|
|
6258
|
-
}
|
|
6259
|
-
return void 0;
|
|
6260
|
-
}
|
|
6261
|
-
function renderMarks(parent, layout) {
|
|
6262
|
-
const g = createSVGElement2("g");
|
|
6263
|
-
g.setAttribute("class", "oc-marks");
|
|
6264
|
-
for (let i = 0; i < layout.marks.length; i++) {
|
|
6265
|
-
const mark = layout.marks[i];
|
|
6266
|
-
const renderer = markRenderers[mark.type];
|
|
6267
|
-
if (!renderer) continue;
|
|
6268
|
-
const el = renderer(mark, i);
|
|
6269
|
-
if (mark.aria?.decorative) {
|
|
6270
|
-
el.setAttribute("aria-hidden", "true");
|
|
6271
|
-
} else if (mark.aria?.label) {
|
|
6272
|
-
el.setAttribute("aria-label", mark.aria.label);
|
|
6273
|
-
}
|
|
6274
|
-
const series = getMarkSeries(mark);
|
|
6275
|
-
if (series) {
|
|
6276
|
-
el.setAttribute("data-series", series);
|
|
6277
|
-
}
|
|
6278
|
-
if (currentAnimation?.enabled && mark.type === "rect") {
|
|
6279
|
-
const rect = mark;
|
|
6280
|
-
if (rect.stackGroup && rect.stackPos !== void 0) {
|
|
6281
|
-
el.setAttribute("data-stack-pos", String(rect.stackPos));
|
|
6282
|
-
el.style.setProperty(
|
|
6283
|
-
"--oc-stack-pos",
|
|
6284
|
-
String(rect.stackPos)
|
|
6285
|
-
);
|
|
6286
|
-
}
|
|
6287
|
-
}
|
|
6288
|
-
g.appendChild(el);
|
|
6289
|
-
}
|
|
6290
|
-
parent.appendChild(g);
|
|
6291
|
-
let labelsGroup;
|
|
6292
|
-
for (let i = 0; i < layout.marks.length; i++) {
|
|
6293
|
-
const mark = layout.marks[i];
|
|
6294
|
-
if (mark.type !== "rect") continue;
|
|
6295
|
-
const rect = mark;
|
|
6296
|
-
if (!rect.label?.visible) continue;
|
|
6297
|
-
if (!labelsGroup) {
|
|
6298
|
-
labelsGroup = createSVGElement2("g");
|
|
6299
|
-
labelsGroup.setAttribute("class", "oc-mark-labels");
|
|
6300
|
-
}
|
|
6301
|
-
const label = createSVGElement2("text");
|
|
6302
|
-
label.setAttribute("class", "oc-mark-label");
|
|
6303
|
-
setAttrs2(label, { x: rect.label.x, y: rect.label.y });
|
|
6304
|
-
applyTextStyle(label, rect.label.style);
|
|
6305
|
-
label.textContent = rect.label.text;
|
|
6306
|
-
if (currentAnimation?.enabled) {
|
|
6307
|
-
const idx = rect.animationIndex ?? i;
|
|
6308
|
-
label.setAttribute("data-animation-index", String(idx));
|
|
6309
|
-
label.style.setProperty(
|
|
6310
|
-
"--oc-mark-index",
|
|
6311
|
-
String(idx)
|
|
6312
|
-
);
|
|
6313
|
-
}
|
|
6314
|
-
labelsGroup.appendChild(label);
|
|
6315
|
-
}
|
|
6316
|
-
return labelsGroup;
|
|
6317
|
-
}
|
|
6318
|
-
|
|
6319
|
-
// src/renderers/metrics.ts
|
|
6320
|
-
var DELTA_SIZE_RATIO = 12 / 22;
|
|
6321
|
-
function renderMetrics(parent, layout) {
|
|
6322
|
-
const bar = layout.metrics;
|
|
6323
|
-
if (!bar || bar.cells.length === 0) return;
|
|
6324
|
-
const labelSize = layout.theme.fonts.sizes.metricLabel;
|
|
6325
|
-
const valueSize = layout.theme.fonts.sizes.metricValue;
|
|
6326
|
-
const deltaSize = Math.round(valueSize * DELTA_SIZE_RATIO);
|
|
6327
|
-
const g = createSVGElement2("g");
|
|
6328
|
-
g.setAttribute("class", "oc-metrics");
|
|
6329
|
-
for (const cell of bar.cells) {
|
|
6330
|
-
const label = createSVGElement2("text");
|
|
6331
|
-
label.setAttribute("class", "oc-metric-label");
|
|
6332
|
-
setAttrs2(label, { x: cell.x, y: cell.labelY, "font-size": labelSize });
|
|
6333
|
-
label.textContent = cell.metric.label.toUpperCase();
|
|
6334
|
-
g.appendChild(label);
|
|
6335
|
-
const value = createSVGElement2("text");
|
|
6336
|
-
value.setAttribute("class", "oc-metric-value");
|
|
6337
|
-
setAttrs2(value, { x: cell.x, y: cell.valueY, "font-size": valueSize });
|
|
6338
|
-
value.textContent = cell.metric.value;
|
|
6339
|
-
if (cell.metric.delta) {
|
|
6340
|
-
const delta = createSVGElement2("tspan");
|
|
6341
|
-
const tone = cell.metric.deltaTone ?? "up";
|
|
6342
|
-
delta.setAttribute("class", tone === "down" ? "oc-metric-delta-down" : "oc-metric-delta-up");
|
|
6343
|
-
delta.setAttribute("dx", "8");
|
|
6344
|
-
delta.setAttribute("font-size", String(deltaSize));
|
|
6345
|
-
delta.textContent = cell.metric.delta;
|
|
6346
|
-
value.appendChild(delta);
|
|
6347
|
-
}
|
|
6348
|
-
if (cell.metric.secondary) {
|
|
6349
|
-
const secondary = createSVGElement2("tspan");
|
|
6350
|
-
secondary.setAttribute("class", "oc-metric-secondary");
|
|
6351
|
-
secondary.setAttribute("dx", "6");
|
|
6352
|
-
secondary.setAttribute("font-size", String(deltaSize));
|
|
6353
|
-
secondary.textContent = cell.metric.secondary;
|
|
6354
|
-
value.appendChild(secondary);
|
|
6355
|
-
}
|
|
6356
|
-
g.appendChild(value);
|
|
6357
|
-
}
|
|
6358
|
-
parent.appendChild(g);
|
|
6359
|
-
}
|
|
6360
|
-
|
|
6361
|
-
// src/svg-renderer.ts
|
|
6362
|
-
var EASE_VAR_MAP = {
|
|
6363
|
-
smooth: "var(--oc-ease-smooth)",
|
|
6364
|
-
snappy: "var(--oc-ease-snappy)"
|
|
6365
|
-
};
|
|
6366
|
-
function renderChartSVG(layout, container, opts) {
|
|
6367
|
-
const { width, height } = layout.dimensions;
|
|
6368
|
-
const animation = layout.animation;
|
|
6369
|
-
const svg = createSVGElement2("svg");
|
|
6370
|
-
setAttrs2(svg, {
|
|
6371
|
-
viewBox: `0 0 ${width} ${height}`,
|
|
6372
|
-
xmlns: SVG_NS3,
|
|
6373
|
-
// The SVG spec default is overflow:"hidden", which clips anything a hair
|
|
6374
|
-
// outside the viewBox. We now position all text on the alphabetic/central
|
|
6375
|
-
// baseline (dominant-baseline:hanging was dropped because WebKit computed
|
|
6376
|
-
// it from different metrics), but WebKit/iOS still reports getBBox extents
|
|
6377
|
-
// with a few pixels of slack around tspans, so text touching an edge can
|
|
6378
|
-
// still get clipped. overflow:visible avoids that. Chart marks are already
|
|
6379
|
-
// constrained by a clipPath, so nothing bleeds out.
|
|
6380
|
-
overflow: "visible",
|
|
6381
|
-
// Hint browsers to enable sub-pixel font hinting and kerning for chart text.
|
|
6382
|
-
"text-rendering": "optimizeLegibility"
|
|
6383
|
-
});
|
|
6384
|
-
svg.style.height = `${height}px`;
|
|
6385
|
-
svg.setAttribute("role", layout.a11y.role);
|
|
6386
|
-
svg.setAttribute("aria-label", layout.a11y.altText);
|
|
6387
|
-
if (layout.display === "sparkline") {
|
|
6388
|
-
svg.setAttribute("data-display", "sparkline");
|
|
6389
|
-
}
|
|
6390
|
-
const classes = opts?.animate ? "oc-chart oc-animate" : "oc-chart";
|
|
6391
|
-
svg.setAttribute("class", classes);
|
|
6392
|
-
if (animation?.enabled) {
|
|
6393
|
-
const markCount = layout.marks.length;
|
|
6394
|
-
const stagger = clampStaggerDelay(animation.staggerDelay, markCount);
|
|
6395
|
-
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
6396
|
-
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
6397
|
-
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
6398
|
-
const easeVar = EASE_VAR_MAP[animation.ease] || EASE_VAR_MAP.smooth;
|
|
6399
|
-
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
6400
|
-
let maxSegments = 0;
|
|
6401
|
-
for (const m2 of layout.marks) {
|
|
6402
|
-
if (m2.type === "rect") {
|
|
6403
|
-
const pos = m2.stackPos;
|
|
6404
|
-
if (pos !== void 0 && pos + 1 > maxSegments) {
|
|
6405
|
-
maxSegments = pos + 1;
|
|
6406
|
-
}
|
|
6407
|
-
}
|
|
6408
|
-
}
|
|
6409
|
-
if (maxSegments > 0) {
|
|
6410
|
-
const segDuration = Math.round(animation.duration / maxSegments);
|
|
6411
|
-
svg.style.setProperty("--oc-stack-segment-duration", `${segDuration}ms`);
|
|
6412
|
-
}
|
|
6413
|
-
}
|
|
6414
|
-
if (layout.display !== "sparkline") {
|
|
6415
|
-
const bg = createSVGElement2("rect");
|
|
6416
|
-
setAttrs2(bg, {
|
|
6417
|
-
x: 0,
|
|
6418
|
-
y: 0,
|
|
6419
|
-
width,
|
|
6420
|
-
height,
|
|
6421
|
-
fill: layout.theme.colors.background
|
|
6422
|
-
});
|
|
6423
|
-
svg.appendChild(bg);
|
|
6424
|
-
}
|
|
6425
|
-
const clipId = nextSvgId("oc-clip");
|
|
6426
|
-
const defs = createSVGElement2("defs");
|
|
6427
|
-
const clipPath = createSVGElement2("clipPath");
|
|
6428
|
-
clipPath.setAttribute("id", clipId);
|
|
6429
|
-
const maxPointR = layout.marks.reduce(
|
|
6430
|
-
(max, m2) => m2.type === "point" && m2.r ? Math.max(max, m2.r) : max,
|
|
6431
|
-
0
|
|
6432
|
-
);
|
|
6433
|
-
const clipPad = Math.max(maxPointR, 2);
|
|
6434
|
-
const clipRect = createSVGElement2("rect");
|
|
6435
|
-
setAttrs2(clipRect, {
|
|
6436
|
-
x: 0,
|
|
6437
|
-
y: layout.area.y - clipPad,
|
|
6438
|
-
width,
|
|
6439
|
-
height: layout.area.height + clipPad * 2
|
|
6440
|
-
});
|
|
6441
|
-
clipPath.appendChild(clipRect);
|
|
6442
|
-
defs.appendChild(clipPath);
|
|
6443
|
-
const gradientMap = buildGradientDefs(layout.marks, defs);
|
|
6444
|
-
svg.appendChild(defs);
|
|
6445
|
-
setMarkRenderState({ animation, gradientMap });
|
|
6446
|
-
try {
|
|
6447
|
-
renderAxes(svg, layout);
|
|
6448
|
-
const clippedGroup = createSVGElement2("g");
|
|
6449
|
-
clippedGroup.setAttribute("clip-path", `url(#${clipId})`);
|
|
6450
|
-
const markLabelsOverlay = renderMarks(clippedGroup, layout);
|
|
6451
|
-
const hasLineOrAreaWithDataPoints = layout.marks.some(
|
|
6452
|
-
(m2) => (m2.type === "line" || m2.type === "area") && m2.dataPoints && m2.dataPoints.length > 0
|
|
6453
|
-
);
|
|
6454
|
-
if (hasLineOrAreaWithDataPoints) {
|
|
6455
|
-
const pointEls = clippedGroup.querySelectorAll("circle.oc-mark-point");
|
|
6456
|
-
for (const el of pointEls) {
|
|
6457
|
-
el.setAttribute("pointer-events", "none");
|
|
6458
|
-
}
|
|
6459
|
-
const overlay = createSVGElement2("rect");
|
|
6460
|
-
setAttrs2(overlay, {
|
|
6461
|
-
x: layout.area.x,
|
|
6462
|
-
y: layout.area.y,
|
|
6463
|
-
width: layout.area.width,
|
|
6464
|
-
height: layout.area.height,
|
|
6465
|
-
fill: "transparent"
|
|
6466
|
-
});
|
|
6467
|
-
overlay.setAttribute("class", "oc-voronoi-overlay");
|
|
6468
|
-
overlay.setAttribute("data-voronoi-overlay", "true");
|
|
6469
|
-
clippedGroup.appendChild(overlay);
|
|
6470
|
-
if (opts?.crosshair) {
|
|
6471
|
-
const crosshairLine = createSVGElement2("line");
|
|
6472
|
-
crosshairLine.setAttribute("data-crosshair", "true");
|
|
6473
|
-
crosshairLine.setAttribute("class", "oc-crosshair");
|
|
6474
|
-
setAttrs2(crosshairLine, {
|
|
6475
|
-
x1: 0,
|
|
6476
|
-
y1: layout.area.y,
|
|
6477
|
-
x2: 0,
|
|
6478
|
-
y2: layout.area.y + layout.area.height,
|
|
6479
|
-
stroke: layout.theme.colors.axis,
|
|
6480
|
-
"stroke-opacity": "0.4",
|
|
6481
|
-
"stroke-dasharray": "3,3",
|
|
6482
|
-
"stroke-width": "1",
|
|
6483
|
-
"pointer-events": "none"
|
|
6484
|
-
});
|
|
6485
|
-
crosshairLine.style.display = "none";
|
|
6486
|
-
clippedGroup.appendChild(crosshairLine);
|
|
6487
|
-
}
|
|
6488
|
-
const dotsGroup = createSVGElement2("g");
|
|
6489
|
-
dotsGroup.setAttribute("data-snap-dots", "true");
|
|
6490
|
-
dotsGroup.setAttribute("class", "oc-snap-dots");
|
|
6491
|
-
dotsGroup.setAttribute("pointer-events", "none");
|
|
6492
|
-
clippedGroup.appendChild(dotsGroup);
|
|
6493
|
-
}
|
|
6494
|
-
svg.appendChild(clippedGroup);
|
|
6495
|
-
if (markLabelsOverlay) {
|
|
6496
|
-
svg.appendChild(markLabelsOverlay);
|
|
6497
|
-
}
|
|
6498
|
-
renderAnnotations(svg, layout);
|
|
6499
|
-
renderEndpointLabels(svg, layout);
|
|
6500
|
-
const epEntries = layout.endpointLabels?.entries ?? [];
|
|
6501
|
-
if (epEntries.length > 0) {
|
|
6502
|
-
const pointEls = clippedGroup.querySelectorAll("circle.oc-mark-point");
|
|
6503
|
-
for (const entry of epEntries) {
|
|
6504
|
-
if (!entry.marker) continue;
|
|
6505
|
-
const mx = entry.marker.dataX;
|
|
6506
|
-
const my = entry.marker.y;
|
|
6507
|
-
for (const el of pointEls) {
|
|
6508
|
-
const cx = Number(el.getAttribute("cx"));
|
|
6509
|
-
const cy = Number(el.getAttribute("cy"));
|
|
6510
|
-
if (Math.abs(cx - mx) < 0.5 && Math.abs(cy - my) < 0.5) {
|
|
6511
|
-
el.setAttribute("opacity", "0");
|
|
6512
|
-
}
|
|
6513
|
-
}
|
|
6514
|
-
}
|
|
6515
|
-
}
|
|
6516
|
-
renderLegend(svg, layout.legend);
|
|
6517
|
-
renderChrome2(svg, layout);
|
|
6518
|
-
renderMetrics(svg, layout);
|
|
6519
|
-
if (layout.watermark && !layout.chrome.brand) {
|
|
6520
|
-
renderBrand(svg, layout);
|
|
6521
|
-
}
|
|
6522
|
-
} finally {
|
|
6523
|
-
resetMarkRenderState();
|
|
6524
|
-
}
|
|
6525
|
-
container.appendChild(svg);
|
|
6526
|
-
return svg;
|
|
6527
|
-
}
|
|
6528
|
-
|
|
6529
|
-
// src/text-edit-overlay.ts
|
|
6530
|
-
function getScale(svg) {
|
|
6531
|
-
const viewBox = svg.viewBox?.baseVal;
|
|
6532
|
-
const svgRect = svg.getBoundingClientRect();
|
|
6533
|
-
return {
|
|
6534
|
-
scaleX: viewBox?.width && svgRect.width ? svgRect.width / viewBox.width : 1,
|
|
6535
|
-
scaleY: viewBox?.height && svgRect.height ? svgRect.height / viewBox.height : 1
|
|
6536
|
-
};
|
|
6537
|
-
}
|
|
6538
|
-
function getTextStyles(targetElement, scale) {
|
|
6539
|
-
const computed = window.getComputedStyle(targetElement);
|
|
6540
|
-
const svgFontSize = parseFloat(
|
|
6541
|
-
targetElement.getAttribute("font-size") ?? computed.fontSize ?? "14"
|
|
6542
|
-
);
|
|
6543
|
-
const pixelFontSize = svgFontSize * scale.scaleY;
|
|
6544
|
-
const fontFamily = targetElement.getAttribute("font-family") ?? computed.fontFamily ?? "inherit";
|
|
6545
|
-
const fontWeight = targetElement.getAttribute("font-weight") ?? computed.fontWeight ?? "400";
|
|
6546
|
-
const fill = targetElement.style.getPropertyValue("fill") || targetElement.getAttribute("fill") || computed.color || "#000";
|
|
6547
|
-
const textAnchor = targetElement.getAttribute("text-anchor") ?? "start";
|
|
6548
|
-
let textAlign = "left";
|
|
6549
|
-
if (textAnchor === "middle") textAlign = "center";
|
|
6550
|
-
else if (textAnchor === "end") textAlign = "right";
|
|
6551
|
-
return {
|
|
6552
|
-
fontFamily,
|
|
6553
|
-
fontSize: `${pixelFontSize}px`,
|
|
6554
|
-
fontWeight,
|
|
6555
|
-
color: fill,
|
|
6556
|
-
textAlign,
|
|
6557
|
-
lineHeight: "1.3"
|
|
6558
|
-
};
|
|
6559
|
-
}
|
|
6560
|
-
function computePosition2(targetElement, svg, container) {
|
|
6561
|
-
const bbox = targetElement.getBBox();
|
|
6562
|
-
const scale = getScale(svg);
|
|
6563
|
-
const svgRect = svg.getBoundingClientRect();
|
|
6564
|
-
const containerRect = container.getBoundingClientRect();
|
|
6565
|
-
const padding = 4;
|
|
6566
|
-
const left = bbox.x * scale.scaleX + (svgRect.left - containerRect.left) - padding;
|
|
6567
|
-
const top = bbox.y * scale.scaleY + (svgRect.top - containerRect.top) - padding;
|
|
6568
|
-
const width = bbox.width * scale.scaleX + padding * 2;
|
|
6569
|
-
const height = bbox.height * scale.scaleY + padding * 2;
|
|
6570
|
-
return {
|
|
6571
|
-
top: Math.max(0, top),
|
|
6572
|
-
left: Math.max(0, left),
|
|
6573
|
-
width: Math.max(width, 60),
|
|
6574
|
-
height: Math.max(height, 24)
|
|
6575
|
-
};
|
|
6576
|
-
}
|
|
6577
|
-
function createTextEditOverlay(config) {
|
|
6578
|
-
const { container, svg, targetElement, currentText, onCommit, onCancel } = config;
|
|
6579
|
-
let destroyed = false;
|
|
6580
|
-
const originalOpacity = targetElement.getAttribute("opacity");
|
|
6581
|
-
targetElement.setAttribute("opacity", "0");
|
|
6582
|
-
const scale = getScale(svg);
|
|
6583
|
-
const styles = getTextStyles(targetElement, scale);
|
|
6584
|
-
const position = computePosition2(targetElement, svg, container);
|
|
6585
|
-
const textarea = document.createElement("textarea");
|
|
6586
|
-
textarea.value = currentText;
|
|
6587
|
-
const containerPosition = window.getComputedStyle(container).position;
|
|
6588
|
-
const containerWasStatic = containerPosition === "static";
|
|
6589
|
-
if (containerWasStatic) {
|
|
6590
|
-
container.style.position = "relative";
|
|
6591
|
-
}
|
|
6592
|
-
Object.assign(textarea.style, {
|
|
6593
|
-
position: "absolute",
|
|
6594
|
-
top: `${position.top}px`,
|
|
6595
|
-
left: `${position.left}px`,
|
|
6596
|
-
width: `${position.width}px`,
|
|
6597
|
-
minHeight: `${position.height}px`,
|
|
6598
|
-
fontFamily: styles.fontFamily,
|
|
6599
|
-
fontSize: styles.fontSize,
|
|
6600
|
-
fontWeight: styles.fontWeight,
|
|
6601
|
-
color: styles.color,
|
|
6602
|
-
textAlign: styles.textAlign,
|
|
6603
|
-
lineHeight: styles.lineHeight,
|
|
6604
|
-
padding: "2px 4px",
|
|
6605
|
-
margin: "0",
|
|
6606
|
-
border: "1px solid rgba(79, 70, 229, 0.4)",
|
|
6607
|
-
borderRadius: "3px",
|
|
6608
|
-
background: "rgba(255, 255, 255, 0.95)",
|
|
6609
|
-
outline: "none",
|
|
6610
|
-
resize: "none",
|
|
6611
|
-
overflow: "hidden",
|
|
6612
|
-
boxSizing: "border-box",
|
|
6613
|
-
zIndex: "10000",
|
|
6614
|
-
// Remove default textarea appearance
|
|
6615
|
-
WebkitAppearance: "none",
|
|
6616
|
-
appearance: "none"
|
|
6617
|
-
});
|
|
6618
|
-
container.appendChild(textarea);
|
|
6619
|
-
textarea.focus();
|
|
6620
|
-
textarea.select();
|
|
6621
|
-
function destroy() {
|
|
6622
|
-
if (destroyed) return;
|
|
6623
|
-
destroyed = true;
|
|
6624
|
-
if (originalOpacity !== null) {
|
|
6625
|
-
targetElement.setAttribute("opacity", originalOpacity);
|
|
6626
|
-
} else {
|
|
6627
|
-
targetElement.removeAttribute("opacity");
|
|
6628
|
-
}
|
|
6629
|
-
textarea.removeEventListener("keydown", handleKeyDown);
|
|
6630
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
6631
|
-
resizeObserver.disconnect();
|
|
6632
|
-
if (containerWasStatic) {
|
|
6633
|
-
container.style.position = "";
|
|
6634
|
-
}
|
|
6635
|
-
if (textarea.parentNode) {
|
|
6636
|
-
textarea.parentNode.removeChild(textarea);
|
|
6637
|
-
}
|
|
6638
|
-
}
|
|
6639
|
-
function commit() {
|
|
6640
|
-
if (destroyed) return;
|
|
6641
|
-
const newText = textarea.value;
|
|
6642
|
-
destroy();
|
|
6643
|
-
onCommit(newText);
|
|
6644
|
-
}
|
|
6645
|
-
function cancel() {
|
|
6646
|
-
if (destroyed) return;
|
|
6647
|
-
destroy();
|
|
6648
|
-
onCancel();
|
|
6649
|
-
}
|
|
6650
|
-
const handleKeyDown = (e) => {
|
|
6651
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
6652
|
-
e.preventDefault();
|
|
6653
|
-
commit();
|
|
6654
|
-
} else if (e.key === "Escape") {
|
|
6655
|
-
e.preventDefault();
|
|
6656
|
-
cancel();
|
|
6657
|
-
}
|
|
6658
|
-
};
|
|
6659
|
-
textarea.addEventListener("keydown", handleKeyDown);
|
|
6660
|
-
const handleClickOutside = (e) => {
|
|
6661
|
-
if (!textarea.contains(e.target)) {
|
|
6662
|
-
commit();
|
|
6663
|
-
}
|
|
6664
|
-
};
|
|
6665
|
-
requestAnimationFrame(() => {
|
|
6666
|
-
if (!destroyed) {
|
|
6667
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
6668
|
-
}
|
|
6669
|
-
});
|
|
6670
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
6671
|
-
if (destroyed) return;
|
|
6672
|
-
const newPosition = computePosition2(targetElement, svg, container);
|
|
6673
|
-
textarea.style.top = `${newPosition.top}px`;
|
|
6674
|
-
textarea.style.left = `${newPosition.left}px`;
|
|
6675
|
-
textarea.style.width = `${newPosition.width}px`;
|
|
6676
|
-
textarea.style.minHeight = `${newPosition.height}px`;
|
|
6677
|
-
});
|
|
6678
|
-
resizeObserver.observe(container);
|
|
6679
|
-
return { destroy };
|
|
6680
|
-
}
|
|
6681
|
-
|
|
6682
|
-
// src/mount.ts
|
|
6683
|
-
function resolveDarkMode3(mode) {
|
|
6684
|
-
if (mode === "force") return true;
|
|
6685
|
-
if (mode === "off" || mode === void 0) return false;
|
|
6686
|
-
if (typeof window !== "undefined" && window.matchMedia) {
|
|
6687
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
6688
|
-
}
|
|
6689
|
-
return false;
|
|
6690
|
-
}
|
|
6691
|
-
var EDITABLE_HOVER_CSS = `
|
|
6692
|
-
.oc-editable-hover {
|
|
6693
|
-
outline: 1.5px solid rgba(79, 70, 229, 0.35);
|
|
6694
|
-
outline-offset: 2px;
|
|
6695
|
-
border-radius: 2px;
|
|
5189
|
+
var EDITABLE_HOVER_CSS = `
|
|
5190
|
+
.oc-editable-hover {
|
|
5191
|
+
outline: 1.5px solid rgba(79, 70, 229, 0.35);
|
|
5192
|
+
outline-offset: 2px;
|
|
5193
|
+
border-radius: 2px;
|
|
6696
5194
|
}
|
|
6697
5195
|
`;
|
|
6698
5196
|
function makeEditable(svg) {
|
|
@@ -7179,6 +5677,7 @@ function createChart(container, spec, options) {
|
|
|
7179
5677
|
} else {
|
|
7180
5678
|
container.classList.remove("oc-dark");
|
|
7181
5679
|
}
|
|
5680
|
+
stampThemeProperties(container, currentLayout.theme);
|
|
7182
5681
|
if (shouldAnimate && svgElement) {
|
|
7183
5682
|
cleanupAnimations = setupAnimationCleanup(svgElement, () => {
|
|
7184
5683
|
cleanupAnimations = null;
|
|
@@ -7221,6 +5720,27 @@ function createChart(container, spec, options) {
|
|
|
7221
5720
|
}
|
|
7222
5721
|
render();
|
|
7223
5722
|
}
|
|
5723
|
+
function setHighlight(values) {
|
|
5724
|
+
if (destroyed) return;
|
|
5725
|
+
if (isLayerSpec(currentSpec) || isGraphSpec(currentSpec))
|
|
5726
|
+
return;
|
|
5727
|
+
const spec2 = currentSpec;
|
|
5728
|
+
const colorEnc = spec2.encoding?.color;
|
|
5729
|
+
if (!colorEnc || typeof colorEnc !== "object" || !("field" in colorEnc)) return;
|
|
5730
|
+
const current = colorEnc.highlight;
|
|
5731
|
+
if (!values?.length && !current) return;
|
|
5732
|
+
const updatedColor = { ...colorEnc };
|
|
5733
|
+
if (values && values.length > 0) {
|
|
5734
|
+
updatedColor.highlight = values;
|
|
5735
|
+
} else {
|
|
5736
|
+
delete updatedColor.highlight;
|
|
5737
|
+
}
|
|
5738
|
+
currentSpec = {
|
|
5739
|
+
...spec2,
|
|
5740
|
+
encoding: { ...spec2.encoding, color: updatedColor }
|
|
5741
|
+
};
|
|
5742
|
+
render();
|
|
5743
|
+
}
|
|
7224
5744
|
function doExport(format, exportOptions) {
|
|
7225
5745
|
if (!svgElement) {
|
|
7226
5746
|
throw new Error("Chart is not rendered yet");
|
|
@@ -7331,7 +5851,8 @@ function createChart(container, spec, options) {
|
|
|
7331
5851
|
},
|
|
7332
5852
|
get isEditing() {
|
|
7333
5853
|
return isTextEditingActive;
|
|
7334
|
-
}
|
|
5854
|
+
},
|
|
5855
|
+
setHighlight
|
|
7335
5856
|
};
|
|
7336
5857
|
}
|
|
7337
5858
|
|
|
@@ -7660,30 +6181,30 @@ import { compileSankey } from "@opendata-ai/openchart-engine";
|
|
|
7660
6181
|
|
|
7661
6182
|
// src/sankey-renderer.ts
|
|
7662
6183
|
import {
|
|
7663
|
-
BRAND_FONT_SIZE as
|
|
7664
|
-
BRAND_MIN_WIDTH as
|
|
7665
|
-
estimateTextWidth as
|
|
7666
|
-
textAscent as
|
|
7667
|
-
wrapText
|
|
6184
|
+
BRAND_FONT_SIZE as BRAND_FONT_SIZE2,
|
|
6185
|
+
BRAND_MIN_WIDTH as BRAND_MIN_WIDTH2,
|
|
6186
|
+
estimateTextWidth as estimateTextWidth2,
|
|
6187
|
+
textAscent as textAscent2,
|
|
6188
|
+
wrapText
|
|
7668
6189
|
} from "@opendata-ai/openchart-core";
|
|
7669
|
-
import { clampStaggerDelay
|
|
7670
|
-
var
|
|
7671
|
-
var
|
|
7672
|
-
var
|
|
7673
|
-
var
|
|
6190
|
+
import { clampStaggerDelay } from "@opendata-ai/openchart-engine";
|
|
6191
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
6192
|
+
var XLINK_NS2 = "http://www.w3.org/1999/xlink";
|
|
6193
|
+
var BRAND_URL2 = "https://tryopendata.ai";
|
|
6194
|
+
var EASE_VAR_MAP = {
|
|
7674
6195
|
smooth: "var(--oc-ease-smooth)",
|
|
7675
6196
|
snappy: "var(--oc-ease-snappy)"
|
|
7676
6197
|
};
|
|
7677
|
-
function
|
|
7678
|
-
return document.createElementNS(
|
|
6198
|
+
function createSVGElement2(tag) {
|
|
6199
|
+
return document.createElementNS(SVG_NS2, tag);
|
|
7679
6200
|
}
|
|
7680
|
-
function
|
|
6201
|
+
function setAttrs2(el, attrs) {
|
|
7681
6202
|
for (const [key, value] of Object.entries(attrs)) {
|
|
7682
6203
|
el.setAttribute(key, String(value));
|
|
7683
6204
|
}
|
|
7684
6205
|
}
|
|
7685
|
-
function
|
|
7686
|
-
|
|
6206
|
+
function applyTextStyle(el, style) {
|
|
6207
|
+
setAttrs2(el, {
|
|
7687
6208
|
"font-family": style.fontFamily,
|
|
7688
6209
|
"font-size": style.fontSize,
|
|
7689
6210
|
"font-weight": style.fontWeight
|
|
@@ -7699,19 +6220,19 @@ function applyTextStyle2(el, style) {
|
|
|
7699
6220
|
el.setAttribute("font-variant", style.fontVariant);
|
|
7700
6221
|
}
|
|
7701
6222
|
}
|
|
7702
|
-
function
|
|
6223
|
+
function stampAnimationAttrs(el, mark, fallbackIndex, animation) {
|
|
7703
6224
|
if (!animation?.enabled) return;
|
|
7704
6225
|
const idx = mark.animationIndex ?? fallbackIndex;
|
|
7705
6226
|
el.setAttribute("data-animation-index", String(idx));
|
|
7706
6227
|
el.style.setProperty("--oc-mark-index", String(idx));
|
|
7707
6228
|
}
|
|
7708
|
-
function
|
|
7709
|
-
const text =
|
|
7710
|
-
|
|
7711
|
-
|
|
6229
|
+
function renderChromeElement(parent, element, className, chromeKey, measureText) {
|
|
6230
|
+
const text = createSVGElement2("text");
|
|
6231
|
+
setAttrs2(text, { x: element.x, y: element.y + textAscent2(element.style.fontSize) });
|
|
6232
|
+
applyTextStyle(text, element.style);
|
|
7712
6233
|
text.setAttribute("class", className);
|
|
7713
6234
|
text.setAttribute("data-chrome-key", chromeKey);
|
|
7714
|
-
const lines =
|
|
6235
|
+
const lines = wrapText(
|
|
7715
6236
|
element.text,
|
|
7716
6237
|
element.style.fontSize,
|
|
7717
6238
|
element.style.fontWeight,
|
|
@@ -7723,27 +6244,27 @@ function renderChromeElement2(parent, element, className, chromeKey, measureText
|
|
|
7723
6244
|
} else {
|
|
7724
6245
|
const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
|
|
7725
6246
|
for (let i = 0; i < lines.length; i++) {
|
|
7726
|
-
const tspan =
|
|
7727
|
-
|
|
6247
|
+
const tspan = createSVGElement2("tspan");
|
|
6248
|
+
setAttrs2(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
|
|
7728
6249
|
tspan.textContent = lines[i];
|
|
7729
6250
|
text.appendChild(tspan);
|
|
7730
6251
|
}
|
|
7731
6252
|
}
|
|
7732
6253
|
parent.appendChild(text);
|
|
7733
6254
|
}
|
|
7734
|
-
function
|
|
7735
|
-
const g =
|
|
6255
|
+
function renderChrome2(parent, layout) {
|
|
6256
|
+
const g = createSVGElement2("g");
|
|
7736
6257
|
g.setAttribute("class", "oc-chrome");
|
|
7737
6258
|
const { chrome, measureText } = layout;
|
|
7738
6259
|
if (chrome.title) {
|
|
7739
|
-
|
|
6260
|
+
renderChromeElement(g, chrome.title, "oc-title", "title", measureText);
|
|
7740
6261
|
}
|
|
7741
6262
|
if (chrome.subtitle) {
|
|
7742
|
-
|
|
6263
|
+
renderChromeElement(g, chrome.subtitle, "oc-subtitle", "subtitle", measureText);
|
|
7743
6264
|
}
|
|
7744
6265
|
const bottomOffset = layout.area.y + layout.area.height;
|
|
7745
6266
|
if (chrome.source) {
|
|
7746
|
-
|
|
6267
|
+
renderChromeElement(
|
|
7747
6268
|
g,
|
|
7748
6269
|
{ ...chrome.source, y: bottomOffset + chrome.source.y },
|
|
7749
6270
|
"oc-source",
|
|
@@ -7752,7 +6273,7 @@ function renderChrome3(parent, layout) {
|
|
|
7752
6273
|
);
|
|
7753
6274
|
}
|
|
7754
6275
|
if (chrome.byline) {
|
|
7755
|
-
|
|
6276
|
+
renderChromeElement(
|
|
7756
6277
|
g,
|
|
7757
6278
|
{ ...chrome.byline, y: bottomOffset + chrome.byline.y },
|
|
7758
6279
|
"oc-byline",
|
|
@@ -7761,7 +6282,7 @@ function renderChrome3(parent, layout) {
|
|
|
7761
6282
|
);
|
|
7762
6283
|
}
|
|
7763
6284
|
if (chrome.footer) {
|
|
7764
|
-
|
|
6285
|
+
renderChromeElement(
|
|
7765
6286
|
g,
|
|
7766
6287
|
{ ...chrome.footer, y: bottomOffset + chrome.footer.y },
|
|
7767
6288
|
"oc-footer",
|
|
@@ -7771,8 +6292,8 @@ function renderChrome3(parent, layout) {
|
|
|
7771
6292
|
}
|
|
7772
6293
|
parent.appendChild(g);
|
|
7773
6294
|
}
|
|
7774
|
-
function
|
|
7775
|
-
if (layout.dimensions.width <
|
|
6295
|
+
function renderBrand(parent, layout) {
|
|
6296
|
+
if (layout.dimensions.width < BRAND_MIN_WIDTH2) return;
|
|
7776
6297
|
const { width } = layout.dimensions;
|
|
7777
6298
|
const padding = layout.theme.spacing.padding;
|
|
7778
6299
|
const rightEdge = width - padding;
|
|
@@ -7781,32 +6302,32 @@ function renderBrand2(parent, layout) {
|
|
|
7781
6302
|
const { chrome } = layout;
|
|
7782
6303
|
const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;
|
|
7783
6304
|
const chromeY = firstBottom ? bottomOffset + firstBottom.y : bottomOffset + layout.theme.spacing.chartToFooter;
|
|
7784
|
-
const a2 =
|
|
7785
|
-
a2.setAttribute("href",
|
|
7786
|
-
a2.setAttributeNS(
|
|
6305
|
+
const a2 = createSVGElement2("a");
|
|
6306
|
+
a2.setAttribute("href", BRAND_URL2);
|
|
6307
|
+
a2.setAttributeNS(XLINK_NS2, "xlink:href", BRAND_URL2);
|
|
7787
6308
|
a2.setAttribute("target", "_blank");
|
|
7788
6309
|
a2.setAttribute("rel", "noopener");
|
|
7789
6310
|
a2.setAttribute("class", "oc-chrome-ref");
|
|
7790
6311
|
const BRAND_LARGE = 16;
|
|
7791
|
-
const text =
|
|
7792
|
-
|
|
6312
|
+
const text = createSVGElement2("text");
|
|
6313
|
+
setAttrs2(text, {
|
|
7793
6314
|
x: rightEdge,
|
|
7794
6315
|
y: chromeY + BRAND_LARGE,
|
|
7795
6316
|
"dominant-baseline": "alphabetic",
|
|
7796
6317
|
"text-anchor": "end",
|
|
7797
6318
|
"font-family": layout.theme.fonts.family,
|
|
7798
|
-
"font-size":
|
|
6319
|
+
"font-size": BRAND_FONT_SIZE2,
|
|
7799
6320
|
"fill-opacity": 0.55
|
|
7800
6321
|
});
|
|
7801
6322
|
text.style.setProperty("fill", fill);
|
|
7802
|
-
const trySpan =
|
|
7803
|
-
|
|
6323
|
+
const trySpan = createSVGElement2("tspan");
|
|
6324
|
+
setAttrs2(trySpan, { "font-weight": 500 });
|
|
7804
6325
|
trySpan.textContent = "try";
|
|
7805
|
-
const openDataSpan =
|
|
7806
|
-
|
|
6326
|
+
const openDataSpan = createSVGElement2("tspan");
|
|
6327
|
+
setAttrs2(openDataSpan, { "font-weight": 600, "font-size": BRAND_LARGE });
|
|
7807
6328
|
openDataSpan.textContent = "OpenData";
|
|
7808
|
-
const aiSpan =
|
|
7809
|
-
|
|
6329
|
+
const aiSpan = createSVGElement2("tspan");
|
|
6330
|
+
setAttrs2(aiSpan, { "font-weight": 500 });
|
|
7810
6331
|
aiSpan.textContent = ".ai";
|
|
7811
6332
|
text.appendChild(trySpan);
|
|
7812
6333
|
text.appendChild(openDataSpan);
|
|
@@ -7814,10 +6335,10 @@ function renderBrand2(parent, layout) {
|
|
|
7814
6335
|
a2.appendChild(text);
|
|
7815
6336
|
parent.appendChild(a2);
|
|
7816
6337
|
}
|
|
7817
|
-
function
|
|
6338
|
+
function renderLegend(parent, legendLayout) {
|
|
7818
6339
|
if (!("entries" in legendLayout) || legendLayout.entries.length === 0) return;
|
|
7819
6340
|
const legend = legendLayout;
|
|
7820
|
-
const g =
|
|
6341
|
+
const g = createSVGElement2("g");
|
|
7821
6342
|
g.setAttribute("class", "oc-legend");
|
|
7822
6343
|
g.setAttribute("role", "list");
|
|
7823
6344
|
g.setAttribute("aria-label", "Chart legend");
|
|
@@ -7827,7 +6348,7 @@ function renderLegend2(parent, legendLayout) {
|
|
|
7827
6348
|
for (let i = 0; i < legend.entries.length; i++) {
|
|
7828
6349
|
const entry = legend.entries[i];
|
|
7829
6350
|
if (isHorizontal && i > 0) {
|
|
7830
|
-
const labelWidth =
|
|
6351
|
+
const labelWidth = estimateTextWidth2(
|
|
7831
6352
|
entry.label,
|
|
7832
6353
|
legend.labelStyle.fontSize,
|
|
7833
6354
|
legend.labelStyle.fontWeight
|
|
@@ -7838,7 +6359,7 @@ function renderLegend2(parent, legendLayout) {
|
|
|
7838
6359
|
offsetY += legend.swatchSize + 6;
|
|
7839
6360
|
}
|
|
7840
6361
|
}
|
|
7841
|
-
const entryG =
|
|
6362
|
+
const entryG = createSVGElement2("g");
|
|
7842
6363
|
entryG.setAttribute("class", "oc-legend-entry");
|
|
7843
6364
|
entryG.setAttribute("role", "listitem");
|
|
7844
6365
|
entryG.setAttribute("data-legend-index", String(i));
|
|
@@ -7857,8 +6378,8 @@ function renderLegend2(parent, legendLayout) {
|
|
|
7857
6378
|
entryG.setAttribute("opacity", "0.3");
|
|
7858
6379
|
}
|
|
7859
6380
|
}
|
|
7860
|
-
const rect =
|
|
7861
|
-
|
|
6381
|
+
const rect = createSVGElement2("rect");
|
|
6382
|
+
setAttrs2(rect, {
|
|
7862
6383
|
x: offsetX,
|
|
7863
6384
|
y: offsetY,
|
|
7864
6385
|
width: legend.swatchSize,
|
|
@@ -7867,18 +6388,18 @@ function renderLegend2(parent, legendLayout) {
|
|
|
7867
6388
|
rx: 2
|
|
7868
6389
|
});
|
|
7869
6390
|
entryG.appendChild(rect);
|
|
7870
|
-
const label =
|
|
7871
|
-
|
|
6391
|
+
const label = createSVGElement2("text");
|
|
6392
|
+
setAttrs2(label, {
|
|
7872
6393
|
x: offsetX + legend.swatchSize + legend.swatchGap,
|
|
7873
6394
|
y: offsetY + legend.swatchSize / 2,
|
|
7874
6395
|
"dominant-baseline": "central"
|
|
7875
6396
|
});
|
|
7876
|
-
|
|
6397
|
+
applyTextStyle(label, legend.labelStyle);
|
|
7877
6398
|
label.textContent = entry.label;
|
|
7878
6399
|
entryG.appendChild(label);
|
|
7879
6400
|
g.appendChild(entryG);
|
|
7880
6401
|
if (isHorizontal) {
|
|
7881
|
-
const labelWidth =
|
|
6402
|
+
const labelWidth = estimateTextWidth2(
|
|
7882
6403
|
entry.label,
|
|
7883
6404
|
legend.labelStyle.fontSize,
|
|
7884
6405
|
legend.labelStyle.fontWeight
|
|
@@ -7906,7 +6427,7 @@ function renderGradientDefs(defs, links, nodePositions) {
|
|
|
7906
6427
|
const link = links[i];
|
|
7907
6428
|
if (link.sourceColor === link.targetColor) continue;
|
|
7908
6429
|
const gradId = `oc-sg-${sanitizeId(link.sourceId)}-${sanitizeId(link.targetId)}-${i}`;
|
|
7909
|
-
const gradient =
|
|
6430
|
+
const gradient = createSVGElement2("linearGradient");
|
|
7910
6431
|
gradient.setAttribute("id", gradId);
|
|
7911
6432
|
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
|
|
7912
6433
|
const sourcePos = nodePositions.get(link.sourceId);
|
|
@@ -7915,21 +6436,21 @@ function renderGradientDefs(defs, links, nodePositions) {
|
|
|
7915
6436
|
const x22 = targetPos ? targetPos.x : 0;
|
|
7916
6437
|
gradient.setAttribute("x1", String(x1));
|
|
7917
6438
|
gradient.setAttribute("x2", String(x22));
|
|
7918
|
-
const stop0 =
|
|
7919
|
-
|
|
6439
|
+
const stop0 = createSVGElement2("stop");
|
|
6440
|
+
setAttrs2(stop0, { offset: "0%", "stop-color": link.sourceColor });
|
|
7920
6441
|
gradient.appendChild(stop0);
|
|
7921
|
-
const stop1 =
|
|
7922
|
-
|
|
6442
|
+
const stop1 = createSVGElement2("stop");
|
|
6443
|
+
setAttrs2(stop1, { offset: "100%", "stop-color": link.targetColor });
|
|
7923
6444
|
gradient.appendChild(stop1);
|
|
7924
6445
|
defs.appendChild(gradient);
|
|
7925
6446
|
}
|
|
7926
6447
|
}
|
|
7927
6448
|
function renderLinks(parent, links, _nodePositions, animation) {
|
|
7928
|
-
const g =
|
|
6449
|
+
const g = createSVGElement2("g");
|
|
7929
6450
|
g.setAttribute("class", "oc-sankey-links");
|
|
7930
6451
|
for (let i = 0; i < links.length; i++) {
|
|
7931
6452
|
const link = links[i];
|
|
7932
|
-
const linkG =
|
|
6453
|
+
const linkG = createSVGElement2("g");
|
|
7933
6454
|
linkG.setAttribute("class", "oc-sankey-link");
|
|
7934
6455
|
linkG.setAttribute("data-mark-id", `link-${link.sourceId}-${link.targetId}-${i}`);
|
|
7935
6456
|
linkG.setAttribute("data-source", link.sourceId);
|
|
@@ -7937,8 +6458,8 @@ function renderLinks(parent, links, _nodePositions, animation) {
|
|
|
7937
6458
|
if (link.aria?.label) {
|
|
7938
6459
|
linkG.setAttribute("aria-label", link.aria.label);
|
|
7939
6460
|
}
|
|
7940
|
-
|
|
7941
|
-
const path =
|
|
6461
|
+
stampAnimationAttrs(linkG, link, i, animation);
|
|
6462
|
+
const path = createSVGElement2("path");
|
|
7942
6463
|
path.setAttribute("d", link.path);
|
|
7943
6464
|
path.setAttribute("stroke", "none");
|
|
7944
6465
|
path.setAttribute("fill-opacity", String(link.fillOpacity));
|
|
@@ -7954,20 +6475,20 @@ function renderLinks(parent, links, _nodePositions, animation) {
|
|
|
7954
6475
|
parent.appendChild(g);
|
|
7955
6476
|
}
|
|
7956
6477
|
function renderNodes(parent, nodes, animation) {
|
|
7957
|
-
const g =
|
|
6478
|
+
const g = createSVGElement2("g");
|
|
7958
6479
|
g.setAttribute("class", "oc-sankey-nodes");
|
|
7959
6480
|
for (let i = 0; i < nodes.length; i++) {
|
|
7960
6481
|
const node = nodes[i];
|
|
7961
|
-
const nodeG =
|
|
6482
|
+
const nodeG = createSVGElement2("g");
|
|
7962
6483
|
nodeG.setAttribute("class", "oc-sankey-node");
|
|
7963
6484
|
nodeG.setAttribute("data-mark-id", `node-${node.nodeId}`);
|
|
7964
6485
|
nodeG.setAttribute("data-node-id", node.nodeId);
|
|
7965
6486
|
if (node.aria?.label) {
|
|
7966
6487
|
nodeG.setAttribute("aria-label", node.aria.label);
|
|
7967
6488
|
}
|
|
7968
|
-
|
|
7969
|
-
const rect =
|
|
7970
|
-
|
|
6489
|
+
stampAnimationAttrs(nodeG, node, i, animation);
|
|
6490
|
+
const rect = createSVGElement2("rect");
|
|
6491
|
+
setAttrs2(rect, {
|
|
7971
6492
|
x: node.x,
|
|
7972
6493
|
y: node.y,
|
|
7973
6494
|
width: node.width,
|
|
@@ -7988,24 +6509,24 @@ function renderNodes(parent, nodes, animation) {
|
|
|
7988
6509
|
parent.appendChild(g);
|
|
7989
6510
|
}
|
|
7990
6511
|
function renderLabels(parent, nodes, measureText) {
|
|
7991
|
-
const g =
|
|
6512
|
+
const g = createSVGElement2("g");
|
|
7992
6513
|
g.setAttribute("class", "oc-sankey-labels");
|
|
7993
6514
|
for (const node of nodes) {
|
|
7994
6515
|
const { label } = node;
|
|
7995
6516
|
if (!label.visible) continue;
|
|
7996
|
-
const text =
|
|
7997
|
-
|
|
7998
|
-
|
|
6517
|
+
const text = createSVGElement2("text");
|
|
6518
|
+
setAttrs2(text, { x: label.x, y: label.y });
|
|
6519
|
+
applyTextStyle(text, label.style);
|
|
7999
6520
|
if (label.maxWidth !== void 0 && label.maxWidth > 0) {
|
|
8000
6521
|
const fontSize = label.style.fontSize ?? 12;
|
|
8001
6522
|
const fontWeight = label.style.fontWeight ?? 400;
|
|
8002
|
-
const lines =
|
|
6523
|
+
const lines = wrapText(label.text, fontSize, fontWeight, label.maxWidth, measureText);
|
|
8003
6524
|
if (lines.length > 1) {
|
|
8004
6525
|
const lineHeight = fontSize * (label.style.lineHeight ?? 1.3);
|
|
8005
6526
|
const totalHeight = (lines.length - 1) * lineHeight;
|
|
8006
6527
|
const startY = label.y - totalHeight / 2;
|
|
8007
6528
|
for (let i = 0; i < lines.length; i++) {
|
|
8008
|
-
const tspan =
|
|
6529
|
+
const tspan = createSVGElement2("tspan");
|
|
8009
6530
|
tspan.setAttribute("x", String(label.x));
|
|
8010
6531
|
tspan.setAttribute("y", String(startY + i * lineHeight));
|
|
8011
6532
|
tspan.textContent = lines[i];
|
|
@@ -8023,10 +6544,10 @@ function renderLabels(parent, nodes, measureText) {
|
|
|
8023
6544
|
}
|
|
8024
6545
|
function renderSankeySVG(layout, animation) {
|
|
8025
6546
|
const { width, height } = layout.dimensions;
|
|
8026
|
-
const svg =
|
|
8027
|
-
|
|
6547
|
+
const svg = createSVGElement2("svg");
|
|
6548
|
+
setAttrs2(svg, {
|
|
8028
6549
|
viewBox: `0 0 ${width} ${height}`,
|
|
8029
|
-
xmlns:
|
|
6550
|
+
xmlns: SVG_NS2,
|
|
8030
6551
|
overflow: "visible"
|
|
8031
6552
|
});
|
|
8032
6553
|
svg.style.height = `${height}px`;
|
|
@@ -8037,16 +6558,16 @@ function renderSankeySVG(layout, animation) {
|
|
|
8037
6558
|
svg.setAttribute("class", classes);
|
|
8038
6559
|
if (animation?.enabled) {
|
|
8039
6560
|
const totalMarks = layout.nodes.length + layout.links.length;
|
|
8040
|
-
const stagger =
|
|
6561
|
+
const stagger = clampStaggerDelay(animation.staggerDelay, totalMarks);
|
|
8041
6562
|
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
8042
6563
|
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
8043
6564
|
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
8044
|
-
const easeVar =
|
|
6565
|
+
const easeVar = EASE_VAR_MAP[animation.ease] || EASE_VAR_MAP.smooth;
|
|
8045
6566
|
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
8046
6567
|
}
|
|
8047
|
-
const bg =
|
|
6568
|
+
const bg = createSVGElement2("rect");
|
|
8048
6569
|
bg.setAttribute("class", "oc-background");
|
|
8049
|
-
|
|
6570
|
+
setAttrs2(bg, {
|
|
8050
6571
|
x: 0,
|
|
8051
6572
|
y: 0,
|
|
8052
6573
|
width,
|
|
@@ -8055,16 +6576,16 @@ function renderSankeySVG(layout, animation) {
|
|
|
8055
6576
|
});
|
|
8056
6577
|
svg.appendChild(bg);
|
|
8057
6578
|
const nodePositions = buildNodePositionMap(layout.nodes);
|
|
8058
|
-
const defs =
|
|
6579
|
+
const defs = createSVGElement2("defs");
|
|
8059
6580
|
renderGradientDefs(defs, layout.links, nodePositions);
|
|
8060
6581
|
svg.appendChild(defs);
|
|
8061
6582
|
renderLinks(svg, layout.links, nodePositions, animation);
|
|
8062
6583
|
renderNodes(svg, layout.nodes, animation);
|
|
8063
6584
|
renderLabels(svg, layout.nodes, layout.measureText);
|
|
8064
|
-
|
|
8065
|
-
|
|
6585
|
+
renderLegend(svg, layout.legend);
|
|
6586
|
+
renderChrome2(svg, layout);
|
|
8066
6587
|
if (layout.watermark) {
|
|
8067
|
-
|
|
6588
|
+
renderBrand(svg, layout);
|
|
8068
6589
|
}
|
|
8069
6590
|
return svg;
|
|
8070
6591
|
}
|
|
@@ -8627,13 +7148,13 @@ import { getBreakpoint } from "@opendata-ai/openchart-core";
|
|
|
8627
7148
|
import { compileTable } from "@opendata-ai/openchart-engine";
|
|
8628
7149
|
|
|
8629
7150
|
// src/table-renderer.ts
|
|
8630
|
-
import { BRAND_FONT_SIZE as
|
|
8631
|
-
import { clampStaggerDelay as
|
|
8632
|
-
var
|
|
7151
|
+
import { BRAND_FONT_SIZE as BRAND_FONT_SIZE3 } from "@opendata-ai/openchart-core";
|
|
7152
|
+
import { clampStaggerDelay as clampStaggerDelay2 } from "@opendata-ai/openchart-engine";
|
|
7153
|
+
var EASE_VAR_MAP2 = {
|
|
8633
7154
|
smooth: "var(--oc-ease-smooth)",
|
|
8634
7155
|
snappy: "var(--oc-ease-snappy)"
|
|
8635
7156
|
};
|
|
8636
|
-
var
|
|
7157
|
+
var BRAND_URL3 = "https://tryopendata.ai";
|
|
8637
7158
|
function renderChromeBlock(layout, position) {
|
|
8638
7159
|
const chrome = layout.chrome;
|
|
8639
7160
|
if (position === "header") {
|
|
@@ -8880,10 +7401,10 @@ function renderTable(layout, container, opts) {
|
|
|
8880
7401
|
const brand = document.createElement("div");
|
|
8881
7402
|
brand.className = "oc-table-ref";
|
|
8882
7403
|
const brandLink = document.createElement("a");
|
|
8883
|
-
brandLink.href =
|
|
7404
|
+
brandLink.href = BRAND_URL3;
|
|
8884
7405
|
brandLink.target = "_blank";
|
|
8885
7406
|
brandLink.rel = "noopener";
|
|
8886
|
-
brandLink.style.cssText = `font-size: ${
|
|
7407
|
+
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"};`;
|
|
8887
7408
|
brandLink.textContent = "tryOpenData.ai";
|
|
8888
7409
|
brand.appendChild(brandLink);
|
|
8889
7410
|
footerRow.appendChild(brand);
|
|
@@ -8894,11 +7415,11 @@ function renderTable(layout, container, opts) {
|
|
|
8894
7415
|
if (opts?.animate && layout.animation?.enabled) {
|
|
8895
7416
|
const anim = layout.animation;
|
|
8896
7417
|
const rowCount = layout.rows.length;
|
|
8897
|
-
const stagger =
|
|
7418
|
+
const stagger = clampStaggerDelay2(anim.staggerDelay, rowCount);
|
|
8898
7419
|
const s = wrapper.style;
|
|
8899
7420
|
s.setProperty("--oc-animation-duration", `${anim.duration}ms`);
|
|
8900
7421
|
s.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
8901
|
-
s.setProperty("--oc-animation-ease",
|
|
7422
|
+
s.setProperty("--oc-animation-ease", EASE_VAR_MAP2[anim.ease] || EASE_VAR_MAP2.smooth);
|
|
8902
7423
|
wrapper.classList.add("oc-animate");
|
|
8903
7424
|
}
|
|
8904
7425
|
container.appendChild(wrapper);
|
|
@@ -9271,345 +7792,6 @@ function createTable(container, spec, options) {
|
|
|
9271
7792
|
|
|
9272
7793
|
// src/tilemap-mount.ts
|
|
9273
7794
|
import { compileTileMap } from "@opendata-ai/openchart-engine";
|
|
9274
|
-
|
|
9275
|
-
// src/tilemap-renderer.ts
|
|
9276
|
-
import { textAscent as textAscent6 } from "@opendata-ai/openchart-core";
|
|
9277
|
-
var SVG_NS5 = "http://www.w3.org/2000/svg";
|
|
9278
|
-
var XLINK_NS4 = "http://www.w3.org/1999/xlink";
|
|
9279
|
-
var BRAND_URL5 = "https://tryopendata.ai";
|
|
9280
|
-
var EASE_VAR_MAP4 = {
|
|
9281
|
-
smooth: "var(--oc-ease-smooth)",
|
|
9282
|
-
snappy: "var(--oc-ease-snappy)"
|
|
9283
|
-
};
|
|
9284
|
-
var gradientIdCounter = 0;
|
|
9285
|
-
function createSVGElement4(tag) {
|
|
9286
|
-
return document.createElementNS(SVG_NS5, tag);
|
|
9287
|
-
}
|
|
9288
|
-
function setAttrs4(el, attrs) {
|
|
9289
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
9290
|
-
el.setAttribute(key, String(value));
|
|
9291
|
-
}
|
|
9292
|
-
}
|
|
9293
|
-
function renderChrome4(parent, layout) {
|
|
9294
|
-
const g = createSVGElement4("g");
|
|
9295
|
-
g.setAttribute("class", "oc-chrome");
|
|
9296
|
-
const { chrome } = layout;
|
|
9297
|
-
const bottomOffset = layout.area.y + layout.area.height;
|
|
9298
|
-
if (chrome.title) {
|
|
9299
|
-
const text = createSVGElement4("text");
|
|
9300
|
-
setAttrs4(text, {
|
|
9301
|
-
x: chrome.title.x,
|
|
9302
|
-
y: chrome.title.y + textAscent6(chrome.title.style.fontSize)
|
|
9303
|
-
});
|
|
9304
|
-
text.setAttribute("class", "oc-title");
|
|
9305
|
-
text.setAttribute("font-family", chrome.title.style.fontFamily);
|
|
9306
|
-
text.setAttribute("font-size", String(chrome.title.style.fontSize));
|
|
9307
|
-
text.setAttribute("font-weight", String(chrome.title.style.fontWeight));
|
|
9308
|
-
text.style.setProperty("fill", chrome.title.style.fill);
|
|
9309
|
-
text.textContent = chrome.title.text;
|
|
9310
|
-
g.appendChild(text);
|
|
9311
|
-
}
|
|
9312
|
-
if (chrome.subtitle) {
|
|
9313
|
-
const text = createSVGElement4("text");
|
|
9314
|
-
setAttrs4(text, {
|
|
9315
|
-
x: chrome.subtitle.x,
|
|
9316
|
-
y: chrome.subtitle.y + textAscent6(chrome.subtitle.style.fontSize)
|
|
9317
|
-
});
|
|
9318
|
-
text.setAttribute("class", "oc-subtitle");
|
|
9319
|
-
text.setAttribute("font-family", chrome.subtitle.style.fontFamily);
|
|
9320
|
-
text.setAttribute("font-size", String(chrome.subtitle.style.fontSize));
|
|
9321
|
-
text.setAttribute("font-weight", String(chrome.subtitle.style.fontWeight));
|
|
9322
|
-
text.style.setProperty(
|
|
9323
|
-
"fill",
|
|
9324
|
-
chrome.subtitle.style.fill
|
|
9325
|
-
);
|
|
9326
|
-
text.textContent = chrome.subtitle.text;
|
|
9327
|
-
g.appendChild(text);
|
|
9328
|
-
}
|
|
9329
|
-
if (chrome.source) {
|
|
9330
|
-
const text = createSVGElement4("text");
|
|
9331
|
-
setAttrs4(text, {
|
|
9332
|
-
x: chrome.source.x,
|
|
9333
|
-
y: bottomOffset + chrome.source.y + textAscent6(chrome.source.style.fontSize)
|
|
9334
|
-
});
|
|
9335
|
-
text.setAttribute("class", "oc-source");
|
|
9336
|
-
text.setAttribute("font-family", chrome.source.style.fontFamily);
|
|
9337
|
-
text.setAttribute("font-size", String(chrome.source.style.fontSize));
|
|
9338
|
-
text.setAttribute("font-weight", String(chrome.source.style.fontWeight));
|
|
9339
|
-
text.style.setProperty(
|
|
9340
|
-
"fill",
|
|
9341
|
-
chrome.source.style.fill
|
|
9342
|
-
);
|
|
9343
|
-
text.textContent = chrome.source.text;
|
|
9344
|
-
g.appendChild(text);
|
|
9345
|
-
}
|
|
9346
|
-
if (chrome.byline) {
|
|
9347
|
-
const text = createSVGElement4("text");
|
|
9348
|
-
setAttrs4(text, {
|
|
9349
|
-
x: chrome.byline.x,
|
|
9350
|
-
y: bottomOffset + chrome.byline.y + textAscent6(chrome.byline.style.fontSize)
|
|
9351
|
-
});
|
|
9352
|
-
text.setAttribute("class", "oc-byline");
|
|
9353
|
-
text.setAttribute("font-family", chrome.byline.style.fontFamily);
|
|
9354
|
-
text.setAttribute("font-size", String(chrome.byline.style.fontSize));
|
|
9355
|
-
text.setAttribute("font-weight", String(chrome.byline.style.fontWeight));
|
|
9356
|
-
text.style.setProperty(
|
|
9357
|
-
"fill",
|
|
9358
|
-
chrome.byline.style.fill
|
|
9359
|
-
);
|
|
9360
|
-
text.textContent = chrome.byline.text;
|
|
9361
|
-
g.appendChild(text);
|
|
9362
|
-
}
|
|
9363
|
-
if (chrome.footer) {
|
|
9364
|
-
const text = createSVGElement4("text");
|
|
9365
|
-
setAttrs4(text, {
|
|
9366
|
-
x: chrome.footer.x,
|
|
9367
|
-
y: bottomOffset + chrome.footer.y + textAscent6(chrome.footer.style.fontSize)
|
|
9368
|
-
});
|
|
9369
|
-
text.setAttribute("class", "oc-footer");
|
|
9370
|
-
text.setAttribute("font-family", chrome.footer.style.fontFamily);
|
|
9371
|
-
text.setAttribute("font-size", String(chrome.footer.style.fontSize));
|
|
9372
|
-
text.setAttribute("font-weight", String(chrome.footer.style.fontWeight));
|
|
9373
|
-
text.style.setProperty(
|
|
9374
|
-
"fill",
|
|
9375
|
-
chrome.footer.style.fill
|
|
9376
|
-
);
|
|
9377
|
-
text.textContent = chrome.footer.text;
|
|
9378
|
-
g.appendChild(text);
|
|
9379
|
-
}
|
|
9380
|
-
parent.appendChild(g);
|
|
9381
|
-
}
|
|
9382
|
-
function renderWatermark2(parent, layout) {
|
|
9383
|
-
if (layout.width < 480) return;
|
|
9384
|
-
const { width, height } = layout;
|
|
9385
|
-
const { theme } = layout;
|
|
9386
|
-
const padding = theme.spacing.padding;
|
|
9387
|
-
const rightEdge = width - padding;
|
|
9388
|
-
const bottomEdge = height - padding;
|
|
9389
|
-
const fill = theme.colors.axis;
|
|
9390
|
-
const a2 = createSVGElement4("a");
|
|
9391
|
-
a2.setAttribute("href", BRAND_URL5);
|
|
9392
|
-
a2.setAttributeNS(XLINK_NS4, "xlink:href", BRAND_URL5);
|
|
9393
|
-
a2.setAttribute("target", "_blank");
|
|
9394
|
-
a2.setAttribute("rel", "noopener");
|
|
9395
|
-
a2.setAttribute("class", "oc-chrome-ref");
|
|
9396
|
-
const text = createSVGElement4("text");
|
|
9397
|
-
setAttrs4(text, {
|
|
9398
|
-
x: rightEdge,
|
|
9399
|
-
y: bottomEdge,
|
|
9400
|
-
"dominant-baseline": "alphabetic",
|
|
9401
|
-
"text-anchor": "end",
|
|
9402
|
-
"font-family": theme.fonts.family,
|
|
9403
|
-
"font-size": 12,
|
|
9404
|
-
"fill-opacity": 0.55
|
|
9405
|
-
});
|
|
9406
|
-
text.style.setProperty("fill", fill);
|
|
9407
|
-
const trySpan = createSVGElement4("tspan");
|
|
9408
|
-
setAttrs4(trySpan, { "font-weight": 500 });
|
|
9409
|
-
trySpan.textContent = "try";
|
|
9410
|
-
const openDataSpan = createSVGElement4("tspan");
|
|
9411
|
-
setAttrs4(openDataSpan, { "font-weight": 600, "font-size": 16 });
|
|
9412
|
-
openDataSpan.textContent = "OpenData";
|
|
9413
|
-
const aiSpan = createSVGElement4("tspan");
|
|
9414
|
-
setAttrs4(aiSpan, { "font-weight": 500 });
|
|
9415
|
-
aiSpan.textContent = ".ai";
|
|
9416
|
-
text.appendChild(trySpan);
|
|
9417
|
-
text.appendChild(openDataSpan);
|
|
9418
|
-
text.appendChild(aiSpan);
|
|
9419
|
-
a2.appendChild(text);
|
|
9420
|
-
parent.appendChild(a2);
|
|
9421
|
-
}
|
|
9422
|
-
function renderTiles(parent, tiles, animation) {
|
|
9423
|
-
const g = createSVGElement4("g");
|
|
9424
|
-
g.setAttribute("class", "oc-tilemap-tiles");
|
|
9425
|
-
g.setAttribute("role", "list");
|
|
9426
|
-
const tileDelays = [];
|
|
9427
|
-
if (animation?.enabled) {
|
|
9428
|
-
const baseStagger = 800 / Math.max(tiles.length, 1);
|
|
9429
|
-
let seed = 17;
|
|
9430
|
-
for (let i = 0; i < tiles.length; i++) {
|
|
9431
|
-
const idx = tiles[i].animationIndex ?? i;
|
|
9432
|
-
seed = seed * 1103515245 + 12345 & 2147483647;
|
|
9433
|
-
const jitter = (seed % 1e3 / 1e3 - 0.5) * 0.8;
|
|
9434
|
-
tileDelays.push(Math.max(0, Math.round(idx * baseStagger * (1 + jitter))));
|
|
9435
|
-
}
|
|
9436
|
-
}
|
|
9437
|
-
for (let i = 0; i < tiles.length; i++) {
|
|
9438
|
-
const tile = tiles[i];
|
|
9439
|
-
const tileGroup = createSVGElement4("g");
|
|
9440
|
-
tileGroup.setAttribute("class", "oc-tilemap-tile");
|
|
9441
|
-
tileGroup.setAttribute("data-state", tile.stateCode);
|
|
9442
|
-
tileGroup.setAttribute("role", "listitem");
|
|
9443
|
-
if (tile.aria?.label) {
|
|
9444
|
-
tileGroup.setAttribute("aria-label", tile.aria.label);
|
|
9445
|
-
}
|
|
9446
|
-
if (animation?.enabled) {
|
|
9447
|
-
const idx = tile.animationIndex ?? i;
|
|
9448
|
-
tileGroup.setAttribute("data-animation-index", String(idx));
|
|
9449
|
-
tileGroup.style.setProperty(
|
|
9450
|
-
"--oc-mark-index",
|
|
9451
|
-
String(idx)
|
|
9452
|
-
);
|
|
9453
|
-
tileGroup.style.setProperty(
|
|
9454
|
-
"--oc-tile-delay",
|
|
9455
|
-
`${tileDelays[i]}ms`
|
|
9456
|
-
);
|
|
9457
|
-
}
|
|
9458
|
-
const rect = createSVGElement4("rect");
|
|
9459
|
-
setAttrs4(rect, {
|
|
9460
|
-
x: tile.x,
|
|
9461
|
-
y: tile.y,
|
|
9462
|
-
width: tile.size,
|
|
9463
|
-
height: tile.size,
|
|
9464
|
-
rx: tile.cornerRadius,
|
|
9465
|
-
fill: tile.fill,
|
|
9466
|
-
"fill-opacity": tile.fillOpacity ?? 1,
|
|
9467
|
-
stroke: tile.stroke,
|
|
9468
|
-
"stroke-width": tile.strokeWidth
|
|
9469
|
-
});
|
|
9470
|
-
tileGroup.appendChild(rect);
|
|
9471
|
-
const codeLabel = createSVGElement4("text");
|
|
9472
|
-
setAttrs4(codeLabel, {
|
|
9473
|
-
x: tile.label.x,
|
|
9474
|
-
y: tile.label.y,
|
|
9475
|
-
"text-anchor": "middle",
|
|
9476
|
-
"dominant-baseline": "central",
|
|
9477
|
-
"font-family": tile.label.style.fontFamily,
|
|
9478
|
-
"font-size": tile.label.style.fontSize,
|
|
9479
|
-
"font-weight": tile.label.style.fontWeight
|
|
9480
|
-
});
|
|
9481
|
-
codeLabel.style.setProperty(
|
|
9482
|
-
"fill",
|
|
9483
|
-
tile.label.style.fill
|
|
9484
|
-
);
|
|
9485
|
-
codeLabel.textContent = tile.label.text;
|
|
9486
|
-
tileGroup.appendChild(codeLabel);
|
|
9487
|
-
if (tile.valueLabel.visible && tile.valueLabel.text) {
|
|
9488
|
-
const valueLabel = createSVGElement4("text");
|
|
9489
|
-
setAttrs4(valueLabel, {
|
|
9490
|
-
x: tile.valueLabel.x,
|
|
9491
|
-
y: tile.valueLabel.y,
|
|
9492
|
-
"text-anchor": "middle",
|
|
9493
|
-
"dominant-baseline": "central",
|
|
9494
|
-
"font-family": tile.valueLabel.style.fontFamily,
|
|
9495
|
-
"font-size": tile.valueLabel.style.fontSize,
|
|
9496
|
-
"font-weight": tile.valueLabel.style.fontWeight
|
|
9497
|
-
});
|
|
9498
|
-
valueLabel.style.setProperty(
|
|
9499
|
-
"fill",
|
|
9500
|
-
tile.valueLabel.style.fill
|
|
9501
|
-
);
|
|
9502
|
-
valueLabel.textContent = tile.valueLabel.text;
|
|
9503
|
-
tileGroup.appendChild(valueLabel);
|
|
9504
|
-
}
|
|
9505
|
-
g.appendChild(tileGroup);
|
|
9506
|
-
}
|
|
9507
|
-
parent.appendChild(g);
|
|
9508
|
-
}
|
|
9509
|
-
function renderGradientLegend(parent, layout) {
|
|
9510
|
-
if (!layout.gradientLegend) return;
|
|
9511
|
-
const { gradientLegend } = layout;
|
|
9512
|
-
const g = createSVGElement4("g");
|
|
9513
|
-
g.setAttribute("class", "oc-tilemap-legend");
|
|
9514
|
-
const defs = parent.querySelector("defs") || createSVGElement4("defs");
|
|
9515
|
-
const exists = parent.querySelector("defs");
|
|
9516
|
-
if (!exists) {
|
|
9517
|
-
parent.insertBefore(defs, parent.firstChild);
|
|
9518
|
-
}
|
|
9519
|
-
const gradientId = `oc-tilemap-legend-gradient-${gradientIdCounter++}`;
|
|
9520
|
-
const grad = createSVGElement4("linearGradient");
|
|
9521
|
-
grad.id = gradientId;
|
|
9522
|
-
grad.setAttribute("x1", "0%");
|
|
9523
|
-
grad.setAttribute("y1", "0%");
|
|
9524
|
-
grad.setAttribute("x2", "100%");
|
|
9525
|
-
grad.setAttribute("y2", "0%");
|
|
9526
|
-
for (const stop of gradientLegend.colorStops) {
|
|
9527
|
-
const s = createSVGElement4("stop");
|
|
9528
|
-
const attrs = {
|
|
9529
|
-
offset: `${stop.offset * 100}%`,
|
|
9530
|
-
"stop-color": stop.color
|
|
9531
|
-
};
|
|
9532
|
-
if (stop.opacity !== void 0) {
|
|
9533
|
-
attrs["stop-opacity"] = stop.opacity;
|
|
9534
|
-
}
|
|
9535
|
-
setAttrs4(s, attrs);
|
|
9536
|
-
grad.appendChild(s);
|
|
9537
|
-
}
|
|
9538
|
-
defs.appendChild(grad);
|
|
9539
|
-
const barHeight = gradientLegend.bounds.height;
|
|
9540
|
-
const bar = createSVGElement4("rect");
|
|
9541
|
-
setAttrs4(bar, {
|
|
9542
|
-
x: gradientLegend.bounds.x,
|
|
9543
|
-
y: gradientLegend.bounds.y,
|
|
9544
|
-
width: gradientLegend.bounds.width,
|
|
9545
|
-
height: barHeight,
|
|
9546
|
-
rx: barHeight / 2,
|
|
9547
|
-
fill: `url(#${gradientId})`
|
|
9548
|
-
});
|
|
9549
|
-
g.appendChild(bar);
|
|
9550
|
-
const minText = createSVGElement4("text");
|
|
9551
|
-
setAttrs4(minText, {
|
|
9552
|
-
x: gradientLegend.bounds.x,
|
|
9553
|
-
y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,
|
|
9554
|
-
"text-anchor": "start",
|
|
9555
|
-
"font-family": gradientLegend.labelStyle.fontFamily,
|
|
9556
|
-
"font-size": gradientLegend.labelStyle.fontSize,
|
|
9557
|
-
"font-weight": gradientLegend.labelStyle.fontWeight
|
|
9558
|
-
});
|
|
9559
|
-
minText.style.setProperty(
|
|
9560
|
-
"fill",
|
|
9561
|
-
gradientLegend.labelStyle.fill
|
|
9562
|
-
);
|
|
9563
|
-
minText.textContent = gradientLegend.minLabel;
|
|
9564
|
-
g.appendChild(minText);
|
|
9565
|
-
const maxText = createSVGElement4("text");
|
|
9566
|
-
setAttrs4(maxText, {
|
|
9567
|
-
x: gradientLegend.bounds.x + gradientLegend.bounds.width,
|
|
9568
|
-
y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,
|
|
9569
|
-
"text-anchor": "end",
|
|
9570
|
-
"font-family": gradientLegend.labelStyle.fontFamily,
|
|
9571
|
-
"font-size": gradientLegend.labelStyle.fontSize,
|
|
9572
|
-
"font-weight": gradientLegend.labelStyle.fontWeight
|
|
9573
|
-
});
|
|
9574
|
-
maxText.style.setProperty(
|
|
9575
|
-
"fill",
|
|
9576
|
-
gradientLegend.labelStyle.fill
|
|
9577
|
-
);
|
|
9578
|
-
maxText.textContent = gradientLegend.maxLabel;
|
|
9579
|
-
g.appendChild(maxText);
|
|
9580
|
-
parent.appendChild(g);
|
|
9581
|
-
}
|
|
9582
|
-
function renderTileMapSVG(layout, opts) {
|
|
9583
|
-
const { width, height, tiles, a11y, watermark, animation } = layout;
|
|
9584
|
-
const animate = opts?.animate && animation?.enabled;
|
|
9585
|
-
const svg = createSVGElement4("svg");
|
|
9586
|
-
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
9587
|
-
svg.setAttribute("role", "img");
|
|
9588
|
-
if (a11y.altText) {
|
|
9589
|
-
svg.setAttribute("aria-label", a11y.altText);
|
|
9590
|
-
}
|
|
9591
|
-
const classes = animate ? "oc-tilemap oc-animate" : "oc-tilemap";
|
|
9592
|
-
svg.setAttribute("class", classes);
|
|
9593
|
-
if (animate && animation) {
|
|
9594
|
-
const stagger = Math.max(5, Math.round(800 / Math.max(tiles.length, 1)));
|
|
9595
|
-
svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
|
|
9596
|
-
svg.style.setProperty("--oc-animation-stagger", `${stagger}ms`);
|
|
9597
|
-
svg.style.setProperty("--oc-annotation-delay", `${animation.annotationDelay}ms`);
|
|
9598
|
-
const easeVar = EASE_VAR_MAP4[animation.ease] || EASE_VAR_MAP4.smooth;
|
|
9599
|
-
svg.style.setProperty("--oc-animation-ease", easeVar);
|
|
9600
|
-
}
|
|
9601
|
-
const defs = createSVGElement4("defs");
|
|
9602
|
-
svg.appendChild(defs);
|
|
9603
|
-
renderChrome4(svg, layout);
|
|
9604
|
-
renderTiles(svg, tiles, animate ? animation : void 0);
|
|
9605
|
-
renderGradientLegend(svg, layout);
|
|
9606
|
-
if (watermark) {
|
|
9607
|
-
renderWatermark2(svg, layout);
|
|
9608
|
-
}
|
|
9609
|
-
return svg;
|
|
9610
|
-
}
|
|
9611
|
-
|
|
9612
|
-
// src/tilemap-mount.ts
|
|
9613
7795
|
function resolveDarkMode6(mode) {
|
|
9614
7796
|
if (mode === "force") return true;
|
|
9615
7797
|
if (mode === "off" || mode === void 0) return false;
|