@opendata-ai/openchart-vanilla 8.4.1 → 8.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,163 +16,32 @@ import {
16
16
  renderSingleMark,
17
17
  resolveMarkFill,
18
18
  resolvePatternFill,
19
- resolvedSurface,
20
- stampAnimationVars,
21
- stampThemeProperties
22
- } from "./chunk-KANNWAEA.js";
19
+ stampAnimationVars
20
+ } from "./chunk-XGII4ZRT.js";
23
21
  import {
24
22
  SVG_NS,
25
23
  createSVGElement,
26
24
  setAttrs
27
25
  } from "./chunk-5RX6ZQGZ.js";
28
-
29
- // src/resize-observer.ts
30
- var DEBOUNCE_MS = 16;
31
- function observeResize(container, callback) {
32
- let timeoutId = null;
33
- let lastDelivered = null;
34
- const observer = new ResizeObserver((entries) => {
35
- if (timeoutId !== null) {
36
- clearTimeout(timeoutId);
37
- }
38
- timeoutId = setTimeout(() => {
39
- for (const entry of entries) {
40
- const { width, height } = entry.contentRect;
41
- if (lastDelivered && lastDelivered.width === width && lastDelivered.height === height) {
42
- continue;
43
- }
44
- lastDelivered = { width, height };
45
- callback(width, height);
46
- }
47
- timeoutId = null;
48
- }, DEBOUNCE_MS);
49
- });
50
- observer.observe(container);
51
- return () => {
52
- if (timeoutId !== null) {
53
- clearTimeout(timeoutId);
54
- }
55
- observer.disconnect();
56
- };
57
- }
58
-
59
- // src/tooltip.ts
60
- import { computePosition, flip, offset, shift } from "@floating-ui/dom";
61
- var TOOLTIP_OFFSET = 12;
62
- function createTooltipManager(container) {
63
- const tooltip = document.createElement("div");
64
- tooltip.className = "oc-tooltip";
65
- tooltip.setAttribute("role", "tooltip");
66
- container.style.position = container.style.position || "relative";
67
- container.appendChild(tooltip);
68
- let lastContentKey = "";
69
- let currentPositionId = 0;
70
- const handleDocumentTouch = (e) => {
71
- if (!container.contains(e.target)) {
72
- hide();
73
- }
74
- };
75
- document.addEventListener("touchstart", handleDocumentTouch);
76
- const handleDocumentKeydown = (e) => {
77
- if (e.key === "Escape" && tooltip.style.display === "block") {
78
- hide();
79
- }
80
- };
81
- document.addEventListener("keydown", handleDocumentKeydown);
82
- function show(input, x, y, opts) {
83
- if ("element" in input) {
84
- lastContentKey = "";
85
- tooltip.replaceChildren(input.element);
86
- position(x, y, opts);
87
- return;
88
- }
89
- if ("text" in input) {
90
- lastContentKey = "";
91
- tooltip.textContent = input.text;
92
- position(x, y, opts);
93
- return;
94
- }
95
- const content = input;
96
- const emphasisIndex = content.fields.findIndex((f) => f.emphasis);
97
- const contentKey = `${content.title}|${content.fields.length}|${content.fields[0]?.value}|${content.fields[content.fields.length - 1]?.value}|${emphasisIndex}`;
98
- if (contentKey !== lastContentKey) {
99
- lastContentKey = contentKey;
100
- let html = "";
101
- const colorRowCount = content.fields.filter((f) => f.color && f.role !== "total").length;
102
- const perRowSwatches = colorRowCount > 1;
103
- if (content.title) {
104
- const titleColor = perRowSwatches ? void 0 : content.fields.find((f) => f.color)?.color;
105
- html += '<div class="oc-tooltip-header">';
106
- if (titleColor) {
107
- html += `<span class="oc-tooltip-dot" style="background:${esc(titleColor)}"></span>`;
108
- }
109
- html += `<span class="oc-tooltip-title">${esc(content.title)}</span>`;
110
- html += "</div>";
111
- }
112
- if (content.fields.length > 0) {
113
- html += '<div class="oc-tooltip-body">';
114
- for (const field of content.fields) {
115
- let rowClass = "oc-tooltip-row";
116
- if (field.emphasis) rowClass += " oc-tooltip-row--emphasis";
117
- if (field.role === "total") rowClass += " oc-tooltip-row--total";
118
- html += `<div class="${rowClass}">`;
119
- html += '<span class="oc-tooltip-label">';
120
- if (perRowSwatches && field.color) {
121
- html += `<span class="oc-tooltip-row-swatch" style="background:${esc(field.color)}"></span>`;
122
- }
123
- html += `${esc(field.label)}</span>`;
124
- html += `<span class="oc-tooltip-value">${esc(field.value)}</span>`;
125
- html += "</div>";
126
- }
127
- html += "</div>";
128
- }
129
- tooltip.innerHTML = html;
130
- }
131
- position(x, y, opts);
132
- }
133
- function position(x, y, opts) {
134
- tooltip.style.display = "block";
135
- const positionId = ++currentPositionId;
136
- const virtualRef = {
137
- getBoundingClientRect() {
138
- const rect = container.getBoundingClientRect();
139
- return {
140
- x: rect.left + x,
141
- y: rect.top + y,
142
- width: 0,
143
- height: 0,
144
- top: rect.top + y,
145
- left: rect.left + x,
146
- right: rect.left + x,
147
- bottom: rect.top + y
148
- };
149
- }
150
- };
151
- computePosition(virtualRef, tooltip, {
152
- placement: opts?.placement ?? "bottom-start",
153
- middleware: [offset(TOOLTIP_OFFSET), flip(), shift({ padding: 8 })]
154
- }).then(({ x: fx, y: fy }) => {
155
- if (positionId !== currentPositionId) return;
156
- tooltip.style.left = `${fx}px`;
157
- tooltip.style.top = `${fy}px`;
158
- });
159
- }
160
- function hide() {
161
- tooltip.style.display = "none";
162
- lastContentKey = "";
163
- }
164
- function destroy() {
165
- document.removeEventListener("touchstart", handleDocumentTouch);
166
- document.removeEventListener("keydown", handleDocumentKeydown);
167
- if (tooltip.parentNode) {
168
- tooltip.parentNode.removeChild(tooltip);
169
- }
170
- }
171
- return { show, hide, destroy };
172
- }
173
- function esc(str) {
174
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
175
- }
26
+ import {
27
+ AnimationScheduler,
28
+ SpatialIndex,
29
+ clamp01,
30
+ createTooltipManager,
31
+ createTween,
32
+ cubicInOut,
33
+ cubicOut,
34
+ lerp,
35
+ linear,
36
+ observeResize,
37
+ prefersReducedMotion,
38
+ resolveDarkMode,
39
+ resolveEase
40
+ } from "./chunk-2OJZBMXM.js";
41
+ import {
42
+ resolvedSurface,
43
+ stampThemeProperties
44
+ } from "./chunk-NG7CVCI2.js";
176
45
 
177
46
  // src/scatter-canvas/renderer.ts
178
47
  var TWO_PI = Math.PI * 2;
@@ -584,20 +453,6 @@ function materializeCanvasModeSVG(layout, opts) {
584
453
  return materializeRasterMarkSVG(layout);
585
454
  }
586
455
 
587
- // src/motion/easing.ts
588
- function linear(t) {
589
- return t;
590
- }
591
- function cubicOut(t) {
592
- const f = 1 - t;
593
- return 1 - f * f * f;
594
- }
595
- function cubicInOut(t) {
596
- return t < 0.5 ? 4 * t * t * t : 1 - (-2 * t + 2) ** 3 / 2;
597
- }
598
- var clamp01 = (t) => Math.min(1, Math.max(0, t));
599
- var lerp = (a, b, t) => a + (b - a) * t;
600
-
601
456
  // src/text-edit-overlay.ts
602
457
  function getScale(svg) {
603
458
  const viewBox = svg.viewBox?.baseVal;
@@ -629,7 +484,7 @@ function getTextStyles(targetElement, scale) {
629
484
  lineHeight: "1.3"
630
485
  };
631
486
  }
632
- function computePosition2(targetElement, svg, container) {
487
+ function computePosition(targetElement, svg, container) {
633
488
  const bbox = targetElement.getBBox();
634
489
  const scale = getScale(svg);
635
490
  const svgRect = svg.getBoundingClientRect();
@@ -653,7 +508,7 @@ function createTextEditOverlay(config) {
653
508
  targetElement.setAttribute("opacity", "0");
654
509
  const scale = getScale(svg);
655
510
  const styles = getTextStyles(targetElement, scale);
656
- const position = computePosition2(targetElement, svg, container);
511
+ const position = computePosition(targetElement, svg, container);
657
512
  const textarea = document.createElement("textarea");
658
513
  textarea.value = currentText;
659
514
  const containerPosition = window.getComputedStyle(container).position;
@@ -742,7 +597,7 @@ function createTextEditOverlay(config) {
742
597
  });
743
598
  const resizeObserver = new ResizeObserver(() => {
744
599
  if (destroyed) return;
745
- const newPosition = computePosition2(targetElement, svg, container);
600
+ const newPosition = computePosition(targetElement, svg, container);
746
601
  textarea.style.top = `${newPosition.top}px`;
747
602
  textarea.style.left = `${newPosition.left}px`;
748
603
  textarea.style.width = `${newPosition.width}px`;
@@ -5312,16 +5167,6 @@ function scheduleFontReload(fontFamily, isAlive, onReady) {
5312
5167
  return true;
5313
5168
  }
5314
5169
 
5315
- // src/resolve-dark-mode.ts
5316
- function resolveDarkMode(mode) {
5317
- if (mode === "force") return true;
5318
- if (mode === "off" || mode === void 0) return false;
5319
- if (typeof window !== "undefined" && window.matchMedia) {
5320
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
5321
- }
5322
- return false;
5323
- }
5324
-
5325
5170
  // src/scatter-canvas/interactions.ts
5326
5171
  var HIT_TOLERANCE_MOUSE = 8;
5327
5172
  var HIT_TOLERANCE_TOUCH = 24;
@@ -5419,191 +5264,6 @@ function wireCanvasInteractions({
5419
5264
  };
5420
5265
  }
5421
5266
 
5422
- // src/motion/scheduler.ts
5423
- var AnimationScheduler = class {
5424
- animations = /* @__PURE__ */ new Set();
5425
- requestFrame;
5426
- /**
5427
- * @param requestFrame Called on the idle→active transition to arm the first
5428
- * frame (typically the mount's `scheduleRender`).
5429
- */
5430
- constructor(requestFrame) {
5431
- this.requestFrame = requestFrame;
5432
- }
5433
- /** Add an animation. Arms the first frame if the scheduler was idle. */
5434
- add(a) {
5435
- const wasIdle = this.animations.size === 0;
5436
- this.animations.add(a);
5437
- if (wasIdle) this.requestFrame();
5438
- }
5439
- /** Remove an animation without finishing or cancelling it. Safe mid-tick. */
5440
- remove(a) {
5441
- this.animations.delete(a);
5442
- }
5443
- /**
5444
- * Advance all animations to `now`. Completed animations are removed. Returns
5445
- * true if any animation ran this frame (so the caller marks the frame dirty).
5446
- *
5447
- * Iterates a snapshot so an `onDone` that mutates the set (via update/destroy)
5448
- * can't corrupt the loop.
5449
- */
5450
- tick(now) {
5451
- if (this.animations.size === 0) return false;
5452
- const snapshot = [...this.animations];
5453
- let ran = false;
5454
- for (const a of snapshot) {
5455
- if (!this.animations.has(a)) continue;
5456
- ran = true;
5457
- const running = a.tick(now);
5458
- if (!running) this.animations.delete(a);
5459
- }
5460
- return ran;
5461
- }
5462
- /** True while any animation is active (drives rAF re-arming). */
5463
- get active() {
5464
- return this.animations.size > 0;
5465
- }
5466
- /** Cancel every animation (no final state, no onDone). Used on teardown. */
5467
- cancelAll() {
5468
- const snapshot = [...this.animations];
5469
- this.animations.clear();
5470
- for (const a of snapshot) a.cancel();
5471
- }
5472
- /** Finish every animation (snap to final, fire onDone). Used before update(). */
5473
- finishAll() {
5474
- const snapshot = [...this.animations];
5475
- this.animations.clear();
5476
- for (const a of snapshot) a.finish();
5477
- }
5478
- };
5479
-
5480
- // src/spatial-index.ts
5481
- import { quadtree } from "d3-quadtree";
5482
- var SpatialIndex = class {
5483
- tree = null;
5484
- nodes = [];
5485
- maxRadius = 0;
5486
- generation = 0;
5487
- /** Rebuild the quadtree from the current set of positioned nodes. */
5488
- rebuild(nodes) {
5489
- this.nodes = nodes;
5490
- this.maxRadius = 0;
5491
- for (const n of nodes) {
5492
- if (n.radius > this.maxRadius) this.maxRadius = n.radius;
5493
- }
5494
- this.tree = quadtree().x((d) => d.x).y((d) => d.y).addAll(nodes);
5495
- this.generation++;
5496
- }
5497
- /** Current generation counter. Increments on each rebuild. */
5498
- getGeneration() {
5499
- return this.generation;
5500
- }
5501
- /**
5502
- * Find the nearest node to (x, y) within maxDistance.
5503
- * Accounts for node radius: a hit occurs if the point is inside
5504
- * the node circle (distance to center < node.radius), or if the
5505
- * edge-to-edge distance is within maxDistance.
5506
- */
5507
- findNearest(x, y, maxDistance = Infinity) {
5508
- if (!this.tree || this.nodes.length === 0) return null;
5509
- const searchRadius = maxDistance + this.maxRadius;
5510
- let best = null;
5511
- let bestEffectiveDist = maxDistance + this.maxRadius + 1;
5512
- this.tree.visit((node, x0, y0, x1, y1) => {
5513
- const closestX = Math.max(x0, Math.min(x, x1));
5514
- const closestY = Math.max(y0, Math.min(y, y1));
5515
- const quadDist = Math.hypot(closestX - x, closestY - y);
5516
- if (quadDist > searchRadius) return true;
5517
- if (!node.length) {
5518
- let current = node;
5519
- do {
5520
- const d = current.data;
5521
- if (d) {
5522
- const dist = Math.hypot(d.x - x, d.y - y);
5523
- const effectiveDist = Math.max(0, dist - d.radius);
5524
- if (effectiveDist <= maxDistance && effectiveDist < bestEffectiveDist) {
5525
- bestEffectiveDist = effectiveDist;
5526
- best = d;
5527
- }
5528
- }
5529
- } while (current = current.next);
5530
- }
5531
- return false;
5532
- });
5533
- return best;
5534
- }
5535
- /** Find all nodes whose centers fall within the given rectangle. */
5536
- findInRect(x1, y1, x2, y2) {
5537
- if (!this.tree) return [];
5538
- const minX = Math.min(x1, x2);
5539
- const minY = Math.min(y1, y2);
5540
- const maxX = Math.max(x1, x2);
5541
- const maxY = Math.max(y1, y2);
5542
- const results = [];
5543
- this.tree.visit((node, qx0, qy0, qx1, qy1) => {
5544
- if (qx0 > maxX || qx1 < minX || qy0 > maxY || qy1 < minY) {
5545
- return true;
5546
- }
5547
- if (!node.length) {
5548
- let current = node;
5549
- do {
5550
- const d = current.data;
5551
- if (d && d.x >= minX && d.x <= maxX && d.y >= minY && d.y <= maxY) {
5552
- results.push(d);
5553
- }
5554
- } while (current = current.next);
5555
- }
5556
- return false;
5557
- });
5558
- return results;
5559
- }
5560
- };
5561
-
5562
- // src/motion/tween.ts
5563
- function resolveEase(ease) {
5564
- return ease === "snappy" ? cubicOut : cubicInOut;
5565
- }
5566
- function prefersReducedMotion() {
5567
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
5568
- try {
5569
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
5570
- } catch {
5571
- return false;
5572
- }
5573
- }
5574
- function createTween(opts) {
5575
- const { duration, ease, apply, onDone } = opts;
5576
- let startTime = null;
5577
- let finished = false;
5578
- function settleAt(t) {
5579
- apply(ease(t));
5580
- }
5581
- return {
5582
- tick(now) {
5583
- if (finished) return false;
5584
- if (startTime === null) startTime = now;
5585
- const elapsed = now - startTime;
5586
- const raw = duration <= 0 ? 1 : Math.min(1, elapsed / duration);
5587
- settleAt(raw);
5588
- if (raw >= 1) {
5589
- finished = true;
5590
- onDone?.();
5591
- return false;
5592
- }
5593
- return true;
5594
- },
5595
- finish() {
5596
- if (finished) return;
5597
- finished = true;
5598
- settleAt(1);
5599
- onDone?.();
5600
- },
5601
- cancel() {
5602
- finished = true;
5603
- }
5604
- };
5605
- }
5606
-
5607
5267
  // src/scatter-canvas/entrance.ts
5608
5268
  var MAX_TOTAL_STAGGER_MS = 300;
5609
5269
  var POINT_DURATION_FRACTION = 0.4;
@@ -8279,19 +7939,9 @@ export {
8279
7939
  createMeasureText,
8280
7940
  resolveFontFamily,
8281
7941
  scheduleFontReload,
8282
- observeResize,
8283
- resolveDarkMode,
8284
- createTooltipManager,
8285
7942
  VECTOR_EXPORT_MAX_POINTS,
8286
7943
  materializeCanvasModeSVG,
8287
7944
  applySrOnlyStyles,
8288
- AnimationScheduler,
8289
- SpatialIndex,
8290
- clamp01,
8291
- lerp,
8292
- resolveEase,
8293
- prefersReducedMotion,
8294
- createTween,
8295
7945
  createTextEditOverlay,
8296
7946
  DEFAULT_UPDATE_MAX_MARKS,
8297
7947
  CANVAS_DEFAULT_UPDATE_MAX_MARKS,
@@ -8300,7 +7950,7 @@ export {
8300
7950
  createChart,
8301
7951
  easingFns,
8302
7952
  storyMotion,
8303
- createTween2,
7953
+ createTween2 as createTween,
8304
7954
  FULL_VIEW,
8305
7955
  fitTarget,
8306
7956
  interpolateCamera,
@@ -8311,4 +7961,4 @@ export {
8311
7961
  scrubCamera,
8312
7962
  createGeoMap
8313
7963
  };
8314
- //# sourceMappingURL=chunk-5A2NLYUW.js.map
7964
+ //# sourceMappingURL=chunk-5KV3ZBBL.js.map