@pond-ts/charts 0.56.2 → 0.58.0
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/CHANGELOG.md +1218 -1
- package/dist/AreaChart.d.ts +12 -1
- package/dist/AreaChart.js +131 -13
- package/dist/BarChart.d.ts +84 -9
- package/dist/BarChart.js +295 -40
- package/dist/BarList.d.ts +85 -5
- package/dist/BarList.js +25 -4
- package/dist/BoxList.d.ts +70 -3
- package/dist/BoxList.js +21 -7
- package/dist/BoxPlot.d.ts +2 -1
- package/dist/BoxPlot.js +101 -9
- package/dist/Candlestick.d.ts +13 -1
- package/dist/Candlestick.js +89 -3
- package/dist/ChartContainer.d.ts +79 -48
- package/dist/ChartContainer.js +482 -60
- package/dist/ChartRow.d.ts +9 -2
- package/dist/ChartRow.js +86 -12
- package/dist/HeatMap.d.ts +176 -0
- package/dist/HeatMap.js +344 -0
- package/dist/Layers.d.ts +5 -1
- package/dist/Layers.js +1014 -253
- package/dist/Legend.js +8 -4
- package/dist/LineChart.d.ts +18 -1
- package/dist/LineChart.js +165 -4
- package/dist/ListTable.d.ts +30 -3
- package/dist/ListTable.js +381 -23
- package/dist/ScatterChart.d.ts +3 -2
- package/dist/ScatterChart.js +68 -4
- package/dist/XAxis.js +40 -22
- package/dist/YAxis.d.ts +28 -1
- package/dist/YAxis.js +24 -2
- package/dist/annotations.d.ts +74 -0
- package/dist/annotations.js +97 -7
- package/dist/area.d.ts +34 -1
- package/dist/area.js +88 -1
- package/dist/bars.d.ts +178 -5
- package/dist/bars.js +504 -46
- package/dist/box.d.ts +2 -2
- package/dist/box.js +158 -40
- package/dist/brush.d.ts +142 -0
- package/dist/brush.js +179 -0
- package/dist/child-index.d.ts +27 -0
- package/dist/child-index.js +57 -0
- package/dist/context.d.ts +871 -36
- package/dist/cursors.d.ts +161 -0
- package/dist/cursors.js +503 -0
- package/dist/decimate.d.ts +78 -1
- package/dist/decimate.js +157 -0
- package/dist/heat.d.ts +163 -0
- package/dist/heat.js +659 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +25 -2
- package/dist/line.d.ts +137 -0
- package/dist/line.js +328 -0
- package/dist/ohlc.d.ts +16 -1
- package/dist/ohlc.js +93 -4
- package/dist/scatter.d.ts +17 -9
- package/dist/scatter.js +221 -33
- package/dist/select.d.ts +13 -5
- package/dist/select.js +14 -6
- package/dist/selection-fixtures.d.ts +174 -0
- package/dist/selection-fixtures.js +569 -0
- package/dist/selection-stories.d.ts +73 -0
- package/dist/selection-stories.js +301 -0
- package/dist/selectors.d.ts +316 -0
- package/dist/selectors.js +391 -0
- package/dist/span.d.ts +122 -0
- package/dist/span.js +203 -0
- package/dist/sweep.d.ts +154 -0
- package/dist/sweep.js +282 -0
- package/dist/theme.d.ts +517 -11
- package/dist/theme.js +220 -39
- package/dist/tracker.d.ts +6 -0
- package/dist/tracker.js +6 -0
- package/dist/tradingAxis.fixture.d.ts +78 -0
- package/dist/tradingAxis.fixture.js +215 -0
- package/dist/useChartLegend.js +18 -3
- package/package.json +3 -3
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { cloneElement, useMemo, useState, } from 'react';
|
|
3
|
+
import { ChartContainer } from './ChartContainer.js';
|
|
4
|
+
import { ChartRow } from './ChartRow.js';
|
|
5
|
+
import { Layers } from './Layers.js';
|
|
6
|
+
import { YAxis } from './YAxis.js';
|
|
7
|
+
import { Selector, MultiSelector } from './selectors.js';
|
|
8
|
+
import { isSpanSelection, sameMark, selectionContains } from './span.js';
|
|
9
|
+
import { RangeCursor } from './cursors.js';
|
|
10
|
+
import { caption } from './selection-fixtures.js';
|
|
11
|
+
const H = 240;
|
|
12
|
+
/**
|
|
13
|
+
* The chart under test, wired identically in every cell. `children` are
|
|
14
|
+
* non-wrapping registrants rendered before the row (e.g. `<Cursor/>`);
|
|
15
|
+
* `selector`, when given, is a `<Selector>`/`<MultiSelector>` element that
|
|
16
|
+
* WRAPS the row instead (A10.1) — `Chart` clones it with the row as its
|
|
17
|
+
* children, so a story only ever writes the selector element itself and never
|
|
18
|
+
* has to hand-nest `<ChartRow>` inside it.
|
|
19
|
+
*/
|
|
20
|
+
function Chart({ fx, children, height = H, selector, ...container }) {
|
|
21
|
+
const row = (_jsxs(ChartRow, { height: height, children: [_jsx(YAxis, { id: fx.axis.id, label: fx.axis.label, min: fx.axis.min, max: fx.axis.max }), _jsx(Layers, { children: fx.renderLayer('svc') })] }));
|
|
22
|
+
return (_jsxs(ChartContainer, { width: 640, ...fx.container, ...container, children: [children, selector ? cloneElement(selector, undefined, row) : row] }));
|
|
23
|
+
}
|
|
24
|
+
const list = (fx, sel) => sel.map((m) => fx.describe(m)).join(', ') || '—';
|
|
25
|
+
export function makeSelectorStories(fx) {
|
|
26
|
+
const Cursor = () => (fx.rangeCursor ? _jsx(RangeCursor, {}) : null);
|
|
27
|
+
return {
|
|
28
|
+
/** **Mounted at the container** — the ordinary case: one `<Selector>` as a
|
|
29
|
+
* child of `<ChartContainer>`, enabling the click for every row. It
|
|
30
|
+
* reports; this story's `useState` is the selection. */
|
|
31
|
+
MountedAtContainer: {
|
|
32
|
+
render: function Render() {
|
|
33
|
+
const [sel, setSel] = useState([]);
|
|
34
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, selector: _jsx(Selector, { selected: sel, onSelect: (h) => setSel(h === null ? [] : [h]) }), children: _jsx(Cursor, {}) }), _jsxs("p", { style: caption, children: ["Click a mark to select it, empty space to clear.", ' ', _jsx("strong", { children: "selected:" }), " ", list(fx, sel)] })] }));
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
/** **Mounted inside a row** — nearest mount wins, mirroring the cursor
|
|
38
|
+
* components. The top row selects; the bottom row's clicks are inert even
|
|
39
|
+
* though its layer carries an `id`. */
|
|
40
|
+
MountedInRow: {
|
|
41
|
+
render: function Render() {
|
|
42
|
+
const [sel, setSel] = useState([]);
|
|
43
|
+
return (_jsxs("div", { children: [_jsxs(ChartContainer, { width: 640, ...fx.container, children: [_jsxs(ChartRow, { height: 150, children: [_jsx(YAxis, { id: fx.axis.id, label: fx.axis.label || 'primary', min: fx.axis.min, max: fx.axis.max }), _jsx(Cursor, {}), _jsx(Selector, { selected: sel, onSelect: (h) => setSel(h === null ? [] : [h]), children: _jsx(Layers, { children: fx.renderLayer('svc') }) })] }), _jsxs(ChartRow, { height: 110, children: [_jsx(YAxis, { id: fx.secondary.axis.id, label: fx.secondary.axis.label, min: fx.secondary.axis.min, max: fx.secondary.axis.max }), _jsx(Layers, { children: fx.secondary.renderLayer('err') })] })] }), _jsxs("p", { style: caption, children: ["The top row selects; the bottom row has no", ' ', _jsx("code", { children: "<Selector>" }), " in scope. ", _jsx("strong", { children: "selected:" }), ' ', list(fx, sel)] })] }));
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
/** **No selector — the plot is inert.** An id-bearing, hit-testable layer
|
|
47
|
+
* whose clicks go nowhere: what a chart that selected on click *before*
|
|
48
|
+
* §7.1 now looks like until one is mounted. In dev the first such click
|
|
49
|
+
* logs a one-time migration warning. */
|
|
50
|
+
NoSelector: {
|
|
51
|
+
render: () => (_jsxs("div", { children: [_jsx(Chart, { fx: fx, children: _jsx(Cursor, {}) }), _jsx("p", { style: caption, children: "Click anywhere \u2014 nothing selects. Check the console for the one-time migration warning." })] })),
|
|
52
|
+
},
|
|
53
|
+
/** **Controlled selection, gesture disabled** — `enabled={false}` exists
|
|
54
|
+
* to protect. The buttons stand in for the legend chip / filter list that
|
|
55
|
+
* really drives this: the chart *displays* a selection owned elsewhere,
|
|
56
|
+
* and the plot is deliberately inert. This is also why §7.1's warning
|
|
57
|
+
* suppresses whenever controlled `selected` is in effect (A2.6). */
|
|
58
|
+
ControlledNoSelector: {
|
|
59
|
+
render: function Render() {
|
|
60
|
+
const [sel, setSel] = useState([
|
|
61
|
+
fx.picks[0].info,
|
|
62
|
+
]);
|
|
63
|
+
const btn = {
|
|
64
|
+
font: '13px system-ui',
|
|
65
|
+
padding: '4px 10px',
|
|
66
|
+
marginRight: 6,
|
|
67
|
+
cursor: 'pointer',
|
|
68
|
+
};
|
|
69
|
+
return (_jsxs("div", { children: [_jsx("div", { style: { marginBottom: 8 }, children: fx.picks.map((p) => (_jsx("button", { style: btn, onClick: () => setSel((cur) => cur.some((m) => sameMark(m, p.info))
|
|
70
|
+
? cur.filter((m) => !sameMark(m, p.info))
|
|
71
|
+
: [...cur, p.info]), children: p.label }, p.label))) }), _jsx(Chart, { fx: fx, selector: _jsx(Selector, { enabled: false, selected: sel }) }), _jsxs("p", { style: caption, children: ["The buttons drive the highlight; clicking the plot does nothing.", ' ', _jsx("strong", { children: "selected:" }), " ", list(fx, sel)] })] }));
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
/** **The modifiers are reported; the policy is yours.** `onSelect`'s second
|
|
75
|
+
* argument carries `additive` (⌘ on macOS, Ctrl elsewhere) plus the raw
|
|
76
|
+
* keys. pond applies no policy and holds no set — the toggle below is this
|
|
77
|
+
* story's few lines, not the library's. */
|
|
78
|
+
ModifiersReported: {
|
|
79
|
+
render: function Render() {
|
|
80
|
+
const [sel, setSel] = useState([]);
|
|
81
|
+
const [last, setLast] = useState('—');
|
|
82
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, selector: _jsx(Selector, { selected: sel, onSelect: (hit, mods) => {
|
|
83
|
+
setLast(hit === null
|
|
84
|
+
? 'null hit → clear'
|
|
85
|
+
: `${fx.describe(hit)} · additive=${mods?.additive ?? false}` +
|
|
86
|
+
` shift=${mods?.shiftKey ?? false}` +
|
|
87
|
+
` alt=${mods?.altKey ?? false}`);
|
|
88
|
+
setSel((cur) => {
|
|
89
|
+
if (hit === null)
|
|
90
|
+
return [];
|
|
91
|
+
if (!(mods?.additive ?? false))
|
|
92
|
+
return [hit];
|
|
93
|
+
return cur.some((m) => sameMark(m, hit))
|
|
94
|
+
? cur.filter((m) => !sameMark(m, hit))
|
|
95
|
+
: [...cur, hit];
|
|
96
|
+
});
|
|
97
|
+
} }), children: _jsx(Cursor, {}) }), _jsxs("p", { style: caption, children: ["\u2318/Ctrl-click to add or remove; try shift and alt too.", ' ', _jsx("strong", { children: "reported:" }), " ", last, _jsx("br", {}), _jsx("strong", { children: "selected:" }), " ", list(fx, sel)] })] }));
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
/** **`onHover` only** — the readout-driving case. The click still selects
|
|
101
|
+
* (the *mount* is the enablement, not the callback), driving the
|
|
102
|
+
* container's own uncontrolled highlight since no `selected` is supplied. */
|
|
103
|
+
HoverOnly: {
|
|
104
|
+
render: function Render() {
|
|
105
|
+
const [hov, setHov] = useState(null);
|
|
106
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, selector: _jsx(Selector, { hovered: hov, onHover: setHov }), children: _jsx(Cursor, {}) }), _jsxs("p", { style: caption, children: [_jsx("strong", { children: "hovered:" }), ' ', hov === null ? '—' : `${fx.describe(hov)} (${hov.value})`] })] }));
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
/** **A bare `<Selector />`** — no callbacks at all; the mount is the whole
|
|
110
|
+
* statement. With no `selected` prop the container keeps the selection
|
|
111
|
+
* itself: the smallest working chart under the new model. */
|
|
112
|
+
BareSelector: {
|
|
113
|
+
render: () => (_jsxs("div", { children: [_jsx(Chart, { fx: fx, selector: _jsx(Selector, {}), children: _jsx(Cursor, {}) }), _jsxs("p", { style: caption, children: ["Uncontrolled: ", _jsx("code", { children: "<Selector />" }), " with no props, no", ' ', _jsx("code", { children: "selected" }), "."] })] })),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// ── <MultiSelector> ────────────────────────────────────────────────────────
|
|
118
|
+
const describeEntries = (fx, sel) => {
|
|
119
|
+
if (sel.length === 0)
|
|
120
|
+
return '—';
|
|
121
|
+
return sel
|
|
122
|
+
.map((e) => 'kind' in e && e.kind === 'span'
|
|
123
|
+
? `span [${fx.describe({ key: e.x[0] })} → ${fx.describe({ key: e.x[1] })})`
|
|
124
|
+
: fx.describe(e))
|
|
125
|
+
.join(', ');
|
|
126
|
+
};
|
|
127
|
+
export function makeMultiSelectorStories(fx) {
|
|
128
|
+
if (!fx.sweep)
|
|
129
|
+
return null;
|
|
130
|
+
const stories = {
|
|
131
|
+
/** **Sweep, freeform.** Drag across the marks: the shared brush band
|
|
132
|
+
* tracks the drag (bin-snapped — a bar layer's own bins feed the snap
|
|
133
|
+
* channel), release reports the covered marks plus the span, and the
|
|
134
|
+
* consumer feeds the **span** back as `selected` — one compact entry
|
|
135
|
+
* however many marks it covers. A click still selects one. */
|
|
136
|
+
SweepMarks: {
|
|
137
|
+
render: function Render() {
|
|
138
|
+
const [sel, setSel] = useState([]);
|
|
139
|
+
const [count, setCount] = useState(0);
|
|
140
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, onSelect: (hits, _mods, spans) => {
|
|
141
|
+
setCount(hits.length);
|
|
142
|
+
setSel(spans.length > 0 ? [...spans] : hits.slice(0, 1));
|
|
143
|
+
} }) }), _jsxs("p", { style: caption, children: ["Drag to sweep \u00B7 click one mark to select just it \u00B7 click away to clear.", _jsx("br", {}), _jsx("strong", { children: "selected:" }), " ", describeEntries(fx, sel), " (", count, ' ', "marks)"] })] }));
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
/** **A click still selects one.** `<MultiSelector>` is a *superset* of
|
|
147
|
+
* `<Selector>`: below `DRAG_SLOP` the gesture is a click and reports
|
|
148
|
+
* `([hit], modifiers, null)` — a null span, since there is no range. */
|
|
149
|
+
ClickStillSelectsOne: {
|
|
150
|
+
render: function Render() {
|
|
151
|
+
const [sel, setSel] = useState([]);
|
|
152
|
+
const [shape, setShape] = useState('—');
|
|
153
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, onSelect: (hits, _mods, spans) => {
|
|
154
|
+
setShape(spans.length === 0
|
|
155
|
+
? `click → ${hits.length} hit, no span`
|
|
156
|
+
: `sweep → ${hits.length} hits, ${spans.length} span(s)`);
|
|
157
|
+
setSel(spans.length > 0 ? [...spans] : hits);
|
|
158
|
+
} }) }), _jsxs("p", { style: caption, children: ["Click, then drag, and watch the shape of the report change.", ' ', _jsx("strong", { children: "last:" }), " ", shape] })] }));
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
/** **Live preview.** Every covered mark lights through the plural
|
|
162
|
+
* `hovered` while the drag is in flight — and at rest the block a drag
|
|
163
|
+
* *would* select is already previewed. The count tracks the band before
|
|
164
|
+
* release; on release the commit equals what was lit. */
|
|
165
|
+
LivePreviewDuringDrag: {
|
|
166
|
+
render: function Render() {
|
|
167
|
+
const [sel, setSel] = useState([]);
|
|
168
|
+
const [preview, setPreview] = useState(0);
|
|
169
|
+
const [committed, setCommitted] = useState(0);
|
|
170
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, onHover: (hits) => setPreview(hits.length), onSelect: (hits, _mods, spans) => {
|
|
171
|
+
setCommitted(hits.length);
|
|
172
|
+
setSel(spans.length > 0 ? [...spans] : hits);
|
|
173
|
+
} }) }), _jsxs("p", { style: caption, children: ["Hover, then drag, and watch the count track the band before you release.", _jsx("br", {}), _jsx("strong", { children: "previewing:" }), " ", preview, " marks \u00B7", ' ', _jsx("strong", { children: "last commit:" }), " ", committed, " marks"] })] }));
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
/** **Additive sweep, then edit.** ⌘/Ctrl-drag adds a span to the
|
|
177
|
+
* selection. The policy is the consumer's — and note it must handle the
|
|
178
|
+
* **click** case too: click and sweep arrive through one callback, so a
|
|
179
|
+
* policy that only handles spans deletes the selection on the very
|
|
180
|
+
* gesture meant to extend it. */
|
|
181
|
+
SweepAdditive: {
|
|
182
|
+
render: function Render() {
|
|
183
|
+
const [sel, setSel] = useState([]);
|
|
184
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, onSelect: (hits, mods, spans) => {
|
|
185
|
+
const add = mods?.additive ?? false;
|
|
186
|
+
if (spans.length > 0) {
|
|
187
|
+
setSel((cur) => (add ? [...cur, ...spans] : [...spans]));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (hits.length === 0)
|
|
191
|
+
return setSel([]);
|
|
192
|
+
setSel((cur) => (add ? [...cur, hits[0]] : [hits[0]]));
|
|
193
|
+
} }) }), _jsxs("p", { style: caption, children: ["Sweep a range, then \u2318/Ctrl-click a mark to add it \u2014 and plain-click to replace. ", _jsx("strong", { children: "selected:" }), ' ', describeEntries(fx, sel)] })] }));
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
/** **Demote on edit (RFC A5.2).** A span is editable only *as a whole*; to
|
|
197
|
+
* edit inside one, swap the span entry for the marks stashed at commit
|
|
198
|
+
* time and filter. Plain array arithmetic — pond computes no policy, which
|
|
199
|
+
* is exactly why the descriptor has this shape. */
|
|
200
|
+
DemoteOnEdit: {
|
|
201
|
+
render: function Render() {
|
|
202
|
+
const [sel, setSel] = useState([]);
|
|
203
|
+
const [stash, setStash] = useState([]);
|
|
204
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, onSelect: (hits, mods, spans) => {
|
|
205
|
+
if (spans.length > 0) {
|
|
206
|
+
setStash(hits);
|
|
207
|
+
setSel([...spans]);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const hit = hits[0];
|
|
211
|
+
if (hit === undefined)
|
|
212
|
+
return setSel([]);
|
|
213
|
+
if (!(mods?.additive ?? false))
|
|
214
|
+
return setSel([hit]);
|
|
215
|
+
// The ⌘-click policy `selectionContains`' doc describes,
|
|
216
|
+
// written out in full: a mark already in the selection comes
|
|
217
|
+
// OUT, one that isn't goes in. The demote is the extra step
|
|
218
|
+
// a span needs before it can lose a member — it becomes the
|
|
219
|
+
// marks it covered, minus this one.
|
|
220
|
+
//
|
|
221
|
+
// Two mistakes this has already made, both worth the words:
|
|
222
|
+
//
|
|
223
|
+
// - **`sameMark`, not `m.key !== hit.key`.** A key IS a
|
|
224
|
+
// bar's identity, so the shorter test looks right — and on
|
|
225
|
+
// a stack or a heat map it knocks out every mark in the
|
|
226
|
+
// bin, which showed up as a column-shaped hole.
|
|
227
|
+
// - **Handle the MARK arm too.** Demoting only rewrote the
|
|
228
|
+
// span entries, so once the span was gone every later
|
|
229
|
+
// ⌘-click fell through the `flatMap` unchanged and the
|
|
230
|
+
// second knock-out silently did nothing.
|
|
231
|
+
setSel((cur) => selectionContains(cur, hit)
|
|
232
|
+
? cur.flatMap((e) => isSpanSelection(e)
|
|
233
|
+
? stash.filter((m) => !sameMark(m, hit))
|
|
234
|
+
: sameMark(e, hit)
|
|
235
|
+
? []
|
|
236
|
+
: [e])
|
|
237
|
+
: [...cur, hit]);
|
|
238
|
+
} }) }), _jsxs("p", { style: caption, children: ["Sweep a run, then \u2318/Ctrl-click marks inside it to knock them out one by one \u2014 the span demotes to its marks on the first, and every \u2318-click after that toggles. ", _jsx("strong", { children: "selected:" }), ' ', describeEntries(fx, sel)] })] }));
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
if (fx.sequence !== undefined) {
|
|
243
|
+
const seq = fx.sequence;
|
|
244
|
+
/** **Sweep, snapped to a sequence.** The band, the preview and the
|
|
245
|
+
* committed span all snap to whole buckets — at rest the block under the
|
|
246
|
+
* pointer is already previewed, so what will be selected is visible before
|
|
247
|
+
* the drag starts. Only fixtures with a time bucketing generate this. */
|
|
248
|
+
stories.SweepWithSequence = {
|
|
249
|
+
render: function Render() {
|
|
250
|
+
const [sel, setSel] = useState([]);
|
|
251
|
+
const [count, setCount] = useState(0);
|
|
252
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, sequence: seq(), onSelect: (hits, _mods, spans) => {
|
|
253
|
+
setCount(hits.length);
|
|
254
|
+
setSel(spans.length > 0 ? [...spans] : hits);
|
|
255
|
+
} }) }), _jsxs("p", { style: caption, children: ["The sweep snaps to whole buckets \u2014 hover first and the whole block previews. ", _jsx("strong", { children: "selected:" }), " ", describeEntries(fx, sel), " (", count, " marks)"] })] }));
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return stories;
|
|
260
|
+
}
|
|
261
|
+
export function makeSessionStories(fx) {
|
|
262
|
+
const sessions = fx.sessions;
|
|
263
|
+
if (sessions === undefined)
|
|
264
|
+
return null;
|
|
265
|
+
/** Both cells are the same chart under a different bucketing — the only
|
|
266
|
+
* variable is the sequence, which is the whole comparison. */
|
|
267
|
+
const cell = (sequence, note) => ({
|
|
268
|
+
render: function Render() {
|
|
269
|
+
const [sel, setSel] = useState([]);
|
|
270
|
+
const [count, setCount] = useState(0);
|
|
271
|
+
const [preview, setPreview] = useState(0);
|
|
272
|
+
const seq = useMemo(sequence, []);
|
|
273
|
+
return (_jsxs("div", { children: [_jsx(Chart, { fx: fx, height: 220, selector: _jsx(MultiSelector, { selected: sel, sequence: seq, onHover: (hits) => setPreview(hits.length), onSelect: (hits, _mods, spans) => {
|
|
274
|
+
setCount(hits.length);
|
|
275
|
+
setSel(spans.length > 0 ? [...spans] : hits);
|
|
276
|
+
} }) }), _jsxs("p", { style: caption, children: [note, _jsx("br", {}), _jsx("strong", { children: "previewing:" }), " ", preview, " bars \u00B7", ' ', _jsx("strong", { children: "selected:" }), " ", describeEntries(fx, sel), " (", count, ' ', "bars)"] })] }));
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
return {
|
|
280
|
+
/** **The bucketing agrees with the sessions.** One block per session, so
|
|
281
|
+
* every block edge lands exactly on a divider and no block spans a
|
|
282
|
+
* collapsed gap. Hover a bar and its whole session lights; a click
|
|
283
|
+
* commits that session. */
|
|
284
|
+
SequenceConformsToSessions: cell(sessions.conforming, _jsxs(_Fragment, { children: ["One bucket ", _jsx("em", { children: "per session" }), ": the band's edges land on the dividers, and no block ever spans a break."] })),
|
|
285
|
+
/**
|
|
286
|
+
* **The bucketing doesn't.** A wall-clock day anchored mid-session, so a
|
|
287
|
+
* block is the afternoon of one session plus the morning of the next, and
|
|
288
|
+
* the band crosses a divider. The collapsed gap has zero width, so the
|
|
289
|
+
* block still draws as one rectangle — what it *contains* is two runs of
|
|
290
|
+
* bars from different days.
|
|
291
|
+
*
|
|
292
|
+
* The weekend is where that stops being cosmetic. Wall-clock buckets keep
|
|
293
|
+
* marching through Saturday and Sunday while the axis has no sessions to
|
|
294
|
+
* give them, so the uniform run of 7-bar blocks breaks into **4, then an
|
|
295
|
+
* empty bucket with no trading time in it at all, then 3** — a bucketing
|
|
296
|
+
* that ignores the session grid cannot keep its blocks the same size.
|
|
297
|
+
*/
|
|
298
|
+
SequenceCrossesSessions: cell(sessions.crossing, _jsxs(_Fragment, { children: ["A wall-clock day anchored ", _jsx("em", { children: "mid-session" }), ": each block takes one afternoon plus the next morning, so the band crosses a divider. Hover either side of the ", _jsx("strong", { children: "weekend" }), " divider \u2014 the blocks there are 4 bars and 3, not 7, and the bucket between them holds no trading time at all."] })),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=selection-stories.js.map
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { Sequence, BoundedSequence } from 'pond-ts';
|
|
3
|
+
import { type SelectInfo, type SelectModifiers, type SelectionEntry, type SelectorEntry, type SpanSelection } from './context.js';
|
|
4
|
+
export interface SelectorProps {
|
|
5
|
+
/**
|
|
6
|
+
* `false` disables the **gesture** — no hit-testing, `onHover`/`onSelect`
|
|
7
|
+
* never fire, a plot click behaves as if `<Selector>` weren't mounted at
|
|
8
|
+
* all. **Omitted ⇒ `true`.**
|
|
9
|
+
*
|
|
10
|
+
* `<Selector enabled={false} selected={sel} />` is controlled highlighting
|
|
11
|
+
* with no plot gesture — a legend chip or an external filter list driving
|
|
12
|
+
* the chart, deliberately inert on click (interaction RFC A10.2).
|
|
13
|
+
*/
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Controlled selection — the selected mark(s) (echo `onSelect`'s hit back),
|
|
17
|
+
* or `null`. **Omitted ⇒ uncontrolled** (a click on a selectable layer
|
|
18
|
+
* manages it internally; pass `null` to force nothing selected). A layer is
|
|
19
|
+
* **selectable only when it carries an `id`** — the stable series identity.
|
|
20
|
+
*
|
|
21
|
+
* **Accepts a set**: an array lights several marks at once, and array
|
|
22
|
+
* entries may be {@link SpanSelection}s (a swept range, demoted to one
|
|
23
|
+
* entry instead of enumerating every covered mark). A single `SelectInfo`
|
|
24
|
+
* still works and means exactly what it did.
|
|
25
|
+
*/
|
|
26
|
+
selected?: SelectInfo | readonly SelectionEntry[] | null;
|
|
27
|
+
/**
|
|
28
|
+
* Controlled hover-highlight — the transiently lit mark(s), or `null`.
|
|
29
|
+
* **Omitted ⇒ uncontrolled.** The hover analog of {@link selected}: pass it
|
|
30
|
+
* to pin lit marks from outside the chart (e.g. hovering a legend / list row
|
|
31
|
+
* lights the matching bar). Accepts a single mark or a set, the same union
|
|
32
|
+
* `selected` takes.
|
|
33
|
+
*/
|
|
34
|
+
hovered?: SelectInfo | readonly SelectInfo[] | null;
|
|
35
|
+
/**
|
|
36
|
+
* What is under the pointer — one {@link SelectInfo}, or `null` on leaving
|
|
37
|
+
* every mark. Deduped by the mark's full identity, so it fires on a mark
|
|
38
|
+
* transition rather than on every pointer move.
|
|
39
|
+
*/
|
|
40
|
+
onHover?: (hit: SelectInfo | null) => void;
|
|
41
|
+
/**
|
|
42
|
+
* What was clicked — one {@link SelectInfo}, or `null` for a click that hit
|
|
43
|
+
* no mark (the deselect path) — plus the modifiers held.
|
|
44
|
+
*
|
|
45
|
+
* **The library reports; you decide.** `modifiers.additive` is the
|
|
46
|
+
* platform-idiomatic add chord (⌘ on macOS, Ctrl elsewhere); pond applies no
|
|
47
|
+
* policy to it and holds no set. Compute the next selection yourself and
|
|
48
|
+
* feed it back as this same component's `selected`:
|
|
49
|
+
*
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <Selector
|
|
52
|
+
* selected={sel}
|
|
53
|
+
* onSelect={(hit, mods) =>
|
|
54
|
+
* setSel(
|
|
55
|
+
* hit === null ? [] : mods?.additive ? toggle(sel, hit) : [hit],
|
|
56
|
+
* )
|
|
57
|
+
* }
|
|
58
|
+
* />
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* `modifiers` is absent for a **programmatic** select (a `<Legend>` chip),
|
|
62
|
+
* which carries no keyboard state.
|
|
63
|
+
*/
|
|
64
|
+
onSelect?: (hit: SelectInfo | null, modifiers?: SelectModifiers) => void;
|
|
65
|
+
/**
|
|
66
|
+
* What it applies to (interaction RFC A10.1): every `<ChartRow>` when
|
|
67
|
+
* mounted as a direct child of `<ChartContainer>`, or just one row when
|
|
68
|
+
* mounted inside that `<ChartRow>`. Optional — `<Selector />` with no
|
|
69
|
+
* children keeps working exactly as it always has.
|
|
70
|
+
*
|
|
71
|
+
* **Row-scoped, wrap the row's `<Layers>` — not its axes.** `<ChartRow>`
|
|
72
|
+
* places axes into gutters by matching its *own* children against `<YAxis>`,
|
|
73
|
+
* so an axis nested inside this component is invisible to that sort and
|
|
74
|
+
* renders in the plot column instead. Dev warns if you do.
|
|
75
|
+
*
|
|
76
|
+
* ```tsx
|
|
77
|
+
* <ChartRow height={180}>
|
|
78
|
+
* <YAxis id="v" /> // stays a direct child of the row
|
|
79
|
+
* <Selector selected={sel} onSelect={setSel}>
|
|
80
|
+
* <Layers>…</Layers>
|
|
81
|
+
* </Selector>
|
|
82
|
+
* </ChartRow>
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
children?: ReactNode;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Mount it to make the plot **click-selectable** (RFC §7.1) and to hold the
|
|
89
|
+
* selection state that produces — wrap every `<ChartRow>` as a direct child of
|
|
90
|
+
* `<ChartContainer>`, or wrap one row's **`<Layers>`** to scope the *gesture*
|
|
91
|
+
* to that row (interaction RFC A10.1; see {@link SelectorProps.children} for
|
|
92
|
+
* why the axes stay outside).
|
|
93
|
+
*
|
|
94
|
+
* **Placement scopes the gesture, not the state.** A {@link SelectInfo} names a
|
|
95
|
+
* *layer*, not a row, so `selected` / `hovered` apply chart-wide wherever this
|
|
96
|
+
* sits — one selector should own each per chart (first registered wins, dev
|
|
97
|
+
* warns otherwise).
|
|
98
|
+
*
|
|
99
|
+
* ```tsx
|
|
100
|
+
* <ChartContainer>
|
|
101
|
+
* <Selector
|
|
102
|
+
* selected={sel}
|
|
103
|
+
* hovered={hov}
|
|
104
|
+
* onSelect={(hit, mods) => …}
|
|
105
|
+
* onHover={setHov}
|
|
106
|
+
* >
|
|
107
|
+
* <ChartRow>…</ChartRow>
|
|
108
|
+
* </Selector>
|
|
109
|
+
* </ChartContainer>
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* `value`/`onChange`, one component: `selected` is what's lit, `onSelect`
|
|
113
|
+
* reports what changes it, and the mount itself is what makes a plot click do
|
|
114
|
+
* anything. Need controlled highlighting with **no** plot click at all?
|
|
115
|
+
* `enabled={false}`.
|
|
116
|
+
*/
|
|
117
|
+
export declare function Selector({ enabled, selected, hovered, onHover, onSelect, children, }?: SelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
118
|
+
export interface MultiSelectorProps {
|
|
119
|
+
/**
|
|
120
|
+
* `false` disables the gesture — no hit-testing, no armed sweep, callbacks
|
|
121
|
+
* never fire. **Omitted ⇒ `true`.** See {@link SelectorProps.enabled};
|
|
122
|
+
* applies identically here.
|
|
123
|
+
*/
|
|
124
|
+
enabled?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Controlled selection — the state half of what `onSelect` reports. See
|
|
127
|
+
* {@link SelectorProps.selected}; `<MultiSelector>` additionally accepts
|
|
128
|
+
* {@link SpanSelection} entries directly, which is exactly the shape a
|
|
129
|
+
* sweep's `onSelect` hands back.
|
|
130
|
+
*/
|
|
131
|
+
selected?: SelectInfo | readonly SelectionEntry[] | null;
|
|
132
|
+
/** Controlled hover-highlight — see {@link SelectorProps.hovered}. Accepts
|
|
133
|
+
* a set, since a sweep's live preview lights several marks at once. */
|
|
134
|
+
hovered?: SelectInfo | readonly SelectInfo[] | null;
|
|
135
|
+
/**
|
|
136
|
+
* Snap the sweep to buckets — a pond `Sequence` (realized over the view) or
|
|
137
|
+
* `BoundedSequence` (used as-is; a trading calendar's sessions). A drag
|
|
138
|
+
* extends **bucket by bucket** over these, capturing every mark the snapped
|
|
139
|
+
* window covers. **Omit ⇒ freeform**: the sweep covers the raw drag span (a
|
|
140
|
+
* bar/histogram layer's bins still snap it when present — the same shared
|
|
141
|
+
* snap-bucket channel `<RangeCursor sequence>` feeds). Pass a stable
|
|
142
|
+
* reference (the realized buckets memoize on it).
|
|
143
|
+
*/
|
|
144
|
+
sequence?: Sequence | BoundedSequence;
|
|
145
|
+
/**
|
|
146
|
+
* The marks the gesture currently covers — or **would** cover:
|
|
147
|
+
*
|
|
148
|
+
* - **At rest**, the marks of the **snap block under the pointer** (the
|
|
149
|
+
* `sequence` bucket, else the layer's own bin/slot), reported once per
|
|
150
|
+
* block transition. This is the resting preview: hovering ANY mark of a
|
|
151
|
+
* block reports the whole block, because that is exactly the set a drag
|
|
152
|
+
* begun and released there would select.
|
|
153
|
+
* - **During a sweep**, every covered mark, updated as the drag crosses
|
|
154
|
+
* marks (coalesced to animation frames past the first cut).
|
|
155
|
+
*
|
|
156
|
+
* Echo it back as this component's `hovered` only when you control hover —
|
|
157
|
+
* uncontrolled, the covered marks already light through the container's
|
|
158
|
+
* own hover state (RFC A3.4: the library owns the state, each layer draws
|
|
159
|
+
* its own hover treatment).
|
|
160
|
+
*/
|
|
161
|
+
onHover?: (hits: readonly SelectInfo[]) => void;
|
|
162
|
+
/**
|
|
163
|
+
* The committed selection, on release (RFC A5.2's signature):
|
|
164
|
+
*
|
|
165
|
+
* - **A sweep** reports every covered mark, the modifiers held, and the
|
|
166
|
+
* {@link SpanSelection}s the coverage demotes to — `hits` are the
|
|
167
|
+
* materialised live preview (no fresh range query), `spans` are the
|
|
168
|
+
* snapped-outward extents whose `selectionContains` test reproduces
|
|
169
|
+
* exactly `hits`. Feed `[...others, ...spans]` back as this component's
|
|
170
|
+
* `selected` and stash `hits` for A5.2's demote-on-edit: to edit *inside*
|
|
171
|
+
* a span later, swap that span entry for the stashed hits and filter —
|
|
172
|
+
* plain array arithmetic, no interval math.
|
|
173
|
+
* - **A click** (no movement past the drag slop) is `<Selector>`'s gesture
|
|
174
|
+
* in this currency: one hit (or none — the deselect path), the modifiers,
|
|
175
|
+
* and an **empty** `spans`. Clicks produce marks; only sweeps produce
|
|
176
|
+
* spans.
|
|
177
|
+
*
|
|
178
|
+
* **`spans` is plural because one sweep can commit several.** A trace sweep
|
|
179
|
+
* produces one span per trace ([PND-TRACESEL]): every trace shares the swept
|
|
180
|
+
* x window, so singling one out by z-order would be arbitrary to the reader.
|
|
181
|
+
* Mark layers keep topmost-wins, so there it holds exactly one. **Topmost
|
|
182
|
+
* layer first**, and compare spans by `id` rather than identity — each
|
|
183
|
+
* span-only layer clamps the window to *its own* key range, so two traces of
|
|
184
|
+
* different extents report different `x` for one drag.
|
|
185
|
+
*
|
|
186
|
+
* `modifiers` is absent for a **programmatic** select (a `<Legend>` chip),
|
|
187
|
+
* as on `<Selector onSelect>`. **The library reports; you decide** — pond
|
|
188
|
+
* applies no policy to the modifiers and holds no set.
|
|
189
|
+
*/
|
|
190
|
+
onSelect?: (hits: readonly SelectInfo[], modifiers: SelectModifiers | undefined, spans: readonly SpanSelection[]) => void;
|
|
191
|
+
/** What it applies to — see {@link SelectorProps.children} (interaction RFC
|
|
192
|
+
* A10.1); identical scoping rule. */
|
|
193
|
+
children?: ReactNode;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* `<MultiSelector>` — **sweep-select as a mounted component** (interaction RFC
|
|
197
|
+
* §8 / A4.2 / A10), a superset of `<Selector>`: a click still selects one mark,
|
|
198
|
+
* and a drag past the slop **sweeps** — the band extends (bucket by bucket
|
|
199
|
+
* with a {@link MultiSelectorProps.sequence}, freeform without), every
|
|
200
|
+
* covered mark lights through the plural `hovered` as the drag moves, and
|
|
201
|
+
* release commits `(hits, modifiers, spans)` once. The gesture rides the
|
|
202
|
+
* shared brush recognizer (`brush.tsx`) and draws the same band `<RangeCursor>`
|
|
203
|
+
* does — identical pixels, different currency (§8.1): the range cursor
|
|
204
|
+
* releases an extent, this releases **marks** (which is what folds the
|
|
205
|
+
* category axis in — ordinal and continuous are the same gesture when nobody
|
|
206
|
+
* sees a numeric range).
|
|
207
|
+
*
|
|
208
|
+
* ```tsx
|
|
209
|
+
* <ChartContainer>
|
|
210
|
+
* <MultiSelector
|
|
211
|
+
* selected={sel}
|
|
212
|
+
* sequence={daily}
|
|
213
|
+
* onSelect={(hits, mods, spans) => …}
|
|
214
|
+
* >
|
|
215
|
+
* <ChartRow>…</ChartRow>
|
|
216
|
+
* </MultiSelector>
|
|
217
|
+
* </ChartContainer>
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* Wraps what it applies to, same as `<Selector>`; `selected` / `hovered` live
|
|
221
|
+
* here too. The sweep captures marks from the row's **topmost** sweep-capable
|
|
222
|
+
* layer (the z-order rule a click already follows); a layer without an `id`
|
|
223
|
+
* is never swept (Q8).
|
|
224
|
+
*
|
|
225
|
+
* **Mounting also changes the row's RESTING state** — the grey band and the
|
|
226
|
+
* hover are a live preview of the block a drag would select:
|
|
227
|
+
*
|
|
228
|
+
* - The shared brush band becomes the **resting cursor**, spanning the snap
|
|
229
|
+
* block under the pointer (the `sequence` bucket, else the layer's own
|
|
230
|
+
* bin/slot), replacing the container's implicit `'line'` default. An
|
|
231
|
+
* explicitly chosen cursor — a mounted component, or a legacy `cursor`
|
|
232
|
+
* string the consumer actually set — still wins the surface.
|
|
233
|
+
* - Hover is **block-scoped**: pointing at any one mark of a block lights
|
|
234
|
+
* (and reports) every mark in it. Rest and drag share one code path — the
|
|
235
|
+
* same snap buckets, the same layer session — so what the rest previews and
|
|
236
|
+
* what a drag commits cannot disagree; the drag just grows the same band.
|
|
237
|
+
*/
|
|
238
|
+
export declare function MultiSelector({ enabled, selected, hovered, sequence, onHover, onSelect, children, }?: MultiSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
239
|
+
/**
|
|
240
|
+
* **Value-equality for a registered selector — the guard that stops a
|
|
241
|
+
* controlled selection from looping.** `registerSelector` must no-op when the
|
|
242
|
+
* incoming entry is value-equal to the stored one, exactly as `registerAxis`
|
|
243
|
+
* does via `axisSpecEqual` (`ChartRow.tsx`), and for the same reason spelled
|
|
244
|
+
* out there: register → `setState` → re-render → register is a
|
|
245
|
+
* "Maximum update depth exceeded" spin.
|
|
246
|
+
*
|
|
247
|
+
* A10.3 made this guard load-bearing rather than defensive. The entry now
|
|
248
|
+
* carries the controlled *values*, and a consumer writing
|
|
249
|
+
* `selected={[hit]}` — or `selected={[{ id, key, … }]}` — mints a fresh
|
|
250
|
+
* reference every render. On its own that is survivable, because a
|
|
251
|
+
* container-only state update does not re-run the consumer's JSX. **But a
|
|
252
|
+
* descendant that consumes the container context and renders that inline
|
|
253
|
+
* array does re-run** — `useChartLegend()` is a supported example — so the
|
|
254
|
+
* chain became: registry update → new frame → context change → descendant
|
|
255
|
+
* re-render → fresh array → register → registry update, without end. Compare
|
|
256
|
+
* by value and the cycle closes on the first iteration. (Codex finding on
|
|
257
|
+
* #638; the reference-only guard it replaces was mine.)
|
|
258
|
+
*
|
|
259
|
+
* Cost: the common case is a stable array from `useState`, which hits the
|
|
260
|
+
* reference fast path. A fresh array costs one element-wise pass with no
|
|
261
|
+
* allocation, which is the right trade against an unbounded render loop.
|
|
262
|
+
*/
|
|
263
|
+
export declare function selectorEntryEqual(a: SelectorEntry, b: SelectorEntry): boolean;
|
|
264
|
+
/**
|
|
265
|
+
* The selectors in effect for a row's GESTURE: the row's own mounts when it has
|
|
266
|
+
* any (the per-row scope — nearest mount wins, mirroring
|
|
267
|
+
* `effectiveCursorEntries`), else the container-scoped mounts. A
|
|
268
|
+
* `gestureEnabled: false` entry (`<Selector enabled={false}>`) is filtered out
|
|
269
|
+
* entirely — it behaves as unmounted for click/hover/sweep purposes, exactly
|
|
270
|
+
* as `enabled` promises.
|
|
271
|
+
*
|
|
272
|
+
* `rowKey` of `null` asks for the **container** scope only — the programmatic
|
|
273
|
+
* (legend) path, which belongs to no row.
|
|
274
|
+
*
|
|
275
|
+
* Controlled-state resolution does **not** use this function — state is
|
|
276
|
+
* chart-wide, not row-scoped, and a disabled selector may still own it. See
|
|
277
|
+
* {@link resolveControlledSelected} / {@link resolveControlledHovered}.
|
|
278
|
+
*/
|
|
279
|
+
export declare function effectiveSelectorEntries(all: readonly SelectorEntry[], rowKey: symbol | null): readonly SelectorEntry[];
|
|
280
|
+
/**
|
|
281
|
+
* Resolve the chart-wide controlled `selected`, from whichever registered
|
|
282
|
+
* selector declared it. Not row-scoped — selection identity spans every row
|
|
283
|
+
* (a `SelectInfo.id` names a layer, not a row) — and not filtered on
|
|
284
|
+
* `gestureEnabled`: `<Selector enabled={false} selected={…}>` is exactly the
|
|
285
|
+
* "state, no gesture" configuration `enabled` exists for.
|
|
286
|
+
*/
|
|
287
|
+
export declare function resolveControlledSelected(all: readonly SelectorEntry[], warned: {
|
|
288
|
+
current: boolean;
|
|
289
|
+
}): {
|
|
290
|
+
readonly present: boolean;
|
|
291
|
+
readonly value: SelectInfo | readonly SelectionEntry[] | null;
|
|
292
|
+
};
|
|
293
|
+
/** As {@link resolveControlledSelected}, for `hovered`. */
|
|
294
|
+
export declare function resolveControlledHovered(all: readonly SelectorEntry[], warned: {
|
|
295
|
+
current: boolean;
|
|
296
|
+
}): {
|
|
297
|
+
readonly present: boolean;
|
|
298
|
+
readonly value: SelectInfo | readonly SelectInfo[] | null;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* RFC §7.1's softening: a plot click resolved to a real mark and there was no
|
|
302
|
+
* `<Selector>` to tell — the exact path that goes silently inert on upgrade.
|
|
303
|
+
*
|
|
304
|
+
* **Suppressed when controlled `selected` is in effect** (A2.6) — that is the
|
|
305
|
+
* runtime signature of the *endorsed* controlled-highlight setup
|
|
306
|
+
* (`<Selector enabled={false} selected={…}>`, plot deliberately inert), and
|
|
307
|
+
* the warning should not spend its loudness on people already doing that.
|
|
308
|
+
*
|
|
309
|
+
* Not permanent: once mounting is the established model, an `id` without a
|
|
310
|
+
* `<Selector>` is a legitimate configuration (Q8) and warning on it forever
|
|
311
|
+
* would flag a supported setup. Fires once per container.
|
|
312
|
+
*/
|
|
313
|
+
export declare function warnInertClick(warned: {
|
|
314
|
+
current: boolean;
|
|
315
|
+
}): void;
|
|
316
|
+
//# sourceMappingURL=selectors.d.ts.map
|