@bicharts/chart-host 0.6.113 → 0.6.116
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/README.md +94 -5
- package/dist/adapters.mjs +3 -3
- package/dist/card.mjs +2 -2
- package/dist/category-colours.mjs +275 -0
- package/dist/{chunk-7XT5OJ6M.mjs → chunk-63IUMWY2.mjs} +40 -3
- package/dist/{chunk-AEO5SFEQ.mjs → chunk-EFKGSDFF.mjs} +1 -1
- package/dist/{chunk-TE54AFRN.mjs → chunk-GZSYTDFN.mjs} +531 -393
- package/dist/{chunk-PELRAFYZ.mjs → chunk-IW6BKFF5.mjs} +1 -1
- package/dist/{chunk-OLCUKXKL.mjs → chunk-JMLSNV5Q.mjs} +1 -1
- package/dist/chunk-JTZTTJJK.mjs +244 -0
- package/dist/{chunk-FIG5DIW7.mjs → chunk-K2TH7D5V.mjs} +335 -335
- package/dist/{chunk-KY4FI2HA.mjs → chunk-QG5TENI3.mjs} +5 -1
- package/dist/{chunk-KGVNZR23.mjs → chunk-RLOIA7QK.mjs} +1 -1
- package/dist/{chunk-IL5M7K6Z.mjs → chunk-TUZCM7SX.mjs} +2 -2
- package/dist/{chunk-GTJG3SMD.mjs → chunk-VGJH63LZ.mjs} +1 -1
- package/dist/{chunk-2KLFIGTQ.mjs → chunk-ZZBX5ZJ6.mjs} +145 -18
- package/dist/dax.mjs +23 -1
- package/dist/index.mjs +97 -16
- package/dist/legend-fit.mjs +3 -3
- package/dist/page.mjs +7 -5
- package/dist/provinces.mjs +4 -4
- package/dist/react.mjs +18 -14
- package/dist/testing.mjs +1 -1
- package/dist/types/categoryColours.d.ts +71 -0
- package/dist/types/contract.d.ts +16 -3
- package/dist/types/dax.d.ts +26 -0
- package/dist/types/filterScope.d.ts +52 -4
- package/dist/types/fit.d.ts +6 -1
- package/dist/types/fitDom.d.ts +13 -0
- package/dist/types/host.d.ts +19 -5
- package/dist/types/index.d.ts +7 -4
- package/dist/types/keyboardMarks.d.ts +1 -1
- package/dist/types/markFollower.d.ts +21 -0
- package/dist/types/page.d.ts +1 -1
- package/dist/types/qualifyTable.d.ts +52 -0
- package/dist/types/react.d.ts +11 -3
- package/dist/types/selectionMarker.d.ts +92 -0
- package/dist/zoom-memory.mjs +1 -1
- package/package.json +6 -1
- /package/dist/{chunk-5YSZABCS.mjs → chunk-LXTW6YII.mjs} +0 -0
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
claimParkedLasso,
|
|
5
5
|
installLasso,
|
|
6
6
|
lassoEffect
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TUZCM7SX.mjs";
|
|
8
8
|
import {
|
|
9
9
|
censusHitBands,
|
|
10
10
|
ensureCrossfilterHitTargets
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EFKGSDFF.mjs";
|
|
12
12
|
import {
|
|
13
13
|
clearPress,
|
|
14
14
|
clearSelectionGlyphs,
|
|
@@ -24,26 +24,34 @@ import {
|
|
|
24
24
|
selectionRuleOf,
|
|
25
25
|
trackPress,
|
|
26
26
|
validRowSet
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-RLOIA7QK.mjs";
|
|
28
28
|
import {
|
|
29
29
|
clipWindowOf,
|
|
30
30
|
fitRenderedChart,
|
|
31
31
|
rememberScroll,
|
|
32
|
+
restoreInnerScroll,
|
|
32
33
|
takeScroll,
|
|
33
34
|
unpinScrolledAxis
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-63IUMWY2.mjs";
|
|
35
36
|
import {
|
|
36
37
|
geoFromCache
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-LXTW6YII.mjs";
|
|
38
39
|
import {
|
|
39
40
|
Fn,
|
|
41
|
+
Lr,
|
|
42
|
+
Nt,
|
|
40
43
|
Q,
|
|
41
|
-
Rt,
|
|
42
44
|
Yn,
|
|
43
|
-
_r,
|
|
44
45
|
tt,
|
|
45
46
|
xt
|
|
46
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-K2TH7D5V.mjs";
|
|
48
|
+
import {
|
|
49
|
+
SAME_SHADE_DELTA_E,
|
|
50
|
+
censusColourSpread,
|
|
51
|
+
deltaE2000,
|
|
52
|
+
hexToLab,
|
|
53
|
+
labToHex
|
|
54
|
+
} from "./chunk-JTZTTJJK.mjs";
|
|
47
55
|
import {
|
|
48
56
|
ACTIVE_TICK_CLASS,
|
|
49
57
|
ANIM_LOOP_DELAY_DEFAULT,
|
|
@@ -88,7 +96,7 @@ import {
|
|
|
88
96
|
XFILTER_REFRESH_EVENT,
|
|
89
97
|
chartOwnsTimeline,
|
|
90
98
|
periodTickSuppressesFeedback
|
|
91
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-QG5TENI3.mjs";
|
|
92
100
|
|
|
93
101
|
// src/labelContrast.ts
|
|
94
102
|
function parseRGBA(c) {
|
|
@@ -249,237 +257,11 @@ function clamp01(v) {
|
|
|
249
257
|
if (typeof v !== "number" || !isFinite(v)) return 1;
|
|
250
258
|
return Math.max(0, Math.min(1, v));
|
|
251
259
|
}
|
|
252
|
-
function hexToRGB(
|
|
253
|
-
const p = toRGBA(
|
|
260
|
+
function hexToRGB(hex) {
|
|
261
|
+
const p = toRGBA(hex);
|
|
254
262
|
return [p[0], p[1], p[2]];
|
|
255
263
|
}
|
|
256
264
|
|
|
257
|
-
// src/deltaE.ts
|
|
258
|
-
function linearise(c) {
|
|
259
|
-
const v = c / 255;
|
|
260
|
-
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
261
|
-
}
|
|
262
|
-
var Xn = 0.95047;
|
|
263
|
-
var Yn2 = 1;
|
|
264
|
-
var Zn = 1.08883;
|
|
265
|
-
var EPS = 216 / 24389;
|
|
266
|
-
var KAPPA = 24389 / 27;
|
|
267
|
-
function f(t) {
|
|
268
|
-
return t > EPS ? Math.cbrt(t) : (KAPPA * t + 16) / 116;
|
|
269
|
-
}
|
|
270
|
-
function rgbToLab(r, g, b) {
|
|
271
|
-
const R = linearise(r), G = linearise(g), B = linearise(b);
|
|
272
|
-
const X = 0.4124564 * R + 0.3575761 * G + 0.1804375 * B;
|
|
273
|
-
const Y = 0.2126729 * R + 0.7151522 * G + 0.072175 * B;
|
|
274
|
-
const Z = 0.0193339 * R + 0.119192 * G + 0.9503041 * B;
|
|
275
|
-
const fx = f(X / Xn), fy = f(Y / Yn2), fz = f(Z / Zn);
|
|
276
|
-
return { L: 116 * fy - 16, a: 500 * (fx - fy), b: 200 * (fy - fz) };
|
|
277
|
-
}
|
|
278
|
-
function hexToLab(hex2) {
|
|
279
|
-
const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex2.trim());
|
|
280
|
-
if (!m) return null;
|
|
281
|
-
const h = m[1].length === 3 ? m[1][0] + m[1][0] + m[1][1] + m[1][1] + m[1][2] + m[1][2] : m[1];
|
|
282
|
-
return rgbToLab(parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16));
|
|
283
|
-
}
|
|
284
|
-
var RAD = Math.PI / 180;
|
|
285
|
-
var DEG = 180 / Math.PI;
|
|
286
|
-
var NEUTRAL_CHROMA = 1e-4;
|
|
287
|
-
function deltaE2000(c1, c2) {
|
|
288
|
-
const C1 = Math.hypot(c1.a, c1.b), C2 = Math.hypot(c2.a, c2.b);
|
|
289
|
-
const Cbar = (C1 + C2) / 2;
|
|
290
|
-
const C7 = Math.pow(Cbar, 7);
|
|
291
|
-
const G = 0.5 * (1 - Math.sqrt(C7 / (C7 + Math.pow(25, 7))));
|
|
292
|
-
const a1p = (1 + G) * c1.a, a2p = (1 + G) * c2.a;
|
|
293
|
-
const C1p = Math.hypot(a1p, c1.b), C2p = Math.hypot(a2p, c2.b);
|
|
294
|
-
const neutral = C1p < NEUTRAL_CHROMA || C2p < NEUTRAL_CHROMA;
|
|
295
|
-
const h1p = C1p < NEUTRAL_CHROMA ? 0 : (Math.atan2(c1.b, a1p) * DEG + 360) % 360;
|
|
296
|
-
const h2p = C2p < NEUTRAL_CHROMA ? 0 : (Math.atan2(c2.b, a2p) * DEG + 360) % 360;
|
|
297
|
-
const dLp = c2.L - c1.L;
|
|
298
|
-
const dCp = C2p - C1p;
|
|
299
|
-
let dhp;
|
|
300
|
-
if (neutral) dhp = 0;
|
|
301
|
-
else if (Math.abs(h2p - h1p) <= 180) dhp = h2p - h1p;
|
|
302
|
-
else if (h2p - h1p > 180) dhp = h2p - h1p - 360;
|
|
303
|
-
else dhp = h2p - h1p + 360;
|
|
304
|
-
const dHp = 2 * Math.sqrt(C1p * C2p) * Math.sin(dhp / 2 * RAD);
|
|
305
|
-
const Lbarp = (c1.L + c2.L) / 2;
|
|
306
|
-
const Cbarp = (C1p + C2p) / 2;
|
|
307
|
-
let hbarp;
|
|
308
|
-
if (neutral) hbarp = h1p + h2p;
|
|
309
|
-
else if (Math.abs(h1p - h2p) <= 180) hbarp = (h1p + h2p) / 2;
|
|
310
|
-
else if (h1p + h2p < 360) hbarp = (h1p + h2p + 360) / 2;
|
|
311
|
-
else hbarp = (h1p + h2p - 360) / 2;
|
|
312
|
-
const T = 1 - 0.17 * Math.cos((hbarp - 30) * RAD) + 0.24 * Math.cos(2 * hbarp * RAD) + 0.32 * Math.cos((3 * hbarp + 6) * RAD) - 0.2 * Math.cos((4 * hbarp - 63) * RAD);
|
|
313
|
-
const dTheta = 30 * Math.exp(-Math.pow((hbarp - 275) / 25, 2));
|
|
314
|
-
const Cbarp7 = Math.pow(Cbarp, 7);
|
|
315
|
-
const RC = 2 * Math.sqrt(Cbarp7 / (Cbarp7 + Math.pow(25, 7)));
|
|
316
|
-
const RT = -RC * Math.sin(2 * dTheta * RAD);
|
|
317
|
-
const Lm50 = Math.pow(Lbarp - 50, 2);
|
|
318
|
-
const SL = 1 + 0.015 * Lm50 / Math.sqrt(20 + Lm50);
|
|
319
|
-
const SC = 1 + 0.045 * Cbarp;
|
|
320
|
-
const SH = 1 + 0.015 * Cbarp * T;
|
|
321
|
-
const tL = dLp / SL, tC = dCp / SC, tH = dHp / SH;
|
|
322
|
-
return Math.sqrt(tL * tL + tC * tC + tH * tH + RT * tC * tH);
|
|
323
|
-
}
|
|
324
|
-
function labToHex(lab) {
|
|
325
|
-
const finv = (t) => {
|
|
326
|
-
const t3 = t * t * t;
|
|
327
|
-
return t3 > EPS ? t3 : (116 * t - 16) / KAPPA;
|
|
328
|
-
};
|
|
329
|
-
const fy = (lab.L + 16) / 116, fx = fy + lab.a / 500, fz = fy - lab.b / 200;
|
|
330
|
-
const X = Xn * finv(fx);
|
|
331
|
-
const Y = Yn2 * (lab.L > KAPPA * EPS ? fy * fy * fy : lab.L / KAPPA);
|
|
332
|
-
const Z = Zn * finv(fz);
|
|
333
|
-
const lin = [
|
|
334
|
-
3.2404542 * X - 1.5371385 * Y - 0.4985314 * Z,
|
|
335
|
-
-0.969266 * X + 1.8760108 * Y + 0.041556 * Z,
|
|
336
|
-
0.0556434 * X - 0.2040259 * Y + 1.0572252 * Z
|
|
337
|
-
];
|
|
338
|
-
return "#" + lin.map((v) => {
|
|
339
|
-
const c = Math.max(0, Math.min(1, v));
|
|
340
|
-
const g = c <= 31308e-7 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
|
|
341
|
-
return Math.round(g * 255).toString(16).padStart(2, "0");
|
|
342
|
-
}).join("");
|
|
343
|
-
}
|
|
344
|
-
function deltaEHex(a, b) {
|
|
345
|
-
const la = hexToLab(a), lb = hexToLab(b);
|
|
346
|
-
return la && lb ? deltaE2000(la, lb) : NaN;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// src/colourSpread.ts
|
|
350
|
-
var SAME_SHADE_DELTA_E = 5;
|
|
351
|
-
var MIN_RAMP_FILLS = 4;
|
|
352
|
-
var RAMP_SLACK_MAX = 0.15;
|
|
353
|
-
var MIN_MARKS = 24;
|
|
354
|
-
var ELEMENT_CAP = 5e3;
|
|
355
|
-
var MAX_CELLS = 48;
|
|
356
|
-
var CELL_STEP = 32;
|
|
357
|
-
var EMPTY = { marks: 0, distinctFills: 0, crowdShare: 0, spanDeltaE: 0 };
|
|
358
|
-
function styleOf(el, doc) {
|
|
359
|
-
try {
|
|
360
|
-
const view = doc?.defaultView;
|
|
361
|
-
return view?.getComputedStyle ? view.getComputedStyle(el) : null;
|
|
362
|
-
} catch {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
function fillOf(el, cs) {
|
|
367
|
-
const a = el.getAttribute?.("fill");
|
|
368
|
-
if (a !== null && a !== void 0 && String(a).trim() !== "") return String(a).trim();
|
|
369
|
-
const c = cs?.fill;
|
|
370
|
-
return c === null || c === void 0 ? "" : String(c).trim();
|
|
371
|
-
}
|
|
372
|
-
function toRgb(raw) {
|
|
373
|
-
const s = raw.trim().toLowerCase();
|
|
374
|
-
if (!s || s === "none" || s === "transparent") return null;
|
|
375
|
-
const hex2 = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(s);
|
|
376
|
-
if (hex2) {
|
|
377
|
-
const h = hex2[1];
|
|
378
|
-
const full = h.length === 3 ? h[0] + h[0] + h[1] + h[1] + h[2] + h[2] : h;
|
|
379
|
-
return [
|
|
380
|
-
parseInt(full.slice(0, 2), 16),
|
|
381
|
-
parseInt(full.slice(2, 4), 16),
|
|
382
|
-
parseInt(full.slice(4, 6), 16)
|
|
383
|
-
];
|
|
384
|
-
}
|
|
385
|
-
const m = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)\s*(?:[,/]\s*([\d.]+)\s*)?\)$/.exec(s);
|
|
386
|
-
if (!m) return null;
|
|
387
|
-
if (m[4] !== void 0 && parseFloat(m[4]) === 0) return null;
|
|
388
|
-
return [Math.round(parseFloat(m[1])), Math.round(parseFloat(m[2])), Math.round(parseFloat(m[3]))];
|
|
389
|
-
}
|
|
390
|
-
function cellKey(rgb) {
|
|
391
|
-
return [
|
|
392
|
-
Math.round(rgb[0] / CELL_STEP),
|
|
393
|
-
Math.round(rgb[1] / CELL_STEP),
|
|
394
|
-
Math.round(rgb[2] / CELL_STEP)
|
|
395
|
-
].join(",");
|
|
396
|
-
}
|
|
397
|
-
function hex(rgb) {
|
|
398
|
-
return "#" + rgb.map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
399
|
-
}
|
|
400
|
-
function censusColourSpread(container, doc) {
|
|
401
|
-
if (!container || typeof container.querySelectorAll !== "function") return EMPTY;
|
|
402
|
-
try {
|
|
403
|
-
const els = Array.from(
|
|
404
|
-
container.querySelectorAll(`.${MARK_CLASS}, [${ROW_IDX_ATTR}]`)
|
|
405
|
-
);
|
|
406
|
-
if (els.length === 0 || els.length > ELEMENT_CAP) return EMPTY;
|
|
407
|
-
const ownerDoc = doc ?? container.ownerDocument;
|
|
408
|
-
const painted = [];
|
|
409
|
-
for (const el of els) {
|
|
410
|
-
const rgb = toRgb(fillOf(el, styleOf(el, ownerDoc)));
|
|
411
|
-
if (rgb) painted.push({ rgb, tag: String(el.tagName || "").toLowerCase() });
|
|
412
|
-
}
|
|
413
|
-
const byTag = /* @__PURE__ */ new Map();
|
|
414
|
-
for (const p of painted) {
|
|
415
|
-
const g = byTag.get(p.tag) ?? { fills: /* @__PURE__ */ new Set(), n: 0 };
|
|
416
|
-
g.fills.add(p.rgb.join(","));
|
|
417
|
-
g.n++;
|
|
418
|
-
byTag.set(p.tag, g);
|
|
419
|
-
}
|
|
420
|
-
const overlays = new Set([...byTag].filter(([, g]) => g.fills.size === 1 && g.n >= MIN_MARKS).map(([t]) => t));
|
|
421
|
-
const restFills = new Set([...byTag].filter(([t]) => !overlays.has(t)).flatMap(([, g]) => [...g.fills]));
|
|
422
|
-
const counted = restFills.size > 1 ? painted.filter((p) => !overlays.has(p.tag)) : painted;
|
|
423
|
-
const distinct = /* @__PURE__ */ new Set();
|
|
424
|
-
const cells = /* @__PURE__ */ new Map();
|
|
425
|
-
let marks = 0;
|
|
426
|
-
for (const { rgb } of counted) {
|
|
427
|
-
marks++;
|
|
428
|
-
distinct.add(rgb.join(","));
|
|
429
|
-
const k = cellKey(rgb);
|
|
430
|
-
const cur = cells.get(k);
|
|
431
|
-
if (cur) cur.n++;
|
|
432
|
-
else cells.set(k, { rgb, n: 1 });
|
|
433
|
-
}
|
|
434
|
-
if (marks < MIN_MARKS || distinct.size < MIN_RAMP_FILLS) return EMPTY;
|
|
435
|
-
const top = [...cells.values()].sort((a, b) => b.n - a.n).slice(0, MAX_CELLS);
|
|
436
|
-
const cs = top.map((c) => hexToLab(hex(c.rgb))).filter((l) => l !== null);
|
|
437
|
-
if (cs.length < MIN_RAMP_FILLS) return EMPTY;
|
|
438
|
-
const d = cs.map(() => new Array(cs.length).fill(0));
|
|
439
|
-
let span = 0, lo = 0, hi = 1;
|
|
440
|
-
for (let i = 0; i < cs.length; i++) {
|
|
441
|
-
for (let j = i + 1; j < cs.length; j++) {
|
|
442
|
-
const v = deltaE2000(cs[i], cs[j]);
|
|
443
|
-
d[i][j] = v;
|
|
444
|
-
d[j][i] = v;
|
|
445
|
-
if (v > span) {
|
|
446
|
-
span = v;
|
|
447
|
-
lo = i;
|
|
448
|
-
hi = j;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if (span <= 0) return EMPTY;
|
|
453
|
-
let slack = 0, interior = 0;
|
|
454
|
-
for (let i = 0; i < cs.length; i++) {
|
|
455
|
-
if (i === lo || i === hi) continue;
|
|
456
|
-
slack += (d[lo][i] + d[i][hi]) / span - 1;
|
|
457
|
-
interior++;
|
|
458
|
-
}
|
|
459
|
-
if (interior < 2 || slack / interior > RAMP_SLACK_MAX) return EMPTY;
|
|
460
|
-
let crowd = 0;
|
|
461
|
-
for (let i = 0; i < cs.length; i++) {
|
|
462
|
-
let n = 0;
|
|
463
|
-
for (let j = 0; j < cs.length; j++) {
|
|
464
|
-
if (i === j || d[i][j] <= SAME_SHADE_DELTA_E) n += top[j].n;
|
|
465
|
-
}
|
|
466
|
-
if (n > crowd) crowd = n;
|
|
467
|
-
}
|
|
468
|
-
return {
|
|
469
|
-
marks,
|
|
470
|
-
distinctFills: distinct.size,
|
|
471
|
-
crowdShare: Math.round(crowd / marks * 100),
|
|
472
|
-
spanDeltaE: Math.round(span)
|
|
473
|
-
};
|
|
474
|
-
} catch {
|
|
475
|
-
return EMPTY;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
function colourSpreadFlag(c) {
|
|
479
|
-
if (c.marks === 0) return "";
|
|
480
|
-
return "ramp:d3:crowd" + Math.min(80, Math.floor(c.crowdShare / 20) * 20);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
265
|
// src/autoRamp.ts
|
|
484
266
|
var AUTO_RAMP_CLASSES = 7;
|
|
485
267
|
var AUTO_RAMP_DRAWN_OPACITY = 0.5;
|
|
@@ -498,9 +280,9 @@ function mixLab(a, b, t) {
|
|
|
498
280
|
return { L: a.L + (b.L - a.L) * t, a: a.a + (b.a - a.a) * t, b: a.b + (b.b - a.b) * t };
|
|
499
281
|
}
|
|
500
282
|
function paintedOver(fg, canvas, alpha) {
|
|
501
|
-
const
|
|
283
|
+
const f = [1, 3, 5].map((i) => parseInt(fg.slice(i, i + 2), 16));
|
|
502
284
|
const c = [1, 3, 5].map((i) => parseInt(canvas.slice(i, i + 2), 16));
|
|
503
|
-
const mixed =
|
|
285
|
+
const mixed = f.map((v, i) => Math.round(v * alpha + c[i] * (1 - alpha))).map((v) => v.toString(16).padStart(2, "0"));
|
|
504
286
|
return hexToLab("#" + mixed.join(""));
|
|
505
287
|
}
|
|
506
288
|
function autoRampMinStep(low, high, canvas, classes = AUTO_RAMP_CLASSES, opacity = AUTO_RAMP_DRAWN_OPACITY) {
|
|
@@ -527,8 +309,8 @@ function resolveAutoColorScale(p) {
|
|
|
527
309
|
if (key === memoKey) return memoValue;
|
|
528
310
|
const bg = hexToLab(canvas);
|
|
529
311
|
const darkCanvas = bg.L < 50;
|
|
530
|
-
const usable = (
|
|
531
|
-
const lab =
|
|
312
|
+
const usable = (hex) => {
|
|
313
|
+
const lab = hex ? hexToLab(hex) : null;
|
|
532
314
|
return !!lab && deltaE2000(lab, bg) >= MIN_ACCENT_VS_CANVAS;
|
|
533
315
|
};
|
|
534
316
|
const candidates = p.isHighContrast ? [normHex(p.themeAccent), normHex(p.themeFg)] : [...palette.map(normHex), normHex(p.themeAccent), normHex(p.themeFg)];
|
|
@@ -554,6 +336,252 @@ function resolveAutoColorScale(p) {
|
|
|
554
336
|
return memoValue;
|
|
555
337
|
}
|
|
556
338
|
|
|
339
|
+
// src/selectionMarker.ts
|
|
340
|
+
var SELECTION_MARKER_DEFAULT = "auto";
|
|
341
|
+
var MAP_SELECTION_MARKER_DEFAULT = "outline";
|
|
342
|
+
function readSelectionMarker(raw) {
|
|
343
|
+
const v = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
344
|
+
return v === "none" || v === "dot" || v === "check" || v === "outline" || v === "auto" ? v : SELECTION_MARKER_DEFAULT;
|
|
345
|
+
}
|
|
346
|
+
function readMapSelectionMarker(raw) {
|
|
347
|
+
const v = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
348
|
+
return v === "none" || v === "dot" || v === "check" || v === "outline" ? v : MAP_SELECTION_MARKER_DEFAULT;
|
|
349
|
+
}
|
|
350
|
+
var REGION_JOIN_COLUMN = "__geoIso__";
|
|
351
|
+
function isRegionMapData(data) {
|
|
352
|
+
const cols = data?.columns;
|
|
353
|
+
if (!Array.isArray(cols)) return false;
|
|
354
|
+
return cols.some((c) => !!c && c.name === REGION_JOIN_COLUMN);
|
|
355
|
+
}
|
|
356
|
+
var NEUTRAL_CENTRE_RAMP_SELECTOR = '.llm-value-ramp[data-kind="diverging"]';
|
|
357
|
+
function hasNeutralCentreRamp(container) {
|
|
358
|
+
try {
|
|
359
|
+
return !!container?.querySelector?.(NEUTRAL_CENTRE_RAMP_SELECTOR);
|
|
360
|
+
} catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function selectionMarkerFor(facts) {
|
|
365
|
+
if (facts.regionMap) return readMapSelectionMarker(facts.mapChoice);
|
|
366
|
+
const c = readSelectionMarker(facts.choice);
|
|
367
|
+
if (c !== "auto") return c;
|
|
368
|
+
return facts.neutralCentreRamp ? "outline" : "none";
|
|
369
|
+
}
|
|
370
|
+
var SELECTION_OUTLINE_CLASS = "lch-sel-outline";
|
|
371
|
+
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
372
|
+
var AREA_TAGS = /* @__PURE__ */ new Set(["path", "rect", "polygon"]);
|
|
373
|
+
var UNFILLED = /^(none|transparent|rgba\([^)]*,\s*0(\.0+)?\s*\))$/i;
|
|
374
|
+
function isAreaMark(el) {
|
|
375
|
+
const tag = (el.localName || el.tagName || "").toLowerCase();
|
|
376
|
+
if (!AREA_TAGS.has(tag)) return false;
|
|
377
|
+
let fill = "", fillOpacity = "";
|
|
378
|
+
try {
|
|
379
|
+
const cs = el.ownerDocument?.defaultView?.getComputedStyle?.(el);
|
|
380
|
+
fill = (cs?.fill || "").trim();
|
|
381
|
+
fillOpacity = (cs?.fillOpacity || "").trim();
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
const style = el.style;
|
|
385
|
+
if (!fill) fill = (style?.fill || el.getAttribute("fill") || "").trim();
|
|
386
|
+
if (!fillOpacity) fillOpacity = (style?.fillOpacity || el.getAttribute("fill-opacity") || "").trim();
|
|
387
|
+
if (fill && UNFILLED.test(fill)) return false;
|
|
388
|
+
if (fillOpacity !== "" && parseFloat(fillOpacity) === 0) return false;
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
function collectOutlineMarks(container, filter) {
|
|
392
|
+
const picked = [];
|
|
393
|
+
const seen = /* @__PURE__ */ new Set();
|
|
394
|
+
const add = (el) => {
|
|
395
|
+
if (seen.has(el)) return;
|
|
396
|
+
seen.add(el);
|
|
397
|
+
if (isAreaMark(el)) picked.push(el);
|
|
398
|
+
};
|
|
399
|
+
for (const m of Array.from(container.querySelectorAll(`.${MARK_CLASS}.${MARK_SELECTED_CLASS}`))) {
|
|
400
|
+
if (filter?.shown && !filter.shown(m)) continue;
|
|
401
|
+
if ((m.localName || "").toLowerCase() === "g") {
|
|
402
|
+
for (const s of Array.from(m.querySelectorAll("path, rect, polygon"))) add(s);
|
|
403
|
+
} else add(m);
|
|
404
|
+
}
|
|
405
|
+
if (picked.length < 2 || picked.length > 300) return picked;
|
|
406
|
+
const EPS = 1;
|
|
407
|
+
const rects = picked.map((m) => {
|
|
408
|
+
try {
|
|
409
|
+
return m.getBoundingClientRect();
|
|
410
|
+
} catch {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
return picked.filter((_, i) => {
|
|
415
|
+
const c = rects[i];
|
|
416
|
+
if (!c) return true;
|
|
417
|
+
const cArea = c.width * c.height;
|
|
418
|
+
if (!(cArea > 0)) return true;
|
|
419
|
+
for (let j = 0; j < rects.length; j++) {
|
|
420
|
+
const r = rects[j];
|
|
421
|
+
if (j === i || !r) continue;
|
|
422
|
+
const a = r.width * r.height;
|
|
423
|
+
if (!(a > 0) || a >= cArea) continue;
|
|
424
|
+
if (r.left >= c.left - EPS && r.right <= c.right + EPS && r.top >= c.top - EPS && r.bottom <= c.bottom + EPS) return false;
|
|
425
|
+
}
|
|
426
|
+
return true;
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
function outlineWindowOf(mark, stop) {
|
|
430
|
+
let acc = null;
|
|
431
|
+
const narrow = (w) => {
|
|
432
|
+
if (!w) return;
|
|
433
|
+
acc = acc ? {
|
|
434
|
+
left: Math.max(acc.left, w.left),
|
|
435
|
+
top: Math.max(acc.top, w.top),
|
|
436
|
+
right: Math.min(acc.right, w.right),
|
|
437
|
+
bottom: Math.min(acc.bottom, w.bottom)
|
|
438
|
+
} : { ...w };
|
|
439
|
+
};
|
|
440
|
+
for (let el = mark; el && el !== stop; el = el.parentElement) {
|
|
441
|
+
try {
|
|
442
|
+
if ((el.localName || "").toLowerCase() === "svg") {
|
|
443
|
+
const r = el.getBoundingClientRect();
|
|
444
|
+
if (r.width > 0 && r.height > 0) narrow({ left: r.left, top: r.top, right: r.right, bottom: r.bottom });
|
|
445
|
+
} else if (el.hasAttribute?.("clip-path")) narrow(clipWindowOf(el));
|
|
446
|
+
} catch {
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return acc;
|
|
450
|
+
}
|
|
451
|
+
function controlBoxesIn(root, opts = {}) {
|
|
452
|
+
const pad = opts.pad ?? 2, join = opts.join ?? 4;
|
|
453
|
+
let boxes = [];
|
|
454
|
+
try {
|
|
455
|
+
for (const el of Array.from(root?.querySelectorAll?.(`.${CONTROL_CLASS}`) ?? [])) {
|
|
456
|
+
const r = el.getBoundingClientRect();
|
|
457
|
+
if (!(r.width > 0) || !(r.height > 0)) continue;
|
|
458
|
+
boxes.push({ left: r.left - pad, top: r.top - pad, right: r.right + pad, bottom: r.bottom + pad });
|
|
459
|
+
}
|
|
460
|
+
} catch {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
const near = (a, b) => a.left - join <= b.right && b.left - join <= a.right && a.top - join <= b.bottom && b.top - join <= a.bottom;
|
|
464
|
+
let merged = true;
|
|
465
|
+
while (merged) {
|
|
466
|
+
merged = false;
|
|
467
|
+
const out = [];
|
|
468
|
+
for (const b of boxes) {
|
|
469
|
+
const hit = out.find((o) => near(o, b));
|
|
470
|
+
if (hit) {
|
|
471
|
+
hit.left = Math.min(hit.left, b.left);
|
|
472
|
+
hit.top = Math.min(hit.top, b.top);
|
|
473
|
+
hit.right = Math.max(hit.right, b.right);
|
|
474
|
+
hit.bottom = Math.max(hit.bottom, b.bottom);
|
|
475
|
+
merged = true;
|
|
476
|
+
} else out.push({ ...b });
|
|
477
|
+
}
|
|
478
|
+
boxes = out;
|
|
479
|
+
}
|
|
480
|
+
return boxes;
|
|
481
|
+
}
|
|
482
|
+
function copyGeometry(doc, el) {
|
|
483
|
+
const tag = (el.localName || "").toLowerCase();
|
|
484
|
+
const keep = tag === "path" ? ["d"] : tag === "rect" ? ["x", "y", "width", "height", "rx", "ry"] : tag === "polygon" ? ["points"] : null;
|
|
485
|
+
if (!keep) return null;
|
|
486
|
+
const out = doc.createElementNS(SVG_NS, tag);
|
|
487
|
+
let any = false;
|
|
488
|
+
for (const a of keep) {
|
|
489
|
+
const v = el.getAttribute(a);
|
|
490
|
+
if (v != null && v !== "") {
|
|
491
|
+
out.setAttribute(a, v);
|
|
492
|
+
any = true;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return any ? out : null;
|
|
496
|
+
}
|
|
497
|
+
var num = (n) => String(Math.round(n * 1e3) / 1e3);
|
|
498
|
+
var maskSeq = 0;
|
|
499
|
+
function paintSelectionOutlines(overlay, marks, style) {
|
|
500
|
+
const doc = overlay.ownerDocument;
|
|
501
|
+
const base = overlay.getBoundingClientRect();
|
|
502
|
+
const width = style.width ?? 2;
|
|
503
|
+
const windowOf = style.windowOf ?? ((m) => outlineWindowOf(m));
|
|
504
|
+
const root = doc.createElementNS(SVG_NS, "svg");
|
|
505
|
+
root.setAttribute("class", SELECTION_OUTLINE_CLASS);
|
|
506
|
+
root.setAttribute("aria-hidden", "true");
|
|
507
|
+
root.setAttribute("focusable", "false");
|
|
508
|
+
root.setAttribute("style", "position:absolute;left:0;top:0;width:1px;height:1px;overflow:visible;pointer-events:none");
|
|
509
|
+
let host = root;
|
|
510
|
+
const holes = (style.avoid ?? []).filter((b) => b.right > b.left && b.bottom > b.top);
|
|
511
|
+
if (holes.length) {
|
|
512
|
+
const id = `${SELECTION_OUTLINE_CLASS}-mask-${++maskSeq}`;
|
|
513
|
+
const defs = doc.createElementNS(SVG_NS, "defs");
|
|
514
|
+
const mask = doc.createElementNS(SVG_NS, "mask");
|
|
515
|
+
mask.setAttribute("id", id);
|
|
516
|
+
mask.setAttribute("maskUnits", "userSpaceOnUse");
|
|
517
|
+
const BIG = 1e5;
|
|
518
|
+
for (const [k, v] of [["x", -BIG], ["y", -BIG], ["width", 2 * BIG], ["height", 2 * BIG]]) mask.setAttribute(k, String(v));
|
|
519
|
+
const open = doc.createElementNS(SVG_NS, "rect");
|
|
520
|
+
for (const [k, v] of [["x", -BIG], ["y", -BIG], ["width", 2 * BIG], ["height", 2 * BIG]]) open.setAttribute(k, String(v));
|
|
521
|
+
open.setAttribute("fill", "#fff");
|
|
522
|
+
mask.appendChild(open);
|
|
523
|
+
for (const b of holes) {
|
|
524
|
+
const r = doc.createElementNS(SVG_NS, "rect");
|
|
525
|
+
r.setAttribute("class", `${SELECTION_OUTLINE_CLASS}-hole`);
|
|
526
|
+
r.setAttribute("x", num(b.left - base.left));
|
|
527
|
+
r.setAttribute("y", num(b.top - base.top));
|
|
528
|
+
r.setAttribute("width", num(b.right - b.left));
|
|
529
|
+
r.setAttribute("height", num(b.bottom - b.top));
|
|
530
|
+
r.setAttribute("fill", "#000");
|
|
531
|
+
mask.appendChild(r);
|
|
532
|
+
}
|
|
533
|
+
defs.appendChild(mask);
|
|
534
|
+
root.appendChild(defs);
|
|
535
|
+
host = doc.createElementNS(SVG_NS, "g");
|
|
536
|
+
host.setAttribute("mask", `url(#${id})`);
|
|
537
|
+
root.appendChild(host);
|
|
538
|
+
}
|
|
539
|
+
let drawn = 0;
|
|
540
|
+
const max = style.max ?? 600;
|
|
541
|
+
for (const m of marks) {
|
|
542
|
+
if (drawn >= max) break;
|
|
543
|
+
let ctm = null;
|
|
544
|
+
try {
|
|
545
|
+
ctm = m.getScreenCTM?.() ?? null;
|
|
546
|
+
} catch {
|
|
547
|
+
ctm = null;
|
|
548
|
+
}
|
|
549
|
+
if (!ctm) continue;
|
|
550
|
+
const shape = copyGeometry(doc, m);
|
|
551
|
+
if (!shape) continue;
|
|
552
|
+
let parent = host;
|
|
553
|
+
let ox = base.left, oy = base.top;
|
|
554
|
+
const w = windowOf(m);
|
|
555
|
+
if (w) {
|
|
556
|
+
if (!(w.right > w.left) || !(w.bottom > w.top)) continue;
|
|
557
|
+
const v = doc.createElementNS(SVG_NS, "svg");
|
|
558
|
+
v.setAttribute("x", num(w.left - base.left));
|
|
559
|
+
v.setAttribute("y", num(w.top - base.top));
|
|
560
|
+
v.setAttribute("width", num(w.right - w.left));
|
|
561
|
+
v.setAttribute("height", num(w.bottom - w.top));
|
|
562
|
+
v.setAttribute("overflow", "hidden");
|
|
563
|
+
host.appendChild(v);
|
|
564
|
+
parent = v;
|
|
565
|
+
ox = w.left;
|
|
566
|
+
oy = w.top;
|
|
567
|
+
}
|
|
568
|
+
const transform = `matrix(${num(ctm.a)} ${num(ctm.b)} ${num(ctm.c)} ${num(ctm.d)} ${num(ctm.e - ox)} ${num(ctm.f - oy)})`;
|
|
569
|
+
for (const [stroke, sw] of [[style.halo, width + 2], [style.color, width]]) {
|
|
570
|
+
const p = shape.cloneNode(false);
|
|
571
|
+
p.setAttribute("transform", transform);
|
|
572
|
+
p.setAttribute("fill", "none");
|
|
573
|
+
p.setAttribute("stroke", stroke);
|
|
574
|
+
p.setAttribute("stroke-width", String(sw));
|
|
575
|
+
p.setAttribute("stroke-linejoin", "round");
|
|
576
|
+
p.setAttribute("vector-effect", "non-scaling-stroke");
|
|
577
|
+
parent.appendChild(p);
|
|
578
|
+
}
|
|
579
|
+
drawn++;
|
|
580
|
+
}
|
|
581
|
+
if (drawn > 0) overlay.appendChild(root);
|
|
582
|
+
return drawn;
|
|
583
|
+
}
|
|
584
|
+
|
|
557
585
|
// src/exportTable.ts
|
|
558
586
|
var EXPORT_FORMATS = Object.freeze(["xlsx", "csv"]);
|
|
559
587
|
var XLSX_MIME = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
@@ -702,8 +730,8 @@ function excelNumberFormat(col, values = []) {
|
|
|
702
730
|
if (!sh) return null;
|
|
703
731
|
let frac = 0;
|
|
704
732
|
for (const v of values) {
|
|
705
|
-
const
|
|
706
|
-
if (
|
|
733
|
+
const f = fractionDigits(sh.percent ? Math.round(v * 100 * 1e9) / 1e9 : v);
|
|
734
|
+
if (f > frac) frac = f;
|
|
707
735
|
}
|
|
708
736
|
return codeFromShape(sh, frac);
|
|
709
737
|
}
|
|
@@ -999,7 +1027,7 @@ function bytesToBase64(bytes) {
|
|
|
999
1027
|
}
|
|
1000
1028
|
function exportFormatsFor(hostFormats) {
|
|
1001
1029
|
if (!Array.isArray(hostFormats)) return EXPORT_FORMATS.slice();
|
|
1002
|
-
return EXPORT_FORMATS.filter((
|
|
1030
|
+
return EXPORT_FORMATS.filter((f) => hostFormats.indexOf(f) >= 0);
|
|
1003
1031
|
}
|
|
1004
1032
|
function exportTableService(save, formats = EXPORT_FORMATS) {
|
|
1005
1033
|
const allowed = formats.slice();
|
|
@@ -1169,12 +1197,20 @@ function resolveOptions(p) {
|
|
|
1169
1197
|
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
|
|
1170
1198
|
return Number.isFinite(n) ? clamp(n, DIM_OPACITY_MIN, 1) : void 0;
|
|
1171
1199
|
})(),
|
|
1172
|
-
// A recognised
|
|
1173
|
-
//
|
|
1200
|
+
// A recognised marker or nothing: an unknown string must never reach the painter as a live mode. "none" and
|
|
1201
|
+
// "auto" read undefined here, as a host that passes nothing does; what tells them apart is selectionMarker.
|
|
1174
1202
|
selectionGlyph: (() => {
|
|
1175
1203
|
const v = (p.selectionGlyph == null ? "" : String(p.selectionGlyph)).toLowerCase();
|
|
1176
|
-
return v === "dot" || v === "check" ? v : void 0;
|
|
1204
|
+
return v === "dot" || v === "check" || v === "outline" ? v : void 0;
|
|
1177
1205
|
})(),
|
|
1206
|
+
// THE WHOLE CHOICE: absent, unknown and "auto" are the default ("auto"), and "none" is a reader's None. The map's
|
|
1207
|
+
// choice beside it, its default "outline" when it made none.
|
|
1208
|
+
selectionMarker: readSelectionMarker(p.selectionGlyph),
|
|
1209
|
+
mapSelectionGlyph: (() => {
|
|
1210
|
+
const v = (p.mapSelectionGlyph == null ? "" : String(p.mapSelectionGlyph)).toLowerCase();
|
|
1211
|
+
return v === "none" || v === "dot" || v === "check" || v === "outline" ? v : void 0;
|
|
1212
|
+
})(),
|
|
1213
|
+
mapSelectionMarker: readMapSelectionMarker(p.mapSelectionGlyph),
|
|
1178
1214
|
// A host FACT from the generate answer, true only for a literal true: silence never suppresses anything.
|
|
1179
1215
|
suppressSelectionGlyph: p.suppressSelectionGlyph === true ? true : void 0,
|
|
1180
1216
|
// A host FACT (1.22.0): the chart works from the keyboard. True only for a literal true, like the one above.
|
|
@@ -1319,7 +1355,7 @@ function resolvePointChannel(cols, rowObjs, point) {
|
|
|
1319
1355
|
let labelCol;
|
|
1320
1356
|
if (bind) {
|
|
1321
1357
|
const dims = cols.filter((c) => !c.isMeasure).map((c) => c.name);
|
|
1322
|
-
const res =
|
|
1358
|
+
const res = Lr(dims, rowObjs, bind, point?.mapKind, cols.map((c) => c.name));
|
|
1323
1359
|
bind = res.bind;
|
|
1324
1360
|
rolesBackfilled = res.backfilled;
|
|
1325
1361
|
rolesRefused = res.refused;
|
|
@@ -1496,7 +1532,7 @@ function buildRenderPayload(cols, rowObjs, geo, point, destination, opts) {
|
|
|
1496
1532
|
}
|
|
1497
1533
|
function longitudeBeside(names) {
|
|
1498
1534
|
const words = (n) => n.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-zÀ-ɏ]+/);
|
|
1499
|
-
return names.find((n) => words(n).some((w) => w === "lon" || w === "lng" || w === "long" || w === "longitude")) ?? names.find((n) =>
|
|
1535
|
+
return names.find((n) => words(n).some((w) => w === "lon" || w === "lng" || w === "long" || w === "longitude")) ?? names.find((n) => Nt(words(n)) === "lon") ?? null;
|
|
1500
1536
|
}
|
|
1501
1537
|
|
|
1502
1538
|
// src/codeComments.ts
|
|
@@ -1634,10 +1670,97 @@ function stripJsComments(code) {
|
|
|
1634
1670
|
return out;
|
|
1635
1671
|
}
|
|
1636
1672
|
|
|
1673
|
+
// src/markFollower.ts
|
|
1674
|
+
var MARK_MOVE_ATTRIBUTES = Object.freeze([
|
|
1675
|
+
"transform",
|
|
1676
|
+
"d",
|
|
1677
|
+
"x",
|
|
1678
|
+
"y",
|
|
1679
|
+
"cx",
|
|
1680
|
+
"cy",
|
|
1681
|
+
"points",
|
|
1682
|
+
"width",
|
|
1683
|
+
"height",
|
|
1684
|
+
"visibility",
|
|
1685
|
+
"opacity",
|
|
1686
|
+
"style",
|
|
1687
|
+
"class"
|
|
1688
|
+
]);
|
|
1689
|
+
function followMarks(container, opts) {
|
|
1690
|
+
const win = opts.win ?? container.ownerDocument?.defaultView ?? null;
|
|
1691
|
+
let observer = null;
|
|
1692
|
+
let resize = null;
|
|
1693
|
+
let size = "";
|
|
1694
|
+
let queued = false;
|
|
1695
|
+
let queuedResize = false;
|
|
1696
|
+
const sizeOf = () => `${container.offsetWidth ?? 0}x${container.offsetHeight ?? 0}`;
|
|
1697
|
+
const movesAMark = (r) => {
|
|
1698
|
+
const t = r.target;
|
|
1699
|
+
if (!t || opts.blind && t.closest?.(opts.blind)) return false;
|
|
1700
|
+
return !!(t.classList?.contains(MARK_CLASS) || t.querySelector?.(`.${MARK_CLASS}`));
|
|
1701
|
+
};
|
|
1702
|
+
const queue = (resized) => {
|
|
1703
|
+
if (resized) queuedResize = true;
|
|
1704
|
+
if (queued) return;
|
|
1705
|
+
queued = true;
|
|
1706
|
+
const run = () => {
|
|
1707
|
+
const r = queuedResize;
|
|
1708
|
+
queued = false;
|
|
1709
|
+
queuedResize = false;
|
|
1710
|
+
if (observer || resize) {
|
|
1711
|
+
try {
|
|
1712
|
+
opts.repaint(r);
|
|
1713
|
+
} catch {
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
if (win?.requestAnimationFrame) win.requestAnimationFrame(run);
|
|
1718
|
+
else setTimeout(run, 16);
|
|
1719
|
+
};
|
|
1720
|
+
return {
|
|
1721
|
+
follow(on) {
|
|
1722
|
+
if (!on) {
|
|
1723
|
+
if (observer) {
|
|
1724
|
+
observer.disconnect();
|
|
1725
|
+
observer = null;
|
|
1726
|
+
}
|
|
1727
|
+
if (resize) {
|
|
1728
|
+
resize.disconnect();
|
|
1729
|
+
resize = null;
|
|
1730
|
+
}
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
const MO = win?.MutationObserver;
|
|
1734
|
+
if (!observer && MO) {
|
|
1735
|
+
observer = new MO((records) => {
|
|
1736
|
+
if (!queued && records.some(movesAMark)) queue(false);
|
|
1737
|
+
});
|
|
1738
|
+
observer.observe(container, { subtree: true, attributes: true, attributeFilter: [...MARK_MOVE_ATTRIBUTES] });
|
|
1739
|
+
}
|
|
1740
|
+
const RO = win?.ResizeObserver;
|
|
1741
|
+
if (!resize && RO) {
|
|
1742
|
+
resize = new RO(() => {
|
|
1743
|
+
if (sizeOf() !== size) queue(true);
|
|
1744
|
+
});
|
|
1745
|
+
resize.observe(container);
|
|
1746
|
+
}
|
|
1747
|
+
},
|
|
1748
|
+
painted() {
|
|
1749
|
+
size = sizeOf();
|
|
1750
|
+
},
|
|
1751
|
+
takeRecords() {
|
|
1752
|
+
observer?.takeRecords();
|
|
1753
|
+
},
|
|
1754
|
+
get following() {
|
|
1755
|
+
return !!(observer || resize);
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1637
1760
|
// src/blankRender.ts
|
|
1638
|
-
var
|
|
1761
|
+
var EMPTY = { markCount: 0, legendMarkCount: 0, containerKind: "empty", declaresNoRowMarks: false };
|
|
1639
1762
|
function censusMarks(container) {
|
|
1640
|
-
if (!container || typeof container.querySelectorAll !== "function") return
|
|
1763
|
+
if (!container || typeof container.querySelectorAll !== "function") return EMPTY;
|
|
1641
1764
|
try {
|
|
1642
1765
|
const marks = /* @__PURE__ */ new Set();
|
|
1643
1766
|
for (const el of Array.from(container.querySelectorAll(`.${MARK_CLASS}`))) marks.add(el);
|
|
@@ -1656,7 +1779,7 @@ function censusMarks(container) {
|
|
|
1656
1779
|
declaresNoRowMarks: !!container.querySelector(`[${NO_ROW_MARKS_ATTR}]`)
|
|
1657
1780
|
};
|
|
1658
1781
|
} catch {
|
|
1659
|
-
return
|
|
1782
|
+
return EMPTY;
|
|
1660
1783
|
}
|
|
1661
1784
|
}
|
|
1662
1785
|
function isBlankRender(i) {
|
|
@@ -1680,8 +1803,8 @@ function censusClippedText(container) {
|
|
|
1680
1803
|
try {
|
|
1681
1804
|
const svg = container?.querySelector?.("svg");
|
|
1682
1805
|
if (!svg) return none;
|
|
1683
|
-
const
|
|
1684
|
-
if (!
|
|
1806
|
+
const f = svg.getBoundingClientRect();
|
|
1807
|
+
if (!f || !(f.width > 0) || !(f.height > 0)) return none;
|
|
1685
1808
|
const view = svg.ownerDocument?.defaultView;
|
|
1686
1809
|
const clipped = [];
|
|
1687
1810
|
let measured = 0;
|
|
@@ -1691,7 +1814,7 @@ function censusClippedText(container) {
|
|
|
1691
1814
|
const s = view?.getComputedStyle?.(t);
|
|
1692
1815
|
if (s && (s.display === "none" || s.visibility === "hidden" || s.opacity === "0")) continue;
|
|
1693
1816
|
measured++;
|
|
1694
|
-
const top =
|
|
1817
|
+
const top = f.top - r.top, left = f.left - r.left, bottom = r.bottom - f.bottom, right = r.right - f.right;
|
|
1695
1818
|
if (top > CLIP_TOLERANCE_PX || left > CLIP_TOLERANCE_PX || bottom > CLIP_TOLERANCE_PX || right > CLIP_TOLERANCE_PX) {
|
|
1696
1819
|
clipped.push({
|
|
1697
1820
|
text: String(t.textContent ?? "").slice(0, 40),
|
|
@@ -1716,13 +1839,13 @@ function clippedTextFlag(c) {
|
|
|
1716
1839
|
}
|
|
1717
1840
|
|
|
1718
1841
|
// src/valuePlacement.ts
|
|
1719
|
-
var
|
|
1720
|
-
var
|
|
1842
|
+
var MIN_MARKS = 24;
|
|
1843
|
+
var ELEMENT_CAP = 5e3;
|
|
1721
1844
|
var FIT_SAMPLE = 90;
|
|
1722
1845
|
var VALUE_AXIS_MIN_FIT = 0.8;
|
|
1723
1846
|
var MIN_AXIS_SPAN_PX = 40;
|
|
1724
|
-
var
|
|
1725
|
-
function
|
|
1847
|
+
var EMPTY2 = { marks: 0, axis: "", column: "", fit: 0, offShare: 0, medianOffRadii: 0 };
|
|
1848
|
+
function num2(el, name) {
|
|
1726
1849
|
const v = el.getAttribute?.(name);
|
|
1727
1850
|
if (v === null || v === void 0 || String(v).trim() === "") return NaN;
|
|
1728
1851
|
return parseFloat(String(v));
|
|
@@ -1754,12 +1877,12 @@ function robustLine(v, pos) {
|
|
|
1754
1877
|
}
|
|
1755
1878
|
function censusValuePlacement(container, data) {
|
|
1756
1879
|
try {
|
|
1757
|
-
if (!container || typeof container.querySelectorAll !== "function") return
|
|
1880
|
+
if (!container || typeof container.querySelectorAll !== "function") return EMPTY2;
|
|
1758
1881
|
const rows = data?.rows;
|
|
1759
1882
|
const columns = data?.columns ?? [];
|
|
1760
|
-
if (!Array.isArray(rows) || !rows.length || !columns.length) return
|
|
1883
|
+
if (!Array.isArray(rows) || !rows.length || !columns.length) return EMPTY2;
|
|
1761
1884
|
const els = Array.from(container.querySelectorAll(`circle[${ROW_IDX_ATTR}]`));
|
|
1762
|
-
if (els.length <
|
|
1885
|
+
if (els.length < MIN_MARKS || els.length > ELEMENT_CAP) return EMPTY2;
|
|
1763
1886
|
const dots = [];
|
|
1764
1887
|
const seen = /* @__PURE__ */ new Set();
|
|
1765
1888
|
for (const el of els) {
|
|
@@ -1767,12 +1890,12 @@ function censusValuePlacement(container, data) {
|
|
|
1767
1890
|
if (!/^\d+$/.test(raw)) continue;
|
|
1768
1891
|
const i = +raw;
|
|
1769
1892
|
const row = rows[i];
|
|
1770
|
-
const x =
|
|
1893
|
+
const x = num2(el, "cx"), y = num2(el, "cy"), r = num2(el, "r");
|
|
1771
1894
|
if (!row || !Number.isFinite(x) || !Number.isFinite(y) || !(r > 0)) continue;
|
|
1772
1895
|
seen.add(i);
|
|
1773
1896
|
dots.push({ row, x, y, r });
|
|
1774
1897
|
}
|
|
1775
|
-
if (dots.length <
|
|
1898
|
+
if (dots.length < MIN_MARKS || seen.size < dots.length * 0.9) return EMPTY2;
|
|
1776
1899
|
let best = null;
|
|
1777
1900
|
for (let c = 0; c < columns.length; c++) {
|
|
1778
1901
|
if (/^__.*__$/.test(String(columns[c]?.name ?? ""))) continue;
|
|
@@ -1817,7 +1940,7 @@ function censusValuePlacement(container, data) {
|
|
|
1817
1940
|
}
|
|
1818
1941
|
}
|
|
1819
1942
|
}
|
|
1820
|
-
if (!best) return
|
|
1943
|
+
if (!best) return EMPTY2;
|
|
1821
1944
|
let off = 0, measured = 0;
|
|
1822
1945
|
const radii = [];
|
|
1823
1946
|
dots.forEach((d, k) => {
|
|
@@ -1828,7 +1951,7 @@ function censusValuePlacement(container, data) {
|
|
|
1828
1951
|
radii.push(inRadii);
|
|
1829
1952
|
if (inRadii > 1) off++;
|
|
1830
1953
|
});
|
|
1831
|
-
if (measured <
|
|
1954
|
+
if (measured < MIN_MARKS) return EMPTY2;
|
|
1832
1955
|
return {
|
|
1833
1956
|
marks: measured,
|
|
1834
1957
|
axis: best.axis,
|
|
@@ -1838,7 +1961,7 @@ function censusValuePlacement(container, data) {
|
|
|
1838
1961
|
medianOffRadii: Math.round(median(radii) * 100) / 100
|
|
1839
1962
|
};
|
|
1840
1963
|
} catch {
|
|
1841
|
-
return
|
|
1964
|
+
return EMPTY2;
|
|
1842
1965
|
}
|
|
1843
1966
|
}
|
|
1844
1967
|
function valuePlacementFlag(c) {
|
|
@@ -1851,7 +1974,7 @@ function valuePlacementFlag(c) {
|
|
|
1851
1974
|
// src/labelContrastDom.ts
|
|
1852
1975
|
var LABEL_CONTRAST_DONE_ATTR = "data-lch-contrast";
|
|
1853
1976
|
var LABEL_CONTRAST_CAP = 2e3;
|
|
1854
|
-
var
|
|
1977
|
+
var EMPTY3 = {
|
|
1855
1978
|
rects: 0,
|
|
1856
1979
|
scanned: 0,
|
|
1857
1980
|
fixed: 0,
|
|
@@ -1929,11 +2052,11 @@ function extendPillOver(pill, tr) {
|
|
|
1929
2052
|
const inv = m.inverse();
|
|
1930
2053
|
const toUser = (x2, y2) => ({ x: inv.a * x2 + inv.c * y2 + inv.e, y: inv.b * x2 + inv.d * y2 + inv.f });
|
|
1931
2054
|
const a = toUser(tr.left, tr.top), b = toUser(tr.right, tr.bottom);
|
|
1932
|
-
const
|
|
2055
|
+
const num3 = (k) => {
|
|
1933
2056
|
const v = parseFloat(pill.getAttribute(k) || "");
|
|
1934
2057
|
return isFinite(v) ? v : NaN;
|
|
1935
2058
|
};
|
|
1936
|
-
const x =
|
|
2059
|
+
const x = num3("x"), y = num3("y"), w = num3("width"), h = num3("height");
|
|
1937
2060
|
if (![x, y, w, h].every((v) => isFinite(v)) || !(w > 0) || !(h > 0)) return false;
|
|
1938
2061
|
const pad = 1 / m.a;
|
|
1939
2062
|
const x0 = Math.min(x, Math.min(a.x, b.x) - pad), x1 = Math.max(x + w, Math.max(a.x, b.x) + pad);
|
|
@@ -2002,7 +2125,7 @@ function addPill(tx, fill) {
|
|
|
2002
2125
|
}
|
|
2003
2126
|
}
|
|
2004
2127
|
function applyLabelContrast(container, opts = {}) {
|
|
2005
|
-
const report = { ...
|
|
2128
|
+
const report = { ...EMPTY3 };
|
|
2006
2129
|
if (!container) {
|
|
2007
2130
|
report.skipped = "no-container";
|
|
2008
2131
|
return report;
|
|
@@ -2340,10 +2463,10 @@ function seenRight(el, right, stop) {
|
|
|
2340
2463
|
}
|
|
2341
2464
|
return right;
|
|
2342
2465
|
}
|
|
2343
|
-
var
|
|
2466
|
+
var SVG_NS2 = "http://www.w3.org/2000/svg";
|
|
2344
2467
|
function overflowWindowOf(el) {
|
|
2345
2468
|
const win = el.ownerDocument.defaultView;
|
|
2346
|
-
if (!win || el.namespaceURI ===
|
|
2469
|
+
if (!win || el.namespaceURI === SVG_NS2) return null;
|
|
2347
2470
|
try {
|
|
2348
2471
|
const o = win.getComputedStyle(el);
|
|
2349
2472
|
const clips = (v) => !!v && v !== "visible";
|
|
@@ -2426,17 +2549,22 @@ function createAnnotationLayer(container, onClick, opts) {
|
|
|
2426
2549
|
const names = (data.columns || []).map((c) => typeof c === "string" ? c : c?.name);
|
|
2427
2550
|
const marks = Array.from(container.querySelectorAll(`.${MARK_CLASS}[${ROW_IDX_ATTR}]`)).filter((m) => !m.closest(`.${ANNOTATION_LAYER_CLASS}`));
|
|
2428
2551
|
const marksByRow = /* @__PURE__ */ new Map();
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2552
|
+
const rowsOf = /* @__PURE__ */ new Map();
|
|
2553
|
+
for (const m of marks) {
|
|
2554
|
+
const own = new Set(parseIdxs(m.getAttribute(ROW_IDX_ATTR)));
|
|
2555
|
+
rowsOf.set(m, own);
|
|
2556
|
+
for (const r of own) {
|
|
2557
|
+
const at = marksByRow.get(r);
|
|
2558
|
+
if (at) at.push(m);
|
|
2559
|
+
else marksByRow.set(r, [m]);
|
|
2560
|
+
}
|
|
2433
2561
|
}
|
|
2434
2562
|
const origin = container.getBoundingClientRect();
|
|
2435
2563
|
const open = origin.width > 0 && origin.height > 0 ? box(origin) : FREE;
|
|
2436
2564
|
const clipOf = /* @__PURE__ */ new Map();
|
|
2437
2565
|
const windowOf = (el) => {
|
|
2438
2566
|
if (!clipOf.has(el)) {
|
|
2439
|
-
if (el.namespaceURI ===
|
|
2567
|
+
if (el.namespaceURI === SVG_NS2) {
|
|
2440
2568
|
const w = clipWindowOf(el);
|
|
2441
2569
|
clipOf.set(el, w && box(w));
|
|
2442
2570
|
} else clipOf.set(el, overflowWindowOf(el));
|
|
@@ -2451,7 +2579,7 @@ function createAnnotationLayer(container, onClick, opts) {
|
|
|
2451
2579
|
const viewOf = (m) => {
|
|
2452
2580
|
let v = inView;
|
|
2453
2581
|
for (let el = m.parentElement; v && el && el !== container; el = el.parentElement) {
|
|
2454
|
-
if (el.namespaceURI ===
|
|
2582
|
+
if (el.namespaceURI === SVG_NS2 && el.localName === "svg") {
|
|
2455
2583
|
const s = el.getBoundingClientRect();
|
|
2456
2584
|
if (s.width > 0 && s.height > 0) v = cut(v, box(s));
|
|
2457
2585
|
}
|
|
@@ -2546,16 +2674,31 @@ function createAnnotationLayer(container, onClick, opts) {
|
|
|
2546
2674
|
for (const a of list) {
|
|
2547
2675
|
const pairs = (a.where ? Object.entries(a.where) : a.column != null ? [[a.column, a.value]] : []).map(([c, v]) => [names.indexOf(c), text(v)]);
|
|
2548
2676
|
const want = pairs.map((p) => p[1]).join(" / ");
|
|
2549
|
-
const hits2 = /* @__PURE__ */ new Set();
|
|
2677
|
+
const hits2 = /* @__PURE__ */ new Set(), keyRows = /* @__PURE__ */ new Set();
|
|
2550
2678
|
if (pairs.length && pairs.every((p) => p[0] >= 0)) (data.rows || []).forEach((row, r) => {
|
|
2551
|
-
if (pairs.every(([ci, v]) => text(row?.[ci]) === v))
|
|
2679
|
+
if (!pairs.every(([ci, v]) => text(row?.[ci]) === v)) return;
|
|
2680
|
+
keyRows.add(r);
|
|
2681
|
+
for (const m of marksByRow.get(r) ?? []) hits2.add(m);
|
|
2552
2682
|
});
|
|
2553
2683
|
if (!hits2.size) continue;
|
|
2554
|
-
|
|
2684
|
+
const rank = (m) => {
|
|
2685
|
+
const own = rowsOf.get(m);
|
|
2686
|
+
let out = 0;
|
|
2687
|
+
for (const r of own) if (!keyRows.has(r)) out++;
|
|
2688
|
+
const level = parseInt(m.getAttribute("aria-level") || "", 10);
|
|
2689
|
+
return [out, out === 0 && own.size === keyRows.size ? 0 : 1, own.size, Number.isFinite(level) ? -level : 0];
|
|
2690
|
+
};
|
|
2691
|
+
const before = (p, q) => p[0] - q[0] || p[1] - q[1] || p[2] - q[2] || p[3] - q[3];
|
|
2692
|
+
const drawn = [];
|
|
2555
2693
|
for (const m of hits2) {
|
|
2556
2694
|
const b = m.getBoundingClientRect();
|
|
2557
|
-
if (
|
|
2558
|
-
|
|
2695
|
+
if (b.width > 0 && b.height > 0) drawn.push({ m, b, k: rank(m) });
|
|
2696
|
+
}
|
|
2697
|
+
const laidOut = drawn.length > 0;
|
|
2698
|
+
const most = drawn.reduce((k, d) => !k || before(d.k, k) < 0 ? d.k : k, null);
|
|
2699
|
+
let best = null, bestWhole = null, bestMark = null;
|
|
2700
|
+
for (const { m, b, k } of drawn) {
|
|
2701
|
+
if (before(k, most) !== 0) continue;
|
|
2559
2702
|
const v = cut(box(b), viewOf(m));
|
|
2560
2703
|
if (v && (!best || area(v) > area(best))) {
|
|
2561
2704
|
best = v;
|
|
@@ -2815,16 +2958,17 @@ function installKeyboard(env) {
|
|
|
2815
2958
|
const { container } = env;
|
|
2816
2959
|
let rovingKey = null;
|
|
2817
2960
|
let destroyed = false;
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
return Array.from(container.querySelectorAll(env.markSelector)).filter((m) => !m.closest(env.skipSelector) && (m.getAttribute("data-row-idx") ?? "") !== "");
|
|
2821
|
-
};
|
|
2961
|
+
const ownRole = /* @__PURE__ */ new WeakSet();
|
|
2962
|
+
const marksNow = () => Array.from(container.querySelectorAll(env.markSelector)).filter((m) => !m.closest(env.skipSelector) && (m.getAttribute("data-row-idx") ?? "") !== "");
|
|
2822
2963
|
const wired = (el) => el.hasAttribute(WIRED_ATTR);
|
|
2823
2964
|
const wire = (el, tabindex, role) => {
|
|
2824
2965
|
if (el.hasAttribute("tabindex") && !wired(el)) return false;
|
|
2825
2966
|
el.setAttribute(WIRED_ATTR, "");
|
|
2826
2967
|
el.setAttribute("tabindex", tabindex);
|
|
2827
|
-
if (!el.hasAttribute("role")
|
|
2968
|
+
if (!el.hasAttribute("role") || ownRole.has(el)) {
|
|
2969
|
+
el.setAttribute("role", role);
|
|
2970
|
+
ownRole.add(el);
|
|
2971
|
+
}
|
|
2828
2972
|
return true;
|
|
2829
2973
|
};
|
|
2830
2974
|
const setRoving = (list, to) => {
|
|
@@ -2835,22 +2979,17 @@ function installKeyboard(env) {
|
|
|
2835
2979
|
if (destroyed) return;
|
|
2836
2980
|
try {
|
|
2837
2981
|
container.classList?.add(KEYBOARD_CLASS);
|
|
2982
|
+
const inert = env.inert();
|
|
2838
2983
|
const list = marksNow();
|
|
2839
2984
|
for (const m of list) {
|
|
2840
|
-
if (!wire(m, "-1", "button")) continue;
|
|
2841
|
-
m.
|
|
2985
|
+
if (!wire(m, "-1", inert ? "img" : "button")) continue;
|
|
2986
|
+
if (inert) m.removeAttribute("aria-pressed");
|
|
2987
|
+
else m.setAttribute("aria-pressed", env.isSelected(m) ? "true" : "false");
|
|
2842
2988
|
if (!m.hasAttribute("aria-label") && !m.querySelector(":scope > title")) {
|
|
2843
2989
|
const name = env.labelOf(m);
|
|
2844
2990
|
if (name) m.setAttribute("aria-label", name);
|
|
2845
2991
|
}
|
|
2846
2992
|
}
|
|
2847
|
-
if (!list.length) {
|
|
2848
|
-
for (const m of Array.from(container.querySelectorAll(`[${WIRED_ATTR}]`)))
|
|
2849
|
-
if (m.matches(env.markSelector)) {
|
|
2850
|
-
m.removeAttribute("tabindex");
|
|
2851
|
-
m.removeAttribute(WIRED_ATTR);
|
|
2852
|
-
}
|
|
2853
|
-
}
|
|
2854
2993
|
const own = list.filter(wired);
|
|
2855
2994
|
if (own.length) {
|
|
2856
2995
|
const keep = own.find((m) => env.keyOf(m) === rovingKey) ?? own.find((m) => env.isSelected(m)) ?? own[0];
|
|
@@ -2879,7 +3018,7 @@ function installKeyboard(env) {
|
|
|
2879
3018
|
e.preventDefault();
|
|
2880
3019
|
const ME = container.ownerDocument.defaultView?.MouseEvent ?? MouseEvent;
|
|
2881
3020
|
press.dispatchEvent(new ME("click", { bubbles: true, cancelable: true }));
|
|
2882
|
-
} else if (k === "Escape" && env.escapeClears !== false) env.clear();
|
|
3021
|
+
} else if (k === "Escape" && env.escapeClears !== false && !env.inert()) env.clear();
|
|
2883
3022
|
return;
|
|
2884
3023
|
}
|
|
2885
3024
|
const mark = t.closest?.(env.markSelector);
|
|
@@ -2921,9 +3060,10 @@ function installKeyboard(env) {
|
|
|
2921
3060
|
rovingKey = env.keyOf(mark);
|
|
2922
3061
|
env.select(mark, { ctrl: !!(e.ctrlKey || e.metaKey), shift: !!e.shiftKey });
|
|
2923
3062
|
break;
|
|
3063
|
+
// An inert chart's click on empty canvas clears nothing, so neither does its Escape.
|
|
2924
3064
|
case "Escape":
|
|
2925
3065
|
hideTip();
|
|
2926
|
-
if (env.escapeClears !== false) env.clear();
|
|
3066
|
+
if (env.escapeClears !== false && !env.inert()) env.clear();
|
|
2927
3067
|
break;
|
|
2928
3068
|
}
|
|
2929
3069
|
};
|
|
@@ -3399,77 +3539,13 @@ function createChartHost(container, config) {
|
|
|
3399
3539
|
};
|
|
3400
3540
|
const GLYPH_LAYER_CLASS = "bic-selection-glyphs";
|
|
3401
3541
|
let glyphPositionWas = null;
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3542
|
+
const glyphFollower = followMarks(container, {
|
|
3543
|
+
win,
|
|
3544
|
+
blind: `.${GLYPH_LAYER_CLASS}, .${LASSO_OUTLINE_CLASS}`,
|
|
3545
|
+
repaint: (resized) => paintGlyphLayer(resized ? "if-changed" : false)
|
|
3546
|
+
});
|
|
3407
3547
|
let glyphReported = "";
|
|
3408
|
-
const
|
|
3409
|
-
const glyphBlind = `.${GLYPH_LAYER_CLASS}, .${LASSO_OUTLINE_CLASS}`;
|
|
3410
|
-
const movesAMark = (r) => {
|
|
3411
|
-
const t = r.target;
|
|
3412
|
-
if (!t || t.closest?.(glyphBlind)) return false;
|
|
3413
|
-
return !!(t.classList?.contains(MARK_CLASS) || t.querySelector?.(`.${MARK_CLASS}`));
|
|
3414
|
-
};
|
|
3415
|
-
const queueGlyphs = (reportIfChanged) => {
|
|
3416
|
-
if (reportIfChanged) glyphQueuedReport = true;
|
|
3417
|
-
if (glyphQueued) return;
|
|
3418
|
-
glyphQueued = true;
|
|
3419
|
-
const run = () => {
|
|
3420
|
-
const r = glyphQueuedReport;
|
|
3421
|
-
glyphQueued = false;
|
|
3422
|
-
glyphQueuedReport = false;
|
|
3423
|
-
if (glyphObserver || glyphResize) paintGlyphLayer(r ? "if-changed" : false);
|
|
3424
|
-
};
|
|
3425
|
-
if (win?.requestAnimationFrame) win.requestAnimationFrame(run);
|
|
3426
|
-
else setTimeout(run, 16);
|
|
3427
|
-
};
|
|
3428
|
-
const followGlyphs = (on) => {
|
|
3429
|
-
if (!on) {
|
|
3430
|
-
if (glyphObserver) {
|
|
3431
|
-
glyphObserver.disconnect();
|
|
3432
|
-
glyphObserver = null;
|
|
3433
|
-
}
|
|
3434
|
-
if (glyphResize) {
|
|
3435
|
-
glyphResize.disconnect();
|
|
3436
|
-
glyphResize = null;
|
|
3437
|
-
}
|
|
3438
|
-
return;
|
|
3439
|
-
}
|
|
3440
|
-
const MO = win?.MutationObserver;
|
|
3441
|
-
if (!glyphObserver && MO) {
|
|
3442
|
-
glyphObserver = new MO((records) => {
|
|
3443
|
-
if (!glyphQueued && records.some(movesAMark)) queueGlyphs(false);
|
|
3444
|
-
});
|
|
3445
|
-
glyphObserver.observe(container, {
|
|
3446
|
-
subtree: true,
|
|
3447
|
-
attributes: true,
|
|
3448
|
-
attributeFilter: [
|
|
3449
|
-
"transform",
|
|
3450
|
-
"d",
|
|
3451
|
-
"x",
|
|
3452
|
-
"y",
|
|
3453
|
-
"cx",
|
|
3454
|
-
"cy",
|
|
3455
|
-
"points",
|
|
3456
|
-
"width",
|
|
3457
|
-
"height",
|
|
3458
|
-
"visibility",
|
|
3459
|
-
"opacity",
|
|
3460
|
-
"style",
|
|
3461
|
-
"class"
|
|
3462
|
-
]
|
|
3463
|
-
});
|
|
3464
|
-
}
|
|
3465
|
-
const RO = win?.ResizeObserver;
|
|
3466
|
-
if (!glyphResize && RO) {
|
|
3467
|
-
glyphResize = new RO(() => {
|
|
3468
|
-
if (sizeOfContainer() !== glyphSize) queueGlyphs(true);
|
|
3469
|
-
});
|
|
3470
|
-
glyphResize.observe(container);
|
|
3471
|
-
}
|
|
3472
|
-
};
|
|
3548
|
+
const followGlyphs = (on) => glyphFollower.follow(on);
|
|
3473
3549
|
const markShown = (m) => {
|
|
3474
3550
|
try {
|
|
3475
3551
|
const cv = m.checkVisibility;
|
|
@@ -3503,11 +3579,11 @@ function createChartHost(container, config) {
|
|
|
3503
3579
|
}
|
|
3504
3580
|
return windowOf.get(el) ?? null;
|
|
3505
3581
|
};
|
|
3506
|
-
const
|
|
3582
|
+
const EPS = 0.5;
|
|
3507
3583
|
return (m, x, y) => {
|
|
3508
3584
|
for (let el = m; el && el !== container; el = el.parentElement) {
|
|
3509
3585
|
const w = read(el);
|
|
3510
|
-
if (w && (x < w.left -
|
|
3586
|
+
if (w && (x < w.left - EPS || x > w.right + EPS || y < w.top - EPS || y > w.bottom + EPS)) return false;
|
|
3511
3587
|
}
|
|
3512
3588
|
return true;
|
|
3513
3589
|
};
|
|
@@ -3518,8 +3594,13 @@ function createChartHost(container, config) {
|
|
|
3518
3594
|
try {
|
|
3519
3595
|
let layer = container.querySelector(`:scope > .${GLYPH_LAYER_CLASS}`);
|
|
3520
3596
|
clearSelectionGlyphs(layer);
|
|
3521
|
-
const mode =
|
|
3522
|
-
|
|
3597
|
+
const mode = selectionMarkerFor({
|
|
3598
|
+
choice: resolved.selectionMarker,
|
|
3599
|
+
mapChoice: resolved.mapSelectionMarker,
|
|
3600
|
+
regionMap: isRegionMapData(data),
|
|
3601
|
+
neutralCentreRamp: hasNeutralCentreRamp(container)
|
|
3602
|
+
});
|
|
3603
|
+
if (mode !== "dot" && mode !== "check" && mode !== "outline") {
|
|
3523
3604
|
glyphReported = "";
|
|
3524
3605
|
return;
|
|
3525
3606
|
}
|
|
@@ -3567,20 +3648,41 @@ function createChartHost(container, config) {
|
|
|
3567
3648
|
const sw = container.scrollWidth || 0, sh = container.scrollHeight || 0;
|
|
3568
3649
|
layer.style.width = `${sw}px`;
|
|
3569
3650
|
layer.style.height = `${sh}px`;
|
|
3570
|
-
|
|
3571
|
-
const centers = [];
|
|
3572
|
-
collectGlyphCentersWhere(container, layer.getBoundingClientRect(), centers, { shown: markShown, at: glyphWindows() });
|
|
3651
|
+
glyphFollower.painted();
|
|
3573
3652
|
const hc = !!resolved.isHighContrast;
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3653
|
+
let drawn = 0, total = 0;
|
|
3654
|
+
if (mode === "outline") {
|
|
3655
|
+
const marks = collectOutlineMarks(container, { shown: markShown });
|
|
3656
|
+
total = marks.length;
|
|
3657
|
+
let ink = resolved.themeFg || "";
|
|
3658
|
+
if (!ink) {
|
|
3659
|
+
try {
|
|
3660
|
+
ink = win?.getComputedStyle?.(container)?.color || "";
|
|
3661
|
+
} catch {
|
|
3662
|
+
ink = "";
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
drawn = total ? paintSelectionOutlines(layer, marks, {
|
|
3666
|
+
color: ink || "#111",
|
|
3667
|
+
halo: resolved.themeBg || (hc ? "#000" : "#fff"),
|
|
3668
|
+
windowOf: (m) => outlineWindowOf(m, container),
|
|
3669
|
+
avoid: controlBoxesIn(container)
|
|
3670
|
+
}) : 0;
|
|
3671
|
+
} else {
|
|
3672
|
+
const centers = [];
|
|
3673
|
+
collectGlyphCentersWhere(container, layer.getBoundingClientRect(), centers, { shown: markShown, at: glyphWindows() });
|
|
3674
|
+
total = centers.length;
|
|
3675
|
+
drawn = centers.length ? paintSelectionGlyphs(layer, centers, {
|
|
3676
|
+
symbol: mode === "check" ? "\u2713" : "\u25CF",
|
|
3677
|
+
color: hc ? resolved.themeFg || "#111" : "#111",
|
|
3678
|
+
halo: hc ? resolved.themeBg || "#fff" : "#fff"
|
|
3679
|
+
}) : 0;
|
|
3680
|
+
}
|
|
3681
|
+
const said = `${mode}:${drawn}:${total}`;
|
|
3580
3682
|
if (report === true || report === "if-changed" && said !== glyphReported) {
|
|
3581
3683
|
glyphReported = said;
|
|
3582
3684
|
try {
|
|
3583
|
-
config.onSelectionGlyphs?.({ mode, drawn, total
|
|
3685
|
+
config.onSelectionGlyphs?.({ mode, drawn, total, suppressed: false });
|
|
3584
3686
|
} catch {
|
|
3585
3687
|
}
|
|
3586
3688
|
}
|
|
@@ -3588,7 +3690,7 @@ function createChartHost(container, config) {
|
|
|
3588
3690
|
} finally {
|
|
3589
3691
|
try {
|
|
3590
3692
|
followGlyphs(follow);
|
|
3591
|
-
|
|
3693
|
+
glyphFollower.takeRecords();
|
|
3592
3694
|
} catch {
|
|
3593
3695
|
}
|
|
3594
3696
|
}
|
|
@@ -3716,7 +3818,15 @@ function createChartHost(container, config) {
|
|
|
3716
3818
|
{ ctrl: !!(e?.ctrlKey || e?.metaKey), shift: !!e?.shiftKey }
|
|
3717
3819
|
));
|
|
3718
3820
|
const fromLabel = !!tick && !multi && !toggledOff && !chartOwnsTimeline(container);
|
|
3719
|
-
|
|
3821
|
+
const fromMark = !tick && !multi && !toggledOff;
|
|
3822
|
+
notify(
|
|
3823
|
+
next,
|
|
3824
|
+
"user",
|
|
3825
|
+
toggledOff ? null : tick,
|
|
3826
|
+
false,
|
|
3827
|
+
true,
|
|
3828
|
+
fromLabel ? { fromLabel: true } : fromMark ? { fromMark: true } : void 0
|
|
3829
|
+
);
|
|
3720
3830
|
};
|
|
3721
3831
|
container.addEventListener(XFILTER_REFRESH_EVENT, onXf);
|
|
3722
3832
|
const untrackPress = trackPress(container);
|
|
@@ -3877,6 +3987,21 @@ function createChartHost(container, config) {
|
|
|
3877
3987
|
} catch {
|
|
3878
3988
|
}
|
|
3879
3989
|
}
|
|
3990
|
+
if (config.onCategoryColourCensus) {
|
|
3991
|
+
const report = config.onCategoryColourCensus;
|
|
3992
|
+
const bg = resolved.backgroundColor ?? resolved.themeBg;
|
|
3993
|
+
const opts = {
|
|
3994
|
+
pageBg: typeof bg === "string" && bg ? bg : void 0,
|
|
3995
|
+
dimOpacity: typeof resolved.dimOpacity === "number" ? resolved.dimOpacity : void 0
|
|
3996
|
+
};
|
|
3997
|
+
import("./category-colours.mjs").then((m) => {
|
|
3998
|
+
try {
|
|
3999
|
+
report(m.censusCategoryColours(container, doc, opts));
|
|
4000
|
+
} catch {
|
|
4001
|
+
}
|
|
4002
|
+
}).catch(() => {
|
|
4003
|
+
});
|
|
4004
|
+
}
|
|
3880
4005
|
if (config.onClippedTextCensus) {
|
|
3881
4006
|
try {
|
|
3882
4007
|
config.onClippedTextCensus(censusClippedText(container));
|
|
@@ -3890,6 +4015,7 @@ function createChartHost(container, config) {
|
|
|
3890
4015
|
}
|
|
3891
4016
|
}
|
|
3892
4017
|
const scrollMem = takeScroll(container);
|
|
4018
|
+
if (keepScroll) restoreInnerScroll(container, scrollMem, scrollKey());
|
|
3893
4019
|
if (fitOpts !== false) {
|
|
3894
4020
|
try {
|
|
3895
4021
|
const base = fitOpts === true ? {} : fitOpts;
|
|
@@ -4208,7 +4334,7 @@ function createChartHost(container, config) {
|
|
|
4208
4334
|
}
|
|
4209
4335
|
|
|
4210
4336
|
// src/group.ts
|
|
4211
|
-
var
|
|
4337
|
+
var EMPTY4 = Object.freeze({ sourceId: null, rows: Object.freeze([]) });
|
|
4212
4338
|
function toSourceRows(rowMap, payloadRowIdxs) {
|
|
4213
4339
|
if (!rowMap) return payloadRowIdxs.slice();
|
|
4214
4340
|
return payloadRowIdxs.map((i) => rowMap[i]).filter((i) => i !== void 0);
|
|
@@ -4246,7 +4372,7 @@ function createChartGroup(columns, rows, opts = {}) {
|
|
|
4246
4372
|
let geo = opts.geo ?? null;
|
|
4247
4373
|
let point = opts.point ?? null;
|
|
4248
4374
|
let destination = opts.destination ?? null;
|
|
4249
|
-
let selection =
|
|
4375
|
+
let selection = EMPTY4;
|
|
4250
4376
|
const subs = /* @__PURE__ */ new Set();
|
|
4251
4377
|
const members = /* @__PURE__ */ new Set();
|
|
4252
4378
|
const notify = (change) => {
|
|
@@ -4379,17 +4505,27 @@ export {
|
|
|
4379
4505
|
straddles,
|
|
4380
4506
|
minContrastOver,
|
|
4381
4507
|
pickColorOver,
|
|
4382
|
-
deltaEHex,
|
|
4383
|
-
SAME_SHADE_DELTA_E,
|
|
4384
|
-
MIN_RAMP_FILLS,
|
|
4385
|
-
censusColourSpread,
|
|
4386
|
-
colourSpreadFlag,
|
|
4387
4508
|
AUTO_RAMP_CLASSES,
|
|
4388
4509
|
AUTO_RAMP_DRAWN_OPACITY,
|
|
4389
4510
|
AUTO_RAMP_MIN_STEP_DELTA_E,
|
|
4390
4511
|
autoRampMinStep,
|
|
4391
4512
|
autoRampCanvas,
|
|
4392
4513
|
resolveAutoColorScale,
|
|
4514
|
+
SELECTION_MARKER_DEFAULT,
|
|
4515
|
+
MAP_SELECTION_MARKER_DEFAULT,
|
|
4516
|
+
readSelectionMarker,
|
|
4517
|
+
readMapSelectionMarker,
|
|
4518
|
+
REGION_JOIN_COLUMN,
|
|
4519
|
+
isRegionMapData,
|
|
4520
|
+
NEUTRAL_CENTRE_RAMP_SELECTOR,
|
|
4521
|
+
hasNeutralCentreRamp,
|
|
4522
|
+
selectionMarkerFor,
|
|
4523
|
+
SELECTION_OUTLINE_CLASS,
|
|
4524
|
+
isAreaMark,
|
|
4525
|
+
collectOutlineMarks,
|
|
4526
|
+
outlineWindowOf,
|
|
4527
|
+
controlBoxesIn,
|
|
4528
|
+
paintSelectionOutlines,
|
|
4393
4529
|
EXPORT_FORMATS,
|
|
4394
4530
|
XLSX_MIME,
|
|
4395
4531
|
CSV_MIME,
|
|
@@ -4409,6 +4545,8 @@ export {
|
|
|
4409
4545
|
resolveOptions,
|
|
4410
4546
|
buildRenderPayload,
|
|
4411
4547
|
stripJsComments,
|
|
4548
|
+
MARK_MOVE_ATTRIBUTES,
|
|
4549
|
+
followMarks,
|
|
4412
4550
|
censusMarks,
|
|
4413
4551
|
isBlankRender,
|
|
4414
4552
|
blankRenderFlag,
|