@opendata-ai/openchart-vanilla 6.27.0 → 6.28.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,236 @@
1
+ // src/barlist-mount.ts
2
+ import { compileBarList } from "@opendata-ai/openchart-engine";
3
+
4
+ // src/animation.ts
5
+ function cancelAnimations(svg) {
6
+ if (svg) {
7
+ svg.classList.remove("oc-animate");
8
+ }
9
+ }
10
+ function setupAnimationCleanup(svg, onComplete) {
11
+ const style = svg.style;
12
+ const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 600;
13
+ const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
14
+ const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) || 200;
15
+ const animatedElements = svg.querySelectorAll("[data-animation-index]").length;
16
+ const totalStagger = stagger * Math.max(0, animatedElements - 1);
17
+ const totalTime = totalStagger + duration + annotationDelay + 500;
18
+ const timer2 = setTimeout(() => {
19
+ svg.classList.remove("oc-animate");
20
+ onComplete?.();
21
+ }, totalTime);
22
+ return () => {
23
+ clearTimeout(timer2);
24
+ cancelAnimations(svg);
25
+ };
26
+ }
27
+ function setupTableAnimationCleanup(wrapper) {
28
+ const style = wrapper.style;
29
+ const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 500;
30
+ const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
31
+ const rows = wrapper.querySelectorAll("tbody tr").length;
32
+ const totalStagger = stagger * Math.max(0, rows - 1);
33
+ const totalTime = totalStagger + duration + 300;
34
+ const timer2 = setTimeout(() => {
35
+ wrapper.classList.remove("oc-animate");
36
+ }, totalTime);
37
+ return () => {
38
+ clearTimeout(timer2);
39
+ wrapper.classList.remove("oc-animate");
40
+ };
41
+ }
42
+
43
+ // src/barlist-renderer.ts
44
+ var SVG_NS = "http://www.w3.org/2000/svg";
45
+ var XLINK_NS = "http://www.w3.org/1999/xlink";
46
+ var BRAND_URL = "https://tryopendata.ai";
47
+ function createSVGElement(tag) {
48
+ return document.createElementNS(SVG_NS, tag);
49
+ }
50
+ function setAttrs(el, attrs) {
51
+ for (const [key, value] of Object.entries(attrs)) {
52
+ el.setAttribute(key, String(value));
53
+ }
54
+ }
55
+ function renderChrome(parent, layout) {
56
+ const g = createSVGElement("g");
57
+ g.setAttribute("class", "oc-chrome");
58
+ const { chrome } = layout;
59
+ const bottomOffset = layout.area.y + layout.area.height;
60
+ for (const key of ["title", "subtitle", "source", "byline", "footer"]) {
61
+ const el = chrome[key];
62
+ if (!el) continue;
63
+ const isBottom = key === "source" || key === "byline" || key === "footer";
64
+ const text = createSVGElement("text");
65
+ setAttrs(text, {
66
+ x: el.x,
67
+ y: isBottom ? bottomOffset + el.y : el.y
68
+ });
69
+ text.setAttribute("class", `oc-${key}`);
70
+ text.setAttribute("font-family", el.style.fontFamily);
71
+ text.setAttribute("font-size", String(el.style.fontSize));
72
+ text.setAttribute("font-weight", String(el.style.fontWeight));
73
+ text.style.setProperty("fill", el.style.fill);
74
+ text.textContent = el.text;
75
+ g.appendChild(text);
76
+ }
77
+ parent.appendChild(g);
78
+ }
79
+ function renderWatermark(parent, layout) {
80
+ if (layout.width < 480) return;
81
+ const { width, height, theme } = layout;
82
+ const padding = theme.spacing.padding;
83
+ const rightEdge = width - padding;
84
+ const bottomEdge = height - padding;
85
+ const fill = theme.colors.axis;
86
+ const a2 = createSVGElement("a");
87
+ a2.setAttribute("href", BRAND_URL);
88
+ a2.setAttributeNS(XLINK_NS, "xlink:href", BRAND_URL);
89
+ a2.setAttribute("target", "_blank");
90
+ a2.setAttribute("rel", "noopener");
91
+ a2.setAttribute("class", "oc-chrome-ref");
92
+ const text = createSVGElement("text");
93
+ setAttrs(text, {
94
+ x: rightEdge,
95
+ y: bottomEdge,
96
+ "dominant-baseline": "alphabetic",
97
+ "text-anchor": "end",
98
+ "font-family": theme.fonts.family,
99
+ "font-size": 12,
100
+ "fill-opacity": 0.55
101
+ });
102
+ text.style.setProperty("fill", fill);
103
+ const trySpan = createSVGElement("tspan");
104
+ setAttrs(trySpan, { "font-weight": 500 });
105
+ trySpan.textContent = "try";
106
+ const openDataSpan = createSVGElement("tspan");
107
+ setAttrs(openDataSpan, { "font-weight": 600, "font-size": 16 });
108
+ openDataSpan.textContent = "OpenData";
109
+ const aiSpan = createSVGElement("tspan");
110
+ setAttrs(aiSpan, { "font-weight": 500 });
111
+ aiSpan.textContent = ".ai";
112
+ text.appendChild(trySpan);
113
+ text.appendChild(openDataSpan);
114
+ text.appendChild(aiSpan);
115
+ a2.appendChild(text);
116
+ parent.appendChild(a2);
117
+ }
118
+ function renderRows(parent, rows, animation) {
119
+ const g = createSVGElement("g");
120
+ g.setAttribute("class", "oc-barlist-rows");
121
+ g.setAttribute("role", "list");
122
+ for (const row of rows) {
123
+ const rowGroup = createSVGElement("g");
124
+ rowGroup.setAttribute("class", "oc-barlist-row");
125
+ rowGroup.setAttribute("data-row-index", String(row.index));
126
+ rowGroup.setAttribute("role", "listitem");
127
+ if (row.aria?.label) {
128
+ rowGroup.setAttribute("aria-label", row.aria.label);
129
+ }
130
+ if (animation?.enabled) {
131
+ rowGroup.setAttribute("data-animation-index", String(row.animationIndex));
132
+ const style = rowGroup.style;
133
+ style.setProperty("--oc-mark-index", String(row.animationIndex));
134
+ style.setProperty("--oc-row-delay", `${row.animationIndex * 40}ms`);
135
+ }
136
+ const labelEl = createSVGElement("text");
137
+ setAttrs(labelEl, {
138
+ x: row.label.x,
139
+ y: row.label.y,
140
+ "dominant-baseline": "central",
141
+ "text-anchor": "start",
142
+ "font-family": row.label.style.fontFamily,
143
+ "font-size": row.label.style.fontSize,
144
+ "font-weight": row.label.style.fontWeight
145
+ });
146
+ labelEl.style.setProperty("fill", row.label.style.fill);
147
+ labelEl.textContent = row.label.text;
148
+ rowGroup.appendChild(labelEl);
149
+ if (row.subtitle?.visible) {
150
+ const subEl = createSVGElement("text");
151
+ setAttrs(subEl, {
152
+ x: row.subtitle.x,
153
+ y: row.subtitle.y,
154
+ "dominant-baseline": "central",
155
+ "text-anchor": "start",
156
+ "font-family": row.subtitle.style.fontFamily,
157
+ "font-size": row.subtitle.style.fontSize,
158
+ "font-weight": row.subtitle.style.fontWeight
159
+ });
160
+ subEl.style.setProperty(
161
+ "fill",
162
+ row.subtitle.style.fill
163
+ );
164
+ subEl.textContent = row.subtitle.text;
165
+ rowGroup.appendChild(subEl);
166
+ }
167
+ const trackRect = createSVGElement("rect");
168
+ setAttrs(trackRect, {
169
+ x: row.track.x,
170
+ y: row.track.y,
171
+ width: row.track.width,
172
+ height: row.track.height,
173
+ rx: row.track.cornerRadius,
174
+ fill: "currentColor",
175
+ "fill-opacity": 0.06
176
+ });
177
+ trackRect.setAttribute("class", "oc-barlist-track");
178
+ rowGroup.appendChild(trackRect);
179
+ const barRect = createSVGElement("rect");
180
+ setAttrs(barRect, {
181
+ x: row.bar.x,
182
+ y: row.bar.y,
183
+ width: row.bar.width,
184
+ height: row.bar.height,
185
+ rx: row.bar.cornerRadius,
186
+ fill: row.bar.fill
187
+ });
188
+ barRect.setAttribute("class", "oc-barlist-bar");
189
+ rowGroup.appendChild(barRect);
190
+ const valueEl = createSVGElement("text");
191
+ setAttrs(valueEl, {
192
+ x: row.valueLabel.x,
193
+ y: row.valueLabel.y,
194
+ "dominant-baseline": "central",
195
+ "text-anchor": "end",
196
+ "font-family": row.valueLabel.style.fontFamily,
197
+ "font-size": row.valueLabel.style.fontSize,
198
+ "font-weight": row.valueLabel.style.fontWeight
199
+ });
200
+ valueEl.style.setProperty(
201
+ "fill",
202
+ row.valueLabel.style.fill
203
+ );
204
+ valueEl.textContent = row.valueLabel.text;
205
+ rowGroup.appendChild(valueEl);
206
+ g.appendChild(rowGroup);
207
+ }
208
+ parent.appendChild(g);
209
+ }
210
+ function renderBarListSVG(layout, opts) {
211
+ const { width, height, rows, a11y, watermark, animation } = layout;
212
+ const animate = opts?.animate && animation?.enabled;
213
+ const svg = createSVGElement("svg");
214
+ svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
215
+ svg.setAttribute("role", "list");
216
+ svg.style.height = `${height}px`;
217
+ if (a11y.altText) {
218
+ svg.setAttribute("aria-label", a11y.altText);
219
+ }
220
+ const classes = animate ? "oc-barlist oc-animate" : "oc-barlist";
221
+ svg.setAttribute("class", classes);
222
+ if (animate && animation) {
223
+ svg.style.setProperty("--oc-animation-duration", `${animation.duration}ms`);
224
+ svg.style.setProperty("--oc-animation-stagger", "40ms");
225
+ }
226
+ renderChrome(svg, layout);
227
+ renderRows(svg, rows, animate ? animation : void 0);
228
+ if (watermark) {
229
+ renderWatermark(svg, layout);
230
+ }
231
+ return svg;
232
+ }
233
+
1
234
  // src/export.ts
2
235
  function getSVGDimensions(svg) {
3
236
  const w = parseFloat(svg.getAttribute("width") || "");
@@ -224,13 +457,351 @@ function exportCSV(data) {
224
457
  const values = headers.map((h) => csvEscape(String(row[h] ?? "")));
225
458
  rows.push(values.join(","));
226
459
  }
227
- return rows.join("\n");
228
- }
229
- function csvEscape(value) {
230
- if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
231
- return `"${value.replace(/"/g, '""')}"`;
460
+ return rows.join("\n");
461
+ }
462
+ function csvEscape(value) {
463
+ if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
464
+ return `"${value.replace(/"/g, '""')}"`;
465
+ }
466
+ return value;
467
+ }
468
+
469
+ // src/measure-text.ts
470
+ function createMeasureText() {
471
+ let canvas = null;
472
+ let ctx = null;
473
+ return (text, fontSize, fontWeight) => {
474
+ if (!canvas) {
475
+ canvas = document.createElement("canvas");
476
+ ctx = canvas.getContext("2d");
477
+ }
478
+ if (!ctx) {
479
+ return { width: text.length * fontSize * 0.6, height: fontSize * 1.2 };
480
+ }
481
+ const weight = fontWeight ?? 400;
482
+ ctx.font = `${weight} ${fontSize}px Inter, sans-serif`;
483
+ const metrics = ctx.measureText(text);
484
+ return {
485
+ width: metrics.width,
486
+ height: fontSize * 1.2
487
+ };
488
+ };
489
+ }
490
+
491
+ // src/resize-observer.ts
492
+ var DEBOUNCE_MS = 16;
493
+ function observeResize(container, callback) {
494
+ let timeoutId = null;
495
+ const observer = new ResizeObserver((entries) => {
496
+ if (timeoutId !== null) {
497
+ clearTimeout(timeoutId);
498
+ }
499
+ timeoutId = setTimeout(() => {
500
+ for (const entry of entries) {
501
+ const { width, height } = entry.contentRect;
502
+ callback(width, height);
503
+ }
504
+ timeoutId = null;
505
+ }, DEBOUNCE_MS);
506
+ });
507
+ observer.observe(container);
508
+ return () => {
509
+ if (timeoutId !== null) {
510
+ clearTimeout(timeoutId);
511
+ }
512
+ observer.disconnect();
513
+ };
514
+ }
515
+
516
+ // src/tooltip.ts
517
+ import { computePosition, flip, offset, shift } from "@floating-ui/dom";
518
+ var TOOLTIP_OFFSET = 12;
519
+ function createTooltipManager(container) {
520
+ const tooltip = document.createElement("div");
521
+ tooltip.className = "oc-tooltip";
522
+ tooltip.setAttribute("role", "tooltip");
523
+ container.style.position = container.style.position || "relative";
524
+ container.appendChild(tooltip);
525
+ let lastContentKey = "";
526
+ let currentPositionId = 0;
527
+ const handleDocumentTouch = (e) => {
528
+ if (!container.contains(e.target)) {
529
+ hide();
530
+ }
531
+ };
532
+ document.addEventListener("touchstart", handleDocumentTouch);
533
+ function show(content, x3, y3) {
534
+ const contentKey = `${content.title}|${content.fields.length}|${content.fields[0]?.value}|${content.fields[content.fields.length - 1]?.value}`;
535
+ if (contentKey !== lastContentKey) {
536
+ lastContentKey = contentKey;
537
+ let html = "";
538
+ if (content.title) {
539
+ const titleColor = content.fields.find((f) => f.color)?.color;
540
+ html += '<div class="oc-tooltip-header">';
541
+ if (titleColor) {
542
+ html += `<span class="oc-tooltip-dot" style="background:${esc(titleColor)}"></span>`;
543
+ }
544
+ html += `<span class="oc-tooltip-title">${esc(content.title)}</span>`;
545
+ html += "</div>";
546
+ }
547
+ if (content.fields.length > 0) {
548
+ html += '<div class="oc-tooltip-body">';
549
+ for (const field of content.fields) {
550
+ html += '<div class="oc-tooltip-row">';
551
+ html += `<span class="oc-tooltip-label">${esc(field.label)}</span>`;
552
+ html += `<span class="oc-tooltip-value">${esc(field.value)}</span>`;
553
+ html += "</div>";
554
+ }
555
+ html += "</div>";
556
+ }
557
+ tooltip.innerHTML = html;
558
+ }
559
+ tooltip.style.display = "block";
560
+ const positionId = ++currentPositionId;
561
+ const virtualRef = {
562
+ getBoundingClientRect() {
563
+ const rect = container.getBoundingClientRect();
564
+ return {
565
+ x: rect.left + x3,
566
+ y: rect.top + y3,
567
+ width: 0,
568
+ height: 0,
569
+ top: rect.top + y3,
570
+ left: rect.left + x3,
571
+ right: rect.left + x3,
572
+ bottom: rect.top + y3
573
+ };
574
+ }
575
+ };
576
+ computePosition(virtualRef, tooltip, {
577
+ placement: "bottom-start",
578
+ middleware: [offset(TOOLTIP_OFFSET), flip(), shift({ padding: 5 })]
579
+ }).then(({ x: fx, y: fy }) => {
580
+ if (positionId !== currentPositionId) return;
581
+ tooltip.style.left = `${fx}px`;
582
+ tooltip.style.top = `${fy}px`;
583
+ });
584
+ }
585
+ function hide() {
586
+ tooltip.style.display = "none";
587
+ lastContentKey = "";
588
+ }
589
+ function destroy() {
590
+ document.removeEventListener("touchstart", handleDocumentTouch);
591
+ if (tooltip.parentNode) {
592
+ tooltip.parentNode.removeChild(tooltip);
593
+ }
594
+ }
595
+ return { show, hide, destroy };
596
+ }
597
+ function esc(str) {
598
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
599
+ }
600
+
601
+ // src/barlist-mount.ts
602
+ function resolveDarkMode(mode) {
603
+ if (mode === "force") return true;
604
+ if (mode === "off" || mode === void 0) return false;
605
+ if (typeof window !== "undefined" && window.matchMedia) {
606
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
607
+ }
608
+ return false;
609
+ }
610
+ function createBarList(container, spec, options) {
611
+ let currentSpec = spec;
612
+ let currentLayout;
613
+ let destroyed = false;
614
+ let svgElement = null;
615
+ let tooltipManager = null;
616
+ let cleanupTooltipEvents = null;
617
+ let disconnectResize = null;
618
+ let animationCleanup = null;
619
+ let pendingResize = false;
620
+ const measureText = createMeasureText();
621
+ function getContainerDimensions() {
622
+ const rect = container.getBoundingClientRect();
623
+ return {
624
+ width: Math.max(rect.width || 600, 100),
625
+ height: Math.max(rect.height || 400, 100)
626
+ };
627
+ }
628
+ function compile() {
629
+ const { width, height } = getContainerDimensions();
630
+ const darkMode = resolveDarkMode(options?.darkMode);
631
+ const compileOpts = {
632
+ width,
633
+ height,
634
+ theme: options?.theme,
635
+ darkMode,
636
+ watermark: options?.watermark,
637
+ measureText
638
+ };
639
+ return compileBarList(currentSpec, compileOpts);
640
+ }
641
+ function wireTooltipAndInteraction(svg, layout) {
642
+ const cleanups = [];
643
+ const rowElements = svg.querySelectorAll(".oc-barlist-row");
644
+ for (const el of rowElements) {
645
+ const indexStr = el.getAttribute("data-row-index");
646
+ if (indexStr === null) continue;
647
+ const content = layout.tooltipDescriptors.get(indexStr);
648
+ const row = layout.rows[Number(indexStr)];
649
+ const handleMouseEnter = (e) => {
650
+ const mouseEvent = e;
651
+ if (content && tooltipManager && options?.tooltip !== false) {
652
+ const svgRect = svg.getBoundingClientRect();
653
+ const x3 = mouseEvent.clientX - svgRect.left;
654
+ const y3 = mouseEvent.clientY - svgRect.top;
655
+ tooltipManager.show(content, x3, y3);
656
+ }
657
+ if (row) {
658
+ options?.onRowHover?.({
659
+ label: row.label.text,
660
+ value: row.value,
661
+ data: row.data
662
+ });
663
+ }
664
+ };
665
+ const handleMouseMove = (e) => {
666
+ if (content && tooltipManager && options?.tooltip !== false) {
667
+ const mouseEvent = e;
668
+ const svgRect = svg.getBoundingClientRect();
669
+ const x3 = mouseEvent.clientX - svgRect.left;
670
+ const y3 = mouseEvent.clientY - svgRect.top;
671
+ tooltipManager.show(content, x3, y3);
672
+ }
673
+ };
674
+ const handleMouseLeave = () => {
675
+ tooltipManager?.hide();
676
+ options?.onRowHover?.(null);
677
+ };
678
+ const handleClick = () => {
679
+ if (row) {
680
+ options?.onRowClick?.({
681
+ label: row.label.text,
682
+ value: row.value,
683
+ data: row.data
684
+ });
685
+ }
686
+ };
687
+ el.addEventListener("mouseenter", handleMouseEnter);
688
+ el.addEventListener("mousemove", handleMouseMove);
689
+ el.addEventListener("mouseleave", handleMouseLeave);
690
+ el.addEventListener("click", handleClick);
691
+ cleanups.push(() => {
692
+ el.removeEventListener("mouseenter", handleMouseEnter);
693
+ el.removeEventListener("mousemove", handleMouseMove);
694
+ el.removeEventListener("mouseleave", handleMouseLeave);
695
+ el.removeEventListener("click", handleClick);
696
+ });
697
+ }
698
+ return () => {
699
+ for (const cleanup of cleanups) cleanup();
700
+ };
701
+ }
702
+ function render(animate = false) {
703
+ if (animationCleanup) {
704
+ animationCleanup();
705
+ animationCleanup = null;
706
+ }
707
+ if (svgElement) {
708
+ if (cleanupTooltipEvents) {
709
+ cleanupTooltipEvents();
710
+ cleanupTooltipEvents = null;
711
+ }
712
+ svgElement.remove();
713
+ }
714
+ const newSvg = renderBarListSVG(currentLayout, { animate });
715
+ container.appendChild(newSvg);
716
+ svgElement = newSvg;
717
+ cleanupTooltipEvents = wireTooltipAndInteraction(newSvg, currentLayout);
718
+ if (options?.tooltip !== false) {
719
+ if (!tooltipManager) {
720
+ tooltipManager = createTooltipManager(container);
721
+ }
722
+ }
723
+ if (currentLayout.animation?.enabled) {
724
+ animationCleanup = setupAnimationCleanup(newSvg, () => {
725
+ if (pendingResize && !destroyed) {
726
+ pendingResize = false;
727
+ resize();
728
+ }
729
+ });
730
+ }
731
+ }
732
+ function update(newSpec) {
733
+ currentSpec = newSpec;
734
+ currentLayout = compile();
735
+ render();
736
+ }
737
+ function resize() {
738
+ if (destroyed) return;
739
+ if (animationCleanup) {
740
+ pendingResize = true;
741
+ return;
742
+ }
743
+ currentLayout = compile();
744
+ render();
745
+ }
746
+ function exportChart(format, options_) {
747
+ if (!svgElement) return "";
748
+ switch (format) {
749
+ case "svg":
750
+ return exportSVG(svgElement);
751
+ case "svg-with-fonts":
752
+ return exportSVGWithFonts(svgElement);
753
+ case "png":
754
+ return exportPNG(svgElement, options_);
755
+ case "jpg":
756
+ return exportJPG(svgElement, options_);
757
+ default:
758
+ return "";
759
+ }
760
+ }
761
+ function destroy() {
762
+ if (destroyed) return;
763
+ destroyed = true;
764
+ if (animationCleanup) {
765
+ cancelAnimations(svgElement);
766
+ animationCleanup();
767
+ animationCleanup = null;
768
+ }
769
+ if (cleanupTooltipEvents) {
770
+ cleanupTooltipEvents();
771
+ cleanupTooltipEvents = null;
772
+ }
773
+ if (svgElement) {
774
+ svgElement.remove();
775
+ svgElement = null;
776
+ }
777
+ if (tooltipManager) {
778
+ tooltipManager.destroy();
779
+ tooltipManager = null;
780
+ }
781
+ if (disconnectResize) {
782
+ disconnectResize();
783
+ disconnectResize = null;
784
+ }
785
+ container.classList.remove("oc-barlist-root", "oc-dark");
786
+ }
787
+ container.classList.add("oc-barlist-root");
788
+ if (resolveDarkMode(options?.darkMode)) {
789
+ container.classList.add("oc-dark");
232
790
  }
233
- return value;
791
+ currentLayout = compile();
792
+ render(true);
793
+ if (options?.responsive !== false) {
794
+ disconnectResize = observeResize(container, () => resize());
795
+ }
796
+ return {
797
+ update,
798
+ resize,
799
+ export: exportChart,
800
+ destroy,
801
+ get layout() {
802
+ return currentLayout;
803
+ }
804
+ };
234
805
  }
235
806
 
236
807
  // src/graph/simulation-worker-url.ts
@@ -2547,118 +3118,8 @@ var SpatialIndex = class {
2547
3118
  }
2548
3119
  };
2549
3120
 
2550
- // src/resize-observer.ts
2551
- var DEBOUNCE_MS = 16;
2552
- function observeResize(container, callback) {
2553
- let timeoutId = null;
2554
- const observer = new ResizeObserver((entries) => {
2555
- if (timeoutId !== null) {
2556
- clearTimeout(timeoutId);
2557
- }
2558
- timeoutId = setTimeout(() => {
2559
- for (const entry of entries) {
2560
- const { width, height } = entry.contentRect;
2561
- callback(width, height);
2562
- }
2563
- timeoutId = null;
2564
- }, DEBOUNCE_MS);
2565
- });
2566
- observer.observe(container);
2567
- return () => {
2568
- if (timeoutId !== null) {
2569
- clearTimeout(timeoutId);
2570
- }
2571
- observer.disconnect();
2572
- };
2573
- }
2574
-
2575
- // src/tooltip.ts
2576
- import { computePosition, flip, offset, shift } from "@floating-ui/dom";
2577
- var TOOLTIP_OFFSET = 12;
2578
- function createTooltipManager(container) {
2579
- const tooltip = document.createElement("div");
2580
- tooltip.className = "oc-tooltip";
2581
- tooltip.setAttribute("role", "tooltip");
2582
- container.style.position = container.style.position || "relative";
2583
- container.appendChild(tooltip);
2584
- let lastContentKey = "";
2585
- let currentPositionId = 0;
2586
- const handleDocumentTouch = (e) => {
2587
- if (!container.contains(e.target)) {
2588
- hide();
2589
- }
2590
- };
2591
- document.addEventListener("touchstart", handleDocumentTouch);
2592
- function show(content, x3, y3) {
2593
- const contentKey = `${content.title}|${content.fields.length}|${content.fields[0]?.value}|${content.fields[content.fields.length - 1]?.value}`;
2594
- if (contentKey !== lastContentKey) {
2595
- lastContentKey = contentKey;
2596
- let html = "";
2597
- if (content.title) {
2598
- const titleColor = content.fields.find((f) => f.color)?.color;
2599
- html += '<div class="oc-tooltip-header">';
2600
- if (titleColor) {
2601
- html += `<span class="oc-tooltip-dot" style="background:${esc(titleColor)}"></span>`;
2602
- }
2603
- html += `<span class="oc-tooltip-title">${esc(content.title)}</span>`;
2604
- html += "</div>";
2605
- }
2606
- if (content.fields.length > 0) {
2607
- html += '<div class="oc-tooltip-body">';
2608
- for (const field of content.fields) {
2609
- html += '<div class="oc-tooltip-row">';
2610
- html += `<span class="oc-tooltip-label">${esc(field.label)}</span>`;
2611
- html += `<span class="oc-tooltip-value">${esc(field.value)}</span>`;
2612
- html += "</div>";
2613
- }
2614
- html += "</div>";
2615
- }
2616
- tooltip.innerHTML = html;
2617
- }
2618
- tooltip.style.display = "block";
2619
- const positionId = ++currentPositionId;
2620
- const virtualRef = {
2621
- getBoundingClientRect() {
2622
- const rect = container.getBoundingClientRect();
2623
- return {
2624
- x: rect.left + x3,
2625
- y: rect.top + y3,
2626
- width: 0,
2627
- height: 0,
2628
- top: rect.top + y3,
2629
- left: rect.left + x3,
2630
- right: rect.left + x3,
2631
- bottom: rect.top + y3
2632
- };
2633
- }
2634
- };
2635
- computePosition(virtualRef, tooltip, {
2636
- placement: "bottom-start",
2637
- middleware: [offset(TOOLTIP_OFFSET), flip(), shift({ padding: 5 })]
2638
- }).then(({ x: fx, y: fy }) => {
2639
- if (positionId !== currentPositionId) return;
2640
- tooltip.style.left = `${fx}px`;
2641
- tooltip.style.top = `${fy}px`;
2642
- });
2643
- }
2644
- function hide() {
2645
- tooltip.style.display = "none";
2646
- lastContentKey = "";
2647
- }
2648
- function destroy() {
2649
- document.removeEventListener("touchstart", handleDocumentTouch);
2650
- if (tooltip.parentNode) {
2651
- tooltip.parentNode.removeChild(tooltip);
2652
- }
2653
- }
2654
- return { show, hide, destroy };
2655
- }
2656
- function esc(str) {
2657
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
2658
- }
2659
-
2660
3121
  // src/graph-mount.ts
2661
- function resolveDarkMode(mode) {
3122
+ function resolveDarkMode2(mode) {
2662
3123
  if (mode === "force") return true;
2663
3124
  if (mode === "off" || mode === void 0) return false;
2664
3125
  if (typeof window !== "undefined" && window.matchMedia) {
@@ -2714,7 +3175,7 @@ function createGraph(container, spec, options) {
2714
3175
  }
2715
3176
  function compile() {
2716
3177
  const { width, height } = getContainerDimensions();
2717
- const darkMode = resolveDarkMode(options?.darkMode);
3178
+ const darkMode = resolveDarkMode2(options?.darkMode);
2718
3179
  const compileOpts = {
2719
3180
  width,
2720
3181
  height,
@@ -2786,7 +3247,7 @@ function createGraph(container, spec, options) {
2786
3247
  }
2787
3248
  function createDOM() {
2788
3249
  const { width, height } = getContainerDimensions();
2789
- const isDark = resolveDarkMode(options?.darkMode);
3250
+ const isDark = resolveDarkMode2(options?.darkMode);
2790
3251
  wrapper = document.createElement("div");
2791
3252
  wrapper.className = isDark ? "oc-graph-wrapper oc-dark" : "oc-graph-wrapper";
2792
3253
  if (isDark) {
@@ -2805,7 +3266,7 @@ function createGraph(container, spec, options) {
2805
3266
  }
2806
3267
  chromeEl = document.createElement("div");
2807
3268
  chromeEl.className = "oc-graph-chrome";
2808
- renderChrome4();
3269
+ renderChrome5();
2809
3270
  wrapper.appendChild(chromeEl);
2810
3271
  canvas = document.createElement("canvas");
2811
3272
  canvas.className = "oc-graph-canvas";
@@ -2825,7 +3286,7 @@ function createGraph(container, spec, options) {
2825
3286
  renderer = new GraphCanvasRenderer(canvas);
2826
3287
  renderer.resize(width, canvasHeight);
2827
3288
  }
2828
- function renderChrome4() {
3289
+ function renderChrome5() {
2829
3290
  if (!chromeEl) return;
2830
3291
  let html = "";
2831
3292
  if (compilation.chrome.title) {
@@ -3139,7 +3600,7 @@ function createGraph(container, spec, options) {
3139
3600
  compilation = compile();
3140
3601
  adjacencyMap = buildAdjacencyMap(compilation.edges);
3141
3602
  buildDataMaps();
3142
- renderChrome4();
3603
+ renderChrome5();
3143
3604
  renderLegend3();
3144
3605
  initSimulation();
3145
3606
  initInteraction();
@@ -3174,7 +3635,7 @@ function createGraph(container, spec, options) {
3174
3635
  };
3175
3636
  });
3176
3637
  spatialIndex.rebuild(positionedNodes);
3177
- renderChrome4();
3638
+ renderChrome5();
3178
3639
  renderLegend3();
3179
3640
  needsRender = true;
3180
3641
  scheduleRender();
@@ -3217,123 +3678,62 @@ function createGraph(container, spec, options) {
3217
3678
  renderer = null;
3218
3679
  container.classList.remove("oc-dark");
3219
3680
  }
3220
- try {
3221
- compilation = compile();
3222
- adjacencyMap = buildAdjacencyMap(compilation.edges);
3223
- buildDataMaps();
3224
- createDOM();
3225
- initSimulation();
3226
- initInteraction();
3227
- } catch (err) {
3228
- console.error("[viz] Graph mount failed:", err);
3229
- return {
3230
- update() {
3231
- },
3232
- updateVisuals() {
3233
- },
3234
- search() {
3235
- },
3236
- clearSearch() {
3237
- },
3238
- zoomToFit() {
3239
- },
3240
- zoomToNode() {
3241
- },
3242
- selectNode() {
3243
- },
3244
- getSelectedNodes: () => [],
3245
- resize() {
3246
- },
3247
- destroy() {
3248
- }
3249
- };
3250
- }
3251
- if (options?.responsive !== false) {
3252
- disconnectResize = observeResize(container, () => {
3253
- doResize();
3254
- });
3255
- }
3256
- return {
3257
- update,
3258
- updateVisuals,
3259
- search,
3260
- clearSearch,
3261
- zoomToFit,
3262
- zoomToNode,
3263
- selectNode,
3264
- getSelectedNodes,
3265
- resize: doResize,
3266
- destroy
3267
- };
3268
- }
3269
- function escapeHtml(str) {
3270
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3271
- }
3272
-
3273
- // src/mount.ts
3274
- import { elementRef, getRepresentativeColor, isLayerSpec } from "@opendata-ai/openchart-core";
3275
- import { compileChart, compileLayer } from "@opendata-ai/openchart-engine";
3276
-
3277
- // src/animation.ts
3278
- function cancelAnimations(svg) {
3279
- if (svg) {
3280
- svg.classList.remove("oc-animate");
3281
- }
3282
- }
3283
- function setupAnimationCleanup(svg, onComplete) {
3284
- const style = svg.style;
3285
- const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 600;
3286
- const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
3287
- const annotationDelay = parseFloat(style.getPropertyValue("--oc-annotation-delay")) || 200;
3288
- const animatedElements = svg.querySelectorAll("[data-animation-index]").length;
3289
- const totalStagger = stagger * Math.max(0, animatedElements - 1);
3290
- const totalTime = totalStagger + duration + annotationDelay + 500;
3291
- const timer2 = setTimeout(() => {
3292
- svg.classList.remove("oc-animate");
3293
- onComplete?.();
3294
- }, totalTime);
3295
- return () => {
3296
- clearTimeout(timer2);
3297
- cancelAnimations(svg);
3298
- };
3299
- }
3300
- function setupTableAnimationCleanup(wrapper) {
3301
- const style = wrapper.style;
3302
- const duration = parseFloat(style.getPropertyValue("--oc-animation-duration")) || 500;
3303
- const stagger = parseFloat(style.getPropertyValue("--oc-animation-stagger")) || 0;
3304
- const rows = wrapper.querySelectorAll("tbody tr").length;
3305
- const totalStagger = stagger * Math.max(0, rows - 1);
3306
- const totalTime = totalStagger + duration + 300;
3307
- const timer2 = setTimeout(() => {
3308
- wrapper.classList.remove("oc-animate");
3309
- }, totalTime);
3310
- return () => {
3311
- clearTimeout(timer2);
3312
- wrapper.classList.remove("oc-animate");
3313
- };
3314
- }
3315
-
3316
- // src/measure-text.ts
3317
- function createMeasureText() {
3318
- let canvas = null;
3319
- let ctx = null;
3320
- return (text, fontSize, fontWeight) => {
3321
- if (!canvas) {
3322
- canvas = document.createElement("canvas");
3323
- ctx = canvas.getContext("2d");
3324
- }
3325
- if (!ctx) {
3326
- return { width: text.length * fontSize * 0.6, height: fontSize * 1.2 };
3327
- }
3328
- const weight = fontWeight ?? 400;
3329
- ctx.font = `${weight} ${fontSize}px Inter, sans-serif`;
3330
- const metrics = ctx.measureText(text);
3681
+ try {
3682
+ compilation = compile();
3683
+ adjacencyMap = buildAdjacencyMap(compilation.edges);
3684
+ buildDataMaps();
3685
+ createDOM();
3686
+ initSimulation();
3687
+ initInteraction();
3688
+ } catch (err) {
3689
+ console.error("[viz] Graph mount failed:", err);
3331
3690
  return {
3332
- width: metrics.width,
3333
- height: fontSize * 1.2
3691
+ update() {
3692
+ },
3693
+ updateVisuals() {
3694
+ },
3695
+ search() {
3696
+ },
3697
+ clearSearch() {
3698
+ },
3699
+ zoomToFit() {
3700
+ },
3701
+ zoomToNode() {
3702
+ },
3703
+ selectNode() {
3704
+ },
3705
+ getSelectedNodes: () => [],
3706
+ resize() {
3707
+ },
3708
+ destroy() {
3709
+ }
3334
3710
  };
3711
+ }
3712
+ if (options?.responsive !== false) {
3713
+ disconnectResize = observeResize(container, () => {
3714
+ doResize();
3715
+ });
3716
+ }
3717
+ return {
3718
+ update,
3719
+ updateVisuals,
3720
+ search,
3721
+ clearSearch,
3722
+ zoomToFit,
3723
+ zoomToNode,
3724
+ selectNode,
3725
+ getSelectedNodes,
3726
+ resize: doResize,
3727
+ destroy
3335
3728
  };
3336
3729
  }
3730
+ function escapeHtml(str) {
3731
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3732
+ }
3733
+
3734
+ // src/mount.ts
3735
+ import { elementRef, getRepresentativeColor, isLayerSpec } from "@opendata-ai/openchart-core";
3736
+ import { compileChart, compileLayer } from "@opendata-ai/openchart-engine";
3337
3737
 
3338
3738
  // src/svg-renderer.ts
3339
3739
  import { clampStaggerDelay } from "@opendata-ai/openchart-engine";
@@ -3348,7 +3748,7 @@ function nextSvgId(prefix) {
3348
3748
  }
3349
3749
 
3350
3750
  // src/gradient-utils.ts
3351
- var SVG_NS = "http://www.w3.org/2000/svg";
3751
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
3352
3752
  function gradientKey(def) {
3353
3753
  return sortedStringify(def);
3354
3754
  }
@@ -3368,7 +3768,7 @@ function createGradientElement(def, id) {
3368
3768
  return createRadialGradient(def, id);
3369
3769
  }
3370
3770
  function createLinearGradient(def, id) {
3371
- const el = document.createElementNS(SVG_NS, "linearGradient");
3771
+ const el = document.createElementNS(SVG_NS2, "linearGradient");
3372
3772
  el.setAttribute("id", id);
3373
3773
  el.setAttribute("gradientUnits", "objectBoundingBox");
3374
3774
  el.setAttribute("x1", String(def.x1 ?? 0));
@@ -3381,7 +3781,7 @@ function createLinearGradient(def, id) {
3381
3781
  return el;
3382
3782
  }
3383
3783
  function createRadialGradient(def, id) {
3384
- const el = document.createElementNS(SVG_NS, "radialGradient");
3784
+ const el = document.createElementNS(SVG_NS2, "radialGradient");
3385
3785
  el.setAttribute("id", id);
3386
3786
  el.setAttribute("gradientUnits", "objectBoundingBox");
3387
3787
  el.setAttribute("cx", String(def.x2 ?? 0.5));
@@ -3396,7 +3796,7 @@ function createRadialGradient(def, id) {
3396
3796
  return el;
3397
3797
  }
3398
3798
  function appendStop(parent, stop) {
3399
- const stopEl = document.createElementNS(SVG_NS, "stop");
3799
+ const stopEl = document.createElementNS(SVG_NS2, "stop");
3400
3800
  stopEl.setAttribute("offset", String(stop.offset));
3401
3801
  stopEl.setAttribute("stop-color", stop.color);
3402
3802
  if (stop.opacity !== void 0) {
@@ -3429,12 +3829,12 @@ function resolveMarkFill(fill, gradientMap) {
3429
3829
 
3430
3830
  // src/renderers/svg-dom.ts
3431
3831
  import { estimateTextWidth } from "@opendata-ai/openchart-core";
3432
- var SVG_NS2 = "http://www.w3.org/2000/svg";
3433
- var XLINK_NS = "http://www.w3.org/1999/xlink";
3434
- function createSVGElement(tag) {
3435
- return document.createElementNS(SVG_NS2, tag);
3832
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
3833
+ var XLINK_NS2 = "http://www.w3.org/1999/xlink";
3834
+ function createSVGElement2(tag) {
3835
+ return document.createElementNS(SVG_NS3, tag);
3436
3836
  }
3437
- function setAttrs(el, attrs) {
3837
+ function setAttrs2(el, attrs) {
3438
3838
  for (const [key, value] of Object.entries(attrs)) {
3439
3839
  el.setAttribute(key, String(value));
3440
3840
  }
@@ -3493,9 +3893,9 @@ function renderCurvedArrow(parent, from, to, stroke) {
3493
3893
  const uy = ty / tLen;
3494
3894
  const baseX = to.x - ux * arrowLen;
3495
3895
  const baseY = tipY - uy * arrowLen;
3496
- const path = createSVGElement("path");
3896
+ const path = createSVGElement2("path");
3497
3897
  path.setAttribute("class", "oc-annotation-connector");
3498
- setAttrs(path, {
3898
+ setAttrs2(path, {
3499
3899
  d: `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${baseX} ${baseY}`,
3500
3900
  fill: "none",
3501
3901
  stroke,
@@ -3504,9 +3904,9 @@ function renderCurvedArrow(parent, from, to, stroke) {
3504
3904
  parent.appendChild(path);
3505
3905
  const px = -uy;
3506
3906
  const py = ux;
3507
- const arrow = createSVGElement("polygon");
3907
+ const arrow = createSVGElement2("polygon");
3508
3908
  arrow.setAttribute("class", "oc-annotation-connector");
3509
- setAttrs(arrow, {
3909
+ setAttrs2(arrow, {
3510
3910
  points: [
3511
3911
  `${to.x},${tipY}`,
3512
3912
  `${baseX + px * arrowWidth},${baseY + py * arrowWidth}`,
@@ -3517,16 +3917,16 @@ function renderCurvedArrow(parent, from, to, stroke) {
3517
3917
  parent.appendChild(arrow);
3518
3918
  }
3519
3919
  function renderAnnotation(parent, annotation, index2, bgColor) {
3520
- const g = createSVGElement("g");
3920
+ const g = createSVGElement2("g");
3521
3921
  g.setAttribute("class", `oc-annotation oc-annotation-${annotation.type}`);
3522
3922
  g.setAttribute("data-annotation-index", String(index2));
3523
3923
  if (annotation.id) {
3524
3924
  g.setAttribute("data-annotation-id", annotation.id);
3525
3925
  }
3526
3926
  if (annotation.rect) {
3527
- const rect = createSVGElement("rect");
3927
+ const rect = createSVGElement2("rect");
3528
3928
  rect.setAttribute("class", "oc-annotation-range");
3529
- setAttrs(rect, {
3929
+ setAttrs2(rect, {
3530
3930
  x: annotation.rect.x,
3531
3931
  y: annotation.rect.y,
3532
3932
  width: annotation.rect.width,
@@ -3539,9 +3939,9 @@ function renderAnnotation(parent, annotation, index2, bgColor) {
3539
3939
  g.appendChild(rect);
3540
3940
  }
3541
3941
  if (annotation.line) {
3542
- const line = createSVGElement("line");
3942
+ const line = createSVGElement2("line");
3543
3943
  line.setAttribute("class", "oc-annotation-line");
3544
- setAttrs(line, {
3944
+ setAttrs2(line, {
3545
3945
  x1: annotation.line.start.x,
3546
3946
  y1: annotation.line.start.y,
3547
3947
  x2: annotation.line.end.x,
@@ -3560,9 +3960,9 @@ function renderAnnotation(parent, annotation, index2, bgColor) {
3560
3960
  if (c2.style === "curve") {
3561
3961
  renderCurvedArrow(g, c2.from, c2.to, c2.stroke);
3562
3962
  } else {
3563
- const connector = createSVGElement("line");
3963
+ const connector = createSVGElement2("line");
3564
3964
  connector.setAttribute("class", "oc-annotation-connector");
3565
- setAttrs(connector, {
3965
+ setAttrs2(connector, {
3566
3966
  x1: c2.from.x,
3567
3967
  y1: c2.from.y,
3568
3968
  x2: c2.to.x,
@@ -3574,9 +3974,9 @@ function renderAnnotation(parent, annotation, index2, bgColor) {
3574
3974
  g.appendChild(connector);
3575
3975
  }
3576
3976
  }
3577
- const text = createSVGElement("text");
3977
+ const text = createSVGElement2("text");
3578
3978
  text.setAttribute("class", "oc-annotation-label");
3579
- setAttrs(text, { x: annotation.label.x, y: annotation.label.y });
3979
+ setAttrs2(text, { x: annotation.label.x, y: annotation.label.y });
3580
3980
  applyTextStyle(text, annotation.label.style);
3581
3981
  const lines = annotation.label.text.split("\n");
3582
3982
  const fontSize = annotation.label.style.fontSize ?? 12;
@@ -3585,8 +3985,8 @@ function renderAnnotation(parent, annotation, index2, bgColor) {
3585
3985
  if (isMultiLine) {
3586
3986
  text.setAttribute("text-anchor", "middle");
3587
3987
  for (let i = 0; i < lines.length; i++) {
3588
- const tspan = createSVGElement("tspan");
3589
- setAttrs(tspan, { x: annotation.label.x, dy: i === 0 ? 0 : lineHeight });
3988
+ const tspan = createSVGElement2("tspan");
3989
+ setAttrs2(tspan, { x: annotation.label.x, dy: i === 0 ? 0 : lineHeight });
3590
3990
  tspan.textContent = lines[i];
3591
3991
  text.appendChild(tspan);
3592
3992
  }
@@ -3599,9 +3999,9 @@ function renderAnnotation(parent, annotation, index2, bgColor) {
3599
3999
  const totalHeight = lines.length * lineHeight;
3600
4000
  const pad = 3;
3601
4001
  const bgX = isMultiLine ? annotation.label.x - maxLineWidth / 2 - pad : annotation.label.x - pad;
3602
- const bgRect = createSVGElement("rect");
4002
+ const bgRect = createSVGElement2("rect");
3603
4003
  bgRect.setAttribute("class", "oc-annotation-bg");
3604
- setAttrs(bgRect, {
4004
+ setAttrs2(bgRect, {
3605
4005
  x: bgX,
3606
4006
  y: annotation.label.y - fontSize + (lineHeight - fontSize) / 2 - pad,
3607
4007
  width: maxLineWidth + pad * 2,
@@ -3622,7 +4022,7 @@ function renderAnnotation(parent, annotation, index2, bgColor) {
3622
4022
  }
3623
4023
  function renderAnnotations(parent, layout) {
3624
4024
  if (layout.annotations.length === 0) return;
3625
- const g = createSVGElement("g");
4025
+ const g = createSVGElement2("g");
3626
4026
  g.setAttribute("class", "oc-annotations");
3627
4027
  const bgColor = layout.theme.colors.background;
3628
4028
  for (let i = 0; i < layout.annotations.length; i++) {
@@ -3638,11 +4038,11 @@ import {
3638
4038
  TICK_LABEL_OFFSET
3639
4039
  } from "@opendata-ai/openchart-core";
3640
4040
  function appendCompoundLabel(parent, primaryText, subtitle, fontWeight) {
3641
- const primarySpan = createSVGElement("tspan");
4041
+ const primarySpan = createSVGElement2("tspan");
3642
4042
  primarySpan.setAttribute("font-weight", String(fontWeight));
3643
4043
  primarySpan.textContent = primaryText;
3644
4044
  parent.appendChild(primarySpan);
3645
- const subtitleSpan = createSVGElement("tspan");
4045
+ const subtitleSpan = createSVGElement2("tspan");
3646
4046
  subtitleSpan.setAttribute("dx", "0.5em");
3647
4047
  subtitleSpan.textContent = subtitle;
3648
4048
  subtitleSpan.setAttribute("font-weight", "400");
@@ -3650,14 +4050,14 @@ function appendCompoundLabel(parent, primaryText, subtitle, fontWeight) {
3650
4050
  parent.appendChild(subtitleSpan);
3651
4051
  }
3652
4052
  function renderAxis(parent, axis, orientation, layout) {
3653
- const g = createSVGElement("g");
4053
+ const g = createSVGElement2("g");
3654
4054
  const isRight = orientation === "y" && axis.orient === "right";
3655
4055
  g.setAttribute("class", `oc-axis oc-axis-${isRight ? "y2" : orientation}`);
3656
4056
  const { area } = layout;
3657
- if (orientation === "x") {
3658
- const line = createSVGElement("line");
4057
+ if (orientation === "x" && axis.domainLine !== false) {
4058
+ const line = createSVGElement2("line");
3659
4059
  line.setAttribute("class", "oc-axis-line");
3660
- setAttrs(line, {
4060
+ setAttrs2(line, {
3661
4061
  x1: axis.start.x,
3662
4062
  y1: axis.start.y,
3663
4063
  x2: axis.end.x,
@@ -3669,12 +4069,12 @@ function renderAxis(parent, axis, orientation, layout) {
3669
4069
  }
3670
4070
  for (const tick of axis.ticks) {
3671
4071
  if (orientation === "x") {
3672
- const label = createSVGElement("text");
4072
+ const label = createSVGElement2("text");
3673
4073
  label.setAttribute("class", "oc-axis-tick");
3674
4074
  if (axis.tickAngle && Math.abs(axis.tickAngle) > 10) {
3675
4075
  const labelX = tick.position;
3676
4076
  const labelY = area.y + area.height + 6;
3677
- setAttrs(label, {
4077
+ setAttrs2(label, {
3678
4078
  x: labelX,
3679
4079
  y: labelY,
3680
4080
  "text-anchor": axis.tickAngle < 0 ? "end" : "start",
@@ -3682,7 +4082,7 @@ function renderAxis(parent, axis, orientation, layout) {
3682
4082
  transform: `rotate(${axis.tickAngle}, ${labelX}, ${labelY})`
3683
4083
  });
3684
4084
  } else {
3685
- setAttrs(label, {
4085
+ setAttrs2(label, {
3686
4086
  x: tick.position,
3687
4087
  y: area.y + area.height + 14,
3688
4088
  "text-anchor": "middle"
@@ -3692,9 +4092,9 @@ function renderAxis(parent, axis, orientation, layout) {
3692
4092
  label.textContent = tick.label;
3693
4093
  g.appendChild(label);
3694
4094
  } else {
3695
- const label = createSVGElement("text");
4095
+ const label = createSVGElement2("text");
3696
4096
  label.setAttribute("class", "oc-axis-tick");
3697
- setAttrs(label, {
4097
+ setAttrs2(label, {
3698
4098
  x: isRight ? area.x + area.width + TICK_LABEL_OFFSET : area.x - TICK_LABEL_OFFSET,
3699
4099
  y: tick.position,
3700
4100
  "text-anchor": isRight ? "start" : "end",
@@ -3732,7 +4132,7 @@ function renderAxis(parent, axis, orientation, layout) {
3732
4132
  primaryText = ellipsis;
3733
4133
  }
3734
4134
  appendCompoundLabel(label, primaryText, tick.subtitle, fontWeight);
3735
- const titleEl = createSVGElement("title");
4135
+ const titleEl = createSVGElement2("title");
3736
4136
  titleEl.textContent = `${tick.label} ${tick.subtitle}`;
3737
4137
  label.appendChild(titleEl);
3738
4138
  } else {
@@ -3755,7 +4155,7 @@ function renderAxis(parent, axis, orientation, layout) {
3755
4155
  }
3756
4156
  }
3757
4157
  label.textContent = lo > 0 ? tick.label.slice(0, lo).trimEnd() + ellipsis : ellipsis;
3758
- const titleEl = createSVGElement("title");
4158
+ const titleEl = createSVGElement2("title");
3759
4159
  titleEl.textContent = tick.label;
3760
4160
  label.appendChild(titleEl);
3761
4161
  } else {
@@ -3770,10 +4170,10 @@ function renderAxis(parent, axis, orientation, layout) {
3770
4170
  }
3771
4171
  if (!isRight) {
3772
4172
  for (const gridline of axis.gridlines) {
3773
- const gl = createSVGElement("line");
4173
+ const gl = createSVGElement2("line");
3774
4174
  gl.setAttribute("class", "oc-gridline");
3775
4175
  if (orientation === "y") {
3776
- setAttrs(gl, {
4176
+ setAttrs2(gl, {
3777
4177
  x1: area.x,
3778
4178
  y1: gridline.position,
3779
4179
  x2: area.x + area.width,
@@ -3783,7 +4183,7 @@ function renderAxis(parent, axis, orientation, layout) {
3783
4183
  "stroke-opacity": 0.6
3784
4184
  });
3785
4185
  } else {
3786
- setAttrs(gl, {
4186
+ setAttrs2(gl, {
3787
4187
  x1: gridline.position,
3788
4188
  y1: area.y,
3789
4189
  x2: gridline.position,
@@ -3797,7 +4197,7 @@ function renderAxis(parent, axis, orientation, layout) {
3797
4197
  }
3798
4198
  }
3799
4199
  if (axis.label && axis.labelStyle) {
3800
- const axisLabel = createSVGElement("text");
4200
+ const axisLabel = createSVGElement2("text");
3801
4201
  axisLabel.setAttribute("class", "oc-axis-title");
3802
4202
  applyTextStyle(axisLabel, axis.labelStyle);
3803
4203
  axisLabel.textContent = axis.label;
@@ -3817,7 +4217,7 @@ function renderAxis(parent, axis, orientation, layout) {
3817
4217
  const rotatedHeight = Math.min(maxLabelWidth * Math.sin(angleRad) + 6, 120);
3818
4218
  titleY = area.y + area.height + rotatedHeight + 14;
3819
4219
  }
3820
- setAttrs(axisLabel, {
4220
+ setAttrs2(axisLabel, {
3821
4221
  x: area.x + area.width / 2,
3822
4222
  y: titleY,
3823
4223
  "text-anchor": "middle"
@@ -3825,7 +4225,7 @@ function renderAxis(parent, axis, orientation, layout) {
3825
4225
  } else if (isRight) {
3826
4226
  const titleOffset = getAxisTitleOffset(layout.dimensions.width);
3827
4227
  const titleX = area.x + area.width + titleOffset;
3828
- setAttrs(axisLabel, {
4228
+ setAttrs2(axisLabel, {
3829
4229
  x: titleX,
3830
4230
  y: area.y + area.height / 2,
3831
4231
  "text-anchor": "middle",
@@ -3833,7 +4233,7 @@ function renderAxis(parent, axis, orientation, layout) {
3833
4233
  });
3834
4234
  } else {
3835
4235
  const titleOffset = getAxisTitleOffset(layout.dimensions.width);
3836
- setAttrs(axisLabel, {
4236
+ setAttrs2(axisLabel, {
3837
4237
  x: area.x - titleOffset,
3838
4238
  y: area.y + area.height / 2,
3839
4239
  "text-anchor": "middle",
@@ -3858,7 +4258,7 @@ function renderAxes(parent, layout) {
3858
4258
 
3859
4259
  // src/renderers/brand.ts
3860
4260
  import { BRAND_FONT_SIZE as BRAND_FONT_SIZE2, BRAND_MIN_WIDTH as BRAND_MIN_WIDTH2 } from "@opendata-ai/openchart-core";
3861
- var BRAND_URL = "https://tryopendata.ai";
4261
+ var BRAND_URL2 = "https://tryopendata.ai";
3862
4262
  function renderBrand(parent, layout) {
3863
4263
  if (layout.dimensions.width < BRAND_MIN_WIDTH2) return;
3864
4264
  const { width } = layout.dimensions;
@@ -3870,15 +4270,15 @@ function renderBrand(parent, layout) {
3870
4270
  const bottomOffset = layout.area.y + layout.area.height + xAxisExtent;
3871
4271
  const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;
3872
4272
  const chromeY = firstBottom ? bottomOffset + firstBottom.y : bottomOffset + layout.theme.spacing.chartToFooter;
3873
- const a2 = createSVGElement("a");
3874
- a2.setAttribute("href", BRAND_URL);
3875
- a2.setAttributeNS(XLINK_NS, "xlink:href", BRAND_URL);
4273
+ const a2 = createSVGElement2("a");
4274
+ a2.setAttribute("href", BRAND_URL2);
4275
+ a2.setAttributeNS(XLINK_NS2, "xlink:href", BRAND_URL2);
3876
4276
  a2.setAttribute("target", "_blank");
3877
4277
  a2.setAttribute("rel", "noopener");
3878
4278
  a2.setAttribute("class", "oc-chrome-ref");
3879
4279
  const BRAND_LARGE = 16;
3880
- const text = createSVGElement("text");
3881
- setAttrs(text, {
4280
+ const text = createSVGElement2("text");
4281
+ setAttrs2(text, {
3882
4282
  x: rightEdge,
3883
4283
  y: chromeY + BRAND_LARGE,
3884
4284
  "dominant-baseline": "alphabetic",
@@ -3888,16 +4288,16 @@ function renderBrand(parent, layout) {
3888
4288
  "fill-opacity": 0.55
3889
4289
  });
3890
4290
  text.style.setProperty("fill", fill);
3891
- const trySpan = createSVGElement("tspan");
4291
+ const trySpan = createSVGElement2("tspan");
3892
4292
  trySpan.setAttribute("font-weight", "500");
3893
4293
  trySpan.textContent = "try";
3894
4294
  text.appendChild(trySpan);
3895
- const openDataSpan = createSVGElement("tspan");
4295
+ const openDataSpan = createSVGElement2("tspan");
3896
4296
  openDataSpan.setAttribute("font-weight", "600");
3897
4297
  openDataSpan.setAttribute("font-size", String(BRAND_LARGE));
3898
4298
  openDataSpan.textContent = "OpenData";
3899
4299
  text.appendChild(openDataSpan);
3900
- const aiSpan = createSVGElement("tspan");
4300
+ const aiSpan = createSVGElement2("tspan");
3901
4301
  aiSpan.setAttribute("font-weight", "500");
3902
4302
  aiSpan.textContent = ".ai";
3903
4303
  text.appendChild(aiSpan);
@@ -3908,8 +4308,8 @@ function renderBrand(parent, layout) {
3908
4308
  // src/renderers/chrome.ts
3909
4309
  import { wrapText } from "@opendata-ai/openchart-core";
3910
4310
  function renderChromeElement(parent, element, className, chromeKey, measureText) {
3911
- const text = createSVGElement("text");
3912
- setAttrs(text, { x: element.x, y: element.y });
4311
+ const text = createSVGElement2("text");
4312
+ setAttrs2(text, { x: element.x, y: element.y });
3913
4313
  applyTextStyle(text, element.style);
3914
4314
  text.setAttribute("class", className);
3915
4315
  text.setAttribute("data-chrome-key", chromeKey);
@@ -3925,16 +4325,16 @@ function renderChromeElement(parent, element, className, chromeKey, measureText)
3925
4325
  } else {
3926
4326
  const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
3927
4327
  for (let i = 0; i < lines.length; i++) {
3928
- const tspan = createSVGElement("tspan");
3929
- setAttrs(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
4328
+ const tspan = createSVGElement2("tspan");
4329
+ setAttrs2(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
3930
4330
  tspan.textContent = lines[i];
3931
4331
  text.appendChild(tspan);
3932
4332
  }
3933
4333
  }
3934
4334
  parent.appendChild(text);
3935
4335
  }
3936
- function renderChrome(parent, layout) {
3937
- const g = createSVGElement("g");
4336
+ function renderChrome2(parent, layout) {
4337
+ const g = createSVGElement2("g");
3938
4338
  g.setAttribute("class", "oc-chrome");
3939
4339
  const { chrome, measureText } = layout;
3940
4340
  if (chrome.title) {
@@ -3982,7 +4382,7 @@ function isCategorical(legend) {
3982
4382
  }
3983
4383
  function renderLegend(parent, legend) {
3984
4384
  if (!isCategorical(legend) || legend.entries.length === 0) return;
3985
- const g = createSVGElement("g");
4385
+ const g = createSVGElement2("g");
3986
4386
  g.setAttribute("class", "oc-legend");
3987
4387
  g.setAttribute("role", "list");
3988
4388
  g.setAttribute("aria-label", "Chart legend");
@@ -4003,7 +4403,7 @@ function renderLegend(parent, legend) {
4003
4403
  offsetY += legend.swatchSize + 6;
4004
4404
  }
4005
4405
  }
4006
- const entryG = createSVGElement("g");
4406
+ const entryG = createSVGElement2("g");
4007
4407
  entryG.setAttribute("class", "oc-legend-entry");
4008
4408
  entryG.setAttribute("role", "listitem");
4009
4409
  entryG.setAttribute("data-legend-index", String(i));
@@ -4023,8 +4423,8 @@ function renderLegend(parent, legend) {
4023
4423
  }
4024
4424
  }
4025
4425
  if (entry.shape === "circle") {
4026
- const circle = createSVGElement("circle");
4027
- setAttrs(circle, {
4426
+ const circle = createSVGElement2("circle");
4427
+ setAttrs2(circle, {
4028
4428
  cx: offsetX + legend.swatchSize / 2,
4029
4429
  cy: offsetY + legend.swatchSize / 2,
4030
4430
  r: legend.swatchSize / 2,
@@ -4032,8 +4432,8 @@ function renderLegend(parent, legend) {
4032
4432
  });
4033
4433
  entryG.appendChild(circle);
4034
4434
  } else if (entry.shape === "line") {
4035
- const line = createSVGElement("line");
4036
- setAttrs(line, {
4435
+ const line = createSVGElement2("line");
4436
+ setAttrs2(line, {
4037
4437
  x1: offsetX,
4038
4438
  y1: offsetY + legend.swatchSize / 2,
4039
4439
  x2: offsetX + legend.swatchSize,
@@ -4042,8 +4442,8 @@ function renderLegend(parent, legend) {
4042
4442
  "stroke-width": 2
4043
4443
  });
4044
4444
  entryG.appendChild(line);
4045
- const dot = createSVGElement("circle");
4046
- setAttrs(dot, {
4445
+ const dot = createSVGElement2("circle");
4446
+ setAttrs2(dot, {
4047
4447
  cx: offsetX + legend.swatchSize / 2,
4048
4448
  cy: offsetY + legend.swatchSize / 2,
4049
4449
  r: 2.5,
@@ -4051,8 +4451,8 @@ function renderLegend(parent, legend) {
4051
4451
  });
4052
4452
  entryG.appendChild(dot);
4053
4453
  } else {
4054
- const rect = createSVGElement("rect");
4055
- setAttrs(rect, {
4454
+ const rect = createSVGElement2("rect");
4455
+ setAttrs2(rect, {
4056
4456
  x: offsetX,
4057
4457
  y: offsetY,
4058
4458
  width: legend.swatchSize,
@@ -4062,8 +4462,8 @@ function renderLegend(parent, legend) {
4062
4462
  });
4063
4463
  entryG.appendChild(rect);
4064
4464
  }
4065
- const label = createSVGElement("text");
4066
- setAttrs(label, {
4465
+ const label = createSVGElement2("text");
4466
+ setAttrs2(label, {
4067
4467
  x: offsetX + legend.swatchSize + legend.swatchGap,
4068
4468
  y: offsetY + legend.swatchSize / 2,
4069
4469
  "dominant-baseline": "central"
@@ -4109,14 +4509,14 @@ function registerMarkRenderer(type, renderer) {
4109
4509
  markRenderers[type] = renderer;
4110
4510
  }
4111
4511
  function renderLineMark(mark, index2) {
4112
- const g = createSVGElement("g");
4512
+ const g = createSVGElement2("g");
4113
4513
  g.setAttribute("data-mark-id", `line-${mark.seriesKey ?? index2}`);
4114
4514
  g.setAttribute("class", "oc-mark oc-mark-line");
4115
4515
  stampAnimationAttrs(g, mark, index2);
4116
4516
  if (mark.points.length > 1) {
4117
- const path = createSVGElement("path");
4517
+ const path = createSVGElement2("path");
4118
4518
  const d = mark.path ?? mark.points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x},${p.y}`).join(" ");
4119
- setAttrs(path, {
4519
+ setAttrs2(path, {
4120
4520
  d,
4121
4521
  fill: "none",
4122
4522
  stroke: mark.stroke,
@@ -4131,19 +4531,19 @@ function renderLineMark(mark, index2) {
4131
4531
  g.appendChild(path);
4132
4532
  }
4133
4533
  if (mark.label?.visible) {
4134
- const label = createSVGElement("text");
4534
+ const label = createSVGElement2("text");
4135
4535
  label.setAttribute("class", "oc-mark-label");
4136
4536
  if (mark.seriesKey) {
4137
4537
  label.setAttribute("data-series", mark.seriesKey);
4138
4538
  }
4139
- setAttrs(label, { x: mark.label.x, y: mark.label.y });
4539
+ setAttrs2(label, { x: mark.label.x, y: mark.label.y });
4140
4540
  applyTextStyle(label, mark.label.style);
4141
4541
  label.textContent = mark.label.text;
4142
4542
  g.appendChild(label);
4143
4543
  if (mark.label.connector) {
4144
- const connector = createSVGElement("line");
4544
+ const connector = createSVGElement2("line");
4145
4545
  connector.setAttribute("class", "oc-mark-connector");
4146
- setAttrs(connector, {
4546
+ setAttrs2(connector, {
4147
4547
  x1: mark.label.connector.from.x,
4148
4548
  y1: mark.label.connector.from.y,
4149
4549
  x2: mark.label.connector.to.x,
@@ -4158,13 +4558,13 @@ function renderLineMark(mark, index2) {
4158
4558
  return g;
4159
4559
  }
4160
4560
  function renderAreaMark(mark, index2) {
4161
- const g = createSVGElement("g");
4561
+ const g = createSVGElement2("g");
4162
4562
  g.setAttribute("data-mark-id", `area-${mark.seriesKey ?? index2}`);
4163
4563
  g.setAttribute("class", "oc-mark oc-mark-area");
4164
4564
  stampAnimationAttrs(g, mark, index2);
4165
4565
  if (mark.path) {
4166
- const fill = createSVGElement("path");
4167
- setAttrs(fill, {
4566
+ const fill = createSVGElement2("path");
4567
+ setAttrs2(fill, {
4168
4568
  d: mark.path,
4169
4569
  fill: resolveMarkFill(mark.fill, currentGradientMap),
4170
4570
  "fill-opacity": mark.fillOpacity,
@@ -4172,9 +4572,9 @@ function renderAreaMark(mark, index2) {
4172
4572
  });
4173
4573
  g.appendChild(fill);
4174
4574
  if (mark.stroke && mark.topPath) {
4175
- const strokePath = createSVGElement("path");
4575
+ const strokePath = createSVGElement2("path");
4176
4576
  strokePath.setAttribute("class", "oc-area-top");
4177
- setAttrs(strokePath, {
4577
+ setAttrs2(strokePath, {
4178
4578
  d: mark.topPath,
4179
4579
  fill: "none",
4180
4580
  stroke: mark.stroke,
@@ -4186,15 +4586,15 @@ function renderAreaMark(mark, index2) {
4186
4586
  return g;
4187
4587
  }
4188
4588
  function renderRectMark(mark, index2) {
4189
- const g = createSVGElement("g");
4589
+ const g = createSVGElement2("g");
4190
4590
  g.setAttribute("data-mark-id", `rect-${index2}`);
4191
4591
  g.setAttribute("class", "oc-mark oc-mark-rect");
4192
4592
  stampAnimationAttrs(g, mark, index2);
4193
4593
  if (currentAnimation?.enabled && mark.orient === "horizontal") {
4194
4594
  g.setAttribute("data-orient", "horizontal");
4195
4595
  }
4196
- const rect = createSVGElement("rect");
4197
- setAttrs(rect, {
4596
+ const rect = createSVGElement2("rect");
4597
+ setAttrs2(rect, {
4198
4598
  x: mark.x,
4199
4599
  y: mark.y,
4200
4600
  width: mark.width,
@@ -4208,13 +4608,13 @@ function renderRectMark(mark, index2) {
4208
4608
  rect.setAttribute("stroke-width", String(mark.strokeWidth));
4209
4609
  }
4210
4610
  if (mark.cornerRadius) {
4211
- setAttrs(rect, { rx: mark.cornerRadius, ry: mark.cornerRadius });
4611
+ setAttrs2(rect, { rx: mark.cornerRadius, ry: mark.cornerRadius });
4212
4612
  }
4213
4613
  g.appendChild(rect);
4214
4614
  if (mark.label?.visible) {
4215
- const label = createSVGElement("text");
4615
+ const label = createSVGElement2("text");
4216
4616
  label.setAttribute("class", "oc-mark-label");
4217
- setAttrs(label, { x: mark.label.x, y: mark.label.y });
4617
+ setAttrs2(label, { x: mark.label.x, y: mark.label.y });
4218
4618
  applyTextStyle(label, mark.label.style);
4219
4619
  label.textContent = mark.label.text;
4220
4620
  g.appendChild(label);
@@ -4222,13 +4622,13 @@ function renderRectMark(mark, index2) {
4222
4622
  return g;
4223
4623
  }
4224
4624
  function renderArcMark(mark, index2) {
4225
- const g = createSVGElement("g");
4625
+ const g = createSVGElement2("g");
4226
4626
  g.setAttribute("data-mark-id", `arc-${index2}`);
4227
4627
  g.setAttribute("class", "oc-mark oc-mark-arc");
4228
4628
  g.setAttribute("transform", `translate(${mark.center.x},${mark.center.y})`);
4229
4629
  stampAnimationAttrs(g, mark, index2);
4230
- const path = createSVGElement("path");
4231
- setAttrs(path, {
4630
+ const path = createSVGElement2("path");
4631
+ setAttrs2(path, {
4232
4632
  d: mark.path,
4233
4633
  fill: resolveMarkFill(mark.fill, currentGradientMap),
4234
4634
  stroke: mark.stroke,
@@ -4236,9 +4636,9 @@ function renderArcMark(mark, index2) {
4236
4636
  });
4237
4637
  g.appendChild(path);
4238
4638
  if (mark.label?.visible) {
4239
- const label = createSVGElement("text");
4639
+ const label = createSVGElement2("text");
4240
4640
  label.setAttribute("class", "oc-mark-label");
4241
- setAttrs(label, {
4641
+ setAttrs2(label, {
4242
4642
  x: mark.label.x - mark.center.x,
4243
4643
  y: mark.label.y - mark.center.y
4244
4644
  });
@@ -4249,11 +4649,11 @@ function renderArcMark(mark, index2) {
4249
4649
  return g;
4250
4650
  }
4251
4651
  function renderPointMark(mark, index2) {
4252
- const circle = createSVGElement("circle");
4652
+ const circle = createSVGElement2("circle");
4253
4653
  circle.setAttribute("data-mark-id", `point-${index2}`);
4254
4654
  circle.setAttribute("class", "oc-mark oc-mark-point");
4255
4655
  stampAnimationAttrs(circle, mark, index2);
4256
- setAttrs(circle, {
4656
+ setAttrs2(circle, {
4257
4657
  cx: mark.cx,
4258
4658
  cy: mark.cy,
4259
4659
  r: mark.r,
@@ -4267,11 +4667,11 @@ function renderPointMark(mark, index2) {
4267
4667
  return circle;
4268
4668
  }
4269
4669
  function renderTextMark(mark, index2) {
4270
- const text = createSVGElement("text");
4670
+ const text = createSVGElement2("text");
4271
4671
  text.setAttribute("data-mark-id", `textMark-${index2}`);
4272
4672
  text.setAttribute("class", "oc-mark oc-mark-text");
4273
4673
  stampAnimationAttrs(text, mark, index2);
4274
- setAttrs(text, {
4674
+ setAttrs2(text, {
4275
4675
  x: mark.x,
4276
4676
  y: mark.y,
4277
4677
  "font-size": mark.fontSize,
@@ -4291,11 +4691,11 @@ function renderTextMark(mark, index2) {
4291
4691
  return text;
4292
4692
  }
4293
4693
  function renderRuleMark(mark, index2) {
4294
- const line = createSVGElement("line");
4694
+ const line = createSVGElement2("line");
4295
4695
  line.setAttribute("data-mark-id", `rule-${index2}`);
4296
4696
  line.setAttribute("class", "oc-mark oc-mark-rule");
4297
4697
  stampAnimationAttrs(line, mark, index2);
4298
- setAttrs(line, {
4698
+ setAttrs2(line, {
4299
4699
  x1: mark.x1,
4300
4700
  y1: mark.y1,
4301
4701
  x2: mark.x2,
@@ -4312,13 +4712,13 @@ function renderRuleMark(mark, index2) {
4312
4712
  return line;
4313
4713
  }
4314
4714
  function renderTickMark(mark, index2) {
4315
- const line = createSVGElement("line");
4715
+ const line = createSVGElement2("line");
4316
4716
  line.setAttribute("data-mark-id", `tick-${index2}`);
4317
4717
  line.setAttribute("class", "oc-mark oc-mark-tick");
4318
4718
  stampAnimationAttrs(line, mark, index2);
4319
4719
  const half = mark.length / 2;
4320
4720
  if (mark.orient === "vertical") {
4321
- setAttrs(line, {
4721
+ setAttrs2(line, {
4322
4722
  x1: mark.x,
4323
4723
  y1: mark.y - half,
4324
4724
  x2: mark.x,
@@ -4327,7 +4727,7 @@ function renderTickMark(mark, index2) {
4327
4727
  "stroke-width": mark.strokeWidth
4328
4728
  });
4329
4729
  } else {
4330
- setAttrs(line, {
4730
+ setAttrs2(line, {
4331
4731
  x1: mark.x - half,
4332
4732
  y1: mark.y,
4333
4733
  x2: mark.x + half,
@@ -4363,7 +4763,7 @@ function getMarkSeries(mark) {
4363
4763
  return void 0;
4364
4764
  }
4365
4765
  function renderMarks(parent, layout) {
4366
- const g = createSVGElement("g");
4766
+ const g = createSVGElement2("g");
4367
4767
  g.setAttribute("class", "oc-marks");
4368
4768
  for (let i = 0; i < layout.marks.length; i++) {
4369
4769
  const mark = layout.marks[i];
@@ -4400,10 +4800,10 @@ var EASE_VAR_MAP = {
4400
4800
  function renderChartSVG(layout, container, opts) {
4401
4801
  const { width, height } = layout.dimensions;
4402
4802
  const animation = layout.animation;
4403
- const svg = createSVGElement("svg");
4404
- setAttrs(svg, {
4803
+ const svg = createSVGElement2("svg");
4804
+ setAttrs2(svg, {
4405
4805
  viewBox: `0 0 ${width} ${height}`,
4406
- xmlns: SVG_NS2,
4806
+ xmlns: SVG_NS3,
4407
4807
  // WebKit/iOS Safari getBBox() bug: text with dominant-baseline:hanging
4408
4808
  // reports bounding boxes extending above y=0. The SVG spec default
4409
4809
  // overflow is "hidden", which clips this phantom extent. Setting
@@ -4441,8 +4841,8 @@ function renderChartSVG(layout, container, opts) {
4441
4841
  svg.style.setProperty("--oc-stack-segment-duration", `${segDuration}ms`);
4442
4842
  }
4443
4843
  }
4444
- const bg = createSVGElement("rect");
4445
- setAttrs(bg, {
4844
+ const bg = createSVGElement2("rect");
4845
+ setAttrs2(bg, {
4446
4846
  x: 0,
4447
4847
  y: 0,
4448
4848
  width,
@@ -4451,15 +4851,20 @@ function renderChartSVG(layout, container, opts) {
4451
4851
  });
4452
4852
  svg.appendChild(bg);
4453
4853
  const clipId = nextSvgId("oc-clip");
4454
- const defs = createSVGElement("defs");
4455
- const clipPath = createSVGElement("clipPath");
4854
+ const defs = createSVGElement2("defs");
4855
+ const clipPath = createSVGElement2("clipPath");
4456
4856
  clipPath.setAttribute("id", clipId);
4457
- const clipRect = createSVGElement("rect");
4458
- setAttrs(clipRect, {
4857
+ const maxPointR = layout.marks.reduce(
4858
+ (max, m2) => m2.type === "point" && m2.r ? Math.max(max, m2.r) : max,
4859
+ 0
4860
+ );
4861
+ const clipPad = Math.max(maxPointR, 2);
4862
+ const clipRect = createSVGElement2("rect");
4863
+ setAttrs2(clipRect, {
4459
4864
  x: 0,
4460
- y: layout.area.y,
4865
+ y: layout.area.y - clipPad,
4461
4866
  width,
4462
- height: layout.area.height + 2
4867
+ height: layout.area.height + clipPad * 2
4463
4868
  });
4464
4869
  clipPath.appendChild(clipRect);
4465
4870
  defs.appendChild(clipPath);
@@ -4468,7 +4873,7 @@ function renderChartSVG(layout, container, opts) {
4468
4873
  setMarkRenderState({ animation, gradientMap });
4469
4874
  try {
4470
4875
  renderAxes(svg, layout);
4471
- const clippedGroup = createSVGElement("g");
4876
+ const clippedGroup = createSVGElement2("g");
4472
4877
  clippedGroup.setAttribute("clip-path", `url(#${clipId})`);
4473
4878
  renderMarks(clippedGroup, layout);
4474
4879
  const hasLineOrAreaWithDataPoints = layout.marks.some(
@@ -4476,8 +4881,8 @@ function renderChartSVG(layout, container, opts) {
4476
4881
  );
4477
4882
  const hasPointMarks = layout.marks.some((m2) => m2.type === "point");
4478
4883
  if (hasLineOrAreaWithDataPoints && !hasPointMarks) {
4479
- const overlay = createSVGElement("rect");
4480
- setAttrs(overlay, {
4884
+ const overlay = createSVGElement2("rect");
4885
+ setAttrs2(overlay, {
4481
4886
  x: layout.area.x,
4482
4887
  y: layout.area.y,
4483
4888
  width: layout.area.width,
@@ -4488,10 +4893,10 @@ function renderChartSVG(layout, container, opts) {
4488
4893
  overlay.setAttribute("data-voronoi-overlay", "true");
4489
4894
  clippedGroup.appendChild(overlay);
4490
4895
  if (opts?.crosshair) {
4491
- const crosshairLine = createSVGElement("line");
4896
+ const crosshairLine = createSVGElement2("line");
4492
4897
  crosshairLine.setAttribute("data-crosshair", "true");
4493
4898
  crosshairLine.setAttribute("class", "oc-crosshair");
4494
- setAttrs(crosshairLine, {
4899
+ setAttrs2(crosshairLine, {
4495
4900
  x1: 0,
4496
4901
  y1: layout.area.y,
4497
4902
  x2: 0,
@@ -4509,7 +4914,7 @@ function renderChartSVG(layout, container, opts) {
4509
4914
  svg.appendChild(clippedGroup);
4510
4915
  renderAnnotations(svg, layout);
4511
4916
  renderLegend(svg, layout.legend);
4512
- renderChrome(svg, layout);
4917
+ renderChrome2(svg, layout);
4513
4918
  if (layout.watermark) {
4514
4919
  renderBrand(svg, layout);
4515
4920
  }
@@ -4674,7 +5079,7 @@ function createTextEditOverlay(config) {
4674
5079
  }
4675
5080
 
4676
5081
  // src/mount.ts
4677
- function resolveDarkMode2(mode) {
5082
+ function resolveDarkMode3(mode) {
4678
5083
  if (mode === "force") return true;
4679
5084
  if (mode === "off" || mode === void 0) return false;
4680
5085
  if (typeof window !== "undefined" && window.matchMedia) {
@@ -5141,7 +5546,7 @@ function wireAnnotationDrag(svg, specAnnotations, onAnnotationEdit, onEdit, setD
5141
5546
  };
5142
5547
  }
5143
5548
  function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
5144
- const SVG_NS5 = "http://www.w3.org/2000/svg";
5549
+ const SVG_NS6 = "http://www.w3.org/2000/svg";
5145
5550
  const cleanups = [];
5146
5551
  const annotationGroups = svg.querySelectorAll(".oc-annotation-text");
5147
5552
  for (const el of annotationGroups) {
@@ -5180,7 +5585,7 @@ function wireConnectorEndpointDrag(svg, specAnnotations, onEdit, setDragging) {
5180
5585
  const createdHandles = [];
5181
5586
  for (const ep of endpoints) {
5182
5587
  if (!Number.isFinite(ep.cx) || !Number.isFinite(ep.cy)) continue;
5183
- const handleEl = document.createElementNS(SVG_NS5, "circle");
5588
+ const handleEl = document.createElementNS(SVG_NS6, "circle");
5184
5589
  handleEl.setAttribute("class", "oc-connector-handle");
5185
5590
  handleEl.setAttribute("data-endpoint", ep.name);
5186
5591
  handleEl.setAttribute("cx", String(ep.cx));
@@ -5800,7 +6205,7 @@ function createChart(container, spec, options) {
5800
6205
  const measureText = createMeasureText();
5801
6206
  function compile() {
5802
6207
  const { width, height } = getContainerDimensions();
5803
- const darkMode = resolveDarkMode2(options?.darkMode);
6208
+ const darkMode = resolveDarkMode3(options?.darkMode);
5804
6209
  const compileOpts = {
5805
6210
  width,
5806
6211
  height,
@@ -6171,7 +6576,7 @@ function createChart(container, spec, options) {
6171
6576
  }
6172
6577
  srTable = createScreenReaderTable(currentLayout, container);
6173
6578
  container.classList.add("oc-root");
6174
- const isDark = resolveDarkMode2(options?.darkMode);
6579
+ const isDark = resolveDarkMode3(options?.darkMode);
6175
6580
  if (isDark) {
6176
6581
  container.classList.add("oc-dark");
6177
6582
  } else {
@@ -6660,23 +7065,23 @@ import {
6660
7065
  wrapText as wrapText2
6661
7066
  } from "@opendata-ai/openchart-core";
6662
7067
  import { clampStaggerDelay as clampStaggerDelay2 } from "@opendata-ai/openchart-engine";
6663
- var SVG_NS3 = "http://www.w3.org/2000/svg";
6664
- var XLINK_NS2 = "http://www.w3.org/1999/xlink";
6665
- var BRAND_URL2 = "https://tryopendata.ai";
7068
+ var SVG_NS4 = "http://www.w3.org/2000/svg";
7069
+ var XLINK_NS3 = "http://www.w3.org/1999/xlink";
7070
+ var BRAND_URL3 = "https://tryopendata.ai";
6666
7071
  var EASE_VAR_MAP2 = {
6667
7072
  smooth: "var(--oc-ease-smooth)",
6668
7073
  snappy: "var(--oc-ease-snappy)"
6669
7074
  };
6670
- function createSVGElement2(tag) {
6671
- return document.createElementNS(SVG_NS3, tag);
7075
+ function createSVGElement3(tag) {
7076
+ return document.createElementNS(SVG_NS4, tag);
6672
7077
  }
6673
- function setAttrs2(el, attrs) {
7078
+ function setAttrs3(el, attrs) {
6674
7079
  for (const [key, value] of Object.entries(attrs)) {
6675
7080
  el.setAttribute(key, String(value));
6676
7081
  }
6677
7082
  }
6678
7083
  function applyTextStyle2(el, style) {
6679
- setAttrs2(el, {
7084
+ setAttrs3(el, {
6680
7085
  "font-family": style.fontFamily,
6681
7086
  "font-size": style.fontSize,
6682
7087
  "font-weight": style.fontWeight
@@ -6699,8 +7104,8 @@ function stampAnimationAttrs2(el, mark, fallbackIndex, animation) {
6699
7104
  el.style.setProperty("--oc-mark-index", String(idx));
6700
7105
  }
6701
7106
  function renderChromeElement2(parent, element, className, chromeKey, measureText) {
6702
- const text = createSVGElement2("text");
6703
- setAttrs2(text, { x: element.x, y: element.y });
7107
+ const text = createSVGElement3("text");
7108
+ setAttrs3(text, { x: element.x, y: element.y });
6704
7109
  applyTextStyle2(text, element.style);
6705
7110
  text.setAttribute("class", className);
6706
7111
  text.setAttribute("data-chrome-key", chromeKey);
@@ -6716,16 +7121,16 @@ function renderChromeElement2(parent, element, className, chromeKey, measureText
6716
7121
  } else {
6717
7122
  const lineHeight = element.style.fontSize * (element.style.lineHeight ?? 1.3);
6718
7123
  for (let i = 0; i < lines.length; i++) {
6719
- const tspan = createSVGElement2("tspan");
6720
- setAttrs2(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
7124
+ const tspan = createSVGElement3("tspan");
7125
+ setAttrs3(tspan, { x: element.x, dy: i === 0 ? 0 : lineHeight });
6721
7126
  tspan.textContent = lines[i];
6722
7127
  text.appendChild(tspan);
6723
7128
  }
6724
7129
  }
6725
7130
  parent.appendChild(text);
6726
7131
  }
6727
- function renderChrome2(parent, layout) {
6728
- const g = createSVGElement2("g");
7132
+ function renderChrome3(parent, layout) {
7133
+ const g = createSVGElement3("g");
6729
7134
  g.setAttribute("class", "oc-chrome");
6730
7135
  const { chrome, measureText } = layout;
6731
7136
  if (chrome.title) {
@@ -6774,15 +7179,15 @@ function renderBrand2(parent, layout) {
6774
7179
  const { chrome } = layout;
6775
7180
  const firstBottom = chrome.source ?? chrome.byline ?? chrome.footer;
6776
7181
  const chromeY = firstBottom ? bottomOffset + firstBottom.y : bottomOffset + layout.theme.spacing.chartToFooter;
6777
- const a2 = createSVGElement2("a");
6778
- a2.setAttribute("href", BRAND_URL2);
6779
- a2.setAttributeNS(XLINK_NS2, "xlink:href", BRAND_URL2);
7182
+ const a2 = createSVGElement3("a");
7183
+ a2.setAttribute("href", BRAND_URL3);
7184
+ a2.setAttributeNS(XLINK_NS3, "xlink:href", BRAND_URL3);
6780
7185
  a2.setAttribute("target", "_blank");
6781
7186
  a2.setAttribute("rel", "noopener");
6782
7187
  a2.setAttribute("class", "oc-chrome-ref");
6783
7188
  const BRAND_LARGE = 16;
6784
- const text = createSVGElement2("text");
6785
- setAttrs2(text, {
7189
+ const text = createSVGElement3("text");
7190
+ setAttrs3(text, {
6786
7191
  x: rightEdge,
6787
7192
  y: chromeY + BRAND_LARGE,
6788
7193
  "dominant-baseline": "alphabetic",
@@ -6792,14 +7197,14 @@ function renderBrand2(parent, layout) {
6792
7197
  "fill-opacity": 0.55
6793
7198
  });
6794
7199
  text.style.setProperty("fill", fill);
6795
- const trySpan = createSVGElement2("tspan");
6796
- setAttrs2(trySpan, { "font-weight": 500 });
7200
+ const trySpan = createSVGElement3("tspan");
7201
+ setAttrs3(trySpan, { "font-weight": 500 });
6797
7202
  trySpan.textContent = "try";
6798
- const openDataSpan = createSVGElement2("tspan");
6799
- setAttrs2(openDataSpan, { "font-weight": 600, "font-size": BRAND_LARGE });
7203
+ const openDataSpan = createSVGElement3("tspan");
7204
+ setAttrs3(openDataSpan, { "font-weight": 600, "font-size": BRAND_LARGE });
6800
7205
  openDataSpan.textContent = "OpenData";
6801
- const aiSpan = createSVGElement2("tspan");
6802
- setAttrs2(aiSpan, { "font-weight": 500 });
7206
+ const aiSpan = createSVGElement3("tspan");
7207
+ setAttrs3(aiSpan, { "font-weight": 500 });
6803
7208
  aiSpan.textContent = ".ai";
6804
7209
  text.appendChild(trySpan);
6805
7210
  text.appendChild(openDataSpan);
@@ -6810,7 +7215,7 @@ function renderBrand2(parent, layout) {
6810
7215
  function renderLegend2(parent, legendLayout) {
6811
7216
  if (!("entries" in legendLayout) || legendLayout.entries.length === 0) return;
6812
7217
  const legend = legendLayout;
6813
- const g = createSVGElement2("g");
7218
+ const g = createSVGElement3("g");
6814
7219
  g.setAttribute("class", "oc-legend");
6815
7220
  g.setAttribute("role", "list");
6816
7221
  g.setAttribute("aria-label", "Chart legend");
@@ -6831,7 +7236,7 @@ function renderLegend2(parent, legendLayout) {
6831
7236
  offsetY += legend.swatchSize + 6;
6832
7237
  }
6833
7238
  }
6834
- const entryG = createSVGElement2("g");
7239
+ const entryG = createSVGElement3("g");
6835
7240
  entryG.setAttribute("class", "oc-legend-entry");
6836
7241
  entryG.setAttribute("role", "listitem");
6837
7242
  entryG.setAttribute("data-legend-index", String(i));
@@ -6850,8 +7255,8 @@ function renderLegend2(parent, legendLayout) {
6850
7255
  entryG.setAttribute("opacity", "0.3");
6851
7256
  }
6852
7257
  }
6853
- const rect = createSVGElement2("rect");
6854
- setAttrs2(rect, {
7258
+ const rect = createSVGElement3("rect");
7259
+ setAttrs3(rect, {
6855
7260
  x: offsetX,
6856
7261
  y: offsetY,
6857
7262
  width: legend.swatchSize,
@@ -6860,8 +7265,8 @@ function renderLegend2(parent, legendLayout) {
6860
7265
  rx: 2
6861
7266
  });
6862
7267
  entryG.appendChild(rect);
6863
- const label = createSVGElement2("text");
6864
- setAttrs2(label, {
7268
+ const label = createSVGElement3("text");
7269
+ setAttrs3(label, {
6865
7270
  x: offsetX + legend.swatchSize + legend.swatchGap,
6866
7271
  y: offsetY + legend.swatchSize / 2,
6867
7272
  "dominant-baseline": "central"
@@ -6899,7 +7304,7 @@ function renderGradientDefs(defs, links, nodePositions) {
6899
7304
  const link = links[i];
6900
7305
  if (link.sourceColor === link.targetColor) continue;
6901
7306
  const gradId = `oc-sg-${sanitizeId(link.sourceId)}-${sanitizeId(link.targetId)}-${i}`;
6902
- const gradient = createSVGElement2("linearGradient");
7307
+ const gradient = createSVGElement3("linearGradient");
6903
7308
  gradient.setAttribute("id", gradId);
6904
7309
  gradient.setAttribute("gradientUnits", "userSpaceOnUse");
6905
7310
  const sourcePos = nodePositions.get(link.sourceId);
@@ -6908,21 +7313,21 @@ function renderGradientDefs(defs, links, nodePositions) {
6908
7313
  const x22 = targetPos ? targetPos.x : 0;
6909
7314
  gradient.setAttribute("x1", String(x1));
6910
7315
  gradient.setAttribute("x2", String(x22));
6911
- const stop0 = createSVGElement2("stop");
6912
- setAttrs2(stop0, { offset: "0%", "stop-color": link.sourceColor });
7316
+ const stop0 = createSVGElement3("stop");
7317
+ setAttrs3(stop0, { offset: "0%", "stop-color": link.sourceColor });
6913
7318
  gradient.appendChild(stop0);
6914
- const stop1 = createSVGElement2("stop");
6915
- setAttrs2(stop1, { offset: "100%", "stop-color": link.targetColor });
7319
+ const stop1 = createSVGElement3("stop");
7320
+ setAttrs3(stop1, { offset: "100%", "stop-color": link.targetColor });
6916
7321
  gradient.appendChild(stop1);
6917
7322
  defs.appendChild(gradient);
6918
7323
  }
6919
7324
  }
6920
7325
  function renderLinks(parent, links, _nodePositions, animation) {
6921
- const g = createSVGElement2("g");
7326
+ const g = createSVGElement3("g");
6922
7327
  g.setAttribute("class", "oc-sankey-links");
6923
7328
  for (let i = 0; i < links.length; i++) {
6924
7329
  const link = links[i];
6925
- const linkG = createSVGElement2("g");
7330
+ const linkG = createSVGElement3("g");
6926
7331
  linkG.setAttribute("class", "oc-sankey-link");
6927
7332
  linkG.setAttribute("data-mark-id", `link-${link.sourceId}-${link.targetId}-${i}`);
6928
7333
  linkG.setAttribute("data-source", link.sourceId);
@@ -6931,7 +7336,7 @@ function renderLinks(parent, links, _nodePositions, animation) {
6931
7336
  linkG.setAttribute("aria-label", link.aria.label);
6932
7337
  }
6933
7338
  stampAnimationAttrs2(linkG, link, i, animation);
6934
- const path = createSVGElement2("path");
7339
+ const path = createSVGElement3("path");
6935
7340
  path.setAttribute("d", link.path);
6936
7341
  path.setAttribute("stroke", "none");
6937
7342
  path.setAttribute("fill-opacity", String(link.fillOpacity));
@@ -6947,11 +7352,11 @@ function renderLinks(parent, links, _nodePositions, animation) {
6947
7352
  parent.appendChild(g);
6948
7353
  }
6949
7354
  function renderNodes(parent, nodes, animation) {
6950
- const g = createSVGElement2("g");
7355
+ const g = createSVGElement3("g");
6951
7356
  g.setAttribute("class", "oc-sankey-nodes");
6952
7357
  for (let i = 0; i < nodes.length; i++) {
6953
7358
  const node = nodes[i];
6954
- const nodeG = createSVGElement2("g");
7359
+ const nodeG = createSVGElement3("g");
6955
7360
  nodeG.setAttribute("class", "oc-sankey-node");
6956
7361
  nodeG.setAttribute("data-mark-id", `node-${node.nodeId}`);
6957
7362
  nodeG.setAttribute("data-node-id", node.nodeId);
@@ -6959,8 +7364,8 @@ function renderNodes(parent, nodes, animation) {
6959
7364
  nodeG.setAttribute("aria-label", node.aria.label);
6960
7365
  }
6961
7366
  stampAnimationAttrs2(nodeG, node, i, animation);
6962
- const rect = createSVGElement2("rect");
6963
- setAttrs2(rect, {
7367
+ const rect = createSVGElement3("rect");
7368
+ setAttrs3(rect, {
6964
7369
  x: node.x,
6965
7370
  y: node.y,
6966
7371
  width: node.width,
@@ -6981,13 +7386,13 @@ function renderNodes(parent, nodes, animation) {
6981
7386
  parent.appendChild(g);
6982
7387
  }
6983
7388
  function renderLabels(parent, nodes, measureText) {
6984
- const g = createSVGElement2("g");
7389
+ const g = createSVGElement3("g");
6985
7390
  g.setAttribute("class", "oc-sankey-labels");
6986
7391
  for (const node of nodes) {
6987
7392
  const { label } = node;
6988
7393
  if (!label.visible) continue;
6989
- const text = createSVGElement2("text");
6990
- setAttrs2(text, { x: label.x, y: label.y });
7394
+ const text = createSVGElement3("text");
7395
+ setAttrs3(text, { x: label.x, y: label.y });
6991
7396
  applyTextStyle2(text, label.style);
6992
7397
  if (label.maxWidth !== void 0 && label.maxWidth > 0) {
6993
7398
  const fontSize = label.style.fontSize ?? 12;
@@ -6998,7 +7403,7 @@ function renderLabels(parent, nodes, measureText) {
6998
7403
  const totalHeight = (lines.length - 1) * lineHeight;
6999
7404
  const startY = label.y - totalHeight / 2;
7000
7405
  for (let i = 0; i < lines.length; i++) {
7001
- const tspan = createSVGElement2("tspan");
7406
+ const tspan = createSVGElement3("tspan");
7002
7407
  tspan.setAttribute("x", String(label.x));
7003
7408
  tspan.setAttribute("y", String(startY + i * lineHeight));
7004
7409
  tspan.textContent = lines[i];
@@ -7016,10 +7421,10 @@ function renderLabels(parent, nodes, measureText) {
7016
7421
  }
7017
7422
  function renderSankeySVG(layout, animation) {
7018
7423
  const { width, height } = layout.dimensions;
7019
- const svg = createSVGElement2("svg");
7020
- setAttrs2(svg, {
7424
+ const svg = createSVGElement3("svg");
7425
+ setAttrs3(svg, {
7021
7426
  viewBox: `0 0 ${width} ${height}`,
7022
- xmlns: SVG_NS3,
7427
+ xmlns: SVG_NS4,
7023
7428
  overflow: "visible"
7024
7429
  });
7025
7430
  svg.style.height = `${height}px`;
@@ -7037,9 +7442,9 @@ function renderSankeySVG(layout, animation) {
7037
7442
  const easeVar = EASE_VAR_MAP2[animation.ease] || EASE_VAR_MAP2.smooth;
7038
7443
  svg.style.setProperty("--oc-animation-ease", easeVar);
7039
7444
  }
7040
- const bg = createSVGElement2("rect");
7445
+ const bg = createSVGElement3("rect");
7041
7446
  bg.setAttribute("class", "oc-background");
7042
- setAttrs2(bg, {
7447
+ setAttrs3(bg, {
7043
7448
  x: 0,
7044
7449
  y: 0,
7045
7450
  width,
@@ -7048,14 +7453,14 @@ function renderSankeySVG(layout, animation) {
7048
7453
  });
7049
7454
  svg.appendChild(bg);
7050
7455
  const nodePositions = buildNodePositionMap(layout.nodes);
7051
- const defs = createSVGElement2("defs");
7456
+ const defs = createSVGElement3("defs");
7052
7457
  renderGradientDefs(defs, layout.links, nodePositions);
7053
7458
  svg.appendChild(defs);
7054
7459
  renderLinks(svg, layout.links, nodePositions, animation);
7055
7460
  renderNodes(svg, layout.nodes, animation);
7056
7461
  renderLabels(svg, layout.nodes, layout.measureText);
7057
7462
  renderLegend2(svg, layout.legend);
7058
- renderChrome2(svg, layout);
7463
+ renderChrome3(svg, layout);
7059
7464
  if (layout.watermark) {
7060
7465
  renderBrand2(svg, layout);
7061
7466
  }
@@ -7063,7 +7468,7 @@ function renderSankeySVG(layout, animation) {
7063
7468
  }
7064
7469
 
7065
7470
  // src/sankey-mount.ts
7066
- function resolveDarkMode3(mode) {
7471
+ function resolveDarkMode4(mode) {
7067
7472
  if (mode === "force") return true;
7068
7473
  if (mode === "off" || mode === void 0) return false;
7069
7474
  if (typeof window !== "undefined" && window.matchMedia) {
@@ -7095,7 +7500,7 @@ function createSankey(container, spec, options) {
7095
7500
  }
7096
7501
  function compile() {
7097
7502
  const { width, height } = getContainerDimensions();
7098
- const darkMode = resolveDarkMode3(options?.darkMode);
7503
+ const darkMode = resolveDarkMode4(options?.darkMode);
7099
7504
  const compileOpts = {
7100
7505
  width,
7101
7506
  height,
@@ -7270,7 +7675,7 @@ function createSankey(container, spec, options) {
7270
7675
  const animation = shouldAnimate ? currentLayout.animation : void 0;
7271
7676
  svgElement = renderSankeySVG(currentLayout, animation);
7272
7677
  container.appendChild(svgElement);
7273
- const isDark = resolveDarkMode3(options?.darkMode);
7678
+ const isDark = resolveDarkMode4(options?.darkMode);
7274
7679
  if (isDark) {
7275
7680
  container.classList.add("oc-dark");
7276
7681
  } else {
@@ -7359,7 +7764,7 @@ function createSankey(container, spec, options) {
7359
7764
  const animation = shouldAnimate ? currentLayout.animation : void 0;
7360
7765
  svgElement = renderSankeySVG(currentLayout, animation);
7361
7766
  container.appendChild(svgElement);
7362
- const isDark = resolveDarkMode3(options?.darkMode);
7767
+ const isDark = resolveDarkMode4(options?.darkMode);
7363
7768
  if (isDark) {
7364
7769
  container.classList.add("oc-dark");
7365
7770
  } else {
@@ -7596,7 +8001,7 @@ var EASE_VAR_MAP3 = {
7596
8001
  smooth: "var(--oc-ease-smooth)",
7597
8002
  snappy: "var(--oc-ease-snappy)"
7598
8003
  };
7599
- var BRAND_URL3 = "https://tryopendata.ai";
8004
+ var BRAND_URL4 = "https://tryopendata.ai";
7600
8005
  function renderChromeBlock(layout, position) {
7601
8006
  const chrome = layout.chrome;
7602
8007
  if (position === "header") {
@@ -7842,7 +8247,7 @@ function renderTable(layout, container, opts) {
7842
8247
  brand.className = "oc-table-ref";
7843
8248
  brand.style.cssText = "text-align: right; padding: 4px 8px;";
7844
8249
  const brandLink = document.createElement("a");
7845
- brandLink.href = BRAND_URL3;
8250
+ brandLink.href = BRAND_URL4;
7846
8251
  brandLink.target = "_blank";
7847
8252
  brandLink.rel = "noopener";
7848
8253
  brandLink.style.cssText = `font-size: ${BRAND_FONT_SIZE4}px; font-weight: 600; color: ${brandColor}; opacity: 0.55; text-decoration: none; font-family: ${theme ? theme.fonts.family : "sans-serif"};`;
@@ -7865,7 +8270,7 @@ function renderTable(layout, container, opts) {
7865
8270
  }
7866
8271
 
7867
8272
  // src/table-mount.ts
7868
- function resolveDarkMode4(mode) {
8273
+ function resolveDarkMode5(mode) {
7869
8274
  if (mode === "force") return true;
7870
8275
  if (mode === "off" || mode === void 0) return false;
7871
8276
  if (typeof window !== "undefined" && window.matchMedia) {
@@ -7933,7 +8338,7 @@ function createTable(container, spec, options) {
7933
8338
  }
7934
8339
  function compile() {
7935
8340
  const state = getState();
7936
- const darkMode = resolveDarkMode4(options?.darkMode);
8341
+ const darkMode = resolveDarkMode5(options?.darkMode);
7937
8342
  const { width } = getContainerDimensions();
7938
8343
  const compileOpts = {
7939
8344
  width,
@@ -7995,7 +8400,7 @@ function createTable(container, spec, options) {
7995
8400
  if (isFirstRender) {
7996
8401
  isFirstRender = false;
7997
8402
  }
7998
- const isDark = resolveDarkMode4(options?.darkMode);
8403
+ const isDark = resolveDarkMode5(options?.darkMode);
7999
8404
  if (isDark) {
8000
8405
  container.classList.add("oc-dark");
8001
8406
  } else {
@@ -8150,7 +8555,7 @@ function createTable(container, spec, options) {
8150
8555
  throw new Error(`Unsupported export format: ${format}`);
8151
8556
  }
8152
8557
  const state = getState();
8153
- const darkMode = resolveDarkMode4(options?.darkMode);
8558
+ const darkMode = resolveDarkMode5(options?.darkMode);
8154
8559
  const { width } = getContainerDimensions();
8155
8560
  const fullLayout = compileTable(currentSpec, {
8156
8561
  width,
@@ -8232,30 +8637,30 @@ function createTable(container, spec, options) {
8232
8637
  import { compileTileMap } from "@opendata-ai/openchart-engine";
8233
8638
 
8234
8639
  // src/tilemap-renderer.ts
8235
- var SVG_NS4 = "http://www.w3.org/2000/svg";
8236
- var XLINK_NS3 = "http://www.w3.org/1999/xlink";
8237
- var BRAND_URL4 = "https://tryopendata.ai";
8640
+ var SVG_NS5 = "http://www.w3.org/2000/svg";
8641
+ var XLINK_NS4 = "http://www.w3.org/1999/xlink";
8642
+ var BRAND_URL5 = "https://tryopendata.ai";
8238
8643
  var EASE_VAR_MAP4 = {
8239
8644
  smooth: "var(--oc-ease-smooth)",
8240
8645
  snappy: "var(--oc-ease-snappy)"
8241
8646
  };
8242
8647
  var gradientIdCounter = 0;
8243
- function createSVGElement3(tag) {
8244
- return document.createElementNS(SVG_NS4, tag);
8648
+ function createSVGElement4(tag) {
8649
+ return document.createElementNS(SVG_NS5, tag);
8245
8650
  }
8246
- function setAttrs3(el, attrs) {
8651
+ function setAttrs4(el, attrs) {
8247
8652
  for (const [key, value] of Object.entries(attrs)) {
8248
8653
  el.setAttribute(key, String(value));
8249
8654
  }
8250
8655
  }
8251
- function renderChrome3(parent, layout) {
8252
- const g = createSVGElement3("g");
8656
+ function renderChrome4(parent, layout) {
8657
+ const g = createSVGElement4("g");
8253
8658
  g.setAttribute("class", "oc-chrome");
8254
8659
  const { chrome } = layout;
8255
8660
  const bottomOffset = layout.area.y + layout.area.height;
8256
8661
  if (chrome.title) {
8257
- const text = createSVGElement3("text");
8258
- setAttrs3(text, { x: chrome.title.x, y: chrome.title.y });
8662
+ const text = createSVGElement4("text");
8663
+ setAttrs4(text, { x: chrome.title.x, y: chrome.title.y });
8259
8664
  text.setAttribute("class", "oc-title");
8260
8665
  text.setAttribute("font-family", chrome.title.style.fontFamily);
8261
8666
  text.setAttribute("font-size", String(chrome.title.style.fontSize));
@@ -8265,8 +8670,8 @@ function renderChrome3(parent, layout) {
8265
8670
  g.appendChild(text);
8266
8671
  }
8267
8672
  if (chrome.subtitle) {
8268
- const text = createSVGElement3("text");
8269
- setAttrs3(text, { x: chrome.subtitle.x, y: chrome.subtitle.y });
8673
+ const text = createSVGElement4("text");
8674
+ setAttrs4(text, { x: chrome.subtitle.x, y: chrome.subtitle.y });
8270
8675
  text.setAttribute("class", "oc-subtitle");
8271
8676
  text.setAttribute("font-family", chrome.subtitle.style.fontFamily);
8272
8677
  text.setAttribute("font-size", String(chrome.subtitle.style.fontSize));
@@ -8279,8 +8684,8 @@ function renderChrome3(parent, layout) {
8279
8684
  g.appendChild(text);
8280
8685
  }
8281
8686
  if (chrome.source) {
8282
- const text = createSVGElement3("text");
8283
- setAttrs3(text, { x: chrome.source.x, y: bottomOffset + chrome.source.y });
8687
+ const text = createSVGElement4("text");
8688
+ setAttrs4(text, { x: chrome.source.x, y: bottomOffset + chrome.source.y });
8284
8689
  text.setAttribute("class", "oc-source");
8285
8690
  text.setAttribute("font-family", chrome.source.style.fontFamily);
8286
8691
  text.setAttribute("font-size", String(chrome.source.style.fontSize));
@@ -8293,8 +8698,8 @@ function renderChrome3(parent, layout) {
8293
8698
  g.appendChild(text);
8294
8699
  }
8295
8700
  if (chrome.byline) {
8296
- const text = createSVGElement3("text");
8297
- setAttrs3(text, { x: chrome.byline.x, y: bottomOffset + chrome.byline.y });
8701
+ const text = createSVGElement4("text");
8702
+ setAttrs4(text, { x: chrome.byline.x, y: bottomOffset + chrome.byline.y });
8298
8703
  text.setAttribute("class", "oc-byline");
8299
8704
  text.setAttribute("font-family", chrome.byline.style.fontFamily);
8300
8705
  text.setAttribute("font-size", String(chrome.byline.style.fontSize));
@@ -8307,8 +8712,8 @@ function renderChrome3(parent, layout) {
8307
8712
  g.appendChild(text);
8308
8713
  }
8309
8714
  if (chrome.footer) {
8310
- const text = createSVGElement3("text");
8311
- setAttrs3(text, { x: chrome.footer.x, y: bottomOffset + chrome.footer.y });
8715
+ const text = createSVGElement4("text");
8716
+ setAttrs4(text, { x: chrome.footer.x, y: bottomOffset + chrome.footer.y });
8312
8717
  text.setAttribute("class", "oc-footer");
8313
8718
  text.setAttribute("font-family", chrome.footer.style.fontFamily);
8314
8719
  text.setAttribute("font-size", String(chrome.footer.style.fontSize));
@@ -8322,7 +8727,7 @@ function renderChrome3(parent, layout) {
8322
8727
  }
8323
8728
  parent.appendChild(g);
8324
8729
  }
8325
- function renderWatermark(parent, layout) {
8730
+ function renderWatermark2(parent, layout) {
8326
8731
  if (layout.width < 480) return;
8327
8732
  const { width, height } = layout;
8328
8733
  const { theme } = layout;
@@ -8330,14 +8735,14 @@ function renderWatermark(parent, layout) {
8330
8735
  const rightEdge = width - padding;
8331
8736
  const bottomEdge = height - padding;
8332
8737
  const fill = theme.colors.axis;
8333
- const a2 = createSVGElement3("a");
8334
- a2.setAttribute("href", BRAND_URL4);
8335
- a2.setAttributeNS(XLINK_NS3, "xlink:href", BRAND_URL4);
8738
+ const a2 = createSVGElement4("a");
8739
+ a2.setAttribute("href", BRAND_URL5);
8740
+ a2.setAttributeNS(XLINK_NS4, "xlink:href", BRAND_URL5);
8336
8741
  a2.setAttribute("target", "_blank");
8337
8742
  a2.setAttribute("rel", "noopener");
8338
8743
  a2.setAttribute("class", "oc-chrome-ref");
8339
- const text = createSVGElement3("text");
8340
- setAttrs3(text, {
8744
+ const text = createSVGElement4("text");
8745
+ setAttrs4(text, {
8341
8746
  x: rightEdge,
8342
8747
  y: bottomEdge,
8343
8748
  "dominant-baseline": "alphabetic",
@@ -8347,14 +8752,14 @@ function renderWatermark(parent, layout) {
8347
8752
  "fill-opacity": 0.55
8348
8753
  });
8349
8754
  text.style.setProperty("fill", fill);
8350
- const trySpan = createSVGElement3("tspan");
8351
- setAttrs3(trySpan, { "font-weight": 500 });
8755
+ const trySpan = createSVGElement4("tspan");
8756
+ setAttrs4(trySpan, { "font-weight": 500 });
8352
8757
  trySpan.textContent = "try";
8353
- const openDataSpan = createSVGElement3("tspan");
8354
- setAttrs3(openDataSpan, { "font-weight": 600, "font-size": 16 });
8758
+ const openDataSpan = createSVGElement4("tspan");
8759
+ setAttrs4(openDataSpan, { "font-weight": 600, "font-size": 16 });
8355
8760
  openDataSpan.textContent = "OpenData";
8356
- const aiSpan = createSVGElement3("tspan");
8357
- setAttrs3(aiSpan, { "font-weight": 500 });
8761
+ const aiSpan = createSVGElement4("tspan");
8762
+ setAttrs4(aiSpan, { "font-weight": 500 });
8358
8763
  aiSpan.textContent = ".ai";
8359
8764
  text.appendChild(trySpan);
8360
8765
  text.appendChild(openDataSpan);
@@ -8363,7 +8768,7 @@ function renderWatermark(parent, layout) {
8363
8768
  parent.appendChild(a2);
8364
8769
  }
8365
8770
  function renderTiles(parent, tiles, animation) {
8366
- const g = createSVGElement3("g");
8771
+ const g = createSVGElement4("g");
8367
8772
  g.setAttribute("class", "oc-tilemap-tiles");
8368
8773
  g.setAttribute("role", "list");
8369
8774
  const tileDelays = [];
@@ -8379,7 +8784,7 @@ function renderTiles(parent, tiles, animation) {
8379
8784
  }
8380
8785
  for (let i = 0; i < tiles.length; i++) {
8381
8786
  const tile = tiles[i];
8382
- const tileGroup = createSVGElement3("g");
8787
+ const tileGroup = createSVGElement4("g");
8383
8788
  tileGroup.setAttribute("class", "oc-tilemap-tile");
8384
8789
  tileGroup.setAttribute("data-state", tile.stateCode);
8385
8790
  tileGroup.setAttribute("role", "listitem");
@@ -8398,20 +8803,21 @@ function renderTiles(parent, tiles, animation) {
8398
8803
  `${tileDelays[i]}ms`
8399
8804
  );
8400
8805
  }
8401
- const rect = createSVGElement3("rect");
8402
- setAttrs3(rect, {
8806
+ const rect = createSVGElement4("rect");
8807
+ setAttrs4(rect, {
8403
8808
  x: tile.x,
8404
8809
  y: tile.y,
8405
8810
  width: tile.size,
8406
8811
  height: tile.size,
8407
8812
  rx: tile.cornerRadius,
8408
8813
  fill: tile.fill,
8814
+ "fill-opacity": tile.fillOpacity ?? 1,
8409
8815
  stroke: tile.stroke,
8410
8816
  "stroke-width": tile.strokeWidth
8411
8817
  });
8412
8818
  tileGroup.appendChild(rect);
8413
- const codeLabel = createSVGElement3("text");
8414
- setAttrs3(codeLabel, {
8819
+ const codeLabel = createSVGElement4("text");
8820
+ setAttrs4(codeLabel, {
8415
8821
  x: tile.label.x,
8416
8822
  y: tile.label.y,
8417
8823
  "text-anchor": "middle",
@@ -8427,8 +8833,8 @@ function renderTiles(parent, tiles, animation) {
8427
8833
  codeLabel.textContent = tile.label.text;
8428
8834
  tileGroup.appendChild(codeLabel);
8429
8835
  if (tile.valueLabel.visible && tile.valueLabel.text) {
8430
- const valueLabel = createSVGElement3("text");
8431
- setAttrs3(valueLabel, {
8836
+ const valueLabel = createSVGElement4("text");
8837
+ setAttrs4(valueLabel, {
8432
8838
  x: tile.valueLabel.x,
8433
8839
  y: tile.valueLabel.y,
8434
8840
  "text-anchor": "middle",
@@ -8451,38 +8857,46 @@ function renderTiles(parent, tiles, animation) {
8451
8857
  function renderGradientLegend(parent, layout) {
8452
8858
  if (!layout.gradientLegend) return;
8453
8859
  const { gradientLegend } = layout;
8454
- const g = createSVGElement3("g");
8860
+ const g = createSVGElement4("g");
8455
8861
  g.setAttribute("class", "oc-tilemap-legend");
8456
- const defs = parent.querySelector("defs") || createSVGElement3("defs");
8862
+ const defs = parent.querySelector("defs") || createSVGElement4("defs");
8457
8863
  const exists = parent.querySelector("defs");
8458
8864
  if (!exists) {
8459
8865
  parent.insertBefore(defs, parent.firstChild);
8460
8866
  }
8461
8867
  const gradientId = `oc-tilemap-legend-gradient-${gradientIdCounter++}`;
8462
- const grad = createSVGElement3("linearGradient");
8868
+ const grad = createSVGElement4("linearGradient");
8463
8869
  grad.id = gradientId;
8464
8870
  grad.setAttribute("x1", "0%");
8465
8871
  grad.setAttribute("y1", "0%");
8466
8872
  grad.setAttribute("x2", "100%");
8467
8873
  grad.setAttribute("y2", "0%");
8468
8874
  for (const stop of gradientLegend.colorStops) {
8469
- const s = createSVGElement3("stop");
8470
- setAttrs3(s, { offset: `${stop.offset * 100}%`, "stop-color": stop.color });
8875
+ const s = createSVGElement4("stop");
8876
+ const attrs = {
8877
+ offset: `${stop.offset * 100}%`,
8878
+ "stop-color": stop.color
8879
+ };
8880
+ if (stop.opacity !== void 0) {
8881
+ attrs["stop-opacity"] = stop.opacity;
8882
+ }
8883
+ setAttrs4(s, attrs);
8471
8884
  grad.appendChild(s);
8472
8885
  }
8473
8886
  defs.appendChild(grad);
8474
- const bar = createSVGElement3("rect");
8475
- setAttrs3(bar, {
8887
+ const barHeight = gradientLegend.bounds.height;
8888
+ const bar = createSVGElement4("rect");
8889
+ setAttrs4(bar, {
8476
8890
  x: gradientLegend.bounds.x,
8477
8891
  y: gradientLegend.bounds.y,
8478
8892
  width: gradientLegend.bounds.width,
8479
- height: gradientLegend.bounds.height,
8480
- rx: 3,
8893
+ height: barHeight,
8894
+ rx: barHeight / 2,
8481
8895
  fill: `url(#${gradientId})`
8482
8896
  });
8483
8897
  g.appendChild(bar);
8484
- const minText = createSVGElement3("text");
8485
- setAttrs3(minText, {
8898
+ const minText = createSVGElement4("text");
8899
+ setAttrs4(minText, {
8486
8900
  x: gradientLegend.bounds.x,
8487
8901
  y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,
8488
8902
  "text-anchor": "start",
@@ -8496,8 +8910,8 @@ function renderGradientLegend(parent, layout) {
8496
8910
  );
8497
8911
  minText.textContent = gradientLegend.minLabel;
8498
8912
  g.appendChild(minText);
8499
- const maxText = createSVGElement3("text");
8500
- setAttrs3(maxText, {
8913
+ const maxText = createSVGElement4("text");
8914
+ setAttrs4(maxText, {
8501
8915
  x: gradientLegend.bounds.x + gradientLegend.bounds.width,
8502
8916
  y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,
8503
8917
  "text-anchor": "end",
@@ -8516,7 +8930,7 @@ function renderGradientLegend(parent, layout) {
8516
8930
  function renderTileMapSVG(layout, opts) {
8517
8931
  const { width, height, tiles, a11y, watermark, animation } = layout;
8518
8932
  const animate = opts?.animate && animation?.enabled;
8519
- const svg = createSVGElement3("svg");
8933
+ const svg = createSVGElement4("svg");
8520
8934
  svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
8521
8935
  svg.setAttribute("width", String(width));
8522
8936
  svg.setAttribute("height", String(height));
@@ -8534,19 +8948,19 @@ function renderTileMapSVG(layout, opts) {
8534
8948
  const easeVar = EASE_VAR_MAP4[animation.ease] || EASE_VAR_MAP4.smooth;
8535
8949
  svg.style.setProperty("--oc-animation-ease", easeVar);
8536
8950
  }
8537
- const defs = createSVGElement3("defs");
8951
+ const defs = createSVGElement4("defs");
8538
8952
  svg.appendChild(defs);
8539
- renderChrome3(svg, layout);
8953
+ renderChrome4(svg, layout);
8540
8954
  renderTiles(svg, tiles, animate ? animation : void 0);
8541
8955
  renderGradientLegend(svg, layout);
8542
8956
  if (watermark) {
8543
- renderWatermark(svg, layout);
8957
+ renderWatermark2(svg, layout);
8544
8958
  }
8545
8959
  return svg;
8546
8960
  }
8547
8961
 
8548
8962
  // src/tilemap-mount.ts
8549
- function resolveDarkMode5(mode) {
8963
+ function resolveDarkMode6(mode) {
8550
8964
  if (mode === "force") return true;
8551
8965
  if (mode === "off" || mode === void 0) return false;
8552
8966
  if (typeof window !== "undefined" && window.matchMedia) {
@@ -8574,7 +8988,7 @@ function createTileMap(container, spec, options) {
8574
8988
  }
8575
8989
  function compile() {
8576
8990
  const { width, height } = getContainerDimensions();
8577
- const darkMode = resolveDarkMode5(options?.darkMode);
8991
+ const darkMode = resolveDarkMode6(options?.darkMode);
8578
8992
  const compileOpts = {
8579
8993
  width,
8580
8994
  height,
@@ -8736,7 +9150,7 @@ function createTileMap(container, spec, options) {
8736
9150
  container.classList.remove("oc-tilemap-root", "oc-dark");
8737
9151
  }
8738
9152
  container.classList.add("oc-tilemap-root");
8739
- if (resolveDarkMode5(options?.darkMode)) {
9153
+ if (resolveDarkMode6(options?.darkMode)) {
8740
9154
  container.classList.add("oc-dark");
8741
9155
  }
8742
9156
  currentLayout = compile();
@@ -8758,6 +9172,7 @@ function createTileMap(container, spec, options) {
8758
9172
  }
8759
9173
  export {
8760
9174
  attachKeyboardNav,
9175
+ createBarList,
8761
9176
  createChart,
8762
9177
  createGraph,
8763
9178
  createSankey,