@sportsdataverse/sdvplot 0.0.0-stage → 0.1.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.
Files changed (166) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +94 -0
  3. package/README.md +908 -2
  4. package/dist/_web-D7Aa5Rd8.d.ts +69 -0
  5. package/dist/aaf-GJ3SH34T.js +13 -0
  6. package/dist/aaf-HCYN5T6Y.js +37 -0
  7. package/dist/aaf-ZAE5KS2Y.js +13 -0
  8. package/dist/ahl-E66MDVCU.js +66 -0
  9. package/dist/ahl-NPQQWZEV.js +249 -0
  10. package/dist/ahl-QRDU73XX.js +309 -0
  11. package/dist/axis-CraWQRUJ.d.ts +49 -0
  12. package/dist/bins.d.ts +174 -0
  13. package/dist/bins.js +4 -0
  14. package/dist/bins.js.map +1 -0
  15. package/dist/cfb-F42SVS4X.js +5000 -0
  16. package/dist/cfb-P3SHEYRZ.js +692 -0
  17. package/dist/cfb-WXRZANLP.js +10226 -0
  18. package/dist/chartjs-surface.d.ts +42 -0
  19. package/dist/chartjs-surface.js +65 -0
  20. package/dist/chartjs-surface.js.map +1 -0
  21. package/dist/chartjs.d.ts +132 -0
  22. package/dist/chartjs.js +339 -0
  23. package/dist/chartjs.js.map +1 -0
  24. package/dist/chunk-4C3GX6QO.js +92 -0
  25. package/dist/chunk-4C3GX6QO.js.map +1 -0
  26. package/dist/chunk-C2CDY5OG.js +137 -0
  27. package/dist/chunk-C2CDY5OG.js.map +1 -0
  28. package/dist/chunk-E7G7VSLV.js +43 -0
  29. package/dist/chunk-E7G7VSLV.js.map +1 -0
  30. package/dist/chunk-J6R2LEJ7.js +58 -0
  31. package/dist/chunk-J6R2LEJ7.js.map +1 -0
  32. package/dist/chunk-JJFTD4VB.js +175 -0
  33. package/dist/chunk-JJFTD4VB.js.map +1 -0
  34. package/dist/chunk-KKH6ABMA.js +223 -0
  35. package/dist/chunk-KKH6ABMA.js.map +1 -0
  36. package/dist/chunk-NLPH2VZT.js +127 -0
  37. package/dist/chunk-NLPH2VZT.js.map +1 -0
  38. package/dist/chunk-OKEE4AH6.js +30 -0
  39. package/dist/chunk-OKEE4AH6.js.map +1 -0
  40. package/dist/chunk-QY65DUEF.js +306 -0
  41. package/dist/chunk-QY65DUEF.js.map +1 -0
  42. package/dist/chunk-RMT2GMFH.js +97 -0
  43. package/dist/chunk-RMT2GMFH.js.map +1 -0
  44. package/dist/chunk-SEBO35DV.js +76 -0
  45. package/dist/chunk-SEBO35DV.js.map +1 -0
  46. package/dist/chunk-TAXO3VXH.js +556 -0
  47. package/dist/chunk-TAXO3VXH.js.map +1 -0
  48. package/dist/chunk-TDNBAEQB.js +105 -0
  49. package/dist/chunk-TDNBAEQB.js.map +1 -0
  50. package/dist/cricket-KPCYG2HL.js +493 -0
  51. package/dist/cricket-NDXLE25B.js +116 -0
  52. package/dist/cricket-YUUF7P6M.js +160 -0
  53. package/dist/d3.d.ts +234 -0
  54. package/dist/d3.js +198 -0
  55. package/dist/d3.js.map +1 -0
  56. package/dist/echarts.d.ts +121 -0
  57. package/dist/echarts.js +228 -0
  58. package/dist/echarts.js.map +1 -0
  59. package/dist/echl-3CR4RGFC.js +241 -0
  60. package/dist/echl-NPC32C23.js +107 -0
  61. package/dist/echl-PI33DGJC.js +64 -0
  62. package/dist/export.d.ts +130 -0
  63. package/dist/export.js +258 -0
  64. package/dist/export.js.map +1 -0
  65. package/dist/headshots-B2J_LOO8.d.ts +40 -0
  66. package/dist/index.d.ts +182 -0
  67. package/dist/index.js +10 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/interact.d.ts +567 -0
  70. package/dist/interact.js +809 -0
  71. package/dist/interact.js.map +1 -0
  72. package/dist/marks-DH-tQIP-.d.ts +75 -0
  73. package/dist/mbb-3TRLVW3H.js +3619 -0
  74. package/dist/mbb-DQUHCHZP.js +5111 -0
  75. package/dist/mbb-PRAIO2P5.js +371 -0
  76. package/dist/milb-6IQ6NCEW.js +291 -0
  77. package/dist/milb-EYY5MD4T.js +1752 -0
  78. package/dist/milb-EYZ4SZLI.js +797 -0
  79. package/dist/mlb-4PBUJSVQ.js +35 -0
  80. package/dist/mlb-FHPY3GQJ.js +701 -0
  81. package/dist/mlb-REVGL3N2.js +665 -0
  82. package/dist/nba-ATT775GS.js +485 -0
  83. package/dist/nba-LKM4UHVU.js +482 -0
  84. package/dist/nba-YX7PXM3R.js +35 -0
  85. package/dist/nbagl-3WGZCZ4S.js +37 -0
  86. package/dist/nbagl-LP4XJRAL.js +229 -0
  87. package/dist/nbagl-VTTOU7LN.js +499 -0
  88. package/dist/ncaa_baseball-UV75LNVR.js +2670 -0
  89. package/dist/ncaa_baseball-VD5J5C72.js +419 -0
  90. package/dist/ncaa_baseball-YRBS32D6.js +4592 -0
  91. package/dist/ncaa_mhockey-IMYHS7QS.js +1033 -0
  92. package/dist/ncaa_mhockey-TXTKWHE3.js +115 -0
  93. package/dist/ncaa_mhockey-U3MFW5VT.js +663 -0
  94. package/dist/ncaa_softball-6WR6KS64.js +420 -0
  95. package/dist/ncaa_softball-RZCTIE56.js +2654 -0
  96. package/dist/ncaa_softball-VOD4PVGY.js +4599 -0
  97. package/dist/ncaa_whockey-6XXGUTBK.js +607 -0
  98. package/dist/ncaa_whockey-AKKYKY67.js +53 -0
  99. package/dist/ncaa_whockey-KBEKXFBB.js +293 -0
  100. package/dist/nfl-BDB2HYAR.js +625 -0
  101. package/dist/nfl-EHYHDLJD.js +37 -0
  102. package/dist/nfl-VF4NJCHY.js +514 -0
  103. package/dist/nfl_gsis-UY64JO37.js +4 -0
  104. package/dist/nhl-6WPHME7H.js +511 -0
  105. package/dist/nhl-EMIJFZHY.js +884 -0
  106. package/dist/nhl-ISFUKRFU.js +37 -0
  107. package/dist/ohl-2FEM554G.js +113 -0
  108. package/dist/ohl-C5Q4W6JB.js +32 -0
  109. package/dist/ohl-TQ4J46VC.js +66 -0
  110. package/dist/phf-H7POU2KW.js +32 -0
  111. package/dist/phf-K5IFMZUM.js +13 -0
  112. package/dist/phf-TWJI3BOW.js +20 -0
  113. package/dist/plot.d.ts +449 -0
  114. package/dist/plot.js +711 -0
  115. package/dist/plot.js.map +1 -0
  116. package/dist/plotly.d.ts +129 -0
  117. package/dist/plotly.js +360 -0
  118. package/dist/plotly.js.map +1 -0
  119. package/dist/pwhl-OX3BO4JI.js +53 -0
  120. package/dist/pwhl-W44OC3C6.js +17 -0
  121. package/dist/pwhl-ZJGG3H7W.js +23 -0
  122. package/dist/qmjhl-5HISEHLL.js +35 -0
  123. package/dist/qmjhl-DA3UHKE5.js +125 -0
  124. package/dist/qmjhl-YG4BOQPF.js +614 -0
  125. package/dist/react.d.ts +101 -0
  126. package/dist/react.js +164 -0
  127. package/dist/react.js.map +1 -0
  128. package/dist/resolve-CQW-jzvr.d.ts +28 -0
  129. package/dist/selection-D4T3PkRt.d.ts +140 -0
  130. package/dist/shots.d.ts +364 -0
  131. package/dist/shots.js +85 -0
  132. package/dist/shots.js.map +1 -0
  133. package/dist/soccer-7BH3ZLVL.js +7898 -0
  134. package/dist/soccer-MRU25OGE.js +2636 -0
  135. package/dist/soccer-PF4IPPVA.js +12342 -0
  136. package/dist/surfaces-ChaD9nei.d.ts +38 -0
  137. package/dist/testing.d.ts +75 -0
  138. package/dist/testing.js +240 -0
  139. package/dist/testing.js.map +1 -0
  140. package/dist/tiers-B6pUBPcE.d.ts +93 -0
  141. package/dist/types-C7n6gJF3.d.ts +62 -0
  142. package/dist/ufl-7VLZTLTT.js +143 -0
  143. package/dist/ufl-CCMBLPSW.js +100 -0
  144. package/dist/ufl-F2BJRXME.js +16 -0
  145. package/dist/usfl-4CR5RMZJ.js +32 -0
  146. package/dist/usfl-LD7HP2YK.js +14 -0
  147. package/dist/usfl-VU4YSJQD.js +19 -0
  148. package/dist/ushl-CPG2LB4L.js +73 -0
  149. package/dist/ushl-PQL6HJAD.js +88 -0
  150. package/dist/ushl-ZT2ATPHK.js +22 -0
  151. package/dist/vega.d.ts +85 -0
  152. package/dist/vega.js +362 -0
  153. package/dist/vega.js.map +1 -0
  154. package/dist/wbb-3KHCDYTK.js +5087 -0
  155. package/dist/wbb-BMX53Q3Q.js +369 -0
  156. package/dist/wbb-QNRN2CJJ.js +3620 -0
  157. package/dist/whl-4ATYZWUB.js +97 -0
  158. package/dist/whl-RVFPL6MH.js +79 -0
  159. package/dist/whl-Y7ASKYMM.js +28 -0
  160. package/dist/wnba-FWX5SFVI.js +232 -0
  161. package/dist/wnba-GX6HZR4B.js +220 -0
  162. package/dist/wnba-NTMWIPP6.js +20 -0
  163. package/dist/xfl-43R2JMJK.js +30 -0
  164. package/dist/xfl-H3BXQPZZ.js +16 -0
  165. package/dist/xfl-XON7A7FI.js +69 -0
  166. package/package.json +154 -3
@@ -0,0 +1,809 @@
1
+ import { toId, sameCursor, focusIds } from './chunk-4C3GX6QO.js';
2
+ import { InputError, warn } from './chunk-E7G7VSLV.js';
3
+ import { select, brush, brushX, brushY, Delaunay, bisector, pointer } from 'd3';
4
+
5
+ // src/interact/highlight.ts
6
+ var SVG_NS = "http://www.w3.org/2000/svg";
7
+ var CSS = ".sdv-focus [data-sdv-id]:not(.sdv-hl):not([data-sdv-axis]){opacity:var(--sdv-dim-opacity,0.2)}";
8
+ var EMPTY = /* @__PURE__ */ new Set();
9
+ var roots = /* @__PURE__ */ new WeakMap();
10
+ function hasDom() {
11
+ return typeof window !== "undefined" && typeof document !== "undefined";
12
+ }
13
+ var markId = (el) => el.hasAttribute("data-sdv-axis") ? "" : el.getAttribute("data-sdv-id") ?? "";
14
+ function stampedIn(node) {
15
+ if (node.nodeType !== 1) return [];
16
+ const el = node;
17
+ const out = Array.from(el.querySelectorAll("[data-sdv-id]"));
18
+ if (el.hasAttribute("data-sdv-id")) out.unshift(el);
19
+ return out;
20
+ }
21
+ function add(index, id, el) {
22
+ const set = index.get(id);
23
+ if (set) set.add(el);
24
+ else index.set(id, /* @__PURE__ */ new Set([el]));
25
+ }
26
+ function apply(root, state, records) {
27
+ for (const r of records) {
28
+ for (const el of Array.from(r.removedNodes).flatMap(stampedIn)) {
29
+ const id = markId(el);
30
+ const set = state.index.get(id);
31
+ if (set === void 0 || root.contains(el)) continue;
32
+ set.delete(el);
33
+ if (set.size === 0) state.index.delete(id);
34
+ }
35
+ for (const el of Array.from(r.addedNodes).flatMap(stampedIn)) {
36
+ const id = markId(el);
37
+ if (id === "" || !root.contains(el)) continue;
38
+ add(state.index, id, el);
39
+ if (state.ids?.has(id)) el.classList.add("sdv-hl");
40
+ }
41
+ }
42
+ }
43
+ function litOf(root) {
44
+ const known = roots.get(root);
45
+ if (known) return known;
46
+ const index = /* @__PURE__ */ new Map();
47
+ for (const el of stampedIn(root)) {
48
+ const id = markId(el);
49
+ if (id !== "" && el !== root) add(index, id, el);
50
+ }
51
+ const doc = root.ownerDocument;
52
+ const style = root.namespaceURI === SVG_NS ? doc.createElementNS(SVG_NS, "style") : doc.createElement("style");
53
+ style.setAttribute("data-sdv-interact", "");
54
+ style.textContent = CSS;
55
+ root.append(style);
56
+ const state = { index, mo: new MutationObserver((records) => apply(root, state, records)), ids: null };
57
+ state.mo.observe(root, { childList: true, subtree: true });
58
+ roots.set(root, state);
59
+ return state;
60
+ }
61
+ function highlight(root, ids) {
62
+ if (!hasDom()) return [];
63
+ const state = litOf(root);
64
+ apply(root, state, state.mo.takeRecords());
65
+ const prev = state.ids ?? EMPTY;
66
+ const next = ids ?? EMPTY;
67
+ for (const id of prev) {
68
+ if (next.has(id)) continue;
69
+ for (const el of state.index.get(id) ?? []) el.classList.remove("sdv-hl");
70
+ }
71
+ const missing = [];
72
+ for (const id of next) {
73
+ const els = state.index.get(id);
74
+ if (els === void 0) missing.push(id);
75
+ else if (!prev.has(id)) for (const el of els) el.classList.add("sdv-hl");
76
+ }
77
+ root.classList.toggle("sdv-focus", ids !== null);
78
+ state.ids = ids === null ? null : new Set(ids);
79
+ return missing;
80
+ }
81
+
82
+ // src/interact/brush.ts
83
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
84
+ var get = (row, f) => typeof f === "function" ? f(row) : row[f];
85
+ var num = (v) => v === null || v === void 0 || v === "" ? Number.NaN : Number(v);
86
+ var span = (a, b) => {
87
+ const p = num(a);
88
+ const q = num(b);
89
+ return p <= q ? [p, q] : [q, p];
90
+ };
91
+ var NOOP = { move: () => {
92
+ }, destroy: () => {
93
+ } };
94
+ function scaleOf(s) {
95
+ if (typeof s !== "function") return s;
96
+ const f = s;
97
+ const { invert: inv, bandwidth: bw, domain: dom } = s;
98
+ return {
99
+ apply: (v) => f(v),
100
+ ...inv && { invert: (p) => inv.call(s, Number(p)) },
101
+ range: s.range(),
102
+ ...bw && { bandwidth: bw.call(s) },
103
+ ...dom && { domain: dom.call(s) }
104
+ };
105
+ }
106
+ var boxOf = (svg) => {
107
+ const v = (svg.getAttribute("viewBox") ?? "").trim().split(/[\s,]+/).map(Number);
108
+ if (v.length === 4 && v.every(Number.isFinite)) return v;
109
+ return [0, 0, Number(svg.getAttribute("width")) || 0, Number(svg.getAttribute("height")) || 0];
110
+ };
111
+ function brushFilter(figure, store, o) {
112
+ if (o.x === void 0 && o.y === void 0)
113
+ throw new InputError("brushFilter needs x, y or both: the fields its brushed axes encode");
114
+ if (o.empty !== void 0 && o.empty !== "dim" && o.empty !== "clear")
115
+ throw new InputError(`brushFilter: empty is "dim" or "clear", not ${JSON.stringify(o.empty)}`);
116
+ const xs = o.x === void 0 ? void 0 : scaleOf(o.scales?.x ?? figure.scale?.("x"));
117
+ const ys = o.y === void 0 ? void 0 : scaleOf(o.scales?.y ?? figure.scale?.("y"));
118
+ const usable = (s) => typeof s?.apply === "function" && typeof s?.invert === "function";
119
+ if (o.x !== void 0 && !usable(xs) || o.y !== void 0 && !usable(ys))
120
+ throw new InputError(
121
+ "brushFilter needs continuous x and y scales on the axes it brushes; band and point scales cannot be inverted, and a chart without figure.scale passes `scales`"
122
+ );
123
+ if (!hasDom()) return NOOP;
124
+ const svg = figure.tagName.toLowerCase() === "svg" ? figure : Array.from(figure.querySelectorAll(":scope > svg")).at(-1);
125
+ if (!svg) throw new InputError("brushFilter: no <svg> in the figure");
126
+ const [bx, by, bw, bh] = boxOf(svg);
127
+ const pixels = (s, from, to) => {
128
+ const r = Array.from(s?.range ?? [], Number).filter(Number.isFinite);
129
+ return r.length > 0 ? [Math.min(...r), Math.max(...r)] : [from, to];
130
+ };
131
+ const [x0px, x1px] = pixels(xs, bx, bx + bw);
132
+ const [y0px, y1px] = pixels(ys, by, by + bh);
133
+ const node = svg.ownerDocument.createElementNS(SVG_NS2, "g");
134
+ node.setAttribute("class", "sdv-brush");
135
+ svg.insertBefore(node, svg.firstChild);
136
+ const g = select(node);
137
+ let mine = null;
138
+ let picked = null;
139
+ let last = "";
140
+ let asked = null;
141
+ const idOf = (row, i) => o.id === void 0 ? String(i) : toId(get(row, o.id));
142
+ const b = (xs && ys ? brush() : xs ? brushX() : brushY()).extent([
143
+ [x0px, y0px],
144
+ [x1px, y1px]
145
+ ]);
146
+ const release = () => {
147
+ last = "";
148
+ if (mine === null) return;
149
+ const own = store.getState().selected === picked;
150
+ mine = null;
151
+ picked = null;
152
+ store.set(own ? { selected: [], predicate: null } : { predicate: null });
153
+ };
154
+ const erase = () => {
155
+ g.call(b.move, null);
156
+ };
157
+ b.on("brush end", (event) => {
158
+ const sel = event.selection;
159
+ if (sel === null) return release();
160
+ const [px, py] = typeof sel[0] === "number" ? xs ? [sel, null] : [null, sel] : [
161
+ [sel[0][0], sel[1][0]],
162
+ [sel[0][1], sel[1][1]]
163
+ ];
164
+ const tests = [];
165
+ if (o.x !== void 0 && px)
166
+ tests.push([o.x, ...asked?.x ?? span(xs?.invert?.(px[0]), xs?.invert?.(px[1]))]);
167
+ if (o.y !== void 0 && py)
168
+ tests.push([o.y, ...asked?.y ?? span(ys?.invert?.(py[0]), ys?.invert?.(py[1]))]);
169
+ const key = tests.map(([, lo, hi]) => `${lo}|${hi}`).join("|");
170
+ if (key === last) return;
171
+ const inside = (row) => tests.every(([f, lo, hi]) => {
172
+ const v = num(get(row, f));
173
+ return v >= lo && v <= hi;
174
+ });
175
+ const ids = o.data.flatMap((row, i) => inside(row) ? [idOf(row, i)] : []);
176
+ if (ids.length === 0 && o.empty === "clear") {
177
+ release();
178
+ if (event.type === "end") erase();
179
+ return;
180
+ }
181
+ last = key;
182
+ mine = inside;
183
+ const had = store.getState().selected;
184
+ store.set({ selected: ids, predicate: inside });
185
+ const now = store.getState().selected;
186
+ picked = now !== had || had === picked ? now : null;
187
+ });
188
+ g.call(b);
189
+ const off = store.subscribe((s) => {
190
+ if (mine === null || s.predicate === mine) return;
191
+ mine = null;
192
+ erase();
193
+ });
194
+ const at = (s, r, axis) => {
195
+ if (s === void 0) return null;
196
+ if (r === void 0)
197
+ throw new InputError(`brushFilter: move needs region.${axis}, the brushed ${axis} range`);
198
+ return span(s.apply(r[0]), s.apply(r[1]));
199
+ };
200
+ return {
201
+ move(region) {
202
+ if (region === null) {
203
+ g.call(b.move, null);
204
+ return;
205
+ }
206
+ const px = at(xs, region.x, "x");
207
+ const py = at(ys, region.y, "y");
208
+ asked = {
209
+ ...px && region.x && { x: span(region.x[0], region.x[1]) },
210
+ ...py && region.y && { y: span(region.y[0], region.y[1]) }
211
+ };
212
+ try {
213
+ g.call(
214
+ b.move,
215
+ px && py ? [
216
+ [px[0], py[0]],
217
+ [px[1], py[1]]
218
+ ] : px ?? py
219
+ );
220
+ } finally {
221
+ asked = null;
222
+ }
223
+ },
224
+ destroy() {
225
+ off();
226
+ node.remove();
227
+ if (mine !== null && store.getState().predicate === mine) release();
228
+ }
229
+ };
230
+ }
231
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
232
+ var CSS2 = ".sdv-cursor line,.sdv-cursor ellipse{fill:none;stroke:var(--sdv-cursor-color,rgba(0,0,0,.2));stroke-width:var(--sdv-cursor-width,10px)}.sdv-cursor rect{fill:var(--sdv-cursor-color,rgba(0,0,0,.2))}.sdv-cursor circle{fill:currentColor}";
233
+ var FLIP_AT = 30 / 35;
234
+ var isNum = (v) => typeof v === "number" && Number.isFinite(v);
235
+ var extent = (r) => {
236
+ const a = Array.from(r ?? [], Number).filter(Number.isFinite);
237
+ return a.length > 0 ? [Math.min(...a), Math.max(...a)] : null;
238
+ };
239
+ var box = (svg) => {
240
+ const v = (svg.getAttribute("viewBox") ?? "").trim().split(/[\s,]+/).map(Number);
241
+ const [x, y, w, h] = v.length === 4 && v.every(Number.isFinite) ? v : [0, 0, Number(svg.getAttribute("width")) || 0, Number(svg.getAttribute("height")) || 0];
242
+ return { x: [x ?? 0, (x ?? 0) + (w ?? 0)], y: [y ?? 0, (y ?? 0) + (h ?? 0)] };
243
+ };
244
+ function linkCursor(root, store, o) {
245
+ const { shape } = o;
246
+ if (typeof o.field !== "string" || o.field === "")
247
+ throw new InputError("linkCursor needs a non-empty cursor field");
248
+ if (typeof shape !== "object" || shape === null)
249
+ throw new InputError("linkCursor needs a shape: what to draw");
250
+ if (shape.axis !== "x" && shape.axis !== "y" && shape.axis !== "ring")
251
+ throw new InputError(`linkCursor shape.axis must be "x", "y" or "ring", got ${String(shape.axis)}`);
252
+ if (o.flipAt !== void 0 && !(o.flipAt > 0 && o.flipAt <= 1))
253
+ throw new InputError(`linkCursor flipAt must be in (0, 1], got ${String(o.flipAt)}`);
254
+ const ring = shape.axis === "ring" ? { ...shape, x: scaleOf(shape.x), y: scaleOf(shape.y) } : null;
255
+ const line = shape.axis === "ring" ? null : { ...shape, scale: scaleOf(shape.scale), cross: scaleOf(shape.cross) };
256
+ const isScale = (s) => typeof s?.apply === "function";
257
+ if (ring ? !isScale(ring.x) || !isScale(ring.y) : !isScale(line?.scale) || line?.cross !== void 0 && !isScale(line.cross))
258
+ throw new InputError(
259
+ `linkCursor: shape.${ring ? "x and shape.y" : line?.cross === void 0 ? "scale" : "scale and shape.cross"} must be scales, a Plot figure's or d3's`
260
+ );
261
+ if (ring) {
262
+ const c = ring.center;
263
+ if (!Array.isArray(c) || c.length !== 2 || !c.every(Number.isFinite))
264
+ throw new InputError(`linkCursor ring center must be two finite numbers, got ${JSON.stringify(c)}`);
265
+ if (o.emit === true) throw new InputError("linkCursor: a ring follows the cursor but never emits it");
266
+ if (o.label || o.dot)
267
+ throw new InputError("linkCursor: label and dot are for an x or y cursor, not a ring");
268
+ }
269
+ if (line?.width !== void 0 && !(isNum(line.width) && line.width > 0))
270
+ throw new InputError(`linkCursor shape.width must be a finite number > 0, got ${String(line.width)}`);
271
+ if (line?.axis === "y" && o.label)
272
+ throw new InputError("linkCursor: label is a readout beside an x cursor");
273
+ let starts = null;
274
+ if (line && typeof line.scale.bandwidth === "number") {
275
+ const d = Array.from(line.scale.domain ?? []);
276
+ if (d.length === 0 || !d.every(isNum))
277
+ throw new InputError("linkCursor: a band scale's domain must be numbers, each band's first value");
278
+ starts = [...d].sort((a, b) => a - b);
279
+ }
280
+ const emit = line !== null && o.emit !== false;
281
+ if (emit && starts === null && typeof line?.scale.invert !== "function")
282
+ throw new InputError("linkCursor: emitting needs a scale with invert (linear) or a bandwidth (band)");
283
+ const figure = root;
284
+ const cross = line?.cross ?? (line && typeof figure.scale === "function" ? figure.scale(line.axis === "x" ? "y" : "x") : void 0);
285
+ if (o.dot && !cross) throw new InputError("linkCursor: dot needs a cross scale (shape.cross)");
286
+ if (!hasDom()) return () => {
287
+ };
288
+ const svg = root.tagName.toLowerCase() === "svg" ? root : Array.from(root.querySelectorAll(":scope > svg")).at(-1);
289
+ if (!svg) throw new InputError("linkCursor: no <svg> in the root");
290
+ const doc = svg.ownerDocument;
291
+ const make = (tag, parent) => {
292
+ const el = doc.createElementNS(SVG_NS3, tag);
293
+ parent.append(el);
294
+ return el;
295
+ };
296
+ if (!svg.querySelector(':scope > style[data-sdv-interact="cursor"]')) {
297
+ const style = make("style", svg);
298
+ style.setAttribute("data-sdv-interact", "cursor");
299
+ style.textContent = CSS2;
300
+ }
301
+ const g = make("g", svg);
302
+ g.setAttribute("class", "sdv-cursor");
303
+ g.setAttribute("pointer-events", "none");
304
+ g.setAttribute("display", "none");
305
+ const mark = make(ring ? "ellipse" : starts || line?.width ? "rect" : "line", g);
306
+ const text = o.label ? make("text", g) : null;
307
+ const tspans = [];
308
+ const circle = o.dot ? make("circle", g) : null;
309
+ circle?.setAttribute("r", "3");
310
+ const vb = box(svg);
311
+ const along = line ? extent(line.scale.range) ?? vb[line.axis] : vb.x;
312
+ const across = line ? extent(cross?.range) ?? vb[line.axis === "x" ? "y" : "x"] : vb.y;
313
+ const end = (s, i) => {
314
+ const next = s[i + 1];
315
+ if (next !== void 0) return next;
316
+ const last = s[i] ?? 0;
317
+ const prev = s[i - 1];
318
+ return prev === void 0 ? Number.POSITIVE_INFINITY : last + (last - prev);
319
+ };
320
+ const set = (el, attrs) => {
321
+ for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, String(v));
322
+ };
323
+ const place = (v) => {
324
+ if (!line) return null;
325
+ const px = (d) => Number(line.scale.apply(d));
326
+ let lo;
327
+ let hi;
328
+ let mid;
329
+ if (starts) {
330
+ let i = -1;
331
+ for (let k = 0; k < starts.length && (starts[k] ?? 0) <= v; k++) i = k;
332
+ if (i < 0 || v >= end(starts, i)) return null;
333
+ lo = px(starts[i] ?? 0);
334
+ hi = lo + (line.scale.bandwidth ?? 0);
335
+ mid = (lo + hi) / 2;
336
+ } else {
337
+ const w = (line.width ?? 0) / 2;
338
+ const [p, q] = [px(v - w), px(v + w)];
339
+ [lo, hi, mid] = [Math.min(p, q), Math.max(p, q), px(v)];
340
+ }
341
+ return [lo, hi, mid].every(Number.isFinite) ? [lo, hi, mid] : null;
342
+ };
343
+ const draw = (c) => {
344
+ const v = c?.value;
345
+ if (v === void 0) {
346
+ g.setAttribute("display", "none");
347
+ return;
348
+ }
349
+ if (ring) {
350
+ const [c0, c1] = ring.center;
351
+ const cx = Number(ring.x.apply(c0));
352
+ const cy = Number(ring.y.apply(c1));
353
+ const rx = Math.abs(Number(ring.x.apply(c0 + v)) - cx);
354
+ const ry = Math.abs(Number(ring.y.apply(c1 + v)) - cy);
355
+ if (![cx, cy, rx, ry].every(Number.isFinite)) {
356
+ g.setAttribute("display", "none");
357
+ return;
358
+ }
359
+ set(mark, { cx, cy, rx, ry });
360
+ g.removeAttribute("display");
361
+ return;
362
+ }
363
+ const at = place(v);
364
+ if (!line || !at) {
365
+ g.setAttribute("display", "none");
366
+ return;
367
+ }
368
+ const [lo, hi, mid] = at;
369
+ const [a0, a1] = across;
370
+ const x = line.axis === "x";
371
+ if (mark.tagName === "rect")
372
+ set(
373
+ mark,
374
+ x ? { x: lo, width: hi - lo, y: a0, height: a1 - a0 } : { y: lo, height: hi - lo, x: a0, width: a1 - a0 }
375
+ );
376
+ else set(mark, x ? { x1: mid, x2: mid, y1: a0, y2: a1 } : { y1: mid, y2: mid, x1: a0, x2: a1 });
377
+ if (text && o.label) {
378
+ const lines = o.label(v);
379
+ const [p0, p1] = along;
380
+ const flip = (mid - p0) / (p1 - p0 || 1) > (o.flipAt ?? FLIP_AT);
381
+ while (tspans.length < lines.length) {
382
+ const el = make("tspan", text);
383
+ el.setAttribute("dy", tspans.length === 0 ? "1em" : "1.3em");
384
+ const t = doc.createTextNode("");
385
+ el.append(t);
386
+ tspans.push({ el, line: t });
387
+ }
388
+ set(text, { y: a0, "text-anchor": flip ? "end" : "start" });
389
+ for (const [i, { el, line: t }] of tspans.entries()) {
390
+ el.setAttribute("x", String(flip ? lo - 10 : hi + 10));
391
+ t.data = lines[i] ?? "";
392
+ }
393
+ }
394
+ if (circle && o.dot && cross) {
395
+ const dv = o.dot(v);
396
+ const cp = dv === null ? Number.NaN : Number(cross.apply(dv));
397
+ if (Number.isFinite(cp)) {
398
+ set(circle, x ? { cx: mid, cy: cp } : { cx: cp, cy: mid });
399
+ circle.removeAttribute("display");
400
+ } else circle.setAttribute("display", "none");
401
+ }
402
+ g.removeAttribute("display");
403
+ };
404
+ let drawn = null;
405
+ const sync = (c) => {
406
+ const mine = c !== null && c.field === o.field ? c : null;
407
+ if (sameCursor(mine, drawn)) return;
408
+ drawn = mine;
409
+ draw(mine);
410
+ };
411
+ const offs = [store.subscribe((s) => sync(s.cursor))];
412
+ offs.push(() => g.remove());
413
+ sync(store.getState().cursor);
414
+ let wrote = null;
415
+ const clear = () => {
416
+ if (wrote !== null && store.getState().cursor === wrote) store.set({ cursor: null });
417
+ wrote = null;
418
+ };
419
+ if (line && emit) {
420
+ const valueAt = (p) => {
421
+ const [p0, p1] = along;
422
+ if (!(p >= p0 && p <= p1)) return null;
423
+ if (starts) {
424
+ let best = 0;
425
+ let gap = Number.POSITIVE_INFINITY;
426
+ for (const [i, s2] of starts.entries()) {
427
+ const d = Math.abs(Number(line.scale.apply(s2)) + (line.scale.bandwidth ?? 0) / 2 - p);
428
+ if (d < gap) [best, gap] = [i, d];
429
+ }
430
+ const e = end(starts, best);
431
+ const s = starts[best] ?? 0;
432
+ return Number.isFinite(e) ? (s + e) / 2 : s;
433
+ }
434
+ const v = Number(line.scale.invert?.(p));
435
+ return o.snap ? o.snap(v) : v;
436
+ };
437
+ const move = (e) => {
438
+ const [mx, my] = pointer(e, svg);
439
+ const v = valueAt(line.axis === "x" ? mx : my);
440
+ if (v === null || !Number.isFinite(v)) clear();
441
+ else {
442
+ const had = store.getState().cursor;
443
+ store.set({ cursor: { field: o.field, value: v } });
444
+ const now = store.getState().cursor;
445
+ wrote = now !== had || had === wrote ? now : null;
446
+ }
447
+ };
448
+ const on = (type, fn, capture) => {
449
+ svg.addEventListener(type, fn, { capture });
450
+ offs.push(() => svg.removeEventListener(type, fn, { capture }));
451
+ };
452
+ on("pointermove", move, true);
453
+ on("pointerdown", move, true);
454
+ on("pointerleave", clear, false);
455
+ on("pointercancel", clear, false);
456
+ }
457
+ return () => {
458
+ for (const off of offs) off();
459
+ clear();
460
+ };
461
+ }
462
+
463
+ // src/interact/link.ts
464
+ var first = (ids) => {
465
+ for (const id of ids) return id;
466
+ return null;
467
+ };
468
+ var MARK = '[data-sdv-id]:not([data-sdv-id=""]):not([data-sdv-axis])';
469
+ var markAt = (figure, e) => {
470
+ const t = e.target;
471
+ const mark = t?.nodeType === 1 ? t.closest(MARK) : null;
472
+ return mark && figure.contains(mark) ? mark : null;
473
+ };
474
+ var warned = /* @__PURE__ */ new WeakSet();
475
+ var links = /* @__PURE__ */ new WeakMap();
476
+ var toggling = /* @__PURE__ */ new WeakSet();
477
+ var TOGGLE_ATTRS = ["role", "tabindex", "aria-checked", "aria-label"];
478
+ function linkSelection(store, o) {
479
+ const toggled = toggleMarks(o);
480
+ const { figure, table, hover = true } = o;
481
+ if (typeof hover !== "boolean" && typeof hover?.id !== "function")
482
+ throw new InputError("linkSelection: hover is true, false or { id: (datum) => link id }");
483
+ if (!hasDom()) return () => {
484
+ };
485
+ if (figure && toggled.length > 0 && toggling.has(figure))
486
+ throw new InputError(
487
+ 'linkSelection: this figure already has a live select: "toggle" link (a click would toggle twice); tear it down first'
488
+ );
489
+ if (figure) links.set(figure, (links.get(figure) ?? 0) + 1);
490
+ const offs = [];
491
+ let wrote = null;
492
+ const writeHover = (id) => {
493
+ const had = store.getState().hover;
494
+ store.set({ hover: id === null ? [] : [id] });
495
+ const now = store.getState().hover;
496
+ wrote = now !== had || had === wrote ? now : null;
497
+ };
498
+ let syncing = false;
499
+ const sync = (s) => {
500
+ if (figure) {
501
+ const focus = focusIds(s);
502
+ const missing = highlight(figure, focus);
503
+ if (focus !== null && focus.size > 0 && missing.length === focus.size && !warned.has(figure)) {
504
+ warned.add(figure);
505
+ const shown = missing.slice(0, 5).join(", ");
506
+ warn(
507
+ `link:missing:${shown}`,
508
+ `none of the linked ids (${shown}${missing.length > 5 ? ", \u2026" : ""}) is drawn in this figure; the marks' id channel and the table's rowKey must hold the same values`
509
+ );
510
+ }
511
+ }
512
+ if (table) {
513
+ syncing = true;
514
+ try {
515
+ table.setSelection(s.selected);
516
+ table.setExternalFilter(s.predicate);
517
+ table.setHover(first(s.hover));
518
+ } finally {
519
+ syncing = false;
520
+ }
521
+ }
522
+ };
523
+ const on = (el, type, fn) => {
524
+ el.addEventListener(type, fn);
525
+ offs.push(() => el.removeEventListener(type, fn));
526
+ };
527
+ if (figure && hover === true) {
528
+ on(figure, "mouseover", (e) => {
529
+ const mark = markAt(figure, e);
530
+ writeHover(mark ? mark.getAttribute("data-sdv-id") ?? "" : null);
531
+ });
532
+ on(figure, "mouseleave", () => writeHover(null));
533
+ } else if (figure && typeof hover === "object") {
534
+ on(figure, "input", (e) => {
535
+ const v = e.target?.value;
536
+ writeHover(v === null || v === void 0 ? null : toId(hover.id(v)));
537
+ });
538
+ }
539
+ if (table) {
540
+ offs.push(
541
+ table.subscribe((e) => {
542
+ if (syncing) return;
543
+ if (e.type === "hover") writeHover(e.id);
544
+ else if (e.type === "select") store.set({ selected: e.ids });
545
+ })
546
+ );
547
+ }
548
+ if (figure && toggled.length > 0) offs.push(toggles(figure, store, toggled));
549
+ offs.push(store.subscribe(sync));
550
+ sync(store.getState());
551
+ let live = true;
552
+ return () => {
553
+ if (!live) return;
554
+ live = false;
555
+ if (wrote !== null && wrote.size > 0 && store.getState().hover === wrote) store.set({ hover: [] });
556
+ wrote = null;
557
+ for (const off of offs) off();
558
+ if (figure) {
559
+ const n = (links.get(figure) ?? 1) - 1;
560
+ if (n > 0) links.set(figure, n);
561
+ else {
562
+ links.delete(figure);
563
+ highlight(figure, null);
564
+ }
565
+ }
566
+ if (table) {
567
+ const s = store.getState();
568
+ if (s.predicate !== null) table.setExternalFilter(null);
569
+ if (s.hover.size > 0) table.setHover(null);
570
+ }
571
+ };
572
+ }
573
+ function toggleMarks(t) {
574
+ if (t.select === void 0) return [];
575
+ if (t.select !== "toggle")
576
+ throw new InputError(`linkSelection: select is "toggle", not ${JSON.stringify(t.select)}`);
577
+ if (t.figure === void 0)
578
+ throw new InputError('linkSelection: select "toggle" needs the figure whose marks it toggles');
579
+ const marks = Array.from(t.figure.querySelectorAll(MARK));
580
+ const linked = marks.find((m) => m.closest("a[href]") !== null);
581
+ if (linked)
582
+ throw new InputError(
583
+ `linkSelection: select "toggle" would put a checkbox inside a link (the mark "${linked.getAttribute("data-sdv-id")}" is in an <a href>); drop one`
584
+ );
585
+ return marks;
586
+ }
587
+ function toggles(figure, store, marks) {
588
+ const byId = /* @__PURE__ */ new Map();
589
+ let shown = store.getState().selected;
590
+ const own = marks.map((m) => TOGGLE_ATTRS.map((a) => m.getAttribute(a)));
591
+ for (const m of marks) {
592
+ const id = m.getAttribute("data-sdv-id") ?? "";
593
+ const same = byId.get(id);
594
+ if (same) same.push(m);
595
+ else byId.set(id, [m]);
596
+ m.setAttribute("role", "checkbox");
597
+ m.setAttribute("tabindex", "0");
598
+ m.setAttribute("aria-checked", String(shown.has(id)));
599
+ if (!m.hasAttribute("aria-label") && !m.hasAttribute("aria-labelledby") && !m.querySelector(":scope > title"))
600
+ m.setAttribute("aria-label", id);
601
+ }
602
+ const flip = (e) => {
603
+ const id = markAt(figure, e)?.getAttribute("data-sdv-id");
604
+ if (!id) return;
605
+ if (e.type === "keydown") {
606
+ const key = e.key;
607
+ if (key !== "Enter" && key !== " ") return;
608
+ e.preventDefault();
609
+ }
610
+ const next = new Set(store.getState().selected);
611
+ if (!next.delete(id)) next.add(id);
612
+ store.set({ selected: next });
613
+ };
614
+ const check = (ids, unless, value) => {
615
+ for (const id of ids)
616
+ if (!unless.has(id)) for (const m of byId.get(id) ?? []) m.setAttribute("aria-checked", value);
617
+ };
618
+ const off = store.subscribe((s) => {
619
+ if (s.selected === shown) return;
620
+ check(shown, s.selected, "false");
621
+ check(s.selected, shown, "true");
622
+ shown = s.selected;
623
+ });
624
+ figure.addEventListener("click", flip);
625
+ figure.addEventListener("keydown", flip);
626
+ toggling.add(figure);
627
+ return () => {
628
+ toggling.delete(figure);
629
+ off();
630
+ figure.removeEventListener("click", flip);
631
+ figure.removeEventListener("keydown", flip);
632
+ for (const [i, m] of marks.entries())
633
+ for (const [k, a] of TOGGLE_ATTRS.entries()) {
634
+ const v = own[i]?.[k] ?? null;
635
+ if (v === null) m.removeAttribute(a);
636
+ else m.setAttribute(a, v);
637
+ }
638
+ };
639
+ }
640
+
641
+ // src/interact/tooltip.ts
642
+ var SVG_NS4 = "http://www.w3.org/2000/svg";
643
+ var CSS3 = ".sdv-tip>.sdv-tip-box{fill:var(--sdv-tip-bg,rgba(34,34,34,.85))}.sdv-tip>text{fill:var(--sdv-tip-fg,#ddd)}.sdv-tip>.sdv-tip-swatch{stroke:var(--sdv-tip-fg,#ddd)}";
644
+ function svgBox(svg) {
645
+ const [x = Number.NaN, y = Number.NaN, width = Number.NaN, height = Number.NaN] = (svg.getAttribute("viewBox") ?? "").trim().split(/[\s,]+/).map(Number);
646
+ if ([x, y, width, height].every(Number.isFinite)) return { x, y, width, height };
647
+ const side = (name) => {
648
+ const v = svg.getAttribute(name)?.trim();
649
+ const n = v ? Number(v) : Number.NaN;
650
+ return Number.isFinite(n) ? n : Number.POSITIVE_INFINITY;
651
+ };
652
+ return { x: 0, y: 0, width: side("width"), height: side("height") };
653
+ }
654
+ function svgRoot(root, fn) {
655
+ if (root.namespaceURI !== SVG_NS4 || root.localName !== "svg")
656
+ throw new InputError(`${fn} needs an <svg> root (got <${root.localName}>)`);
657
+ return root;
658
+ }
659
+ var NOOP2 = { show: () => {
660
+ }, hide: () => {
661
+ }, destroy: () => {
662
+ } };
663
+ function tooltip(root, o = {}) {
664
+ const { fontSize = 11 } = o;
665
+ if (!(fontSize > 0)) throw new InputError(`tooltip: fontSize must be a number > 0 (got ${fontSize})`);
666
+ for (const [k, v] of [
667
+ ["width", o.width],
668
+ ["height", o.height]
669
+ ])
670
+ if (v !== void 0 && !(v >= 0)) throw new InputError(`tooltip: ${k} must be a number >= 0 (got ${v})`);
671
+ if (!hasDom()) return NOOP2;
672
+ const svg = svgRoot(root, "tooltip");
673
+ const el = (name, attrs) => {
674
+ const e = svg.ownerDocument.createElementNS(SVG_NS4, name);
675
+ for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, String(v));
676
+ return e;
677
+ };
678
+ if (!svg.querySelector(':scope > style[data-sdv-interact="tip"]')) {
679
+ const style = el("style", { "data-sdv-interact": "tip" });
680
+ style.textContent = CSS3;
681
+ svg.append(style);
682
+ }
683
+ const box2 = el("rect", { class: "sdv-tip-box", rx: 4 });
684
+ const text = el("text", { "font-size": fontSize });
685
+ const swatch = el("rect", { class: "sdv-tip-swatch", width: 12, height: 12, rx: 2 });
686
+ const g = el("g", { class: "sdv-tip", "pointer-events": "none", display: "none" });
687
+ g.append(box2, text);
688
+ svg.append(g);
689
+ return {
690
+ show(x, y, lines, color) {
691
+ const b = svgBox(svg);
692
+ const w = Math.ceil(
693
+ 16 + 0.6 * fontSize * Math.max(0, ...lines.map((l) => l.length)) + (color ? 20 : 0)
694
+ );
695
+ const h = Math.ceil(10 + 1.35 * fontSize * lines.length);
696
+ text.replaceChildren(
697
+ ...lines.map((line, i) => {
698
+ const t = el("tspan", { x: 8, dy: i === 0 ? "1.3em" : "1.35em" });
699
+ if (i === 0) t.setAttribute("font-weight", "bold");
700
+ t.textContent = line;
701
+ return t;
702
+ })
703
+ );
704
+ box2.setAttribute("width", String(w));
705
+ box2.setAttribute("height", String(h));
706
+ if (color) {
707
+ swatch.setAttribute("fill", color);
708
+ swatch.setAttribute("x", String(w - 20));
709
+ swatch.setAttribute("y", String((h - 12) / 2));
710
+ g.append(swatch);
711
+ } else swatch.remove();
712
+ const right = b.x + (o.width ?? b.width);
713
+ const bottom = b.y + (o.height ?? b.height);
714
+ const tx = Math.max(b.x, x + 12 + w > right ? x - 12 - w : x + 12);
715
+ const ty = Math.min(Math.max(b.y, y - h - 8), bottom - h);
716
+ g.setAttribute("transform", `translate(${tx},${ty})`);
717
+ g.removeAttribute("display");
718
+ if (svg.lastElementChild !== g) svg.append(g);
719
+ },
720
+ hide() {
721
+ g.setAttribute("display", "none");
722
+ },
723
+ destroy() {
724
+ g.remove();
725
+ }
726
+ };
727
+ }
728
+
729
+ // src/interact/nearest.ts
730
+ var NOOP3 = { update: () => {
731
+ }, destroy: () => {
732
+ } };
733
+ var DIMENSIONS = ["x", "y", "xy"];
734
+ function nearestHover(root, store, o) {
735
+ const { dimension = "xy", radius = Number.POSITIVE_INFINITY, padding = 0, label } = o;
736
+ if (!DIMENSIONS.includes(dimension))
737
+ throw new InputError(`nearestHover: dimension must be "x", "y" or "xy" (got ${String(dimension)})`);
738
+ if (!(radius >= 0)) throw new InputError(`nearestHover: radius must be a number >= 0 (got ${radius})`);
739
+ if (!(padding >= 0)) throw new InputError(`nearestHover: padding must be a number >= 0 (got ${padding})`);
740
+ if (!hasDom()) return NOOP3;
741
+ const svg = svgRoot(root, "nearestHover");
742
+ const tip = label ? tooltip(svg) : null;
743
+ const far = (p, px, py) => dimension === "x" ? Math.abs(p.x - px) : dimension === "y" ? Math.abs(p.y - py) : Math.hypot(p.x - px, p.y - py);
744
+ let find = () => void 0;
745
+ const index = (points) => {
746
+ const ps = points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
747
+ if (ps.length === 0) find = () => void 0;
748
+ else if (dimension === "xy") {
749
+ const d = Delaunay.from(
750
+ ps,
751
+ (p) => p.x,
752
+ (p) => p.y
753
+ );
754
+ find = (px, py) => ps[d.find(px, py)];
755
+ } else {
756
+ const k = dimension;
757
+ const sorted = ps.slice().sort((a, b2) => a[k] - b2[k]);
758
+ const b = bisector((p) => p[k]);
759
+ find = (px, py) => sorted[b.center(sorted, k === "x" ? px : py)];
760
+ }
761
+ };
762
+ index(o.points);
763
+ let cur;
764
+ let wrote = null;
765
+ const to = (p) => {
766
+ const id = p === void 0 ? null : p.id;
767
+ if (id === cur && (id === null || store.getState().hover === wrote)) return;
768
+ cur = id;
769
+ const had = store.getState().hover;
770
+ store.set({ hover: id === null ? [] : [id] });
771
+ const now = store.getState().hover;
772
+ wrote = now !== had || had === wrote ? now : null;
773
+ const shown = p !== void 0 && label ? label(p.id) : null;
774
+ if (shown && p) tip?.show(p.x, p.y, shown.lines, shown.swatch);
775
+ else tip?.hide();
776
+ };
777
+ const move = (e) => {
778
+ const [px, py] = pointer(e, svg);
779
+ const b = svgBox(svg);
780
+ const inside = px >= b.x + padding && px <= b.x + b.width - padding && py >= b.y + padding && py <= b.y + b.height - padding;
781
+ const p = inside ? find(px, py) : void 0;
782
+ to(p !== void 0 && far(p, px, py) <= radius ? p : void 0);
783
+ };
784
+ const leave = () => to(void 0);
785
+ const listeners = [
786
+ ["pointermove", move, true],
787
+ ["pointerdown", move, true],
788
+ ["pointerleave", leave, false],
789
+ ["pointercancel", leave, false]
790
+ ];
791
+ for (const [type, fn, capture] of listeners) svg.addEventListener(type, fn, { capture });
792
+ return {
793
+ update(points) {
794
+ index(points);
795
+ cur = void 0;
796
+ },
797
+ destroy() {
798
+ for (const [type, fn, capture] of listeners) svg.removeEventListener(type, fn, { capture });
799
+ tip?.destroy();
800
+ if (wrote !== null && wrote.size > 0 && store.getState().hover === wrote) store.set({ hover: [] });
801
+ cur = void 0;
802
+ wrote = null;
803
+ }
804
+ };
805
+ }
806
+
807
+ export { brushFilter, hasDom, highlight, linkCursor, linkSelection, nearestHover, tooltip };
808
+ //# sourceMappingURL=interact.js.map
809
+ //# sourceMappingURL=interact.js.map