@bicharts/chart-host 0.6.76 → 0.6.78
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/adapters.mjs +75 -0
- package/dist/card.mjs +234 -0
- package/dist/chunk-BFN4NY22.mjs +3286 -0
- package/dist/{chunk-ZEXMBZHP.mjs → chunk-L554L6HF.mjs} +40 -3830
- package/dist/chunk-M7VM6F73.mjs +614 -0
- package/dist/chunk-MGAO2DR7.mjs +543 -0
- package/dist/index.mjs +72 -664
- package/dist/react.mjs +3 -1
- package/dist/types/adapters/d3.d.ts +3 -0
- package/dist/types/adapters/index.d.ts +3 -0
- package/dist/types/adapters/types.d.ts +108 -0
- package/dist/types/card.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/selectionCardChrome.d.ts +29 -0
- package/package.json +11 -1
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
/*! @bicharts/chart-host — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; Natural Earth and US Census/TIGER (public domain). Full text: NOTICE in this package. */
|
|
2
|
+
import {
|
|
3
|
+
AXIS_FILTER_CLASS,
|
|
4
|
+
CONTROL_CLASS,
|
|
5
|
+
LEGEND_MARK_CLASS,
|
|
6
|
+
MARK_CLASS,
|
|
7
|
+
MARK_KEY_ATTR,
|
|
8
|
+
MARK_SELECTED_CLASS,
|
|
9
|
+
ROW_IDX_ATTR,
|
|
10
|
+
SELECTION_RULE_ATTR,
|
|
11
|
+
SELECTION_RULE_CLICKED_MARKS
|
|
12
|
+
} from "./chunk-7JWL6LYE.mjs";
|
|
13
|
+
|
|
14
|
+
// src/selection.ts
|
|
15
|
+
function rowIdxsFromMark(el) {
|
|
16
|
+
if (!el) return [];
|
|
17
|
+
const raw = el.getAttribute("data-row-idx");
|
|
18
|
+
if (!raw) return [];
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const tok of raw.split(",")) {
|
|
21
|
+
const n = parseInt(tok.trim(), 10);
|
|
22
|
+
if (!isNaN(n) && n >= 0) out.push(n);
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
function nextSelection(current, rowIdxs, mods = {}) {
|
|
27
|
+
const cur = Array.from(current ?? []);
|
|
28
|
+
if (mods.ctrl || mods.shift) {
|
|
29
|
+
const next = new Set(cur);
|
|
30
|
+
for (const r of rowIdxs) {
|
|
31
|
+
if (next.has(r)) next.delete(r);
|
|
32
|
+
else next.add(r);
|
|
33
|
+
}
|
|
34
|
+
return Array.from(next);
|
|
35
|
+
}
|
|
36
|
+
const same = cur.length === rowIdxs.length && rowIdxs.every((r) => cur.includes(r));
|
|
37
|
+
return same ? [] : rowIdxs.slice();
|
|
38
|
+
}
|
|
39
|
+
function selectionRuleOf(container) {
|
|
40
|
+
try {
|
|
41
|
+
const el = container?.querySelector?.(`[${SELECTION_RULE_ATTR}]`);
|
|
42
|
+
return el?.getAttribute(SELECTION_RULE_ATTR) === SELECTION_RULE_CLICKED_MARKS ? "clicked-marks" : "any-row";
|
|
43
|
+
} catch {
|
|
44
|
+
return "any-row";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function markKeyOf(el) {
|
|
48
|
+
if (!el) return "";
|
|
49
|
+
const k = el.getAttribute(MARK_KEY_ATTR);
|
|
50
|
+
if (k != null && k !== "") return k;
|
|
51
|
+
return "rows:" + (el.getAttribute(ROW_IDX_ATTR) || "").replace(/\s+/g, "");
|
|
52
|
+
}
|
|
53
|
+
var toSet = (s) => new Set(Array.from(s ?? []));
|
|
54
|
+
function sameRowSet(a, b) {
|
|
55
|
+
const A = toSet(a), B = toSet(b);
|
|
56
|
+
if (A.size !== B.size) return false;
|
|
57
|
+
for (const r of A) if (!B.has(r)) return false;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
function planSelectionPaint(marks, selection, rule, clicked) {
|
|
61
|
+
const sel = toSet(selection);
|
|
62
|
+
if (sel.size === 0) return { on: marks.map(() => false), mode: "none", keys: [] };
|
|
63
|
+
const anyRow = () => marks.map((m) => m.rows.some((r) => sel.has(r)));
|
|
64
|
+
if (rule !== "clicked-marks") return { on: anyRow(), mode: "any-row", keys: [] };
|
|
65
|
+
if (clicked && clicked.keys.length && sameRowSet(clicked.selection, sel)) {
|
|
66
|
+
const want = new Set(clicked.keys);
|
|
67
|
+
const on = marks.map((m) => m.rows.length > 0 && want.has(m.key));
|
|
68
|
+
if (on.some(Boolean)) return { on, mode: "clicked", keys: marks.filter((_, i) => on[i]).map((m) => m.key) };
|
|
69
|
+
}
|
|
70
|
+
const exact = marks.map((m) => m.rows.length > 0 && sameRowSet(m.rows, sel));
|
|
71
|
+
if (exact.some(Boolean)) return { on: exact, mode: "exact", keys: marks.filter((_, i) => exact[i]).map((m) => m.key) };
|
|
72
|
+
return { on: anyRow(), mode: "fallback-any-row", keys: [] };
|
|
73
|
+
}
|
|
74
|
+
function nextClickedMarks(prev, current, key, next, mods = {}) {
|
|
75
|
+
if (!next.length) return null;
|
|
76
|
+
if (!(mods.ctrl || mods.shift)) return { keys: [key], selection: next.slice() };
|
|
77
|
+
const held = prev && sameRowSet(prev.selection, current) ? prev.keys : [];
|
|
78
|
+
const keys = held.includes(key) ? held.filter((k) => k !== key) : [...held, key];
|
|
79
|
+
return { keys, selection: next.slice() };
|
|
80
|
+
}
|
|
81
|
+
function isInsideControl(target, container) {
|
|
82
|
+
let el = target;
|
|
83
|
+
if (el && el.nodeType !== 1) el = el.parentElement ?? el.parentNode ?? null;
|
|
84
|
+
while (el && el !== container) {
|
|
85
|
+
if (el.nodeType === 1 && el.classList?.contains?.(CONTROL_CLASS)) return true;
|
|
86
|
+
el = el.parentElement ?? el.parentNode ?? null;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
function createMarkResolver(env) {
|
|
91
|
+
const { root, doc, log } = env;
|
|
92
|
+
const isElement = (t) => {
|
|
93
|
+
const E = doc?.defaultView?.Element ?? globalThis.Element;
|
|
94
|
+
if (E && t instanceof E) return true;
|
|
95
|
+
return !!t && typeof t.closest === "function" && typeof t.getAttribute === "function";
|
|
96
|
+
};
|
|
97
|
+
const viewStyle = (n) => {
|
|
98
|
+
try {
|
|
99
|
+
const gcs = doc?.defaultView?.getComputedStyle ?? (typeof getComputedStyle === "function" ? getComputedStyle : null);
|
|
100
|
+
return gcs ? gcs(n).pointerEvents : "";
|
|
101
|
+
} catch {
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const isInvisibleStrokePath = (el) => {
|
|
106
|
+
if (el.tagName.toLowerCase() !== "path") return false;
|
|
107
|
+
const f = el.getAttribute("fill");
|
|
108
|
+
if (f !== null && f !== "" && f !== "none" && f !== "transparent") return false;
|
|
109
|
+
const s = (el.getAttribute("stroke") || "").trim().toLowerCase();
|
|
110
|
+
const so = parseFloat(el.getAttribute("stroke-opacity") || "1");
|
|
111
|
+
return s === "transparent" || s === "none" || so <= 0.01;
|
|
112
|
+
};
|
|
113
|
+
const distToPathCenterline = (el, x, y) => {
|
|
114
|
+
const L = el.getTotalLength();
|
|
115
|
+
if (!(L > 0)) return Number.MAX_VALUE;
|
|
116
|
+
const m = el.getScreenCTM();
|
|
117
|
+
if (!m) return Number.MAX_VALUE;
|
|
118
|
+
let best = Number.MAX_VALUE;
|
|
119
|
+
const N = 64;
|
|
120
|
+
for (let i = 0; i <= N; i++) {
|
|
121
|
+
const pt = el.getPointAtLength(L * i / N);
|
|
122
|
+
const dx = m.a * pt.x + m.c * pt.y + m.e - x;
|
|
123
|
+
const dy = m.b * pt.x + m.d * pt.y + m.f - y;
|
|
124
|
+
const d = dx * dx + dy * dy;
|
|
125
|
+
if (d < best) best = d;
|
|
126
|
+
}
|
|
127
|
+
return Math.sqrt(best);
|
|
128
|
+
};
|
|
129
|
+
const refineHitCorridor = (mark, x, y, sel) => {
|
|
130
|
+
try {
|
|
131
|
+
if (!mark || !isInvisibleStrokePath(mark) || !mark.getAttribute("data-row-idx")) return mark;
|
|
132
|
+
const stack = doc.elementsFromPoint(x, y);
|
|
133
|
+
const candidates = [];
|
|
134
|
+
for (const el of stack) {
|
|
135
|
+
if (!root.contains(el)) continue;
|
|
136
|
+
if (!el.matches || !el.matches(sel)) continue;
|
|
137
|
+
if (!el.getAttribute("data-row-idx")) continue;
|
|
138
|
+
if (!isInvisibleStrokePath(el)) continue;
|
|
139
|
+
candidates.push(el);
|
|
140
|
+
}
|
|
141
|
+
if (candidates.indexOf(mark) < 0) candidates.push(mark);
|
|
142
|
+
if (candidates.length < 2) return mark;
|
|
143
|
+
let best = mark;
|
|
144
|
+
let bestD = Number.MAX_VALUE;
|
|
145
|
+
for (const el of candidates) {
|
|
146
|
+
const d = distToPathCenterline(el, x, y);
|
|
147
|
+
if (d < bestD) {
|
|
148
|
+
bestD = d;
|
|
149
|
+
best = el;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (best !== mark) {
|
|
153
|
+
log("d3-hit-corridor-refined", {
|
|
154
|
+
candidates: candidates.length,
|
|
155
|
+
distPx: Math.round(bestD),
|
|
156
|
+
fromRows: (mark.getAttribute("data-row-idx") || "").slice(0, 24),
|
|
157
|
+
toRows: (best.getAttribute("data-row-idx") || "").slice(0, 24)
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return best;
|
|
161
|
+
} catch {
|
|
162
|
+
return mark;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const findMark = (target, sel = ".d3-mark", ev) => {
|
|
166
|
+
if (!isElement(target)) return null;
|
|
167
|
+
const el = target;
|
|
168
|
+
const m = el.closest(sel);
|
|
169
|
+
if (m && ev) return refineHitCorridor(m, ev.clientX, ev.clientY, sel);
|
|
170
|
+
return m;
|
|
171
|
+
};
|
|
172
|
+
const penetrateOverlayAt = (x, y, sel) => {
|
|
173
|
+
try {
|
|
174
|
+
const stack = doc.elementsFromPoint(x, y);
|
|
175
|
+
for (const el of stack) {
|
|
176
|
+
if (!root.contains(el) || !isElement(el)) continue;
|
|
177
|
+
const cand = el.matches(sel) ? el : el.closest(sel);
|
|
178
|
+
if (!cand || !root.contains(cand) || !cand.getAttribute("data-row-idx")) continue;
|
|
179
|
+
log("d3_click-overlay-penetrated", { tag: el.tagName, cls: el.getAttribute("class"), via: el === cand ? "self" : "closest" });
|
|
180
|
+
return cand;
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
};
|
|
186
|
+
const resolveByGeometry = (x, y, sel) => {
|
|
187
|
+
try {
|
|
188
|
+
const cands = root.querySelectorAll(sel);
|
|
189
|
+
let best = null, bestArea = Number.MAX_VALUE;
|
|
190
|
+
if (cands.length <= 600) {
|
|
191
|
+
for (const el of Array.from(cands)) {
|
|
192
|
+
if (!el.getAttribute("data-row-idx")) continue;
|
|
193
|
+
const r = el.getBoundingClientRect ? el.getBoundingClientRect() : null;
|
|
194
|
+
if (!r || r.width <= 0 || r.height <= 0) continue;
|
|
195
|
+
if (x < r.left || x > r.right || y < r.top || y > r.bottom) continue;
|
|
196
|
+
const area = r.width * r.height;
|
|
197
|
+
if (area < bestArea) {
|
|
198
|
+
bestArea = area;
|
|
199
|
+
best = el;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const hasHittableShape = (node) => {
|
|
204
|
+
const SHAPE = /^(rect|circle|ellipse|path|line|polygon|polyline|text|image|use)$/;
|
|
205
|
+
const all = [node, ...Array.from(node.querySelectorAll("*"))];
|
|
206
|
+
for (const n of all) {
|
|
207
|
+
const tag = (n.tagName || "").toLowerCase();
|
|
208
|
+
if (!SHAPE.test(tag)) continue;
|
|
209
|
+
const pe = viewStyle(n);
|
|
210
|
+
if (pe && pe !== "none") return true;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
};
|
|
214
|
+
if (best && !hasHittableShape(best)) {
|
|
215
|
+
log("d3_click-geom-resolved", { rows: (best.getAttribute("data-row-idx") || "").slice(0, 24) });
|
|
216
|
+
return best;
|
|
217
|
+
} else if (best) {
|
|
218
|
+
log("d3_click-geom-skip-hittable", {});
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
};
|
|
224
|
+
return { isInvisibleStrokePath, distToPathCenterline, refineHitCorridor, findMark, rowIdxsFromMark, penetrateOverlayAt, resolveByGeometry };
|
|
225
|
+
}
|
|
226
|
+
function collectD3GlyphCenters(container, baseRect, out) {
|
|
227
|
+
const Point = container.ownerDocument?.defaultView?.DOMPoint ?? globalThis.DOMPoint;
|
|
228
|
+
const marks = container.querySelectorAll(`.${MARK_CLASS}.${MARK_SELECTED_CLASS}`);
|
|
229
|
+
const rects = [];
|
|
230
|
+
for (let i = 0; i < marks.length; i++) {
|
|
231
|
+
const r = marks[i].getBoundingClientRect();
|
|
232
|
+
rects.push(r);
|
|
233
|
+
}
|
|
234
|
+
const CONTAINMENT_CAP = 300;
|
|
235
|
+
const EPS = 1;
|
|
236
|
+
const filterContainers = marks.length <= CONTAINMENT_CAP;
|
|
237
|
+
const enclosesAnother = (ci) => {
|
|
238
|
+
const c = rects[ci];
|
|
239
|
+
const cArea = c.width * c.height;
|
|
240
|
+
if (cArea <= 0) return false;
|
|
241
|
+
for (let mj = 0; mj < rects.length; mj++) {
|
|
242
|
+
if (mj === ci) continue;
|
|
243
|
+
const m = rects[mj];
|
|
244
|
+
const mArea = m.width * m.height;
|
|
245
|
+
if (mArea <= 0 || mArea >= cArea) continue;
|
|
246
|
+
if (m.left >= c.left - EPS && m.right <= c.right + EPS && m.top >= c.top - EPS && m.bottom <= c.bottom + EPS) return true;
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
};
|
|
250
|
+
for (let i = 0; i < marks.length; i++) {
|
|
251
|
+
const el = marks[i];
|
|
252
|
+
const r = rects[i];
|
|
253
|
+
if (r.width === 0 && r.height === 0) continue;
|
|
254
|
+
if (filterContainers && enclosesAnother(i)) continue;
|
|
255
|
+
const tag = el.tagName.toLowerCase();
|
|
256
|
+
let sx;
|
|
257
|
+
let sy;
|
|
258
|
+
const cxA = el.getAttribute("data-cx"), cyA = el.getAttribute("data-cy");
|
|
259
|
+
if (cxA != null && cyA != null && typeof el.getScreenCTM === "function") {
|
|
260
|
+
const ctm = el.getScreenCTM();
|
|
261
|
+
const cxN = parseFloat(cxA), cyN = parseFloat(cyA);
|
|
262
|
+
if (ctm && isFinite(cxN) && isFinite(cyN)) {
|
|
263
|
+
const p = new Point(cxN, cyN).matrixTransform(ctm);
|
|
264
|
+
sx = p.x;
|
|
265
|
+
sy = p.y;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (sx === void 0 && tag === "path" && typeof el.getPointAtLength === "function") {
|
|
269
|
+
try {
|
|
270
|
+
const len = el.getTotalLength();
|
|
271
|
+
const ctm = el.getScreenCTM();
|
|
272
|
+
if (len > 0 && ctm) {
|
|
273
|
+
const dAttr = el.getAttribute("d") || "";
|
|
274
|
+
const isArcSector = /[Aa]/.test(dAttr) && !/[CcSsQqTt]/.test(dAttr);
|
|
275
|
+
let local;
|
|
276
|
+
if (isArcSector) {
|
|
277
|
+
const N = 24;
|
|
278
|
+
let ax = 0, ay = 0;
|
|
279
|
+
for (let k = 0; k < N; k++) {
|
|
280
|
+
const sp = el.getPointAtLength(len * k / N);
|
|
281
|
+
ax += sp.x;
|
|
282
|
+
ay += sp.y;
|
|
283
|
+
}
|
|
284
|
+
const cand = new Point(ax / N, ay / N);
|
|
285
|
+
let inside = true;
|
|
286
|
+
try {
|
|
287
|
+
if (typeof el.isPointInFill === "function") {
|
|
288
|
+
inside = el.isPointInFill(cand);
|
|
289
|
+
}
|
|
290
|
+
} catch {
|
|
291
|
+
inside = true;
|
|
292
|
+
}
|
|
293
|
+
if (inside) local = cand;
|
|
294
|
+
}
|
|
295
|
+
if (!local) local = el.getPointAtLength(len / 2);
|
|
296
|
+
const p = local.matrixTransform(ctm);
|
|
297
|
+
sx = p.x;
|
|
298
|
+
sy = p.y;
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (sx === void 0 || sy === void 0) {
|
|
304
|
+
const isRect = tag === "rect";
|
|
305
|
+
sx = isRect ? r.left + r.width * 0.8 : r.left + r.width / 2;
|
|
306
|
+
sy = isRect ? r.top + r.height * 0.2 : r.top + r.height / 2;
|
|
307
|
+
}
|
|
308
|
+
out.push({ x: sx - baseRect.left, y: sy - baseRect.top });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
var SELECTION_GLYPH_CLASS = "lch-sel-glyph";
|
|
312
|
+
function clearSelectionGlyphs(overlay) {
|
|
313
|
+
if (overlay) {
|
|
314
|
+
while (overlay.firstChild) overlay.removeChild(overlay.firstChild);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function paintSelectionGlyphs(overlay, centers, style) {
|
|
318
|
+
const doc = overlay.ownerDocument;
|
|
319
|
+
const frag = doc.createDocumentFragment();
|
|
320
|
+
const count = Math.min(centers.length, style.max ?? 600);
|
|
321
|
+
for (let i = 0; i < count; i++) {
|
|
322
|
+
const c = centers[i];
|
|
323
|
+
const g = doc.createElement("span");
|
|
324
|
+
g.className = SELECTION_GLYPH_CLASS;
|
|
325
|
+
g.textContent = style.symbol;
|
|
326
|
+
g.style.position = "absolute";
|
|
327
|
+
g.style.left = c.x + "px";
|
|
328
|
+
g.style.top = c.y + "px";
|
|
329
|
+
g.style.transform = "translate(-50%, -50%)";
|
|
330
|
+
g.style.font = "700 11px/1 system-ui, -apple-system, Segoe UI, sans-serif";
|
|
331
|
+
g.style.color = style.color;
|
|
332
|
+
const halo = style.halo;
|
|
333
|
+
g.style.textShadow = `-1px -1px 1px ${halo}, 1px -1px 1px ${halo}, -1px 1px 1px ${halo}, 1px 1px 1px ${halo}, 0 0 2px ${halo}`;
|
|
334
|
+
g.style.pointerEvents = "none";
|
|
335
|
+
frag.appendChild(g);
|
|
336
|
+
}
|
|
337
|
+
overlay.appendChild(frag);
|
|
338
|
+
return count;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/hitTargets.ts
|
|
342
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
343
|
+
var HIT_ATTR = "data-lch-hit";
|
|
344
|
+
var ELEMENT_CAP = 5e3;
|
|
345
|
+
var mapsPresentationAttributes = /* @__PURE__ */ new WeakMap();
|
|
346
|
+
function engineMapsPresentationAttributes(ownerDoc, view) {
|
|
347
|
+
if (!ownerDoc || typeof ownerDoc !== "object") return false;
|
|
348
|
+
const known = mapsPresentationAttributes.get(ownerDoc);
|
|
349
|
+
if (known !== void 0) return known;
|
|
350
|
+
let maps = false;
|
|
351
|
+
try {
|
|
352
|
+
const host = ownerDoc.documentElement;
|
|
353
|
+
const probe = ownerDoc.createElementNS(SVG_NS, "svg");
|
|
354
|
+
const shape = ownerDoc.createElementNS(SVG_NS, "rect");
|
|
355
|
+
shape.setAttribute("fill", "none");
|
|
356
|
+
probe.appendChild(shape);
|
|
357
|
+
host.appendChild(probe);
|
|
358
|
+
try {
|
|
359
|
+
maps = String(view.getComputedStyle(shape).fill ?? "").trim().toLowerCase() === "none";
|
|
360
|
+
} finally {
|
|
361
|
+
host.removeChild(probe);
|
|
362
|
+
}
|
|
363
|
+
} catch {
|
|
364
|
+
maps = false;
|
|
365
|
+
}
|
|
366
|
+
mapsPresentationAttributes.set(ownerDoc, maps);
|
|
367
|
+
return maps;
|
|
368
|
+
}
|
|
369
|
+
function ensureCrossfilterHitTargets(container, doc) {
|
|
370
|
+
const report = { tagged: 0, hitRects: 0, peFlips: 0, peStrokeOnly: 0 };
|
|
371
|
+
try {
|
|
372
|
+
if (!container || typeof container.querySelectorAll !== "function") return report;
|
|
373
|
+
const els = container.querySelectorAll(
|
|
374
|
+
`.${MARK_CLASS}, .${LEGEND_MARK_CLASS}, .${AXIS_FILTER_CLASS}`
|
|
375
|
+
);
|
|
376
|
+
report.tagged = els.length;
|
|
377
|
+
if (els.length === 0 || els.length > ELEMENT_CAP) return report;
|
|
378
|
+
const ownerDoc = doc ?? container.ownerDocument;
|
|
379
|
+
const view = ownerDoc?.defaultView;
|
|
380
|
+
const computed = (el) => {
|
|
381
|
+
try {
|
|
382
|
+
return view?.getComputedStyle ? view.getComputedStyle(el) : null;
|
|
383
|
+
} catch {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
for (const el of Array.from(els)) {
|
|
388
|
+
const tag = String(el.tagName || "").toLowerCase();
|
|
389
|
+
if (tag === "g") {
|
|
390
|
+
if (!el.hasAttribute?.(ROW_IDX_ATTR)) continue;
|
|
391
|
+
if (el.querySelector?.(`rect[${HIT_ATTR}="1"]`)) continue;
|
|
392
|
+
const cls = el.getAttribute?.("class") || "";
|
|
393
|
+
const isFurniture = cls.indexOf(LEGEND_MARK_CLASS) >= 0 || cls.indexOf(AXIS_FILTER_CLASS) >= 0;
|
|
394
|
+
if (!isFurniture) {
|
|
395
|
+
let hasHittableChild = false;
|
|
396
|
+
const kids = el.querySelectorAll?.("*") ?? [];
|
|
397
|
+
for (const kid of Array.from(kids)) {
|
|
398
|
+
const ktag = String(kid.tagName || "").toLowerCase();
|
|
399
|
+
if (ktag === "g" || ktag === "tspan" || ktag === "title" || ktag === "defs") continue;
|
|
400
|
+
const cs = computed(kid);
|
|
401
|
+
if (!cs || cs.pointerEvents !== "none") {
|
|
402
|
+
hasHittableChild = true;
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (hasHittableChild) continue;
|
|
407
|
+
}
|
|
408
|
+
let bb = null;
|
|
409
|
+
try {
|
|
410
|
+
bb = el.getBBox?.();
|
|
411
|
+
} catch {
|
|
412
|
+
bb = null;
|
|
413
|
+
}
|
|
414
|
+
if (!bb || !(bb.width > 0) || !(bb.height > 0)) continue;
|
|
415
|
+
const rect = ownerDoc.createElementNS(SVG_NS, "rect");
|
|
416
|
+
rect.setAttribute("x", String(bb.x));
|
|
417
|
+
rect.setAttribute("y", String(bb.y));
|
|
418
|
+
rect.setAttribute("width", String(bb.width));
|
|
419
|
+
rect.setAttribute("height", String(bb.height));
|
|
420
|
+
rect.setAttribute("fill", "transparent");
|
|
421
|
+
rect.setAttribute(HIT_ATTR, "1");
|
|
422
|
+
if (rect.style) {
|
|
423
|
+
rect.style.pointerEvents = "all";
|
|
424
|
+
rect.style.cursor = "pointer";
|
|
425
|
+
}
|
|
426
|
+
el.insertBefore(rect, el.firstChild);
|
|
427
|
+
report.hitRects++;
|
|
428
|
+
} else {
|
|
429
|
+
const cs = computed(el);
|
|
430
|
+
if (cs && cs.pointerEvents === "none") {
|
|
431
|
+
const fill = String((engineMapsPresentationAttributes(ownerDoc, view) ? cs.fill : el.getAttribute?.("fill") ?? cs.fill) ?? "").trim().toLowerCase();
|
|
432
|
+
const unfilled = fill === "none" || fill === "transparent" || fill === "rgba(0, 0, 0, 0)";
|
|
433
|
+
if (el.style) el.style.pointerEvents = unfilled ? "stroke" : "all";
|
|
434
|
+
report.peFlips++;
|
|
435
|
+
if (unfilled) report.peStrokeOnly++;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
return report;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/hitBands.ts
|
|
445
|
+
var MIN_HIT_BAND_PX = 8;
|
|
446
|
+
var ELEMENT_CAP2 = 5e3;
|
|
447
|
+
var EMPTY = {
|
|
448
|
+
openStrokes: 0,
|
|
449
|
+
interactive: 0,
|
|
450
|
+
hairline: 0,
|
|
451
|
+
uncovered: 0,
|
|
452
|
+
widestPx: 0
|
|
453
|
+
};
|
|
454
|
+
var OPEN_TAGS = /* @__PURE__ */ new Set(["path", "line", "polyline"]);
|
|
455
|
+
function styleOf(el, doc) {
|
|
456
|
+
try {
|
|
457
|
+
const view = doc?.defaultView;
|
|
458
|
+
return view?.getComputedStyle ? view.getComputedStyle(el) : null;
|
|
459
|
+
} catch {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function attrOrComputed(el, name, cs) {
|
|
464
|
+
const a = el.getAttribute?.(name);
|
|
465
|
+
if (a !== null && a !== void 0 && String(a).trim() !== "") return String(a).trim();
|
|
466
|
+
const c = cs?.[name === "stroke-width" ? "strokeWidth" : name];
|
|
467
|
+
return c === null || c === void 0 ? "" : String(c).trim();
|
|
468
|
+
}
|
|
469
|
+
function isUnfilled(fill) {
|
|
470
|
+
const f = fill.toLowerCase();
|
|
471
|
+
return f === "none" || f === "transparent" || f === "rgba(0, 0, 0, 0)" || f === "rgba(0,0,0,0)";
|
|
472
|
+
}
|
|
473
|
+
function widthPx(raw) {
|
|
474
|
+
const m = /^(-?\d+(?:\.\d+)?)\s*(px)?$/i.exec(raw);
|
|
475
|
+
if (!m) return NaN;
|
|
476
|
+
const n = parseFloat(m[1]);
|
|
477
|
+
return isFinite(n) ? n : NaN;
|
|
478
|
+
}
|
|
479
|
+
function censusHitBands(container, doc) {
|
|
480
|
+
if (!container || typeof container.querySelectorAll !== "function") return EMPTY;
|
|
481
|
+
try {
|
|
482
|
+
const els = Array.from(
|
|
483
|
+
container.querySelectorAll(`.${MARK_CLASS}, [${ROW_IDX_ATTR}]`)
|
|
484
|
+
);
|
|
485
|
+
if (els.length === 0 || els.length > ELEMENT_CAP2) return EMPTY;
|
|
486
|
+
const ownerDoc = doc ?? container.ownerDocument;
|
|
487
|
+
const widestByGeom = /* @__PURE__ */ new Map();
|
|
488
|
+
const rows = [];
|
|
489
|
+
for (const el of els) {
|
|
490
|
+
const tag = String(el.tagName || "").toLowerCase();
|
|
491
|
+
if (!OPEN_TAGS.has(tag)) continue;
|
|
492
|
+
const cs = styleOf(el, ownerDoc);
|
|
493
|
+
const open = isUnfilled(attrOrComputed(el, "fill", cs));
|
|
494
|
+
const w = widthPx(attrOrComputed(el, "stroke-width", cs) || "1");
|
|
495
|
+
const geom = String(el.getAttribute?.("d") ?? "") || `${tag}#${String(el.getAttribute?.(ROW_IDX_ATTR) ?? "")}`;
|
|
496
|
+
const inert = String(cs?.pointerEvents ?? el.style?.pointerEvents ?? "") === "none";
|
|
497
|
+
rows.push({ el, geom, w, open, inert });
|
|
498
|
+
if (isFinite(w)) {
|
|
499
|
+
const prev = widestByGeom.get(geom);
|
|
500
|
+
if (prev === void 0 || w > prev) widestByGeom.set(geom, w);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
const out = { ...EMPTY };
|
|
504
|
+
for (const r of rows) {
|
|
505
|
+
if (!r.open) continue;
|
|
506
|
+
out.openStrokes++;
|
|
507
|
+
if (r.inert) continue;
|
|
508
|
+
out.interactive++;
|
|
509
|
+
if (isFinite(r.w) && r.w > out.widestPx) out.widestPx = Math.round(r.w);
|
|
510
|
+
if (!(r.w < MIN_HIT_BAND_PX)) continue;
|
|
511
|
+
out.hairline++;
|
|
512
|
+
const best = widestByGeom.get(r.geom);
|
|
513
|
+
if (best === void 0 || best < MIN_HIT_BAND_PX) out.uncovered++;
|
|
514
|
+
}
|
|
515
|
+
return out;
|
|
516
|
+
} catch {
|
|
517
|
+
return EMPTY;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function hitBandFlag(c) {
|
|
521
|
+
if (c.interactive === 0) return "";
|
|
522
|
+
return c.uncovered > 0 ? "hitband:d3:thin" : "hitband:d3:ok";
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export {
|
|
526
|
+
rowIdxsFromMark,
|
|
527
|
+
nextSelection,
|
|
528
|
+
selectionRuleOf,
|
|
529
|
+
markKeyOf,
|
|
530
|
+
sameRowSet,
|
|
531
|
+
planSelectionPaint,
|
|
532
|
+
nextClickedMarks,
|
|
533
|
+
isInsideControl,
|
|
534
|
+
createMarkResolver,
|
|
535
|
+
collectD3GlyphCenters,
|
|
536
|
+
SELECTION_GLYPH_CLASS,
|
|
537
|
+
clearSelectionGlyphs,
|
|
538
|
+
paintSelectionGlyphs,
|
|
539
|
+
ensureCrossfilterHitTargets,
|
|
540
|
+
MIN_HIT_BAND_PX,
|
|
541
|
+
censusHitBands,
|
|
542
|
+
hitBandFlag
|
|
543
|
+
};
|