@bicharts/chart-host 0.6.113 → 0.6.115

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/index.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  formatSourceDateFor,
6
6
  isExcelDateFormat,
7
7
  normaliseAggregation
8
- } from "./chunk-OLCUKXKL.mjs";
8
+ } from "./chunk-YSRMSDRP.mjs";
9
9
  import {
10
10
  captureSvgSnapshot,
11
11
  chartSvgOf,
@@ -15,7 +15,7 @@ import {
15
15
  svgNaturalSize,
16
16
  svgToDataUrl,
17
17
  tableHostOf
18
- } from "./chunk-PELRAFYZ.mjs";
18
+ } from "./chunk-QLIRQJ6B.mjs";
19
19
  import {
20
20
  ANNOTATION_CLASS,
21
21
  ANNOTATION_LAYER_CLASS,
@@ -35,15 +35,21 @@ import {
35
35
  LABEL_CONTRAST_DONE_ATTR,
36
36
  LABEL_PILL_CLASS,
37
37
  LIGHT_TEXT,
38
+ MAP_SELECTION_MARKER_DEFAULT,
39
+ MARK_MOVE_ATTRIBUTES,
38
40
  MIN_CONTRAST,
39
41
  MIN_RAMP_FILLS,
42
+ NEUTRAL_CENTRE_RAMP_SELECTOR,
40
43
  PAGE_MATCH_TOLERANCE,
41
44
  PILL_MIN_ALPHA,
42
45
  PILL_MIN_COVERAGE,
43
46
  PILL_OPAQUE_ALPHA,
47
+ REGION_JOIN_COLUMN,
44
48
  ROW_GROWTH_MIN_EXTRA,
45
49
  ROW_GROWTH_MIN_RATIO,
46
50
  SAME_SHADE_DELTA_E,
51
+ SELECTION_MARKER_DEFAULT,
52
+ SELECTION_OUTLINE_CLASS,
47
53
  STRADDLE_COLS,
48
54
  STRADDLE_MIN_SHARE,
49
55
  STRADDLE_PILL_ALPHA,
@@ -72,10 +78,12 @@ import {
72
78
  cleanHostSelection,
73
79
  clippedTextFlag,
74
80
  codeHasKeyboardStamp,
81
+ collectOutlineMarks,
75
82
  colourSpreadFlag,
76
83
  compileRenderFn,
77
84
  compositeOver,
78
85
  contrastRatio,
86
+ controlBoxesIn,
79
87
  createChartGroup,
80
88
  createChartHost,
81
89
  decideLabelColor,
@@ -86,17 +94,25 @@ import {
86
94
  exportFileName,
87
95
  exportFormatsFor,
88
96
  exportTableService,
97
+ followMarks,
89
98
  glyphSampleGrid,
99
+ hasNeutralCentreRamp,
90
100
  installKeyboard,
91
101
  invalidSentinelReason,
102
+ isAreaMark,
92
103
  isBlankRender,
93
104
  isInvalidSentinelError,
94
105
  isPillBackdropAlpha,
106
+ isRegionMapData,
95
107
  minContrastOver,
96
108
  newIdScope,
97
109
  noopViewStateProvider,
110
+ outlineWindowOf,
111
+ paintSelectionOutlines,
98
112
  pickColorOver,
99
113
  pillBacksGlyph,
114
+ readMapSelectionMarker,
115
+ readSelectionMarker,
100
116
  relativeLuminance,
101
117
  rememberFocus,
102
118
  requiredD3Plugins,
@@ -106,6 +122,7 @@ import {
106
122
  rowGrowthNotice,
107
123
  rowNameOf,
108
124
  scopeChartIds,
125
+ selectionMarkerFor,
109
126
  sessionViewStateProvider,
110
127
  sheetName,
111
128
  straddleSampleGrid,
@@ -119,7 +136,7 @@ import {
119
136
  toSourceRows,
120
137
  trackFocus,
121
138
  valuePlacementFlag
122
- } from "./chunk-TE54AFRN.mjs";
139
+ } from "./chunk-7K3F3RZ6.mjs";
123
140
  import {
124
141
  LASSO_AXES_SLOT,
125
142
  LASSO_COUNT_CLASS,
@@ -146,13 +163,13 @@ import {
146
163
  lassoVerdict,
147
164
  pointInShape,
148
165
  registerLassoAction
149
- } from "./chunk-IL5M7K6Z.mjs";
166
+ } from "./chunk-CVTCR7HQ.mjs";
150
167
  import {
151
168
  MIN_HIT_BAND_PX,
152
169
  censusHitBands,
153
170
  ensureCrossfilterHitTargets,
154
171
  hitBandFlag
155
- } from "./chunk-AEO5SFEQ.mjs";
172
+ } from "./chunk-YOCYWON4.mjs";
156
173
  import {
157
174
  CONTAINER_SLOT_PRESS_AT,
158
175
  DRAG_NOT_CLICK_PX,
@@ -177,7 +194,7 @@ import {
177
194
  selectionRuleOf,
178
195
  trackPress,
179
196
  validRowSet
180
- } from "./chunk-KGVNZR23.mjs";
197
+ } from "./chunk-PQKLWYKK.mjs";
181
198
  import {
182
199
  AXIS_PIN_BAND_PAD_PX,
183
200
  AXIS_PIN_MAX_BAND_FRACTION,
@@ -206,7 +223,7 @@ import {
206
223
  svgInkReach,
207
224
  takeScroll,
208
225
  unpinScrolledAxis
209
- } from "./chunk-7XT5OJ6M.mjs";
226
+ } from "./chunk-AQO4ROMU.mjs";
210
227
  import {
211
228
  clearGeoCache,
212
229
  geoAssetFor,
@@ -219,7 +236,7 @@ import {
219
236
  Ot,
220
237
  l,
221
238
  o
222
- } from "./chunk-FIG5DIW7.mjs";
239
+ } from "./chunk-VPFP35XI.mjs";
223
240
  import "./chunk-A2GMXZP7.mjs";
224
241
  import {
225
242
  ACTIVE_TICK_CLASS,
@@ -276,7 +293,7 @@ import {
276
293
  chartOwnsTimeline,
277
294
  periodTickSuppressesFeedback,
278
295
  withoutViewOnlyKeys
279
- } from "./chunk-KY4FI2HA.mjs";
296
+ } from "./chunk-GXR7T6G5.mjs";
280
297
  import "./chunk-GHODWOAL.mjs";
281
298
 
282
299
  // src/hostLinks.ts
@@ -2411,9 +2428,11 @@ export {
2411
2428
  LIGHT_TEXT,
2412
2429
  LIMIT_COUNT_MAX,
2413
2430
  LIVE_OPTION_KEYS,
2431
+ MAP_SELECTION_MARKER_DEFAULT,
2414
2432
  MARKS_INERT_CLASS,
2415
2433
  MARK_CLASS,
2416
2434
  MARK_KEY_ATTR,
2435
+ MARK_MOVE_ATTRIBUTES,
2417
2436
  MARK_SELECTED_CLASS,
2418
2437
  MAX_FRAME_GROW_FACTOR,
2419
2438
  MAX_MAP_POINTS_DEFAULT,
@@ -2423,6 +2442,7 @@ export {
2423
2442
  MIN_DELTA_E,
2424
2443
  MIN_HIT_BAND_PX,
2425
2444
  MIN_RAMP_FILLS,
2445
+ NEUTRAL_CENTRE_RAMP_SELECTOR,
2426
2446
  NOTE_STYLE_ATTR,
2427
2447
  NO_ROW_MARKS_ATTR,
2428
2448
  OPTIONS_VOCABULARY,
@@ -2445,6 +2465,7 @@ export {
2445
2465
  QUALIFY_TILE_REFUSAL_CODES,
2446
2466
  QUALIFY_TOO_SMALL_HEADING,
2447
2467
  RECOVERY_UNREACHABLE_MESSAGE,
2468
+ REGION_JOIN_COLUMN,
2448
2469
  ROW_GROWTH_MIN_EXTRA,
2449
2470
  ROW_GROWTH_MIN_RATIO,
2450
2471
  ROW_IDX_ATTR,
@@ -2454,6 +2475,8 @@ export {
2454
2475
  SEASONAL_MARKERS_DEFAULT,
2455
2476
  SELECTION_ACTIVE_CLASS,
2456
2477
  SELECTION_GLYPH_CLASS,
2478
+ SELECTION_MARKER_DEFAULT,
2479
+ SELECTION_OUTLINE_CLASS,
2457
2480
  SELECTION_RULE_ATTR,
2458
2481
  SELECTION_RULE_CLICKED_MARKS,
2459
2482
  SNAPSHOT_SVG_ATTR,
@@ -2512,6 +2535,7 @@ export {
2512
2535
  codeReadsDatesInLocalTime,
2513
2536
  codeReadsOption,
2514
2537
  collectD3GlyphCenters,
2538
+ collectOutlineMarks,
2515
2539
  colourSpreadFlag,
2516
2540
  compileRenderFn,
2517
2541
  compileTrivialSource,
@@ -2522,6 +2546,7 @@ export {
2522
2546
  confirmLaunch,
2523
2547
  contentExtentOf,
2524
2548
  contrastRatio,
2549
+ controlBoxesIn,
2525
2550
  createChartGroup,
2526
2551
  createChartHost,
2527
2552
  createMarkResolver,
@@ -2546,6 +2571,7 @@ export {
2546
2571
  fingerprintComponentDigest,
2547
2572
  fitReadingFor,
2548
2573
  fitRenderedChart,
2574
+ followMarks,
2549
2575
  formatSourceDate,
2550
2576
  formatSourceDateFor,
2551
2577
  generationCancelledMessage,
@@ -2553,6 +2579,7 @@ export {
2553
2579
  geoFromCache,
2554
2580
  gestureWasDrag,
2555
2581
  glyphSampleGrid,
2582
+ hasNeutralCentreRamp,
2556
2583
  hasRefusalsToShow,
2557
2584
  hitBandFlag,
2558
2585
  hostLinkServices,
@@ -2562,6 +2589,7 @@ export {
2562
2589
  installKeyboard,
2563
2590
  installLasso,
2564
2591
  invalidSentinelReason,
2592
+ isAreaMark,
2565
2593
  isBlankRender,
2566
2594
  isBlindMarker,
2567
2595
  isDuplicateGenerate,
@@ -2574,6 +2602,7 @@ export {
2574
2602
  isPhantomBox,
2575
2603
  isPickableModelCode,
2576
2604
  isPillBackdropAlpha,
2605
+ isRegionMapData,
2577
2606
  labelBand,
2578
2607
  lassoActions,
2579
2608
  lassoEffect,
@@ -2605,7 +2634,9 @@ export {
2605
2634
  normalizeFilterTerm,
2606
2635
  orderMostDistinct,
2607
2636
  orderRefusalsForDisplay,
2637
+ outlineWindowOf,
2608
2638
  paintSelectionGlyphs,
2639
+ paintSelectionOutlines,
2609
2640
  parsePendingGenerate,
2610
2641
  pendingRecoveryNextDelayMs,
2611
2642
  pendingRecoveryRemainingMs,
@@ -2634,8 +2665,10 @@ export {
2634
2665
  qualifyRefusalReason,
2635
2666
  qualifyTableRows,
2636
2667
  rasterizeSvgToPngDataUrl,
2668
+ readMapSelectionMarker,
2637
2669
  readQualifyChartRow,
2638
2670
  readQualifyRefusalRow,
2671
+ readSelectionMarker,
2639
2672
  reconcileChartPick,
2640
2673
  reconcileModelPick,
2641
2674
  recoveryAnswerIsCancelled,
@@ -2666,6 +2699,7 @@ export {
2666
2699
  sameRowSet,
2667
2700
  scopeChartIds,
2668
2701
  scrollFitFor,
2702
+ selectionMarkerFor,
2669
2703
  selectionRuleOf,
2670
2704
  servedCorrelationProves,
2671
2705
  sessionViewStateProvider,
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  chartSvgOf,
4
4
  tableHostOf
5
- } from "./chunk-PELRAFYZ.mjs";
5
+ } from "./chunk-QLIRQJ6B.mjs";
6
6
  import {
7
7
  FIT_CONTENT_SELECTOR,
8
8
  ctmScaleOf,
9
9
  isPhantomBox
10
- } from "./chunk-7XT5OJ6M.mjs";
11
- import "./chunk-KY4FI2HA.mjs";
10
+ } from "./chunk-AQO4ROMU.mjs";
11
+ import "./chunk-GXR7T6G5.mjs";
12
12
  import "./chunk-GHODWOAL.mjs";
13
13
 
14
14
  // src/legendFit/legendReconcile.ts
package/dist/page.mjs CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  noteBadges,
7
7
  notesFor,
8
8
  readControls
9
- } from "./chunk-GTJG3SMD.mjs";
10
- import "./chunk-IL5M7K6Z.mjs";
11
- import "./chunk-KGVNZR23.mjs";
12
- import "./chunk-KY4FI2HA.mjs";
9
+ } from "./chunk-DILJDN6C.mjs";
10
+ import "./chunk-CVTCR7HQ.mjs";
11
+ import "./chunk-PQKLWYKK.mjs";
12
+ import "./chunk-GXR7T6G5.mjs";
13
13
  import {
14
14
  ANY_VALUE,
15
15
  LINKED_HOVER_CLASS,
@@ -20,9 +20,10 @@ import {
20
20
  fromVegaInteraction,
21
21
  isAnyValue,
22
22
  payloadRowReader,
23
+ scopeFilterOf,
23
24
  viewFromToken,
24
25
  viewToken
25
- } from "./chunk-2KLFIGTQ.mjs";
26
+ } from "./chunk-2GW3S5VD.mjs";
26
27
  import "./chunk-GHODWOAL.mjs";
27
28
  export {
28
29
  ANY_VALUE,
@@ -40,6 +41,7 @@ export {
40
41
  notesFor,
41
42
  payloadRowReader,
42
43
  readControls,
44
+ scopeFilterOf,
43
45
  viewFromToken,
44
46
  viewToken
45
47
  };
@@ -8,7 +8,7 @@ import {
8
8
  Rt,
9
9
  W,
10
10
  bt
11
- } from "./chunk-FIG5DIW7.mjs";
11
+ } from "./chunk-VPFP35XI.mjs";
12
12
  import "./chunk-A2GMXZP7.mjs";
13
13
  import "./chunk-GHODWOAL.mjs";
14
14
 
package/dist/react.mjs CHANGED
@@ -5,25 +5,25 @@ import {
5
5
  createControls,
6
6
  noteBadges,
7
7
  notesFor
8
- } from "./chunk-GTJG3SMD.mjs";
8
+ } from "./chunk-DILJDN6C.mjs";
9
9
  import {
10
10
  assembleD3,
11
11
  createChartGroup,
12
12
  createChartHost,
13
13
  syncMemberSelection,
14
14
  toSourceRows
15
- } from "./chunk-TE54AFRN.mjs";
16
- import "./chunk-IL5M7K6Z.mjs";
17
- import "./chunk-AEO5SFEQ.mjs";
18
- import "./chunk-KGVNZR23.mjs";
19
- import "./chunk-7XT5OJ6M.mjs";
15
+ } from "./chunk-7K3F3RZ6.mjs";
16
+ import "./chunk-CVTCR7HQ.mjs";
17
+ import "./chunk-YOCYWON4.mjs";
18
+ import "./chunk-PQKLWYKK.mjs";
19
+ import "./chunk-AQO4ROMU.mjs";
20
20
  import {
21
21
  geoFromCache,
22
22
  loadGeo
23
23
  } from "./chunk-5YSZABCS.mjs";
24
- import "./chunk-FIG5DIW7.mjs";
24
+ import "./chunk-VPFP35XI.mjs";
25
25
  import "./chunk-A2GMXZP7.mjs";
26
- import "./chunk-KY4FI2HA.mjs";
26
+ import "./chunk-GXR7T6G5.mjs";
27
27
  import {
28
28
  ANY_VALUE,
29
29
  LINKED_HOVER_CLASS,
@@ -34,9 +34,10 @@ import {
34
34
  fromVegaInteraction,
35
35
  isAnyValue,
36
36
  payloadRowReader,
37
+ scopeFilterOf,
37
38
  viewFromToken,
38
39
  viewToken
39
- } from "./chunk-2KLFIGTQ.mjs";
40
+ } from "./chunk-2GW3S5VD.mjs";
40
41
  import "./chunk-GHODWOAL.mjs";
41
42
 
42
43
  // src/react.tsx
@@ -96,7 +97,8 @@ function BicChart(props) {
96
97
  controls,
97
98
  ariaLabel
98
99
  } = props;
99
- const scopeList = scopedBy == null ? [] : Array.isArray(scopedBy) ? scopedBy : [scopedBy];
100
+ const scopeSources = scopedBy == null ? [] : Array.isArray(scopedBy) ? scopedBy : [scopedBy];
101
+ const scopeList = scopeSources.map(scopeFilterOf).filter((f) => !!f);
100
102
  const scopeIds = scopeList.map((f) => f?.id).join("");
101
103
  const subscribeScope = useCallback((cb) => {
102
104
  const offs = scopeList.map((f) => f?.onChange?.(() => cb()));
@@ -108,7 +110,7 @@ function BicChart(props) {
108
110
  useSyncExternalStore(subscribeScope, scopeSnapshot, scopeSnapshot);
109
111
  const [scopeEver, setScopeEver] = useState(scopedBy !== void 0);
110
112
  if (scopedBy !== void 0 && !scopeEver) setScopeEver(true);
111
- const scopeFact = scopedBy === void 0 ? void 0 : filterScopeOf(scopeList);
113
+ const scopeFact = scopedBy === void 0 ? void 0 : filterScopeOf(scopeSources);
112
114
  const scopeKey = scopeFact ? JSON.stringify(scopeFact) : "";
113
115
  const options = useMemo(
114
116
  () => scopeEver ? { ...pageOptions ?? {}, filterScope: scopeFact ?? pageOptions?.filterScope } : pageOptions,
@@ -338,7 +340,8 @@ function useBicFilter(columns, opts) {
338
340
  if (!ref.current) ref.current = createFilter(columns, opts);
339
341
  const f = ref.current;
340
342
  useEffect(() => scope.add(f), [scope, f]);
341
- useSyncExternalStore((cb) => f.onChange(() => cb()), () => f.version, () => f.version);
343
+ const snapshot = () => `${f.version}:${f.pick?.id ?? ""}:${f.pick?.version ?? ""}`;
344
+ useSyncExternalStore((cb) => f.onPick ? f.onPick(() => cb()) : f.onChange(() => cb()), snapshot, snapshot);
342
345
  return f;
343
346
  }
344
347
  function useBicControls(initial, opts) {
package/dist/testing.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  MARK_CLASS,
5
5
  MARK_SELECTED_CLASS,
6
6
  ROW_IDX_ATTR
7
- } from "./chunk-KY4FI2HA.mjs";
7
+ } from "./chunk-GXR7T6G5.mjs";
8
8
  import "./chunk-GHODWOAL.mjs";
9
9
 
10
10
  // src/testing/interact.ts
@@ -1,5 +1,5 @@
1
1
  import type { ExportFormat, ExportTableService, SaveFileService } from "./exportTable";
2
- export declare const HOST_CONTRACT_VERSION = "1.23.0";
2
+ export declare const HOST_CONTRACT_VERSION = "1.24.0";
3
3
  export type GeoPointPrecision = "latlon" | "city" | "zip3" | "state" | "country";
4
4
  /** Every valid tier, ordered most precise → coarsest. Runtime form of GeoPointPrecision. */
5
5
  export declare const GEO_POINT_PRECISIONS: readonly GeoPointPrecision[];
@@ -206,8 +206,19 @@ export interface RenderOptions {
206
206
  /** How far a selection dims the marks outside it, as a share of full opacity (DIM_OPACITY_MIN-1). Absent: the
207
207
  * stylesheet decides - DIM_OPACITY_DEFAULT, or whatever the page sets on --lch-dim-opacity. */
208
208
  dimOpacity?: number;
209
- /** A small glyph on each selected mark: "dot" or "check". Absent or "none": no glyph (the default). */
210
- selectionGlyph?: "none" | "dot" | "check";
209
+ /** The SELECTION MARKER on every chart that is not a region map (the key keeps its first name, from when the only
210
+ * markers were glyphs): "auto" (the default; an outline on a chart whose colour ramp has a neutral centre, nothing
211
+ * elsewhere), "none", "dot" or "check" (a small glyph on each selected mark), or "outline" (a stroke in the text
212
+ * colour around each selected area mark). Resolved, it reads "dot", "check" or "outline" when one of those was
213
+ * chosen and undefined otherwise; `selectionMarker` holds the whole choice. */
214
+ selectionGlyph?: "auto" | "none" | "dot" | "check" | "outline";
215
+ /** The selection marker on a REGION MAP (a chart whose rows carry the host's region join column): "outline" (the
216
+ * default), "none", "dot" or "check". */
217
+ mapSelectionGlyph?: "none" | "dot" | "check" | "outline";
218
+ /** Resolved only: the whole choice `selectionGlyph` made, "auto" when it made none. */
219
+ selectionMarker?: "auto" | "none" | "dot" | "check" | "outline";
220
+ /** Resolved only: the whole choice `mapSelectionGlyph` made, "outline" when it made none. */
221
+ mapSelectionMarker?: "none" | "dot" | "check" | "outline";
211
222
  /** A host FACT from the generate answer: this chart's family never takes a selection glyph. */
212
223
  suppressSelectionGlyph?: boolean;
213
224
  /** A host FACT: this chart works from the keyboard (its code carries the keyboard stamp), so the host has made its
@@ -23,6 +23,15 @@ export interface FilterOptions {
23
23
  * chart bound to it (a glow, never a filter). Off by default.
24
24
  */
25
25
  hover?: boolean;
26
+ /**
27
+ * ONE PICK ACROSS SEVERAL PANELS. Filters with the same group name in one scope (one page) are one pick: a map, a
28
+ * table and a scatter that each pick a country, each with its own filter so each panel's query can leave its own pick
29
+ * out. A pick in one member clears the others, so the most recent pick is the page's one pick, and only the member
30
+ * holding it is active: one chip, one scope line, and a side list over the members' keys names that pick alone.
31
+ * Every member's chart shows the pick, so clicking the picked mark again in whichever panel shows it clears it.
32
+ * The members share their key columns; a filter keyed differently stays out of the group (said once in the console).
33
+ */
34
+ group?: string;
26
35
  }
27
36
  export interface Filter {
28
37
  readonly id: string;
@@ -56,9 +65,11 @@ export interface Filter {
56
65
  /**
57
66
  * The display text for ANY key - a badge's, a key set by value - read from `rows` (a table carrying the display
58
67
  * columns, e.g. the chart's own rows) or else from the rows this filter has seen; the key's values when neither
59
- * has it.
68
+ * has it. `parts` names the key columns to say (a query that maps only some of them, see ScopedFilter): a display
69
+ * column speaks for the key column it is, or for the key column in its place when there is one display column per
70
+ * key column; a wildcard part says nothing. Absent, or naming every key column: the whole key.
60
71
  */
61
- textOf(key: FilterKey | unknown, rows?: readonly Record<string, unknown>[]): string;
72
+ textOf(key: FilterKey | unknown, rows?: readonly Record<string, unknown>[], parts?: readonly string[]): string;
62
73
  /** Why the last change happened (null before any). */
63
74
  readonly reason: FilterChangeReason | null;
64
75
  /** Bumped on every change: a cheap identity for memoising on the filter's state. */
@@ -82,6 +93,15 @@ export interface Filter {
82
93
  keep<T extends Record<string, unknown>>(rows: readonly T[], columns?: readonly string[]): T[];
83
94
  /** Subscribe to changes. Returns the unsubscribe. */
84
95
  onChange(cb: (filter: Filter, reason: FilterChangeReason) => void): () => void;
96
+ /** Its group's name (FilterOptions.group), or null. */
97
+ readonly group?: string | null;
98
+ /**
99
+ * The filter holding the pick this one shows: the group member a reader last picked in, or this filter itself when it
100
+ * has no group; null while nothing is picked. A chart bound to a member paints this filter's keys.
101
+ */
102
+ readonly pick?: Filter | null;
103
+ /** Subscribe to the pick changing: this filter's, or any member's of its group. Returns the unsubscribe. */
104
+ onPick?(cb: () => void): () => void;
85
105
  }
86
106
  /** A page's filters: what a chip bar lists and what clear-all clears. */
87
107
  export interface FilterScope {
@@ -130,13 +150,32 @@ export declare function isAnyValue(v: unknown): boolean;
130
150
  export declare function createFilter(columns: string | readonly string[], opts?: FilterOptions): Filter;
131
151
  /** Make a page's filter scope. */
132
152
  export declare function createFilterScope(): FilterScope;
153
+ /**
154
+ * A FILTER AS ONE PANEL'S QUERY TAKES IT, for filterScopeOf and BicChart's scopedBy. A query that maps only some of a
155
+ * compound key's columns (treatAs(cell, "Accounts[Region]") on a Region x Stage pick) is narrowed by those parts alone,
156
+ * so its scope line names only them: `{ filter: cell, takes: ["Region"] }` says "Europe", never "Europe → 3-Propose".
157
+ */
158
+ export interface ScopedFilter {
159
+ filter: Filter;
160
+ /** The filter's key columns this panel's query maps (by name, as the filter is keyed). Absent: the whole key. */
161
+ takes?: readonly string[];
162
+ /** What the scope line calls it. Default: the filter's label, or the taken columns while the filter has no label of its own. */
163
+ label?: string;
164
+ }
165
+ /** What filterScopeOf and scopedBy take: a filter whose whole key the query maps, or a ScopedFilter. */
166
+ export type ScopeSource = Filter | ScopedFilter;
167
+ /** The filter behind a scope source, or null. */
168
+ export declare function scopeFilterOf(src: ScopeSource | null | undefined): Filter | null;
133
169
  /**
134
170
  * THE PAGE FILTERS THAT NARROWED A CHART'S ROWS, as the fact the chart is told (RenderOptions.filterScope, contract
135
171
  * 1.23.0). For filters applied to the chart's QUERY, so its rows arrive already narrowed and it can't see the filter.
136
172
  * Only the active ones count. The field is each one's label (its key columns when it has none), joined ", "; the values
137
173
  * are what its chip shows for each key, in order, each once. Nothing active, nothing to say: undefined.
174
+ *
175
+ * A ScopedFilter whose `takes` leaves key columns out says only the taken parts, as treatAs maps them: a pick whose
176
+ * taken parts are all wildcards keeps every row of that query, so while one is held the filter narrows nothing there.
138
177
  */
139
- export declare function filterScopeOf(filters: Filter | readonly Filter[] | null | undefined): FilterScopeFact | undefined;
178
+ export declare function filterScopeOf(filters: ScopeSource | readonly ScopeSource[] | null | undefined): FilterScopeFact | undefined;
140
179
  export interface FilterBinding {
141
180
  /**
142
181
  * Call after the chart is handed new data. The selection is repainted where its keys are drawn now, and a key the new
@@ -9,6 +9,9 @@ export interface SelectionChangeMeta {
9
9
  /** The pick was a category label (an axis label carrying its category's rows), not a mark: a page filter selects on
10
10
  * the label's own field (bindFilter), leaving the key columns the label doesn't name free. */
11
11
  fromLabel?: boolean;
12
+ /** The pick was ONE mark clicked on its own (no Ctrl/Shift, not a lasso): a page filter selects the key columns that
13
+ * mark's rows share, leaving the ones they differ on free (bindFilter), so a region row over its stages picks the region. */
14
+ fromMark?: boolean;
12
15
  }
13
16
  export interface SelectionPaintReport {
14
17
  rule: SelectionRule;
@@ -224,13 +227,14 @@ export interface ChartHostConfig {
224
227
  /** What the label-contrast pass did after each render - counts, so a host can log them. */
225
228
  onLabelContrast?: (report: LabelContrastReport) => void;
226
229
  /**
227
- * What the selection-glyph pass did after each paint of an active selection - counts, so a host can log them.
228
- * Called only when options.selectionGlyph is "dot" or "check"; `suppressed` is true when the chart's family never
229
- * takes a glyph (options.suppressSelectionGlyph, or the same fact in data.meta). A repaint that only follows marks
230
- * moving without a render (a pan, a transition) is not reported again.
230
+ * What the selection-marker pass did after each paint of an active selection - counts, so a host can log them.
231
+ * Called only when the chart's marker is "dot", "check" or "outline" (options.selectionGlyph, or on a region map
232
+ * options.mapSelectionGlyph; "auto" decided); `suppressed` is true when the chart's family never takes a marker
233
+ * (options.suppressSelectionGlyph, or the same fact in data.meta). For an outline, `total` counts the selected
234
+ * area marks. A repaint that only follows marks moving without a render (a pan, a transition) is not reported again.
231
235
  */
232
236
  onSelectionGlyphs?: (report: {
233
- mode: "dot" | "check";
237
+ mode: "dot" | "check" | "outline";
234
238
  drawn: number;
235
239
  total: number;
236
240
  suppressed: boolean;
@@ -16,8 +16,10 @@ export { rememberFocus, restoreFocus, trackFocus, type FocusMemory, type FocusTr
16
16
  export type { MarkNoteLike, NoteBadgeOptions } from "./markNotes";
17
17
  export type { Controls, ControlInfo, ControlsAttachment } from "./controls";
18
18
  export { createChartGroup, toSourceRows, syncMemberSelection, type ChartGroup, type ChartGroupMember, type ChartGroupSelection, type ChartGroupChange, type ChartGroupMemberOptions, type ChartGroupSourceOptions, type ChartGroupPayload } from "./group";
19
- export type { Filter, FilterScope, FilterOptions, FilterKey, FilterChangeReason, FilterBinding, BindFilterOptions, VegaInteractionEvent, PageView } from "./filterScope";
19
+ export type { Filter, FilterScope, FilterOptions, FilterKey, FilterChangeReason, FilterBinding, BindFilterOptions, VegaInteractionEvent, PageView, ScopedFilter, ScopeSource } from "./filterScope";
20
20
  export { createMarkResolver, isInsideControl, rowIdxsFromMark, nextSelection, DRAG_NOT_CLICK_PX, gestureWasDrag, trackPress, pressOf, clearPress, clickWasDrag, CONTAINER_SLOT_PRESS_AT, type PressPoint, dispatchRowSet, validRowSet, ROW_SET_MAX, selectionRuleOf, markKeyOf, planSelectionPaint, nextClickedMarks, sameRowSet, type SelectionRule, type ClickedMarks, type SelectionPaintMark, type SelectionPaintMode, type SelectionPaintPlan, collectD3GlyphCenters, clearSelectionGlyphs, paintSelectionGlyphs, SELECTION_GLYPH_CLASS, type MarkResolver, type MarkResolverEnv, type GlyphCenter, type SelectionGlyphStyle, type SelectionModifiers, } from "./selection";
21
+ export { readSelectionMarker, readMapSelectionMarker, selectionMarkerFor, SELECTION_MARKER_DEFAULT, MAP_SELECTION_MARKER_DEFAULT, REGION_JOIN_COLUMN, isRegionMapData, NEUTRAL_CENTRE_RAMP_SELECTOR, hasNeutralCentreRamp, isAreaMark, collectOutlineMarks, outlineWindowOf, controlBoxesIn, paintSelectionOutlines, SELECTION_OUTLINE_CLASS, type SelectionMarkerChoice, type MapSelectionMarkerChoice, type SelectionMarker, type SelectionOutlineStyle, type OutlineMarkFilter, } from "./selectionMarker";
22
+ export { followMarks, MARK_MOVE_ATTRIBUTES, type MarkFollower, type MarkFollowerOptions } from "./markFollower";
21
23
  export { installLasso, clearLasso, lassoVerdict, lassoRowCap, lassoActions, lassoEffect, registerLassoAction, pointInShape, LASSO_ROW_CAP, LASSO_DRAG_PX, LASSO_SAMPLE_PX, LASSO_MAX_VERTICES, LASSO_RESTORE_MAX_VERTICES, LASSO_RESTORE_MAX_SHAPES, LASSO_EVENT, LASSO_SURFACE_CLASS, LASSO_OUTLINE_CLASS, LASSO_COUNT_CLASS, LASSO_NOTE_CLASS, LASSO_PLOT_CLASS, LASSO_MODE_ATTR, LASSO_KNOB_KEY, LASSO_AXES_SLOT, LASSO_DEFAULT_ACTIONS, LASSO_DEFAULT_EFFECT, type LassoSetting, type LassoVerdict, type LassoEffect, type LassoMode, type LassoSpace, type LassoShape, type LassoStored, type LassoEventDetail, type LassoAxes, type LassoAction, type LassoActionContext, type LassoEnv, type LassoHandle, } from "./lasso";
22
24
  export type { AdapterEvent, AdapterHit, AdapterId, AdapterScene, MarkHandle, MarkInfo, MarkRole, PaintRequest, RendererAdapter, RowTable, } from "./adapters/types";
23
25
  export { planTrivialChart, compileTrivialSource, type TrivialPlan, type TrivialShapeKind } from "./trivial";
@@ -13,7 +13,7 @@ export interface KeyboardEnv {
13
13
  pressSelector: string;
14
14
  /** Elements never to treat as marks (the host's own layers). */
15
15
  skipSelector: string;
16
- /** True while a click on a mark selects nothing (the marks are then not made focusable). */
16
+ /** True while a click on a mark selects nothing (the marks are then read as images, with no pressed state). */
17
17
  inert(): boolean;
18
18
  /** Select a mark as a click on it would. */
19
19
  select(mark: Element, mods: {
@@ -0,0 +1,21 @@
1
+ export interface MarkFollowerOptions {
2
+ /** Elements whose own writes never count (the host's layers), as a selector. */
3
+ blind?: string;
4
+ /** The window whose observers and frames to use. Default: the container's own. */
5
+ win?: any;
6
+ /** Called on the next frame after the marks moved; `resized` is true when the container's size changed. */
7
+ repaint(resized: boolean): void;
8
+ }
9
+ export interface MarkFollower {
10
+ /** Start (true) or stop (false) following. Connecting twice is harmless. */
11
+ follow(on: boolean): void;
12
+ /** Note the container's size now, as painted at: a later size equal to it repaints nothing. Call at each paint. */
13
+ painted(): void;
14
+ /** Drop the writes already recorded: call after a paint, whose own writes (the selection's classes) are in it. */
15
+ takeRecords(): void;
16
+ /** Whether it is following now. */
17
+ readonly following: boolean;
18
+ }
19
+ /** The attributes whose writes can move or hide a mark. */
20
+ export declare const MARK_MOVE_ATTRIBUTES: readonly string[];
21
+ export declare function followMarks(container: HTMLElement, opts: MarkFollowerOptions): MarkFollower;
@@ -1,4 +1,4 @@
1
1
  export { viewToken, viewFromToken, LINKED_HOVER_CLASS, type PageView } from "./filterScope";
2
- export { createFilter, createFilterScope, bindFilter, payloadRowReader, fromVegaInteraction, filterScopeOf, ANY_VALUE, isAnyValue, type Filter, type FilterScope, type FilterOptions, type FilterKey, type FilterChangeReason, type FilterBinding, type BindFilterOptions, type VegaInteractionEvent } from "./filterScope";
2
+ export { createFilter, createFilterScope, bindFilter, payloadRowReader, fromVegaInteraction, filterScopeOf, scopeFilterOf, ANY_VALUE, isAnyValue, type Filter, type FilterScope, type FilterOptions, type FilterKey, type FilterChangeReason, type FilterBinding, type BindFilterOptions, type VegaInteractionEvent, type ScopedFilter, type ScopeSource } from "./filterScope";
3
3
  export { createControls, attachControls, readControls, type Controls, type ControlInfo, type ControlsAttachment } from "./controls";
4
4
  export { noteBadges, badgeKey, notesFor, type MarkNoteLike, type NoteBadgeOptions } from "./markNotes";
@@ -2,13 +2,13 @@ import type { ReactNode, RefObject } from "react";
2
2
  import { type ChartHostConfig } from "./host";
3
3
  import type { ResolveOptionsInput } from "./defaults";
4
4
  import type { GeoPointBinding } from "./payload";
5
- import { type PageView, type Filter, type FilterOptions } from "./filterScope";
5
+ import { type PageView, type Filter, type FilterOptions, type ScopeSource } from "./filterScope";
6
6
  export { assembleD3 } from "./host";
7
7
  import { type Controls } from "./controls";
8
8
  export { createControls, type Controls, type ControlInfo } from "./controls";
9
9
  export { noteBadges, badgeKey, notesFor, type MarkNoteLike, type NoteBadgeOptions } from "./markNotes";
10
10
  export { viewToken, viewFromToken, LINKED_HOVER_CLASS, type PageView } from "./filterScope";
11
- export { createFilter, createFilterScope, fromVegaInteraction, filterScopeOf, ANY_VALUE, isAnyValue, type Filter, type FilterOptions, type FilterScope, type FilterChangeReason } from "./filterScope";
11
+ export { createFilter, createFilterScope, fromVegaInteraction, filterScopeOf, ANY_VALUE, isAnyValue, type Filter, type FilterOptions, type FilterScope, type FilterChangeReason, type ScopedFilter, type ScopeSource } from "./filterScope";
12
12
  export interface BicChartProps {
13
13
  /** Generated render() source (an ES module's `code` export, or the raw string). */
14
14
  code?: string;
@@ -111,8 +111,11 @@ export interface BicChartProps {
111
111
  * chip shows), and its titles and comparisons say so ("North America · United States only", "rank 2 of 3 (United
112
112
  * States only)"). Cleared again when the filters clear. Not for the filter this chart `selects` unless its own rows
113
113
  * are narrowed by it too. Absent: the page's own options.filterScope, if any, goes through untouched.
114
+ * A query that maps only some of a compound key's columns names them, `{ filter, takes }`: with
115
+ * `treatAs(cell, "Accounts[Region]")` on a Region x Stage pick, `scopedBy={{ filter: cell, takes: ["Region"] }}` says
116
+ * the region alone, and a stage-label pick (any region) says nothing, since that query keeps every row.
114
117
  */
115
- scopedBy?: Filter | readonly Filter[];
118
+ scopedBy?: ScopeSource | readonly ScopeSource[];
116
119
  /**
117
120
  * THE CHART'S OWN CONTROLS AS PAGE STATE - from useBicControls(). For a chart that draws
118
121
  * sliders (the What-if projection's rate and horizon): `controls.values` holds every
@@ -183,6 +186,11 @@ export declare function BicPage({ children }: {
183
186
  * ["OriginCode", "DestinationCode"]). Pass it to a chart's `selects` and read
184
187
  * `value` / `values` / `row` / `active` for the rest of the page; `clear()` clears it and the
185
188
  * chart's marks. Re-renders the component on every change. The key columns are read once.
189
+ *
190
+ * SEVERAL PANELS PICKING ONE KEY ARE ONE PICK: give their filters one `group` ("country") and the most recent pick is
191
+ * the page's one pick - a pick in one clears the others, the picked mark shows in every member's chart and a click on it
192
+ * in any of them clears it, and only the member holding it is active (one chip, one scope line). Each panel's query
193
+ * still takes every member but its own, so the panel a reader picked in keeps all its marks.
186
194
  */
187
195
  export declare function useBicFilter(columns: string | readonly string[], opts?: FilterOptions): Filter;
188
196
  /**