@fjader/svelte 0.2.2 → 0.2.4
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 +2 -0
- package/dist/components/axes/AxisBottom.svelte +23 -6
- package/dist/components/axes/AxisBottom.svelte.d.ts +10 -0
- package/dist/components/axes/AxisLeft.svelte +77 -8
- package/dist/components/axes/AxisLeft.svelte.d.ts +17 -0
- package/dist/components/axes/BandAxisTicks.svelte +64 -25
- package/dist/components/axes/BandAxisTicks.svelte.d.ts +5 -5
- package/dist/components/axes/axis-title.d.ts +30 -0
- package/dist/components/axes/axis-title.js +77 -0
- package/dist/components/charts/ChartCell.svelte +1 -1
- package/dist/components/charts/ChartContainer.svelte +12 -3
- package/dist/components/charts/ChartContainer.svelte.d.ts +7 -0
- package/dist/components/guides/legend/ContinuousColorLegend.svelte +9 -2
- package/dist/components/guides/legend/OrdinalColorLegend.svelte +96 -35
- package/dist/components/marks/AreaMarkX.svelte +21 -6
- package/dist/components/marks/AreaMarkY.svelte +21 -6
- package/dist/components/marks/BarMarkX.svelte +16 -1
- package/dist/components/marks/BarMarkY.svelte +16 -1
- package/dist/components/marks/DotsMark.svelte +5 -0
- package/dist/components/marks/LineMark.svelte +8 -1
- package/dist/components/marks/ParallelCoordinatesMark.svelte +10 -0
- package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +5 -0
- package/dist/components/marks/RectMark.svelte +5 -0
- package/dist/components/marks/RectXMark.svelte +5 -0
- package/dist/components/marks/RectYMark.svelte +5 -0
- package/dist/components/marks/RuleMarkX.svelte +12 -2
- package/dist/components/marks/RuleMarkY.svelte +12 -2
- package/dist/components/marks/TextMark.svelte +76 -13
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +21 -2
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +300 -42
- package/dist/components/marks/lifecycle/domain-queries.d.ts +3 -1
- package/dist/components/marks/lifecycle/domain-queries.js +22 -15
- package/dist/components/marks/lifecycle/domain-registration.js +2 -6
- package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +104 -0
- package/dist/components/marks/lifecycle/other-rows.svelte.js +193 -0
- package/dist/components/marks/lifecycle/other-split.d.ts +80 -0
- package/dist/components/marks/lifecycle/other-split.js +112 -0
- package/dist/components/marks/lifecycle/query-builder.d.ts +26 -1
- package/dist/components/marks/lifecycle/query-builder.js +25 -5
- package/dist/components/marks/lifecycle/transform-scale-key.d.ts +16 -0
- package/dist/components/marks/lifecycle/transform-scale-key.js +27 -0
- package/dist/components/marks/utils/area-outline.d.ts +24 -0
- package/dist/components/marks/utils/area-outline.js +10 -0
- package/dist/components/marks/utils/reactive-config.d.ts +1 -1
- package/dist/components/marks/utils/reactive-config.js +9 -0
- package/dist/components/marks/utils/tooltip-helpers.d.ts +4 -1
- package/dist/components/marks/utils/tooltip-helpers.js +2 -1
- package/dist/components/rendering/Areas.svelte +30 -6
- package/dist/components/rendering/Areas.svelte.d.ts +2 -0
- package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +3 -1
- package/dist/components/rendering/BaseGPURenderer.svelte.js +8 -3
- package/dist/components/rendering/Dots.svelte +24 -3
- package/dist/components/rendering/Dots.svelte.d.ts +2 -0
- package/dist/components/rendering/GridLine.svelte +2 -1
- package/dist/components/rendering/Lines.svelte +49 -7
- package/dist/components/rendering/Lines.svelte.d.ts +9 -0
- package/dist/components/rendering/ParallelLines.svelte +26 -7
- package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
- package/dist/components/rendering/Rects.svelte +129 -20
- package/dist/components/rendering/Rects.svelte.d.ts +9 -0
- package/dist/components/rendering/RuleX.svelte +20 -5
- package/dist/components/rendering/RuleX.svelte.d.ts +2 -0
- package/dist/components/rendering/RuleY.svelte +20 -5
- package/dist/components/rendering/RuleY.svelte.d.ts +2 -0
- package/dist/components/rendering/activity-reads.d.ts +111 -0
- package/dist/components/rendering/activity-reads.js +154 -0
- package/dist/components/rendering/uniform-sizes.d.ts +26 -0
- package/dist/components/rendering/uniform-sizes.js +28 -0
- package/dist/config/ChartConfig.svelte.d.ts +5 -1
- package/dist/gpu/ColorScaleShaderLib.d.ts +13 -1
- package/dist/gpu/ColorScaleShaderLib.js +56 -5
- package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
- package/dist/gpu/GPUCoordinator.svelte.js +68 -32
- package/dist/gpu/MorphedChannels.d.ts +6 -32
- package/dist/gpu/MorphedChannels.js +45 -3
- package/dist/gpu/ResolveShader.d.ts +2 -2
- package/dist/gpu/ResolveShader.js +2 -2
- package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
- package/dist/gpu/ScaleTemplateSystem.js +111 -11
- package/dist/gpu/SizeScaleShaderLib.d.ts +4 -4
- package/dist/gpu/SizeScaleShaderLib.js +4 -4
- package/dist/gpu/identity-slot-packing.d.ts +157 -40
- package/dist/gpu/identity-slot-packing.js +262 -62
- package/dist/gpu/tracked-scales.d.ts +14 -0
- package/dist/gpu/tracked-scales.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/interactors/BandLabelToggle.svelte +5 -9
- package/dist/interactors/BandLabelToggle.svelte.d.ts +3 -3
- package/dist/interactors/DerivedInterval1D.svelte +26 -54
- package/dist/interactors/Interval1D.svelte +8 -2
- package/dist/interactors/Interval2D.svelte +8 -2
- package/dist/interactors/ParallelAxisSource.svelte +14 -5
- package/dist/interactors/ParallelAxisSource.svelte.d.ts +2 -1
- package/dist/interactors/ParallelBrushes.svelte +8 -9
- package/dist/interactors/ParallelBrushes.svelte.d.ts +3 -3
- package/dist/interactors/Toggle.svelte +48 -38
- package/dist/interactors/Toggle.svelte.d.ts +4 -3
- package/dist/interactors/Tooltip.svelte +140 -42
- package/dist/interactors/TooltipManager.svelte.d.ts +20 -5
- package/dist/interactors/TooltipManager.svelte.js +40 -10
- package/dist/interactors/other-tooltip.d.ts +16 -0
- package/dist/interactors/other-tooltip.js +22 -0
- package/dist/interactors/relay-clauses.d.ts +23 -0
- package/dist/interactors/relay-clauses.js +60 -0
- package/dist/query-engine/CrossfilterSelection.svelte.d.ts +88 -15
- package/dist/query-engine/CrossfilterSelection.svelte.js +94 -20
- package/dist/query-engine/FieldActivity.svelte.d.ts +128 -0
- package/dist/query-engine/FieldActivity.svelte.js +378 -0
- package/dist/query-engine/FilterGroup.svelte.d.ts +76 -30
- package/dist/query-engine/FilterGroup.svelte.js +223 -124
- package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +73 -3
- package/dist/query-engine/FilterGroupRegistry.svelte.js +161 -12
- package/dist/query-engine/MarkHandle.svelte.d.ts +9 -1
- package/dist/query-engine/MarkHandle.svelte.js +12 -2
- package/dist/query-engine/QueryEngine.d.ts +52 -16
- package/dist/query-engine/QueryEngine.js +116 -34
- package/dist/query-engine/categorical-identity.d.ts +114 -27
- package/dist/query-engine/categorical-identity.js +225 -51
- package/dist/query-engine/index.d.ts +2 -1
- package/dist/query-engine/index.js +1 -0
- package/dist/query-engine/preagg/Preagg.d.ts +1 -1
- package/dist/query-engine/preagg/Preagg.js +11 -9
- package/dist/query-engine/preagg/plan.d.ts +14 -5
- package/dist/query-engine/preagg/plan.js +44 -18
- package/dist/query-engine/toggle-predicate.d.ts +21 -0
- package/dist/query-engine/toggle-predicate.js +41 -2
- package/dist/scales/ActivityTable.d.ts +103 -0
- package/dist/scales/ActivityTable.js +220 -0
- package/dist/scales/BandScaleState.svelte.d.ts +41 -15
- package/dist/scales/BandScaleState.svelte.js +78 -36
- package/dist/scales/ChartScales.svelte.d.ts +2 -2
- package/dist/scales/ColorScaleState.svelte.d.ts +36 -8
- package/dist/scales/ColorScaleState.svelte.js +118 -24
- package/dist/scales/DomainResolver.d.ts +2 -0
- package/dist/scales/DomainResolver.js +15 -8
- package/dist/scales/ScaleRegistry.svelte.d.ts +9 -0
- package/dist/scales/ScaleRegistry.svelte.js +22 -1
- package/dist/scales/TimeScaleState.svelte.js +7 -1
- package/dist/spec/index.js +1 -0
- package/dist/spec/types.d.ts +4 -0
- package/dist/theme.css +5 -4
- package/dist/transforms/TransformCompiler.d.ts +37 -2
- package/dist/transforms/TransformCompiler.js +44 -1
- package/dist/transforms/index.d.ts +27 -0
- package/dist/transforms/index.js +127 -19
- package/dist/types.d.ts +71 -0
- package/package.json +3 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { ClockLoop } from '../animation';
|
|
2
|
+
import { ActivityTable } from '../scales/ActivityTable';
|
|
3
|
+
import type { FieldIdentity } from './categorical-identity';
|
|
4
|
+
/** What a FieldActivity needs from the page around it. */
|
|
5
|
+
/** One mark's Other rows for one bucket: what a click on it selects. */
|
|
6
|
+
export interface OtherEntry {
|
|
7
|
+
/** The mark's filter group, or null when it is filtered by the selection (`'filter'`). */
|
|
8
|
+
group: number | null;
|
|
9
|
+
/** One per Other row: a facet cell (null without facets) and its folded ids. */
|
|
10
|
+
rows: {
|
|
11
|
+
facetId: number | null;
|
|
12
|
+
ids: ArrayLike<number>;
|
|
13
|
+
}[];
|
|
14
|
+
}
|
|
15
|
+
/** What a click on Other does to a selection: values to set, add or remove. */
|
|
16
|
+
export interface OtherClick {
|
|
17
|
+
action: 'replace' | 'add' | 'remove';
|
|
18
|
+
values: unknown[];
|
|
19
|
+
}
|
|
20
|
+
export interface FieldActivityHost {
|
|
21
|
+
/** Drives the fades; null in unit tests and before a coordinator exists, and fades land at once. */
|
|
22
|
+
readonly loop: ClockLoop | null;
|
|
23
|
+
/** Fill `out` with the charts drawing any scale on `key`, for the fade's redraws. */
|
|
24
|
+
chartsFor(key: string, out: string[]): string[];
|
|
25
|
+
/** The field's live identity, or null; must never create one. */
|
|
26
|
+
identityOf(key: string): FieldIdentity | null;
|
|
27
|
+
}
|
|
28
|
+
/** A host with no page: no loop, no charts, no identity. */
|
|
29
|
+
export declare const DETACHED_HOST: FieldActivityHost;
|
|
30
|
+
/**
|
|
31
|
+
* Which categories of one field are selected, per filter group, and how far
|
|
32
|
+
* each one has faded.
|
|
33
|
+
*
|
|
34
|
+
* Activity is indexed by the field's identity id, and FieldIdentity is one per
|
|
35
|
+
* field, so it belongs to the field, not to any scale on it: the carrier colour
|
|
36
|
+
* scale and every carrier band scale read the same activity. One fade per
|
|
37
|
+
* filter group, indexed by the group's page-wide index, because a selection
|
|
38
|
+
* belongs to the group it was made in: a click in group A dims group A's marks
|
|
39
|
+
* and leaves group B's alone.
|
|
40
|
+
*
|
|
41
|
+
* The packer writes these tables into the field's activity block, and legends
|
|
42
|
+
* and band labels read them through `opacityOfValue`.
|
|
43
|
+
*
|
|
44
|
+
* IDS AND IDENTITY. Ids are only meaningful within one identity: across a
|
|
45
|
+
* reload the same id can name another category. So the tables follow the
|
|
46
|
+
* field's LIVE identity, which `reconcile` compares with the one last applied:
|
|
47
|
+
* a different object is a reload, and every fade is dropped and snapped to its
|
|
48
|
+
* selection; the same object at a higher version is a dim that grew, and the
|
|
49
|
+
* existing ids keep their fades while new ones start in their state.
|
|
50
|
+
* `reconcile` runs from the packer and from `set`, never from a reader, so
|
|
51
|
+
* nothing is resized or retargeted while a template renders.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* THE FREE SLOT. Each group's table holds one slot past the field's ids: 1 while
|
|
55
|
+
* the group selects nothing on the field, 0 while it selects anything, faded on
|
|
56
|
+
* the same clock as every id. The shader reads it as the last slot of the
|
|
57
|
+
* group's run (identityFreeActivity): an Other bar's unselected part dims by it,
|
|
58
|
+
* so clearing a selection fades it back in with the other bars, rather than its
|
|
59
|
+
* split sliding.
|
|
60
|
+
*/
|
|
61
|
+
export declare class FieldActivity {
|
|
62
|
+
#private;
|
|
63
|
+
readonly key: string;
|
|
64
|
+
constructor(key: string, host?: FieldActivityHost);
|
|
65
|
+
/**
|
|
66
|
+
* How many slots the tables cover: one per id of the applied identity, and
|
|
67
|
+
* one past them (FREE_SLOT); 0 without an identity.
|
|
68
|
+
*/
|
|
69
|
+
get count(): number;
|
|
70
|
+
/**
|
|
71
|
+
* Group `index`'s selected values, or null when it selects nothing on this
|
|
72
|
+
* field. Reactive: a reader re-runs when the selection changes.
|
|
73
|
+
*/
|
|
74
|
+
selectedValues(index: number): ReadonlySet<unknown> | null;
|
|
75
|
+
/** Group `index`'s fade, or null when that group never selected on this field. */
|
|
76
|
+
fadeOf(index: number): ActivityTable | null;
|
|
77
|
+
/**
|
|
78
|
+
* Select `values` in group `index`, or nothing for null.
|
|
79
|
+
*
|
|
80
|
+
* The table is first brought up to the live identity under the selection it
|
|
81
|
+
* had, so the change from that selection to this one fades. Sizing it with
|
|
82
|
+
* the new values instead would put every id straight into its new state: a
|
|
83
|
+
* group's first click, or the first one after the dim grew, would snap.
|
|
84
|
+
*/
|
|
85
|
+
set(index: number, values: ReadonlySet<unknown> | null): void;
|
|
86
|
+
/**
|
|
87
|
+
* Follow the field's live identity: snap every fade on a reload, extend them
|
|
88
|
+
* when the dim grew. Two comparisons when nothing moved, no allocation.
|
|
89
|
+
* Returns whether it re-applied the groups.
|
|
90
|
+
*/
|
|
91
|
+
reconcile(): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* How visible a category is now in group `index`: its fade when the value
|
|
94
|
+
* has an id in the current identity, and otherwise simply selected or not.
|
|
95
|
+
* Pure, so it is safe in a template: it never reconciles, and a table that
|
|
96
|
+
* belongs to an identity no longer live is not trusted.
|
|
97
|
+
*/
|
|
98
|
+
opacityOfValue(index: number, value: unknown, inactiveOpacity?: number): number;
|
|
99
|
+
/**
|
|
100
|
+
* How visible the category with dim id `id` is now in group `index`: its fade.
|
|
101
|
+
* The id form of `opacityOfValue`, and the only one that reaches a synthetic
|
|
102
|
+
* id (an Other bucket): a bucket has a label, not a value, and several may
|
|
103
|
+
* share one label. Pure, so it is safe in a template.
|
|
104
|
+
*
|
|
105
|
+
* Before the group's table covers the id, falls back to the selected values
|
|
106
|
+
* for a real id, and to fully visible for a bucket or an identity that is not
|
|
107
|
+
* live: a label is never dimmed on a guess.
|
|
108
|
+
*/
|
|
109
|
+
opacityOfId(index: number, id: number, inactiveOpacity?: number): number;
|
|
110
|
+
/** Register `markId`'s Other rows under `otherId`; null removes them. */
|
|
111
|
+
setOther(otherId: number, markId: string, entry: OtherEntry | null): void;
|
|
112
|
+
/**
|
|
113
|
+
* What a click on Other `otherId` does to group `group`'s selection, given
|
|
114
|
+
* the values it holds now (`current`), and records it. Null when nothing is
|
|
115
|
+
* registered under the id (no result yet) or it holds no members.
|
|
116
|
+
*
|
|
117
|
+
* - The members: every registered row under the id, or only facet cell
|
|
118
|
+
* `facetId`'s when the click came from a cell.
|
|
119
|
+
* - Shift-click adds them, or takes back what this Other added before if it
|
|
120
|
+
* is still selected.
|
|
121
|
+
* - A plain click on an Other whose marks all filter by the selection (no
|
|
122
|
+
* group: `'filter'`) drills down, replacing the selection: such an Other is
|
|
123
|
+
* computed inside the selection, so a second click means "the rest of it".
|
|
124
|
+
* - Otherwise a plain click toggles: it takes back what this Other added if
|
|
125
|
+
* that is still selected, and replaces the selection with the members if not.
|
|
126
|
+
*/
|
|
127
|
+
otherClick(group: number, otherId: number, facetId: number | null, current: ReadonlySet<unknown>, multi: boolean): OtherClick | null;
|
|
128
|
+
}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { ActivityTable, DEFAULT_INACTIVE_OPACITY, selectionOpacity } from '../scales/ActivityTable';
|
|
2
|
+
/** A host with no page: no loop, no charts, no identity. */
|
|
3
|
+
export const DETACHED_HOST = {
|
|
4
|
+
loop: null,
|
|
5
|
+
chartsFor: (_key, out) => out,
|
|
6
|
+
identityOf: () => null
|
|
7
|
+
};
|
|
8
|
+
/** One filter group's selection on one field, and its fade. */
|
|
9
|
+
class GroupFade {
|
|
10
|
+
table;
|
|
11
|
+
/** The selected values, or null for none. State, for the by-value fallback. */
|
|
12
|
+
values = $state.raw(null);
|
|
13
|
+
/** Bumped on every frame the fade is driven, and once at rest. */
|
|
14
|
+
frame = $state(0);
|
|
15
|
+
constructor(host, key, charts) {
|
|
16
|
+
this.table = new ActivityTable(host.loop, {
|
|
17
|
+
charts: () => {
|
|
18
|
+
charts.length = 0;
|
|
19
|
+
return host.chartsFor(key, charts);
|
|
20
|
+
},
|
|
21
|
+
onFrame: () => this.frame++
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Which categories of one field are selected, per filter group, and how far
|
|
27
|
+
* each one has faded.
|
|
28
|
+
*
|
|
29
|
+
* Activity is indexed by the field's identity id, and FieldIdentity is one per
|
|
30
|
+
* field, so it belongs to the field, not to any scale on it: the carrier colour
|
|
31
|
+
* scale and every carrier band scale read the same activity. One fade per
|
|
32
|
+
* filter group, indexed by the group's page-wide index, because a selection
|
|
33
|
+
* belongs to the group it was made in: a click in group A dims group A's marks
|
|
34
|
+
* and leaves group B's alone.
|
|
35
|
+
*
|
|
36
|
+
* The packer writes these tables into the field's activity block, and legends
|
|
37
|
+
* and band labels read them through `opacityOfValue`.
|
|
38
|
+
*
|
|
39
|
+
* IDS AND IDENTITY. Ids are only meaningful within one identity: across a
|
|
40
|
+
* reload the same id can name another category. So the tables follow the
|
|
41
|
+
* field's LIVE identity, which `reconcile` compares with the one last applied:
|
|
42
|
+
* a different object is a reload, and every fade is dropped and snapped to its
|
|
43
|
+
* selection; the same object at a higher version is a dim that grew, and the
|
|
44
|
+
* existing ids keep their fades while new ones start in their state.
|
|
45
|
+
* `reconcile` runs from the packer and from `set`, never from a reader, so
|
|
46
|
+
* nothing is resized or retargeted while a template renders.
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* THE FREE SLOT. Each group's table holds one slot past the field's ids: 1 while
|
|
50
|
+
* the group selects nothing on the field, 0 while it selects anything, faded on
|
|
51
|
+
* the same clock as every id. The shader reads it as the last slot of the
|
|
52
|
+
* group's run (identityFreeActivity): an Other bar's unselected part dims by it,
|
|
53
|
+
* so clearing a selection fades it back in with the other bars, rather than its
|
|
54
|
+
* split sliding.
|
|
55
|
+
*/
|
|
56
|
+
export class FieldActivity {
|
|
57
|
+
key;
|
|
58
|
+
#host;
|
|
59
|
+
#groups = [];
|
|
60
|
+
/**
|
|
61
|
+
* Bumped when a group's fade is created, so a reader that found none runs
|
|
62
|
+
* again once it exists. Fades are created by `set`, never while rendering.
|
|
63
|
+
*/
|
|
64
|
+
#groupsVersion = $state(0);
|
|
65
|
+
/** The identity the tables' ids belong to, or null when they have none. */
|
|
66
|
+
#identity = null;
|
|
67
|
+
#version = -1;
|
|
68
|
+
/** Reused id list: selections are applied on every categorical click. */
|
|
69
|
+
#ids = [];
|
|
70
|
+
constructor(key, host = DETACHED_HOST) {
|
|
71
|
+
this.key = key;
|
|
72
|
+
this.#host = host;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* How many slots the tables cover: one per id of the applied identity, and
|
|
76
|
+
* one past them (FREE_SLOT); 0 without an identity.
|
|
77
|
+
*/
|
|
78
|
+
get count() {
|
|
79
|
+
return this.#identity ? this.#identity.values.length + 1 : 0;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Group `index`'s selected values, or null when it selects nothing on this
|
|
83
|
+
* field. Reactive: a reader re-runs when the selection changes.
|
|
84
|
+
*/
|
|
85
|
+
selectedValues(index) {
|
|
86
|
+
void this.#groupsVersion;
|
|
87
|
+
return this.#groups[index]?.values ?? null;
|
|
88
|
+
}
|
|
89
|
+
/** Group `index`'s fade, or null when that group never selected on this field. */
|
|
90
|
+
fadeOf(index) {
|
|
91
|
+
return this.#groups[index]?.table ?? null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Select `values` in group `index`, or nothing for null.
|
|
95
|
+
*
|
|
96
|
+
* The table is first brought up to the live identity under the selection it
|
|
97
|
+
* had, so the change from that selection to this one fades. Sizing it with
|
|
98
|
+
* the new values instead would put every id straight into its new state: a
|
|
99
|
+
* group's first click, or the first one after the dim grew, would snap.
|
|
100
|
+
*/
|
|
101
|
+
set(index, values) {
|
|
102
|
+
const group = this.#group(index);
|
|
103
|
+
this.reconcile();
|
|
104
|
+
if (group.table.count !== this.count)
|
|
105
|
+
this.#apply(group);
|
|
106
|
+
group.values = values;
|
|
107
|
+
this.#apply(group);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Follow the field's live identity: snap every fade on a reload, extend them
|
|
111
|
+
* when the dim grew. Two comparisons when nothing moved, no allocation.
|
|
112
|
+
* Returns whether it re-applied the groups.
|
|
113
|
+
*/
|
|
114
|
+
reconcile() {
|
|
115
|
+
const live = this.#host.identityOf(this.key);
|
|
116
|
+
const usable = live && live.values.length > 0 ? live : null;
|
|
117
|
+
if (usable === this.#identity) {
|
|
118
|
+
if (!usable || usable.version === this.#version)
|
|
119
|
+
return false;
|
|
120
|
+
// The same identity, grown.
|
|
121
|
+
this.#version = usable.version;
|
|
122
|
+
for (const group of this.#groups)
|
|
123
|
+
this.#apply(group);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
// Another identity, or none: the old ids mean nothing now.
|
|
127
|
+
this.#identity = usable;
|
|
128
|
+
this.#version = usable ? usable.version : -1;
|
|
129
|
+
for (const group of this.#groups) {
|
|
130
|
+
group.table.resize(0);
|
|
131
|
+
this.#apply(group);
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* How visible a category is now in group `index`: its fade when the value
|
|
137
|
+
* has an id in the current identity, and otherwise simply selected or not.
|
|
138
|
+
* Pure, so it is safe in a template: it never reconciles, and a table that
|
|
139
|
+
* belongs to an identity no longer live is not trusted.
|
|
140
|
+
*/
|
|
141
|
+
opacityOfValue(index, value, inactiveOpacity = DEFAULT_INACTIVE_OPACITY) {
|
|
142
|
+
void this.#groupsVersion;
|
|
143
|
+
const group = this.#groups[index];
|
|
144
|
+
if (!group)
|
|
145
|
+
return 1;
|
|
146
|
+
// Read first, so a reader that ran before identity landed already depends
|
|
147
|
+
// on the frame counter for the fade that follows.
|
|
148
|
+
void group.frame;
|
|
149
|
+
const identity = this.#identity;
|
|
150
|
+
if (identity && identity === this.#host.identityOf(this.key)) {
|
|
151
|
+
const id = identity.ids.get(value);
|
|
152
|
+
if (id != null)
|
|
153
|
+
return this.opacityOfId(index, id, inactiveOpacity);
|
|
154
|
+
}
|
|
155
|
+
const values = group.values;
|
|
156
|
+
return values == null || values.has(value) ? 1 : inactiveOpacity;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* How visible the category with dim id `id` is now in group `index`: its fade.
|
|
160
|
+
* The id form of `opacityOfValue`, and the only one that reaches a synthetic
|
|
161
|
+
* id (an Other bucket): a bucket has a label, not a value, and several may
|
|
162
|
+
* share one label. Pure, so it is safe in a template.
|
|
163
|
+
*
|
|
164
|
+
* Before the group's table covers the id, falls back to the selected values
|
|
165
|
+
* for a real id, and to fully visible for a bucket or an identity that is not
|
|
166
|
+
* live: a label is never dimmed on a guess.
|
|
167
|
+
*/
|
|
168
|
+
opacityOfId(index, id, inactiveOpacity = DEFAULT_INACTIVE_OPACITY) {
|
|
169
|
+
void this.#groupsVersion;
|
|
170
|
+
const group = this.#groups[index];
|
|
171
|
+
if (!group)
|
|
172
|
+
return 1;
|
|
173
|
+
void group.frame;
|
|
174
|
+
const values = group.values;
|
|
175
|
+
const identity = this.#identity;
|
|
176
|
+
if (!identity || identity !== this.#host.identityOf(this.key))
|
|
177
|
+
return 1;
|
|
178
|
+
if (id >= 0 && id < group.table.count) {
|
|
179
|
+
return selectionOpacity(group.table.activityOf(id), inactiveOpacity);
|
|
180
|
+
}
|
|
181
|
+
if (values == null || identity.synthetic.has(id) || id < 0 || id >= identity.values.length) {
|
|
182
|
+
return 1;
|
|
183
|
+
}
|
|
184
|
+
return values.has(identity.values[id]) ? 1 : inactiveOpacity;
|
|
185
|
+
}
|
|
186
|
+
// -----------------------------------------------------------------
|
|
187
|
+
// Other buckets: what each one holds, for a click to select
|
|
188
|
+
// -----------------------------------------------------------------
|
|
189
|
+
/**
|
|
190
|
+
* Every Other bucket's members, by its id and the mark that drew it. Each
|
|
191
|
+
* mark registers its Other rows per result (`setOther`): the folded ids its
|
|
192
|
+
* query returned, per facet cell. A click on Other reads them, so what it
|
|
193
|
+
* selects is exactly what the bar summed.
|
|
194
|
+
*/
|
|
195
|
+
#others = new Map();
|
|
196
|
+
/**
|
|
197
|
+
* What an Other click contributed to a group's selection, by `group:otherId`:
|
|
198
|
+
* how a second click on the same Other knows what to take back. Dropped with
|
|
199
|
+
* the id's last entry, so a reused id starts with no memory.
|
|
200
|
+
*/
|
|
201
|
+
#otherMemory = new Map();
|
|
202
|
+
/** Register `markId`'s Other rows under `otherId`; null removes them. */
|
|
203
|
+
setOther(otherId, markId, entry) {
|
|
204
|
+
let byMark = this.#others.get(otherId);
|
|
205
|
+
const before = byMark?.get(markId)?.group;
|
|
206
|
+
if (entry) {
|
|
207
|
+
if (!byMark)
|
|
208
|
+
this.#others.set(otherId, (byMark = new Map()));
|
|
209
|
+
byMark.set(markId, entry);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
if (!byMark?.delete(markId))
|
|
213
|
+
return;
|
|
214
|
+
if (byMark.size === 0) {
|
|
215
|
+
this.#others.delete(otherId);
|
|
216
|
+
for (const memKey of [...this.#otherMemory.keys()]) {
|
|
217
|
+
if (memKey.endsWith(`:${otherId}`))
|
|
218
|
+
this.#otherMemory.delete(memKey);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// The bucket's members moved (a new result), or it came or went: whether it
|
|
223
|
+
// is lit may have changed in the group its marks dim under. A fade it does
|
|
224
|
+
// not change costs nothing (#retarget returns early).
|
|
225
|
+
this.#reapply(before);
|
|
226
|
+
if (entry && entry.group !== before)
|
|
227
|
+
this.#reapply(entry.group);
|
|
228
|
+
}
|
|
229
|
+
#reapply(index) {
|
|
230
|
+
if (index == null)
|
|
231
|
+
return;
|
|
232
|
+
const group = this.#groups[index];
|
|
233
|
+
if (group && group.values != null)
|
|
234
|
+
this.#apply(group);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* What a click on Other `otherId` does to group `group`'s selection, given
|
|
238
|
+
* the values it holds now (`current`), and records it. Null when nothing is
|
|
239
|
+
* registered under the id (no result yet) or it holds no members.
|
|
240
|
+
*
|
|
241
|
+
* - The members: every registered row under the id, or only facet cell
|
|
242
|
+
* `facetId`'s when the click came from a cell.
|
|
243
|
+
* - Shift-click adds them, or takes back what this Other added before if it
|
|
244
|
+
* is still selected.
|
|
245
|
+
* - A plain click on an Other whose marks all filter by the selection (no
|
|
246
|
+
* group: `'filter'`) drills down, replacing the selection: such an Other is
|
|
247
|
+
* computed inside the selection, so a second click means "the rest of it".
|
|
248
|
+
* - Otherwise a plain click toggles: it takes back what this Other added if
|
|
249
|
+
* that is still selected, and replaces the selection with the members if not.
|
|
250
|
+
*/
|
|
251
|
+
otherClick(group, otherId, facetId, current, multi) {
|
|
252
|
+
const entries = this.#others.get(otherId);
|
|
253
|
+
const identity = this.#host.identityOf(this.key);
|
|
254
|
+
if (!entries || entries.size === 0 || !identity)
|
|
255
|
+
return null;
|
|
256
|
+
const members = new Set();
|
|
257
|
+
let drill = true;
|
|
258
|
+
for (const entry of entries.values()) {
|
|
259
|
+
if (entry.group !== null)
|
|
260
|
+
drill = false;
|
|
261
|
+
for (const row of entry.rows) {
|
|
262
|
+
if (facetId !== null && row.facetId !== facetId)
|
|
263
|
+
continue;
|
|
264
|
+
for (let i = 0; i < row.ids.length; i++) {
|
|
265
|
+
const id = row.ids[i];
|
|
266
|
+
if (identity.synthetic.has(id))
|
|
267
|
+
continue;
|
|
268
|
+
const value = identity.values[id];
|
|
269
|
+
if (value !== undefined)
|
|
270
|
+
members.add(value);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (members.size === 0)
|
|
275
|
+
return null;
|
|
276
|
+
const memKey = `${group}:${otherId}`;
|
|
277
|
+
const before = this.#otherMemory.get(memKey);
|
|
278
|
+
const stillSelected = !!before && before.size > 0 && [...before].every((v) => current.has(v));
|
|
279
|
+
if (stillSelected && (multi || !drill)) {
|
|
280
|
+
this.#otherMemory.delete(memKey);
|
|
281
|
+
return { action: 'remove', values: [...before] };
|
|
282
|
+
}
|
|
283
|
+
if (!multi) {
|
|
284
|
+
// A replace: what any other Other added is gone from the selection.
|
|
285
|
+
for (const key of [...this.#otherMemory.keys()]) {
|
|
286
|
+
if (key.startsWith(`${group}:`))
|
|
287
|
+
this.#otherMemory.delete(key);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
this.#otherMemory.set(memKey, members);
|
|
291
|
+
return { action: multi ? 'add' : 'replace', values: [...members] };
|
|
292
|
+
}
|
|
293
|
+
#group(index) {
|
|
294
|
+
let group = this.#groups[index];
|
|
295
|
+
if (!group) {
|
|
296
|
+
// Fill any gap below it too: the packer walks the groups by index.
|
|
297
|
+
for (let i = this.#groups.length; i <= index; i++) {
|
|
298
|
+
this.#groups[i] = new GroupFade(this.#host, this.key, []);
|
|
299
|
+
}
|
|
300
|
+
group = this.#groups[index];
|
|
301
|
+
this.#groupsVersion++;
|
|
302
|
+
}
|
|
303
|
+
return group;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Point a group's table at its selection under the applied identity. A new
|
|
307
|
+
* id starts in its state rather than fading in, which is what keeps a
|
|
308
|
+
* selection made before its ids existed from fading on arrival.
|
|
309
|
+
*/
|
|
310
|
+
#apply(group) {
|
|
311
|
+
const identity = this.#identity;
|
|
312
|
+
if (!identity) {
|
|
313
|
+
group.table.resize(0);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// Every id, and the free slot past them: clear() sets it to 1 and
|
|
317
|
+
// setActive() leaves it out, so it fades to 0 while anything is selected
|
|
318
|
+
// and back to 1 once nothing is (see FieldActivity's FREE_SLOT note).
|
|
319
|
+
const count = identity.values.length + 1;
|
|
320
|
+
const values = group.values;
|
|
321
|
+
if (values == null) {
|
|
322
|
+
group.table.resize(count, null, true);
|
|
323
|
+
group.table.clear();
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const ids = this.#ids;
|
|
327
|
+
ids.length = 0;
|
|
328
|
+
for (const value of values) {
|
|
329
|
+
const id = identity.ids.get(value);
|
|
330
|
+
if (id != null)
|
|
331
|
+
ids.push(id);
|
|
332
|
+
}
|
|
333
|
+
this.#lightOthers(this.#groups.indexOf(group), ids, count);
|
|
334
|
+
group.table.resize(count, ids, true);
|
|
335
|
+
group.table.setActive(ids);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Add to `ids` (the selected ids) every Other bucket that holds one of them,
|
|
339
|
+
* by the members its marks in group `index` registered: a bucket is lit while
|
|
340
|
+
* the selection reaches into it. In the same pass as the rest of the group's
|
|
341
|
+
* fade, so it is one retarget per update, never two. Marks filtered by the
|
|
342
|
+
* selection (no group) are skipped: they never dim.
|
|
343
|
+
*/
|
|
344
|
+
#lightOthers(index, ids, count) {
|
|
345
|
+
if (this.#others.size === 0 || index < 0)
|
|
346
|
+
return;
|
|
347
|
+
let mask = this.#selectedMask;
|
|
348
|
+
if (mask.length < count)
|
|
349
|
+
mask = this.#selectedMask = new Uint8Array(count);
|
|
350
|
+
const selected = ids.length;
|
|
351
|
+
for (let i = 0; i < selected; i++)
|
|
352
|
+
mask[ids[i]] = 1;
|
|
353
|
+
for (const [otherId, entries] of this.#others) {
|
|
354
|
+
if (otherId >= count)
|
|
355
|
+
continue;
|
|
356
|
+
let lit = false;
|
|
357
|
+
for (const entry of entries.values()) {
|
|
358
|
+
if (entry.group !== index)
|
|
359
|
+
continue;
|
|
360
|
+
for (const row of entry.rows) {
|
|
361
|
+
for (let i = 0; i < row.ids.length && !lit; i++)
|
|
362
|
+
lit = mask[row.ids[i]] === 1;
|
|
363
|
+
if (lit)
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
if (lit)
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
if (lit)
|
|
370
|
+
ids.push(otherId);
|
|
371
|
+
}
|
|
372
|
+
// Unmark only what was marked: the selected ids, not the Others pushed after.
|
|
373
|
+
for (let i = 0; i < selected; i++)
|
|
374
|
+
mask[ids[i]] = 0;
|
|
375
|
+
}
|
|
376
|
+
/** Reused by #lightOthers: 1 at each selected id, 0 elsewhere between calls. */
|
|
377
|
+
#selectedMask = new Uint8Array(0);
|
|
378
|
+
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* FilterGroup — visual concerns layer on top of CrossfilterSelection.
|
|
3
3
|
*
|
|
4
4
|
* Manages the interactor API (register, registerToggle), schema augmentation
|
|
5
|
-
* for UI filter pills, and
|
|
6
|
-
* legends
|
|
5
|
+
* for UI filter pills, and this group's selection on each categorical field
|
|
6
|
+
* (FieldActivity), which marks, legends and band axes dim by.
|
|
7
7
|
*
|
|
8
8
|
* Owns clauses: a clause lives while at least one registration for its id
|
|
9
9
|
* exists. Components own their registrations through their own lifecycle.
|
|
@@ -12,16 +12,9 @@
|
|
|
12
12
|
* objects, no event listeners.
|
|
13
13
|
*/
|
|
14
14
|
import { CrossfilterSelection } from './CrossfilterSelection.svelte';
|
|
15
|
-
import type { FilterExpr, FilterSchema } from './CrossfilterSelection.svelte';
|
|
15
|
+
import type { Clause, FilterExpr, FilterSchema, SourceRegistration } from './CrossfilterSelection.svelte';
|
|
16
16
|
import type { ActiveDim, QueryEngine } from './QueryEngine';
|
|
17
|
-
|
|
18
|
-
* Narrow protocol for scale states that support active-value projection.
|
|
19
|
-
* Implemented by ColorScaleState and BandScaleState.
|
|
20
|
-
*/
|
|
21
|
-
export interface ActiveValueScale {
|
|
22
|
-
setActiveValues(values: Set<unknown>): void;
|
|
23
|
-
clearActiveValues(): void;
|
|
24
|
-
}
|
|
17
|
+
import { FieldActivity } from './FieldActivity.svelte';
|
|
25
18
|
export interface FilterItem {
|
|
26
19
|
sourceId: string;
|
|
27
20
|
predicate: FilterExpr;
|
|
@@ -29,10 +22,23 @@ export interface FilterItem {
|
|
|
29
22
|
schema?: FilterSchema;
|
|
30
23
|
}
|
|
31
24
|
/** One registration of a selection source: a brush, a legend, a Toggle. */
|
|
32
|
-
export interface SourceOpts {
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
|
|
25
|
+
export interface SourceOpts extends SourceRegistration {
|
|
26
|
+
/**
|
|
27
|
+
* The chart this registration skips: its ChartConfig, or nothing. A chart
|
|
28
|
+
* holding a control for a selection is not filtered by it (crossfilter), so
|
|
29
|
+
* its marks that do not read the field keep their data; those that do, dim.
|
|
30
|
+
* Opt-in: a control that wants its own chart filtered passes none.
|
|
31
|
+
*/
|
|
32
|
+
chart?: object | null;
|
|
33
|
+
/**
|
|
34
|
+
* For a source that selects categories of one field: that field's identity
|
|
35
|
+
* key, and how to read the selected values from a write. Every clause the
|
|
36
|
+
* source writes carries both (`Clause.categorical`).
|
|
37
|
+
*/
|
|
38
|
+
categorical?: {
|
|
39
|
+
key: string;
|
|
40
|
+
values: (value: unknown) => ReadonlySet<unknown>;
|
|
41
|
+
};
|
|
36
42
|
/** How the source selects, read at hover time for preagg activation. */
|
|
37
43
|
activeDims?: () => ActiveDim[];
|
|
38
44
|
/**
|
|
@@ -41,16 +47,24 @@ export interface SourceOpts {
|
|
|
41
47
|
* while its bins are as fine as the pixels.
|
|
42
48
|
*/
|
|
43
49
|
view?: (out: Float64Array) => void;
|
|
44
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Schema metadata for UI filter pills, derived from the clause value. Run
|
|
52
|
+
* once per write and stored on the clause.
|
|
53
|
+
*/
|
|
45
54
|
augment?: (value: unknown) => FilterSchema | null;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
/**
|
|
56
|
+
* The source's clause was removed by someone other than this registration:
|
|
57
|
+
* `clearAll`, a `clearSource` from outside (a filter pill's remove button), or
|
|
58
|
+
* another registration under the same id clearing it. A control that keeps what
|
|
59
|
+
* it drew (a brush's rectangle) resets it here, because it reads no clause. Not
|
|
60
|
+
* called for the registration's own `clear()` or `destroy()`, which it initiated;
|
|
61
|
+
* and a control that clears itself from here does not loop, since the clause is
|
|
62
|
+
* already gone.
|
|
63
|
+
*/
|
|
64
|
+
onCleared?: () => void;
|
|
51
65
|
}
|
|
52
66
|
export interface SourceHandle {
|
|
53
|
-
/** Write the
|
|
67
|
+
/** Write the source's clause. */
|
|
54
68
|
update(predicate: FilterExpr, value: unknown): void;
|
|
55
69
|
clear(): void;
|
|
56
70
|
/** Prepare preagg tables for this source, ahead of its first use. */
|
|
@@ -77,12 +91,34 @@ export interface ToggleHandle extends Omit<SourceHandle, 'update'> {
|
|
|
77
91
|
toggle(tuple: unknown[], multi: boolean, total?: number): void;
|
|
78
92
|
/** Select exactly these tuples; empty clears. */
|
|
79
93
|
set(tuples: unknown[][]): void;
|
|
94
|
+
/**
|
|
95
|
+
* A click on Other bucket `otherId` (from facet cell `facetId`, or null):
|
|
96
|
+
* selects, adds or takes back the categories it holds, as its field's
|
|
97
|
+
* registry says (FieldActivity.otherClick). A one-field toggle only: a
|
|
98
|
+
* bucket names no value in a tuple of several.
|
|
99
|
+
*/
|
|
100
|
+
toggleOther(otherId: number, facetId: number | null, multi: boolean): void;
|
|
80
101
|
}
|
|
81
102
|
export declare class FilterGroup {
|
|
82
103
|
#private;
|
|
83
104
|
readonly selection: CrossfilterSelection;
|
|
105
|
+
/**
|
|
106
|
+
* This group's page-wide index, assigned by FilterGroupRegistry and never
|
|
107
|
+
* reused: a mark stores it to read this group's half of an activity block.
|
|
108
|
+
*/
|
|
109
|
+
readonly index: number;
|
|
110
|
+
/**
|
|
111
|
+
* A group made outside a registry (unit tests) keeps its field activity to
|
|
112
|
+
* itself, with no loop: its fades land at once.
|
|
113
|
+
*/
|
|
114
|
+
constructor(index?: number, activityOf?: (key: string) => FieldActivity);
|
|
115
|
+
/** The selection state of a categorical field, by identity key: this group's fade is `index`. */
|
|
116
|
+
activity(key: string): FieldActivity;
|
|
84
117
|
setEngine(engine: QueryEngine): void;
|
|
85
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Active filter items with schema metadata, for UI consumption. The schema
|
|
120
|
+
* is the clause's own, computed when it was written (see `update`).
|
|
121
|
+
*/
|
|
86
122
|
get items(): FilterItem[];
|
|
87
123
|
/** Number of active filters. */
|
|
88
124
|
get activeCount(): number;
|
|
@@ -90,8 +126,7 @@ export declare class FilterGroup {
|
|
|
90
126
|
fieldSql(field: string): string | null;
|
|
91
127
|
/**
|
|
92
128
|
* Register a selection source. Several registrations under one id are one
|
|
93
|
-
* source
|
|
94
|
-
* source dims are the union of theirs.
|
|
129
|
+
* source and write one clause.
|
|
95
130
|
*/
|
|
96
131
|
register(sourceId: string, opts: SourceOpts): SourceHandle;
|
|
97
132
|
/**
|
|
@@ -103,16 +138,27 @@ export declare class FilterGroup {
|
|
|
103
138
|
* as the selection. Nothing else writes those ids (`upsertManual` prefixes
|
|
104
139
|
* its own with `manual:`).
|
|
105
140
|
*/
|
|
106
|
-
registerToggle(fields: string | string[], opts
|
|
107
|
-
|
|
108
|
-
scales?: () => ActiveValueScale[];
|
|
141
|
+
registerToggle(fields: string | string[], opts?: {
|
|
142
|
+
chart?: object | null;
|
|
109
143
|
}): ToggleHandle;
|
|
110
|
-
upsertManual(key: string, predicate: FilterExpr, value: unknown,
|
|
144
|
+
upsertManual(key: string, predicate: FilterExpr, value: unknown, schema?: FilterSchema): void;
|
|
145
|
+
/**
|
|
146
|
+
* Hold `clause` under a manual key, as given: the relay passes another
|
|
147
|
+
* group's clause on. The clause it already holds there changes nothing, so
|
|
148
|
+
* it neither re-projects nor re-queries: the relay runs on every change in
|
|
149
|
+
* its source group, on every brush frame there.
|
|
150
|
+
*/
|
|
151
|
+
relayClause(key: string, clause: Clause): void;
|
|
111
152
|
removeManual(key: string): void;
|
|
112
153
|
/**
|
|
113
154
|
* Remove a source's clause. Re-queries only when there was a clause to
|
|
114
155
|
* remove: an unmount or a clear with nothing selected changes no mark's data.
|
|
156
|
+
*
|
|
157
|
+
* The source's registrations are told (`onCleared`) so a control that drew the
|
|
158
|
+
* selection resets it, all of them from outside, and all but `by` when a
|
|
159
|
+
* registration is the one clearing (it has reset itself).
|
|
115
160
|
*/
|
|
116
|
-
clearSource(sourceId: string): void;
|
|
161
|
+
clearSource(sourceId: string, by?: SourceOpts): void;
|
|
162
|
+
/** Remove every clause, and tell the controls that drew them (`onCleared`). */
|
|
117
163
|
clearAll(): void;
|
|
118
164
|
}
|