@bicharts/chart-host 0.6.88 → 0.6.89
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 +35 -0
- package/dist/adapters.mjs +1 -1
- package/dist/{chunk-HXH6NIRV.mjs → chunk-562LF7XM.mjs} +222 -2
- package/dist/{chunk-IUXNCX7D.mjs → chunk-DJ5IJV57.mjs} +7 -1
- package/dist/{chunk-56IZWXW2.mjs → chunk-GOIG756B.mjs} +1 -1
- package/dist/{chunk-IZXVV74H.mjs → chunk-OG6RR2AJ.mjs} +1 -1
- package/dist/index.mjs +3 -3
- package/dist/page.mjs +3 -3
- package/dist/react.mjs +8 -4
- package/dist/types/contract.d.ts +4 -0
- package/dist/types/host.d.ts +15 -0
- package/dist/types/react.d.ts +3 -0
- package/dist/types/selection.d.ts +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -277,6 +277,41 @@ stylesheet's `0.25`, or whatever your page sets on the variable.
|
|
|
277
277
|
createChartHost(el, { code, data, options: { dimOpacity: 0.15 } });
|
|
278
278
|
```
|
|
279
279
|
|
|
280
|
+
**A glyph on each selected mark.** `options.selectionGlyph` (`"dot"` or `"check"`; absent or
|
|
281
|
+
`"none"` draws nothing, the default) puts a small glyph on every selected mark, in a layer the host
|
|
282
|
+
owns inside the container (`.bic-selection-glyphs`, each glyph `.lch-sel-glyph`, never hit-tested).
|
|
283
|
+
It is painted after every selection change and, on a redraw, after the layout pass and the fit, so
|
|
284
|
+
nothing measures it as part of the chart. Only a mark the reader can see gets one: a mark hidden by
|
|
285
|
+
`visibility` or `opacity` (a legend toggle) and a mark outside its svg's frame or its plot's
|
|
286
|
+
`clip-path` (a point panned into the axis margin) get none. While a selection is lit, a mark that
|
|
287
|
+
moves or hides without a redraw (a pan or zoom, a transition, an animation frame, a legend toggle)
|
|
288
|
+
takes its glyph with it on the next frame; a write that holds no mark (a hover dot, a tooltip)
|
|
289
|
+
repaints nothing, and that repaint is not reported again. Where the browser has a `ResizeObserver`,
|
|
290
|
+
a container resized without a redraw (a responsive chart, a hidden tab shown) is repainted too, and
|
|
291
|
+
reported when it drew a different count. The layer clips at the container's scroll area, so a
|
|
292
|
+
glyph never widens what the container scrolls, and it sits under the note badges, under a chart's
|
|
293
|
+
own tooltip or control that sets a `z-index` above 1, and under a pinned axis, so a glyph on a row
|
|
294
|
+
scrolled beneath the axis is covered with its row. When the page left the container
|
|
295
|
+
`position: static`, the host makes it `relative` so the layer scrolls and clips with the marks, and
|
|
296
|
+
`destroy()` puts back what the page had. A period picked on a chart's own timeline dims nothing and
|
|
297
|
+
marks nothing.
|
|
298
|
+
|
|
299
|
+
Some charts never take a glyph: on a flow or relationship chart a selected series would get one on
|
|
300
|
+
every point. The generate answer says so (`suppressSelectionGlyph`, read by shape-core's
|
|
301
|
+
`parseGenerateResponse`); pass it as `options.suppressSelectionGlyph`, or leave it in the payload's
|
|
302
|
+
`meta` (`data.meta.suppressSelectionGlyph`) and the host promotes it, the way it promotes
|
|
303
|
+
`geoPoint`. Keep it with the chart's code, so a redraw from a cache honors it too.
|
|
304
|
+
`onSelectionGlyphs` reports what each paint did (`{ mode, drawn, total, suppressed }`), and is
|
|
305
|
+
called only when a glyph mode is set:
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
createChartHost(el, {
|
|
309
|
+
code, data,
|
|
310
|
+
options: { selectionGlyph: "dot", suppressSelectionGlyph: answer.suppressSelectionGlyph },
|
|
311
|
+
onSelectionGlyphs: r => console.debug("glyphs", r),
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
280
315
|
**Which marks count as selected.** By default a mark is selected when *any* of the rows in its
|
|
281
316
|
`data-row-idx` list is selected, which is what lights a legend swatch together with its series. A
|
|
282
317
|
chart whose marks share rows declares the other rule by stamping
|
package/dist/adapters.mjs
CHANGED
|
@@ -2,25 +2,28 @@
|
|
|
2
2
|
import {
|
|
3
3
|
LASSO_OUTLINE_CLASS,
|
|
4
4
|
installLasso
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OG6RR2AJ.mjs";
|
|
6
6
|
import {
|
|
7
7
|
censusHitBands,
|
|
8
8
|
ensureCrossfilterHitTargets
|
|
9
9
|
} from "./chunk-XNS34AMM.mjs";
|
|
10
10
|
import {
|
|
11
11
|
clearPress,
|
|
12
|
+
clearSelectionGlyphs,
|
|
12
13
|
clickWasDrag,
|
|
14
|
+
collectGlyphCentersWhere,
|
|
13
15
|
createMarkResolver,
|
|
14
16
|
isInsideControl,
|
|
15
17
|
markKeyOf,
|
|
16
18
|
nextClickedMarks,
|
|
17
19
|
nextSelection,
|
|
20
|
+
paintSelectionGlyphs,
|
|
18
21
|
planSelectionPaint,
|
|
19
22
|
sameRowSet,
|
|
20
23
|
selectionRuleOf,
|
|
21
24
|
trackPress,
|
|
22
25
|
validRowSet
|
|
23
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DJ5IJV57.mjs";
|
|
24
27
|
import {
|
|
25
28
|
clipWindowOf,
|
|
26
29
|
fitRenderedChart,
|
|
@@ -615,6 +618,14 @@ function resolveOptions(p) {
|
|
|
615
618
|
const n = numberOr(p.dimOpacity, NaN);
|
|
616
619
|
return Number.isFinite(n) ? clamp(n, DIM_OPACITY_MIN, 1) : void 0;
|
|
617
620
|
})(),
|
|
621
|
+
// A recognised glyph or none: an unknown string must never reach the painter as a live mode, and "none" is
|
|
622
|
+
// undefined rather than a value, so a host that passes nothing and one that passes "none" resolve alike.
|
|
623
|
+
selectionGlyph: (() => {
|
|
624
|
+
const v = (p.selectionGlyph == null ? "" : String(p.selectionGlyph)).toLowerCase();
|
|
625
|
+
return v === "dot" || v === "check" ? v : void 0;
|
|
626
|
+
})(),
|
|
627
|
+
// A host FACT from the generate answer, true only for a literal true: silence never suppresses anything.
|
|
628
|
+
suppressSelectionGlyph: p.suppressSelectionGlyph === true ? true : void 0,
|
|
618
629
|
onSelect: p.onSelect,
|
|
619
630
|
// Control gestures (1.10.0). Passed straight through like onSelect: the host owns what a gesture MEANS,
|
|
620
631
|
// and a host that supplies nothing leaves every control behaving exactly as before.
|
|
@@ -2208,12 +2219,209 @@ function createChartHost(container, config) {
|
|
|
2208
2219
|
} catch {
|
|
2209
2220
|
}
|
|
2210
2221
|
};
|
|
2222
|
+
const GLYPH_LAYER_CLASS = "bic-selection-glyphs";
|
|
2223
|
+
let glyphPositionWas = null;
|
|
2224
|
+
let glyphObserver = null;
|
|
2225
|
+
let glyphResize = null;
|
|
2226
|
+
let glyphSize = "";
|
|
2227
|
+
let glyphQueued = false;
|
|
2228
|
+
let glyphQueuedReport = false;
|
|
2229
|
+
let glyphReported = "";
|
|
2230
|
+
const sizeOfContainer = () => `${container.offsetWidth ?? 0}x${container.offsetHeight ?? 0}`;
|
|
2231
|
+
const glyphBlind = `.${GLYPH_LAYER_CLASS}, .${LASSO_OUTLINE_CLASS}`;
|
|
2232
|
+
const movesAMark = (r) => {
|
|
2233
|
+
const t = r.target;
|
|
2234
|
+
if (!t || t.closest?.(glyphBlind)) return false;
|
|
2235
|
+
return !!(t.classList?.contains(MARK_CLASS) || t.querySelector?.(`.${MARK_CLASS}`));
|
|
2236
|
+
};
|
|
2237
|
+
const queueGlyphs = (reportIfChanged) => {
|
|
2238
|
+
if (reportIfChanged) glyphQueuedReport = true;
|
|
2239
|
+
if (glyphQueued) return;
|
|
2240
|
+
glyphQueued = true;
|
|
2241
|
+
const run = () => {
|
|
2242
|
+
const r = glyphQueuedReport;
|
|
2243
|
+
glyphQueued = false;
|
|
2244
|
+
glyphQueuedReport = false;
|
|
2245
|
+
if (glyphObserver || glyphResize) paintGlyphLayer(r ? "if-changed" : false);
|
|
2246
|
+
};
|
|
2247
|
+
if (win?.requestAnimationFrame) win.requestAnimationFrame(run);
|
|
2248
|
+
else setTimeout(run, 16);
|
|
2249
|
+
};
|
|
2250
|
+
const followGlyphs = (on) => {
|
|
2251
|
+
if (!on) {
|
|
2252
|
+
if (glyphObserver) {
|
|
2253
|
+
glyphObserver.disconnect();
|
|
2254
|
+
glyphObserver = null;
|
|
2255
|
+
}
|
|
2256
|
+
if (glyphResize) {
|
|
2257
|
+
glyphResize.disconnect();
|
|
2258
|
+
glyphResize = null;
|
|
2259
|
+
}
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
const MO = win?.MutationObserver;
|
|
2263
|
+
if (!glyphObserver && MO) {
|
|
2264
|
+
glyphObserver = new MO((records) => {
|
|
2265
|
+
if (!glyphQueued && records.some(movesAMark)) queueGlyphs(false);
|
|
2266
|
+
});
|
|
2267
|
+
glyphObserver.observe(container, {
|
|
2268
|
+
subtree: true,
|
|
2269
|
+
attributes: true,
|
|
2270
|
+
attributeFilter: [
|
|
2271
|
+
"transform",
|
|
2272
|
+
"d",
|
|
2273
|
+
"x",
|
|
2274
|
+
"y",
|
|
2275
|
+
"cx",
|
|
2276
|
+
"cy",
|
|
2277
|
+
"points",
|
|
2278
|
+
"width",
|
|
2279
|
+
"height",
|
|
2280
|
+
"visibility",
|
|
2281
|
+
"opacity",
|
|
2282
|
+
"style",
|
|
2283
|
+
"class"
|
|
2284
|
+
]
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
const RO = win?.ResizeObserver;
|
|
2288
|
+
if (!glyphResize && RO) {
|
|
2289
|
+
glyphResize = new RO(() => {
|
|
2290
|
+
if (sizeOfContainer() !== glyphSize) queueGlyphs(true);
|
|
2291
|
+
});
|
|
2292
|
+
glyphResize.observe(container);
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
const markShown = (m) => {
|
|
2296
|
+
try {
|
|
2297
|
+
const cv = m.checkVisibility;
|
|
2298
|
+
if (typeof cv === "function") return cv.call(m, { visibilityProperty: true, opacityProperty: true }) !== false;
|
|
2299
|
+
const cs = win?.getComputedStyle;
|
|
2300
|
+
if (typeof cs !== "function") return true;
|
|
2301
|
+
const own = cs.call(win, m);
|
|
2302
|
+
if (own.visibility === "hidden" || own.visibility === "collapse") return false;
|
|
2303
|
+
for (let el = m; el && el !== container; el = el.parentElement) {
|
|
2304
|
+
const s = el === m ? own : cs.call(win, el);
|
|
2305
|
+
if (s.display === "none" || s.opacity !== "" && parseFloat(s.opacity) === 0) return false;
|
|
2306
|
+
}
|
|
2307
|
+
} catch {
|
|
2308
|
+
}
|
|
2309
|
+
return true;
|
|
2310
|
+
};
|
|
2311
|
+
const glyphWindows = () => {
|
|
2312
|
+
const windowOf = /* @__PURE__ */ new Map();
|
|
2313
|
+
const read = (el) => {
|
|
2314
|
+
if (!windowOf.has(el)) {
|
|
2315
|
+
let w = null;
|
|
2316
|
+
try {
|
|
2317
|
+
if ((el.tagName || "").toLowerCase() === "svg") {
|
|
2318
|
+
const r = el.getBoundingClientRect();
|
|
2319
|
+
if (r.width > 0 && r.height > 0) w = r;
|
|
2320
|
+
} else if (el.hasAttribute?.("clip-path")) w = clipWindowOf(el);
|
|
2321
|
+
} catch {
|
|
2322
|
+
w = null;
|
|
2323
|
+
}
|
|
2324
|
+
windowOf.set(el, w);
|
|
2325
|
+
}
|
|
2326
|
+
return windowOf.get(el) ?? null;
|
|
2327
|
+
};
|
|
2328
|
+
const EPS2 = 0.5;
|
|
2329
|
+
return (m, x, y) => {
|
|
2330
|
+
for (let el = m; el && el !== container; el = el.parentElement) {
|
|
2331
|
+
const w = read(el);
|
|
2332
|
+
if (w && (x < w.left - EPS2 || x > w.right + EPS2 || y < w.top - EPS2 || y > w.bottom + EPS2)) return false;
|
|
2333
|
+
}
|
|
2334
|
+
return true;
|
|
2335
|
+
};
|
|
2336
|
+
};
|
|
2337
|
+
const paintGlyphLayer = (report = true) => {
|
|
2338
|
+
if (destroyed) return;
|
|
2339
|
+
let follow = false;
|
|
2340
|
+
try {
|
|
2341
|
+
let layer = container.querySelector(`:scope > .${GLYPH_LAYER_CLASS}`);
|
|
2342
|
+
clearSelectionGlyphs(layer);
|
|
2343
|
+
const mode = resolved.selectionGlyph;
|
|
2344
|
+
if (mode !== "dot" && mode !== "check") {
|
|
2345
|
+
glyphReported = "";
|
|
2346
|
+
return;
|
|
2347
|
+
}
|
|
2348
|
+
const active = !!current && current.length > 0 && !selectionIsPeriod;
|
|
2349
|
+
if (!active) {
|
|
2350
|
+
glyphReported = "";
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
if (resolved.suppressSelectionGlyph) {
|
|
2354
|
+
if (report === true) try {
|
|
2355
|
+
config.onSelectionGlyphs?.({ mode, drawn: 0, total: 0, suppressed: true });
|
|
2356
|
+
} catch {
|
|
2357
|
+
}
|
|
2358
|
+
glyphReported = "";
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
follow = true;
|
|
2362
|
+
if (!layer) {
|
|
2363
|
+
layer = doc.createElement("div");
|
|
2364
|
+
layer.className = GLYPH_LAYER_CLASS;
|
|
2365
|
+
layer.setAttribute("aria-hidden", "true");
|
|
2366
|
+
Object.assign(layer.style, {
|
|
2367
|
+
position: "absolute",
|
|
2368
|
+
left: "0",
|
|
2369
|
+
top: "0",
|
|
2370
|
+
width: "0",
|
|
2371
|
+
height: "0",
|
|
2372
|
+
overflow: "hidden",
|
|
2373
|
+
pointerEvents: "none",
|
|
2374
|
+
zIndex: "1"
|
|
2375
|
+
});
|
|
2376
|
+
try {
|
|
2377
|
+
const pos = win?.getComputedStyle?.(container)?.position || container.style.position;
|
|
2378
|
+
if (!pos || pos === "static") {
|
|
2379
|
+
const was = container.style.position;
|
|
2380
|
+
container.style.position = "relative";
|
|
2381
|
+
if (glyphPositionWas === null) glyphPositionWas = was;
|
|
2382
|
+
}
|
|
2383
|
+
} catch {
|
|
2384
|
+
}
|
|
2385
|
+
container.appendChild(layer);
|
|
2386
|
+
}
|
|
2387
|
+
layer.style.width = "0";
|
|
2388
|
+
layer.style.height = "0";
|
|
2389
|
+
const sw = container.scrollWidth || 0, sh = container.scrollHeight || 0;
|
|
2390
|
+
layer.style.width = `${sw}px`;
|
|
2391
|
+
layer.style.height = `${sh}px`;
|
|
2392
|
+
glyphSize = sizeOfContainer();
|
|
2393
|
+
const centers = [];
|
|
2394
|
+
collectGlyphCentersWhere(container, layer.getBoundingClientRect(), centers, { shown: markShown, at: glyphWindows() });
|
|
2395
|
+
const hc = !!resolved.isHighContrast;
|
|
2396
|
+
const drawn = centers.length ? paintSelectionGlyphs(layer, centers, {
|
|
2397
|
+
symbol: mode === "check" ? "\u2713" : "\u25CF",
|
|
2398
|
+
color: hc ? resolved.themeFg || "#111" : "#111",
|
|
2399
|
+
halo: hc ? resolved.themeBg || "#fff" : "#fff"
|
|
2400
|
+
}) : 0;
|
|
2401
|
+
const said = `${mode}:${drawn}:${centers.length}`;
|
|
2402
|
+
if (report === true || report === "if-changed" && said !== glyphReported) {
|
|
2403
|
+
glyphReported = said;
|
|
2404
|
+
try {
|
|
2405
|
+
config.onSelectionGlyphs?.({ mode, drawn, total: centers.length, suppressed: false });
|
|
2406
|
+
} catch {
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
} catch {
|
|
2410
|
+
} finally {
|
|
2411
|
+
try {
|
|
2412
|
+
followGlyphs(follow);
|
|
2413
|
+
glyphObserver?.takeRecords();
|
|
2414
|
+
} catch {
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2211
2418
|
const notify = (rowIdxs, source, tick, fromChart = false) => {
|
|
2212
2419
|
current = rowIdxs;
|
|
2213
2420
|
selectionFromChart = rowIdxs.length > 0 && fromChart;
|
|
2214
2421
|
activeTickEl = rowIdxs.length ? tick ?? null : null;
|
|
2215
2422
|
selectionIsPeriod = rowIdxs.length > 0 && periodTickSuppressesFeedback(!!tick, chartOwnsTimeline(container));
|
|
2216
2423
|
paintSelection();
|
|
2424
|
+
paintGlyphLayer();
|
|
2217
2425
|
for (const cb of subs) {
|
|
2218
2426
|
try {
|
|
2219
2427
|
cb(rowIdxs, source);
|
|
@@ -2411,6 +2619,7 @@ function createChartHost(container, config) {
|
|
|
2411
2619
|
drawAnnotations();
|
|
2412
2620
|
} catch {
|
|
2413
2621
|
}
|
|
2622
|
+
paintGlyphLayer();
|
|
2414
2623
|
try {
|
|
2415
2624
|
lasso = installLasso({
|
|
2416
2625
|
container,
|
|
@@ -2437,6 +2646,7 @@ function createChartHost(container, config) {
|
|
|
2437
2646
|
if (destroyed) return;
|
|
2438
2647
|
const seq2 = ++renderSeq;
|
|
2439
2648
|
stopAnim();
|
|
2649
|
+
followGlyphs(false);
|
|
2440
2650
|
disposeLasso();
|
|
2441
2651
|
flushZoom();
|
|
2442
2652
|
container.innerHTML = "";
|
|
@@ -2461,6 +2671,8 @@ function createChartHost(container, config) {
|
|
|
2461
2671
|
if (resolved.geoPoint === void 0 && data.geoPoint) resolved = { ...resolved, geoPoint: data.geoPoint };
|
|
2462
2672
|
if (resolved.geoPointDest === void 0 && data.geoPointDest) resolved = { ...resolved, geoPointDest: data.geoPointDest };
|
|
2463
2673
|
if (resolved.dateCellsAreUtcDays === void 0 && data.dateCellsAreUtcDays) resolved = { ...resolved, dateCellsAreUtcDays: true };
|
|
2674
|
+
if (resolved.suppressSelectionGlyph === void 0 && data.meta?.suppressSelectionGlyph === true)
|
|
2675
|
+
resolved = { ...resolved, suppressSelectionGlyph: true };
|
|
2464
2676
|
let outcome;
|
|
2465
2677
|
zoomCap = null;
|
|
2466
2678
|
if (zoomMemory && config.code && !config.renderFn) {
|
|
@@ -2606,7 +2818,15 @@ function createChartHost(container, config) {
|
|
|
2606
2818
|
annotationLayer.destroy();
|
|
2607
2819
|
unpinScrolledAxis(container);
|
|
2608
2820
|
flushZoom();
|
|
2821
|
+
followGlyphs(false);
|
|
2609
2822
|
container.innerHTML = "";
|
|
2823
|
+
if (glyphPositionWas !== null) {
|
|
2824
|
+
try {
|
|
2825
|
+
if (container.style.position === "relative") container.style.position = glyphPositionWas;
|
|
2826
|
+
} catch {
|
|
2827
|
+
}
|
|
2828
|
+
glyphPositionWas = null;
|
|
2829
|
+
}
|
|
2610
2830
|
}
|
|
2611
2831
|
};
|
|
2612
2832
|
return host;
|
|
@@ -304,8 +304,12 @@ function createMarkResolver(env) {
|
|
|
304
304
|
return { isInvisibleStrokePath, distToPathCenterline, refineHitCorridor, findMark, rowIdxsFromMark, penetrateOverlayAt, resolveByGeometry };
|
|
305
305
|
}
|
|
306
306
|
function collectD3GlyphCenters(container, baseRect, out) {
|
|
307
|
+
collectGlyphCentersWhere(container, baseRect, out);
|
|
308
|
+
}
|
|
309
|
+
function collectGlyphCentersWhere(container, baseRect, out, filter) {
|
|
307
310
|
const Point = container.ownerDocument?.defaultView?.DOMPoint ?? globalThis.DOMPoint;
|
|
308
|
-
|
|
311
|
+
let marks = container.querySelectorAll(`.${MARK_CLASS}.${MARK_SELECTED_CLASS}`);
|
|
312
|
+
if (filter?.shown) marks = Array.from(marks).filter((m) => filter.shown(m));
|
|
309
313
|
const rects = [];
|
|
310
314
|
for (let i = 0; i < marks.length; i++) {
|
|
311
315
|
const r = marks[i].getBoundingClientRect();
|
|
@@ -385,6 +389,7 @@ function collectD3GlyphCenters(container, baseRect, out) {
|
|
|
385
389
|
sx = isRect ? r.left + r.width * 0.8 : r.left + r.width / 2;
|
|
386
390
|
sy = isRect ? r.top + r.height * 0.2 : r.top + r.height / 2;
|
|
387
391
|
}
|
|
392
|
+
if (filter?.at && !filter.at(el, sx, sy)) continue;
|
|
388
393
|
out.push({ x: sx - baseRect.left, y: sy - baseRect.top });
|
|
389
394
|
}
|
|
390
395
|
}
|
|
@@ -439,6 +444,7 @@ export {
|
|
|
439
444
|
clickWasDrag,
|
|
440
445
|
createMarkResolver,
|
|
441
446
|
collectD3GlyphCenters,
|
|
447
|
+
collectGlyphCentersWhere,
|
|
442
448
|
SELECTION_GLYPH_CLASS,
|
|
443
449
|
clearSelectionGlyphs,
|
|
444
450
|
paintSelectionGlyphs
|
|
@@ -1,7 +1,7 @@
|
|
|
1
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
2
|
import {
|
|
3
3
|
LASSO_KNOB_KEY
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OG6RR2AJ.mjs";
|
|
5
5
|
|
|
6
6
|
// src/filterScope.ts
|
|
7
7
|
function viewToken(view) {
|
package/dist/index.mjs
CHANGED
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
toRGBA,
|
|
98
98
|
toSourceRows,
|
|
99
99
|
valuePlacementFlag
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-562LF7XM.mjs";
|
|
101
101
|
import {
|
|
102
102
|
LASSO_AXES_SLOT,
|
|
103
103
|
LASSO_COUNT_CLASS,
|
|
@@ -122,7 +122,7 @@ import {
|
|
|
122
122
|
lassoVerdict,
|
|
123
123
|
pointInShape,
|
|
124
124
|
registerLassoAction
|
|
125
|
-
} from "./chunk-
|
|
125
|
+
} from "./chunk-OG6RR2AJ.mjs";
|
|
126
126
|
import {
|
|
127
127
|
MIN_HIT_BAND_PX,
|
|
128
128
|
censusHitBands,
|
|
@@ -153,7 +153,7 @@ import {
|
|
|
153
153
|
selectionRuleOf,
|
|
154
154
|
trackPress,
|
|
155
155
|
validRowSet
|
|
156
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-DJ5IJV57.mjs";
|
|
157
157
|
import {
|
|
158
158
|
AXIS_PIN_BAND_PAD_PX,
|
|
159
159
|
AXIS_PIN_MAX_BAND_FRACTION,
|
package/dist/page.mjs
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
readControls,
|
|
15
15
|
viewFromToken,
|
|
16
16
|
viewToken
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-GOIG756B.mjs";
|
|
18
|
+
import "./chunk-OG6RR2AJ.mjs";
|
|
19
|
+
import "./chunk-DJ5IJV57.mjs";
|
|
20
20
|
import "./chunk-YDHXOIAI.mjs";
|
|
21
21
|
import "./chunk-GHODWOAL.mjs";
|
|
22
22
|
export {
|
package/dist/react.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
payloadRowReader,
|
|
14
14
|
viewFromToken,
|
|
15
15
|
viewToken
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-GOIG756B.mjs";
|
|
17
17
|
import {
|
|
18
18
|
assembleD3,
|
|
19
19
|
createChartGroup,
|
|
@@ -22,10 +22,10 @@ import {
|
|
|
22
22
|
loadGeo,
|
|
23
23
|
syncMemberSelection,
|
|
24
24
|
toSourceRows
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
25
|
+
} from "./chunk-562LF7XM.mjs";
|
|
26
|
+
import "./chunk-OG6RR2AJ.mjs";
|
|
27
27
|
import "./chunk-XNS34AMM.mjs";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-DJ5IJV57.mjs";
|
|
29
29
|
import "./chunk-QHIPRAZK.mjs";
|
|
30
30
|
import "./chunk-KQN5C7SZ.mjs";
|
|
31
31
|
import "./chunk-A2GMXZP7.mjs";
|
|
@@ -70,6 +70,7 @@ function BicChart(props) {
|
|
|
70
70
|
zoomMemory,
|
|
71
71
|
labelContrast,
|
|
72
72
|
onLabelContrast,
|
|
73
|
+
onSelectionGlyphs,
|
|
73
74
|
layoutPass,
|
|
74
75
|
onInvalidSentinel,
|
|
75
76
|
id,
|
|
@@ -116,6 +117,8 @@ function BicChart(props) {
|
|
|
116
117
|
onSelectRef.current = onSelect;
|
|
117
118
|
const onLabelContrastRef = useRef(onLabelContrast);
|
|
118
119
|
onLabelContrastRef.current = onLabelContrast;
|
|
120
|
+
const onSelectionGlyphsRef = useRef(onSelectionGlyphs);
|
|
121
|
+
onSelectionGlyphsRef.current = onSelectionGlyphs;
|
|
119
122
|
const onInvalidSentinelRef = useRef(onInvalidSentinel);
|
|
120
123
|
onInvalidSentinelRef.current = onInvalidSentinel;
|
|
121
124
|
const onAnnotationClickRef = useRef(onAnnotationClick);
|
|
@@ -155,6 +158,7 @@ function BicChart(props) {
|
|
|
155
158
|
labelContrast,
|
|
156
159
|
layoutPass,
|
|
157
160
|
onLabelContrast: (r) => onLabelContrastRef.current?.(r),
|
|
161
|
+
onSelectionGlyphs: (r) => onSelectionGlyphsRef.current?.(r),
|
|
158
162
|
// A GETTER, not a wrapper: the host decides whether to
|
|
159
163
|
// swallow the sentinel throw by whether this is SET, so
|
|
160
164
|
// an always-present wrapper would hide the throw from a
|
package/dist/types/contract.d.ts
CHANGED
|
@@ -163,6 +163,10 @@ export interface RenderOptions {
|
|
|
163
163
|
/** How far a selection dims the marks outside it, as a share of full opacity (DIM_OPACITY_MIN-1). Absent: the
|
|
164
164
|
* stylesheet decides - DIM_OPACITY_DEFAULT, or whatever the page sets on --lch-dim-opacity. */
|
|
165
165
|
dimOpacity?: number;
|
|
166
|
+
/** A small glyph on each selected mark: "dot" or "check". Absent or "none": no glyph (the default). */
|
|
167
|
+
selectionGlyph?: "none" | "dot" | "check";
|
|
168
|
+
/** A host FACT from the generate answer: this chart's family never takes a selection glyph. */
|
|
169
|
+
suppressSelectionGlyph?: boolean;
|
|
166
170
|
animAutoPlay?: boolean;
|
|
167
171
|
animPlaySpeedMs?: number;
|
|
168
172
|
animLoopDelaySec?: number;
|
package/dist/types/host.d.ts
CHANGED
|
@@ -62,6 +62,9 @@ export interface ChartHostConfig {
|
|
|
62
62
|
meta?: {
|
|
63
63
|
hostContract?: string;
|
|
64
64
|
chart?: string;
|
|
65
|
+
/** The generate answer's fact that this chart's family never takes a selection glyph. Promoted into
|
|
66
|
+
* options.suppressSelectionGlyph, so a host that hands over the whole payload honors it unasked. */
|
|
67
|
+
suppressSelectionGlyph?: boolean;
|
|
65
68
|
};
|
|
66
69
|
};
|
|
67
70
|
/** Generated render() source — compiled exactly like the visual/preview/exec-gate. */
|
|
@@ -199,6 +202,18 @@ export interface ChartHostConfig {
|
|
|
199
202
|
labelContrast?: boolean | LabelContrastOptions;
|
|
200
203
|
/** What the label-contrast pass did after each render - counts, so a host can log them. */
|
|
201
204
|
onLabelContrast?: (report: LabelContrastReport) => void;
|
|
205
|
+
/**
|
|
206
|
+
* What the selection-glyph pass did after each paint of an active selection - counts, so a host can log them.
|
|
207
|
+
* Called only when options.selectionGlyph is "dot" or "check"; `suppressed` is true when the chart's family never
|
|
208
|
+
* takes a glyph (options.suppressSelectionGlyph, or the same fact in data.meta). A repaint that only follows marks
|
|
209
|
+
* moving without a render (a pan, a transition) is not reported again.
|
|
210
|
+
*/
|
|
211
|
+
onSelectionGlyphs?: (report: {
|
|
212
|
+
mode: "dot" | "check";
|
|
213
|
+
drawn: number;
|
|
214
|
+
total: number;
|
|
215
|
+
suppressed: boolean;
|
|
216
|
+
}) => void;
|
|
202
217
|
/**
|
|
203
218
|
* A LAYOUT PASS the host brings, run after every render (both the synchronous and the promise lane, and every
|
|
204
219
|
* setOptions / setData re-render) AFTER the label-contrast pass and BEFORE the hit-target heal, the selection
|
package/dist/types/react.d.ts
CHANGED
|
@@ -48,6 +48,9 @@ export interface BicChartProps {
|
|
|
48
48
|
labelContrast?: ChartHostConfig["labelContrast"];
|
|
49
49
|
/** What the label-contrast pass did after each render - counts, for a page that logs. */
|
|
50
50
|
onLabelContrast?: ChartHostConfig["onLabelContrast"];
|
|
51
|
+
/** What the selection-glyph pass did after each paint, when `options.selectionGlyph` is set - counts, for a page
|
|
52
|
+
* that logs. The glyph itself, the dim level and the suppression fact ride the `options` prop. */
|
|
53
|
+
onSelectionGlyphs?: ChartHostConfig["onSelectionGlyphs"];
|
|
51
54
|
/**
|
|
52
55
|
* A LAYOUT PASS run after every render, after label contrast and before the fit - e.g.
|
|
53
56
|
* `createLegendFitPass()` from "@bicharts/chart-host/legend-fit", which slides a legend clear of
|
|
@@ -169,6 +169,18 @@ export declare function collectD3GlyphCenters(container: Element, baseRect: {
|
|
|
169
169
|
left: number;
|
|
170
170
|
top: number;
|
|
171
171
|
}, out: GlyphCenter[]): void;
|
|
172
|
+
/** Which selected marks get a glyph, for a host that draws its own layer: `shown` drops a mark before anything is
|
|
173
|
+
* measured against it (a mark the reader can't see), `at` drops one by where its glyph would go, in client
|
|
174
|
+
* coordinates (a mark panned or zoomed out of its plot). Either absent keeps every mark. */
|
|
175
|
+
export interface GlyphCenterFilter {
|
|
176
|
+
shown?(mark: Element): boolean;
|
|
177
|
+
at?(mark: Element, clientX: number, clientY: number): boolean;
|
|
178
|
+
}
|
|
179
|
+
/** collectD3GlyphCenters with a filter. Package-internal: the exported function keeps every mark, as it always has. */
|
|
180
|
+
export declare function collectGlyphCentersWhere(container: Element, baseRect: {
|
|
181
|
+
left: number;
|
|
182
|
+
top: number;
|
|
183
|
+
}, out: GlyphCenter[], filter?: GlyphCenterFilter): void;
|
|
172
184
|
/** The class every painted selection glyph carries. */
|
|
173
185
|
export declare const SELECTION_GLYPH_CLASS = "lch-sel-glyph";
|
|
174
186
|
/** Removes every glyph (every child) from the overlay. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-host",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.89",
|
|
4
4
|
"description": "Run a BIC-generated D3 chart in any web host: compiles the generated render() function, applies the shared option defaults, resolves mark clicks (through tooltip overlays), owns the selection affordance, and translates row indices between cross-filtered charts. The same contract the BIC Power BI visual implements, minus Power BI. React bindings at @bicharts/chart-host/react.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|