@ggts-sh/skill 0.43.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/LICENSE +21 -0
- package/README.md +80 -0
- package/SKILL.md +196 -0
- package/package.json +39 -0
- package/references/composition-surfaces.md +199 -0
- package/references/geoms-and-stats.md +276 -0
- package/references/grammar.md +111 -0
- package/references/interactions.md +312 -0
- package/references/react.md +101 -0
- package/references/recipes.md +441 -0
- package/references/registration.md +72 -0
- package/references/scales-and-palettes.md +396 -0
- package/references/svelte.md +79 -0
- package/references/themes.md +96 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
<!-- Source of truth: packages/svelte/src/lib/plot-props.ts, packages/svelte/src/lib/interaction/ (controller, config, diagnostics), packages/svelte/src/lib/index.ts. -->
|
|
2
|
+
|
|
3
|
+
# Svelte interactions
|
|
4
|
+
|
|
5
|
+
`@ggts-sh/svelte` requires Svelte `^5.33.1` (peerDependency). Interactions are
|
|
6
|
+
opt-in host capabilities — they are not PortableSpec fields. Prefer declaration
|
|
7
|
+
children where they exist (`<Inspect>`, `<GuideLegend focus>`). Row identity for
|
|
8
|
+
selection, legend focus, and coordinated intervals **defaults** to an `id`
|
|
9
|
+
column when present, otherwise the row index (order-stable only) — ordinary
|
|
10
|
+
charts omit custom identity. Prefer `identity` on `<Inspect>`, object-form
|
|
11
|
+
`select`, or `createPlotInteraction` for a non-`id` durable field or accessor
|
|
12
|
+
(for example `<Inspect identity="year" />`). Plot-level `key` is deprecated.
|
|
13
|
+
|
|
14
|
+
## Inspect capability (preferred: child)
|
|
15
|
+
|
|
16
|
+
```svelte fragment
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import { GeomPoint, GGPlot, Inspect } from "@ggts-sh/svelte";
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<GGPlot data={rows} aes={{ x: "x", y: "y" }}>
|
|
22
|
+
<GeomPoint />
|
|
23
|
+
<Inspect mode="xy" />
|
|
24
|
+
</GGPlot>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Empty `<Inspect />` enables defaults. Options match `InspectOptions`: `mode`,
|
|
28
|
+
`pin`, `maxDistance`, `contentMode`, `muteSiblings`, `tooltipTotal`, `identity`
|
|
29
|
+
(column or accessor), `content` (Snippet). Prefer the child form in new code;
|
|
30
|
+
the GGPlot `inspect` prop is a dual-read alias, not the primary teaching surface.
|
|
31
|
+
|
|
32
|
+
### Inspect mode by geom family
|
|
33
|
+
|
|
34
|
+
Leave `mode` as `"auto"` (or set `"exact"`) unless the chart needs axis-group
|
|
35
|
+
hover on a continuous series. **Never put `x` / `y` / `xy` guides on violin,
|
|
36
|
+
boxplot, errorbar, linerange, pointrange, or crossbar** — those marks sit on a
|
|
37
|
+
discrete band; freescrolling guides cut the mark body and often leave the band
|
|
38
|
+
tooltip row blank. Prefer exact mark focus (click/hover the violin or interval
|
|
39
|
+
itself). The same rule already applies to bar/col. Explicit axis guides on
|
|
40
|
+
those families emit advisories (`INTERACTION_INSPECT_AXIS_ON_*`,
|
|
41
|
+
`INTERACTION_INSPECT_X_ON_COL` / `_BAR`).
|
|
42
|
+
|
|
43
|
+
### Mark eligibility (opt-out)
|
|
44
|
+
|
|
45
|
+
Layers are inspectable by default. Set `inspect={false}` on a geom (or
|
|
46
|
+
`"inspect": false` in the portable layer) so decorative bands, labels, or
|
|
47
|
+
full-panel rects never become tooltip targets (#1065 / #1068). This is portable
|
|
48
|
+
and unrelated to the host `<Inspect>` capability.
|
|
49
|
+
|
|
50
|
+
## Choosing inspect mode
|
|
51
|
+
|
|
52
|
+
`mode` is a **host** option on `<Inspect>` (and the dual-read plot `inspect`
|
|
53
|
+
alias). It is **not** a PortableSpec field. Empty `<Inspect />` defaults to
|
|
54
|
+
`mode="auto"`. Auto matches common mark families (bar/col/violin/boxplot/
|
|
55
|
+
interval geoms → exact; continuous lines → x). Prefer auto unless geometry
|
|
56
|
+
needs an explicit axis-group or free 2d mode.
|
|
57
|
+
|
|
58
|
+
### Default preference
|
|
59
|
+
|
|
60
|
+
1. Prefer `mode="auto"` when library auto matches the mark (bar, col, tile, point,
|
|
61
|
+
violin, boxplot, interval geoms, continuous line/area/freqpoly). Prefer an
|
|
62
|
+
explicit mode when you know the geometry better than auto.
|
|
63
|
+
2. Use `mode="x"` only for continuous shared-x series: time series, multi-series
|
|
64
|
+
lines/areas/freqpolys at a common continuous x, stacked continuous areas.
|
|
65
|
+
3. Use `mode="y"` only when the shared continuous axis is vertical and grouping
|
|
66
|
+
by y is the product intent.
|
|
67
|
+
4. Use `mode="xy"` for free 2d hits (scatters, paths, maps, jitter clouds) when
|
|
68
|
+
nearest-point inspection is better than snapping to one axis.
|
|
69
|
+
5. **Pin `mode="exact"`** (or leave `auto`) on violin, boxplot, errorbar,
|
|
70
|
+
pointrange, linerange, and crossbar. Never use freescrolling `x` / `y` / `xy`
|
|
71
|
+
on those band geoms (#1528).
|
|
72
|
+
|
|
73
|
+
### Anti-patterns (do not ship these)
|
|
74
|
+
|
|
75
|
+
| Pattern | Why it is wrong | Use instead |
|
|
76
|
+
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
77
|
+
| Violin with `mode="x"` / `"y"` / `"xy"` | Categorical run labels get freescrolling axis guides and blank axis tooltip rows | Pin `mode="exact"` or leave `auto` (auto → exact) |
|
|
78
|
+
| Boxplot with `mode="x"` / `"y"` / `"xy"` | Same discrete-group problem as violin | Pin `mode="exact"` or leave `auto` |
|
|
79
|
+
| Discrete-axis errorbar / pointrange / linerange / crossbar with `x`/`y`/`xy` | Caps and intervals are not continuous shared-x series; freescrolling guides add noise | Pin `mode="exact"` or leave `auto` |
|
|
80
|
+
| Bar / col with explicit `mode="x"` or `"xy"` | Vertical guide cuts filled bands; library already advises exact/auto | `mode="exact"` or leave `auto` (auto → exact for bar/col) |
|
|
81
|
+
| Copying a gallery family's mode without reading the mark geometry | Family-to-mode tables go stale; geometry decides | Match scale type + mark geometry |
|
|
82
|
+
|
|
83
|
+
Treat **violin, boxplot, and discrete-axis errorbar / pointrange / linerange /
|
|
84
|
+
crossbar** the same: exact mark focus (auto or explicit). When in doubt on other
|
|
85
|
+
geoms, ship `auto`.
|
|
86
|
+
|
|
87
|
+
## Multi-layer hit hygiene
|
|
88
|
+
|
|
89
|
+
Every inspectable layer competes for pointer hits. Decorative furniture that
|
|
90
|
+
stays hit-testable steals focus from the data story.
|
|
91
|
+
|
|
92
|
+
Rules:
|
|
93
|
+
|
|
94
|
+
1. Mark decorative layers `inspect={false}`: rivers, annotation labels, full-panel
|
|
95
|
+
rects, value labels that are only visual guides, background rugs, and similar
|
|
96
|
+
non-primary marks.
|
|
97
|
+
2. For multi-layer maps and stories (**Minard-class** charts), keep **one primary
|
|
98
|
+
story family** on the map panel (troop path). Furniture stays quiet with
|
|
99
|
+
`inspect={false}` (rivers, city labels, strength text). Do not invent a second
|
|
100
|
+
hit series just to drive cross-chart selection when the underlying tables are
|
|
101
|
+
different cardinalities (Minard's nine cold readings vs many path vertices).
|
|
102
|
+
3. When a summary layer sits on a raw scatter (mean–SE errorbar over points), prefer
|
|
103
|
+
inspecting the summary and set `inspect={false}` on the background cloud unless
|
|
104
|
+
both layers are intentional hit targets.
|
|
105
|
+
|
|
106
|
+
Gallery reference: `examples/path/trajectory` (Minard's 1812 map) opts rivers and
|
|
107
|
+
city/strength text out of hits; the troop path is the primary story mark on the
|
|
108
|
+
map. The cold strip is a separate panel with its own path and points — shared
|
|
109
|
+
longitude only, not linked selection between series that do not share row identity.
|
|
110
|
+
|
|
111
|
+
## CLI cannot catch host inspect behaviour
|
|
112
|
+
|
|
113
|
+
`ggts render` validates PortableSpec and prints pipeline warnings/advisories
|
|
114
|
+
on stderr. It does **not** validate host Inspect mode, tooltip content, or which
|
|
115
|
+
layers steal hover hits. Layer `"inspect": false` is portable and can appear in
|
|
116
|
+
the JSON loop; **mode is not**. Agents that only re-render SVG never see blank
|
|
117
|
+
x-rows, freescrolling guides, or noisy multi-layer hits.
|
|
118
|
+
|
|
119
|
+
After changing interaction, verify with a **real hover and pin** (docs site,
|
|
120
|
+
playground, or browser test) — not only `ggts render` / an SVG dump. Read
|
|
121
|
+
this file before enabling Inspect on any chart.
|
|
122
|
+
|
|
123
|
+
**Skill packaging decision (#1530):** keep a **single skill** (`@ggts-sh/skill`)
|
|
124
|
+
for both PortableSpec agents and Svelte app agents. Do not split until progressive
|
|
125
|
+
disclosure fails (agents ignore `references/`, or the frontmatter description
|
|
126
|
+
cannot route both audiences).
|
|
127
|
+
|
|
128
|
+
## Capability props
|
|
129
|
+
|
|
130
|
+
| Prop / surface | Input | What it enables |
|
|
131
|
+
| -------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
132
|
+
| `<Inspect>` | child of `<GGPlot>` (options as props) | Tooltip, semantic crosshair, keyboard traversal, pinning. **Preferred.** Options: `mode` (`"auto" \| "exact" \| "x" \| "y" \| "xy"`), `pin`, `maxDistance` (CSS px), `contentMode` (`"informational" \| "interactive"`), `muteSiblings`, `tooltipTotal` (`"auto" \| "off" \| "top" \| "bottom"`), `identity` (column or accessor), `content` (Snippet, see Tooltip below). Do not put `inspect=` on `<GGPlot>` in new examples. |
|
|
133
|
+
| `select` | `false \| "point" \| "interval" \| SelectOptions` | Point or interval selection. Options: `type` (`"point" \| "interval"`), `mode` (`"x" \| "y" \| "xy"`, interval only), `multiple`, `persistent`, `preset` (faceted intervals, below), `identity` (column or accessor). |
|
|
134
|
+
| `zoom` | `boolean \| ZoomOptions` | Brush zoom. Options: `mode` (`"x" \| "y" \| "xy"`), `trigger` (`"brush"`). Currently requires one unfaceted panel. |
|
|
135
|
+
| `focus` | `boolean \| { preview?: boolean }` on `<GuideLegend>` | Discrete legend preview, focus, and linked emphasis **for that aesthetic channel**. Emphasis only — never changes included rows. Discrete legends only; continuous ramps stay static. Mute de-emphasizes non-focused marks; dashed rings appear only on sparse point marks (≤48 anchors), never on paths/areas/bars/segments/text. Host-only — not a PortableSpec / `guideLegend()` field. |
|
|
136
|
+
| `filter` | `boolean \| LegendFilterOptions` on `<GuideLegend>` | Data-changing filtering on **that aesthetic's** discrete legend: changes included rows and reruns the grammar while preserving stable color identity. Options: `mode` (`"exclude" \| "include"`), `multiple`. Combine with focus: `<GuideLegend channel="color" focus filter />`. |
|
|
137
|
+
| `identity` | column or `(row, index) => PropertyKey` on Inspect / Select / controller | Durable row identity for public interaction payloads. **Default:** `id` column when present, else row index (order-stable only). Prefer this over plot-level `key`. |
|
|
138
|
+
| `key` | column or accessor on `<GGPlot>` (**deprecated** since 0.21) | Dual-read until 0.22. Prefer `identity` on Inspect / Select / `createPlotInteraction`. |
|
|
139
|
+
| `tool` | `"inspect" \| "point" \| "select-area" \| "zoom-area"` | Controlled initial/active tool; observe changes with `ontoolchange`. |
|
|
140
|
+
| `ariaLabel` | `string` | Accessible chart name; falls back to the plot title or a generated label. |
|
|
141
|
+
| `a11y` | `A11yMode` | `"force-svg"` keeps every layer as SVG marks. |
|
|
142
|
+
|
|
143
|
+
### Deprecated: plot-level `legendFocus` / `legendFilter`
|
|
144
|
+
|
|
145
|
+
Since 0.19.0, use `<GuideLegend channel="color" focus />` and
|
|
146
|
+
`<GuideLegend channel="color" filter />`. The old GGPlot props still dual-read
|
|
147
|
+
and emit `DEPRECATED_PLOT_PROP` — never teach them as the current API.
|
|
148
|
+
|
|
149
|
+
After an interval or zoom commit, accessible controls accept exact bounds.
|
|
150
|
+
|
|
151
|
+
## Faceted interval presets
|
|
152
|
+
|
|
153
|
+
`select={{ type: "interval", preset }}` coordinates durable intervals across
|
|
154
|
+
facet panels:
|
|
155
|
+
|
|
156
|
+
- `independent` — only the matching panel consumes its interval.
|
|
157
|
+
- `union` — matching rows from every stored panel interval are combined.
|
|
158
|
+
- `cross-panel` — the sole origin interval is projected into compatible panels.
|
|
159
|
+
|
|
160
|
+
`union` and `cross-panel` use the resolved row identity (default `id` / index,
|
|
161
|
+
or an explicit `identity` override on Select / Inspect / controller). The
|
|
162
|
+
engine always supplies an identity, so these presets no longer fail solely
|
|
163
|
+
because identity was omitted.
|
|
164
|
+
|
|
165
|
+
## Controller: durable shared state
|
|
166
|
+
|
|
167
|
+
`createPlotInteraction<Key>()` creates chart-independent semantic state that
|
|
168
|
+
outlives any one plot. Pass the same controller to every plot that should link,
|
|
169
|
+
via the `interaction` prop. The controller owns stable keys and scoped data
|
|
170
|
+
domains only; each chart translates them to its own render model.
|
|
171
|
+
|
|
172
|
+
- Mutation methods: `setSelection(keys, {scope, source?})`, `toggleSelection`,
|
|
173
|
+
`clearSelection`, `setEmphasis`, `clearEmphasis`, `setInterval`,
|
|
174
|
+
`clearInterval`, `clearIntervals`, `setZoom(domains, {scope, source?})`,
|
|
175
|
+
`resetZoom`, `reconcileKeys` (call after replacing rows).
|
|
176
|
+
- Reads: `selected(scope)`, `emphasized(scope)`, `isSelected(key, scope)`,
|
|
177
|
+
`intervals(scope)`, `zoom(scope)`, `snapshot`, `revision` (reactive),
|
|
178
|
+
`identity` (optional, from create options).
|
|
179
|
+
- `createPlotInteraction({ onchange, identity })` observes every transition
|
|
180
|
+
(`kind`, `source`, `revision`) and may set a shared row-identity override.
|
|
181
|
+
|
|
182
|
+
`interactionScope` (`{ keys, x?, y?, intervals? }`) names the identity
|
|
183
|
+
namespaces used for linking: key state crosses charts only through `keys`;
|
|
184
|
+
data-space zoom crosses one positional channel only when that channel's scope
|
|
185
|
+
also matches; `intervals` defaults to `keys`. Give plots matching scope
|
|
186
|
+
channels only when they should coordinate.
|
|
187
|
+
|
|
188
|
+
A `<GGPlot>` component instance also exports `resetScales()` and
|
|
189
|
+
`setZoom(domains)` for imperative per-chart control.
|
|
190
|
+
|
|
191
|
+
## Handlers
|
|
192
|
+
|
|
193
|
+
`oninspect`, `onselect`, `onzoom`, `onlegendfocus`, `onlegendfilter`,
|
|
194
|
+
`oninteraction` (union of all five event types), `ondiagnostic`,
|
|
195
|
+
`ontoolchange`, and `onrender(model, spec)` — called after each committed
|
|
196
|
+
render with the RenderModel (warnings, advisories, scales) and the normalized
|
|
197
|
+
PortableSpec.
|
|
198
|
+
|
|
199
|
+
A handler without its matching capability (for example `onselect` without
|
|
200
|
+
`select` or an enabled capability child), or `interactionScope` without an
|
|
201
|
+
`interaction` controller, is inert and emits a development advisory
|
|
202
|
+
(`INTERACTION_HANDLER_WITHOUT_CAPABILITY`, `INTERACTION_SCOPE_WITHOUT_CONTROLLER`).
|
|
203
|
+
Two `<Inspect>` children last-win with
|
|
204
|
+
`INTERACTION_DUPLICATE_INSPECT_CAPABILITY`.
|
|
205
|
+
|
|
206
|
+
## Tooltip
|
|
207
|
+
|
|
208
|
+
Enabling inspect with `<Inspect />` renders the default HTML tooltip; no extra
|
|
209
|
+
component is needed. Customize its body with the `content` prop on `<Inspect>`,
|
|
210
|
+
which receives a `PlotInspectionChange` (`focus`/`members` are `PlotDatum`
|
|
211
|
+
values whose `fields` are `TooltipField` rows: `{ channel, field, value }`).
|
|
212
|
+
The exported `Tooltip` component is the same positioned shell for advanced
|
|
213
|
+
custom rendering. `TooltipContext` is a deprecated (0.1.0) alias of
|
|
214
|
+
`PlotInspectionChange`.
|
|
215
|
+
|
|
216
|
+
### Default content policy (high-n stacks)
|
|
217
|
+
|
|
218
|
+
For axis-group inspection (`mode: "x"` / `"y"`), the **default** hover tooltip
|
|
219
|
+
does not dump every series:
|
|
220
|
+
|
|
221
|
+
1. **Focused series** is always included (the band under the pointer).
|
|
222
|
+
2. Up to **7 more** members are chosen by **largest absolute value** at that
|
|
223
|
+
axis position (y when grouping by x, x when grouping by y) — not by stacking
|
|
224
|
+
order — so tiny early series cannot crowd out the large contributors.
|
|
225
|
+
3. A **Total** row appears only when the group includes a **stack** or **fill**
|
|
226
|
+
layer. It sums unique numeric contributions in the full group across layers
|
|
227
|
+
(not just the capped hover window or the focus layer). Multi-layer paints of
|
|
228
|
+
the same series (line+point) count once; a thin overlay over a stack still
|
|
229
|
+
includes every distinct series in the total. Parallel multi-series lines or
|
|
230
|
+
points (`position: identity` / dodge) omit Total — their y-values are not
|
|
231
|
+
parts of a whole. Axis-group modes show this Total automatically. Exact /
|
|
232
|
+
xy inspection does not — set `tooltipTotal="bottom"` or `"top"` on
|
|
233
|
+
`<Inspect>` (or `inspect={{ tooltipTotal: "bottom" }}`) to show one Total
|
|
234
|
+
without switching `mode` to `"x"` / `"y"`. `"off"` hides it even in axis
|
|
235
|
+
mode.
|
|
236
|
+
|
|
237
|
+
4. An overflow line (`+N more · pin to inspect all`) appears when the group is
|
|
238
|
+
larger than eight display members (full-group unique count, all layers).
|
|
239
|
+
|
|
240
|
+
**Pin** the tooltip (click, or keyboard pin) to scroll the full group inside the
|
|
241
|
+
320px panel. **Opt into a full custom listing** with
|
|
242
|
+
`<Inspect content={snippet} />` or `oninspect` — public `members` still lists
|
|
243
|
+
the complete group when those paths request complete snapshots. With a large
|
|
244
|
+
discrete color/fill domain and inspect enabled, an advisory
|
|
245
|
+
(`INTERACTION_INSPECT_HIGH_CARDINALITY_DISCRETE`) suggests top-n data prep or a
|
|
246
|
+
custom content snippet.
|
|
247
|
+
|
|
248
|
+
## Diagnostics
|
|
249
|
+
|
|
250
|
+
`ondiagnostic` receives `PlotDiagnostic`, the union of three catalogs (each
|
|
251
|
+
entry has `severity`, `code`, `message`, `prop`, `suggestions`, `docUrl`).
|
|
252
|
+
|
|
253
|
+
**Agent/CLI path:** inspect mode is host-only. `ggts render --inspect MODE`
|
|
254
|
+
runs the same pure inspect×geom collectors and emits matching codes on stderr
|
|
255
|
+
with `source: "interaction"` (for example `INTERACTION_INSPECT_X_ON_COL` when
|
|
256
|
+
`MODE` is `x`/`xy` and the spec has a `col` layer). Without `--inspect`, the
|
|
257
|
+
CLI does not invent host intent. Full wiring/key/lineage diagnostics still need
|
|
258
|
+
a mounted plot.
|
|
259
|
+
|
|
260
|
+
- `INTERACTION_DIAGNOSTIC_CATALOG` — capability/key/lineage checks, e.g.
|
|
261
|
+
`INTERACTION_POINT_REQUIRES_KEY`, `INTERACTION_DUPLICATE_KEY`,
|
|
262
|
+
`INTERACTION_UNSTABLE_KEY`, `INTERACTION_INTERVAL_FACET_UNSUPPORTED`,
|
|
263
|
+
`INTERACTION_INTERVAL_SCALE_UNSUPPORTED` (continuous linear/log/time only),
|
|
264
|
+
`INTERACTION_LEGEND_DISCRETE_ONLY`, `INTERACTION_TOOL_UNAVAILABLE`,
|
|
265
|
+
`INTERACTION_INSPECT_X_ON_COL` / `INTERACTION_INSPECT_X_ON_BAR` (vertical
|
|
266
|
+
guide through columns/bars), `INTERACTION_INSPECT_X_BISECTS_COL_LABELS` /
|
|
267
|
+
`INTERACTION_INSPECT_X_BISECTS_BAR_LABELS` (same guide through on-bar value
|
|
268
|
+
labels), `INTERACTION_INSPECT_AXIS_ON_VIOLIN` / `_BOXPLOT` / `_ERRORBAR` /
|
|
269
|
+
`_LINERANGE` / `_POINTRANGE` / `_CROSSBAR` (any freescrolling x/y/xy guide
|
|
270
|
+
through distribution or interval band geoms — use exact),
|
|
271
|
+
`INTERACTION_INSPECT_HIGH_CARDINALITY_DISCRETE` (discrete color/fill domain
|
|
272
|
+
≥ 16 with inspect on — default tooltip is top-k + total, not a full dump),
|
|
273
|
+
and the two wiring advisories named above.
|
|
274
|
+
- `DEPRECATION_DIAGNOSTIC_CATALOG` — one code, `DEPRECATED_PLOT_PROP`, for
|
|
275
|
+
every grammar prop deprecated in 0.11.0 (`prop` carries the name).
|
|
276
|
+
- `COMPOSITION_DIAGNOSTIC_CATALOG` — child-layer composition collisions:
|
|
277
|
+
`DUPLICATE_PLOT_LAYER`, `DUPLICATE_SCALE_CHANNEL`, `DUPLICATE_MERGE_KEY`.
|
|
278
|
+
|
|
279
|
+
## Worked example: linked faceted selection
|
|
280
|
+
|
|
281
|
+
```svelte fragment
|
|
282
|
+
<script lang="ts">
|
|
283
|
+
import {
|
|
284
|
+
createPlotInteraction,
|
|
285
|
+
Facet,
|
|
286
|
+
GeomPoint,
|
|
287
|
+
GGPlot,
|
|
288
|
+
GuideLegend,
|
|
289
|
+
} from "@ggts-sh/svelte";
|
|
290
|
+
|
|
291
|
+
const interaction = createPlotInteraction<number>();
|
|
292
|
+
const scope = { keys: "sales-rows", intervals: "sales-range" } as const;
|
|
293
|
+
</script>
|
|
294
|
+
|
|
295
|
+
<GGPlot
|
|
296
|
+
data={rows}
|
|
297
|
+
aes={{ x: "date", y: "value", color: "series" }}
|
|
298
|
+
select={{ type: "interval", mode: "x", preset: "cross-panel" }}
|
|
299
|
+
{interaction}
|
|
300
|
+
interactionScope={scope}
|
|
301
|
+
oninteraction={(event) => console.log(event.type, event)}
|
|
302
|
+
>
|
|
303
|
+
<GeomPoint />
|
|
304
|
+
<Facet wrap="region" ncol={3} />
|
|
305
|
+
<GuideLegend channel="color" focus filter />
|
|
306
|
+
</GGPlot>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Any second plot given the same `interaction` controller and a matching
|
|
310
|
+
`interactionScope` stays linked: brushing an interval in one panel projects it
|
|
311
|
+
cross-panel, external UI can call `interaction.setSelection(...)`, and legend
|
|
312
|
+
filtering reruns the grammar without reshuffling series colors.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# React DOM
|
|
2
|
+
|
|
3
|
+
Read this reference for a React target. The shared [grammar](grammar.md) and
|
|
4
|
+
[validation loop](../SKILL.md#the-validation-contract-use-it) apply unchanged.
|
|
5
|
+
Use React DOM 18.2 or 19 and install `@ggts-sh/react`.
|
|
6
|
+
For the bundled dataset example below, also install `@ggts-sh/core`:
|
|
7
|
+
|
|
8
|
+
```sh fragment
|
|
9
|
+
npm install @ggts-sh/react @ggts-sh/core
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
In a server-component application, place interactive charts behind a client component boundary.
|
|
13
|
+
|
|
14
|
+
## Render a portable spec
|
|
15
|
+
|
|
16
|
+
Use the same spec that passed `ggts check`. Spec-driven charts need full
|
|
17
|
+
registration because no geom children self-register.
|
|
18
|
+
|
|
19
|
+
```tsx complete
|
|
20
|
+
"use client";
|
|
21
|
+
|
|
22
|
+
import { GGPlot, registerAll, type PortableSpec } from "@ggts-sh/react";
|
|
23
|
+
|
|
24
|
+
registerAll();
|
|
25
|
+
|
|
26
|
+
const spec: PortableSpec = {
|
|
27
|
+
data: {
|
|
28
|
+
values: [
|
|
29
|
+
{ year: "2023", sales: 12 },
|
|
30
|
+
{ year: "2024", sales: 18 },
|
|
31
|
+
{ year: "2025", sales: 25 },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
aes: { x: { field: "year" }, y: { field: "sales" } },
|
|
35
|
+
layers: [{ geom: "col" }],
|
|
36
|
+
scales: { x: { type: "band" } },
|
|
37
|
+
labs: { title: "Annual sales", x: "Year", y: "Sales" },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default function SalesChart() {
|
|
41
|
+
return <GGPlot spec={spec} height={400} />;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Compose the grammar in JSX
|
|
46
|
+
|
|
47
|
+
Geom and grammar children declare layers. They render no markup themselves.
|
|
48
|
+
Bare strings in component `aes` map fields; JSON specs use `{ field: name }`.
|
|
49
|
+
`spec` takes precedence over component authoring inputs. Keep mark order as
|
|
50
|
+
the desired drawing order.
|
|
51
|
+
|
|
52
|
+
```tsx complete
|
|
53
|
+
"use client";
|
|
54
|
+
|
|
55
|
+
import {
|
|
56
|
+
GGPlot,
|
|
57
|
+
GeomPoint,
|
|
58
|
+
GeomSmooth,
|
|
59
|
+
Labs,
|
|
60
|
+
ThemeMinimal,
|
|
61
|
+
} from "@ggts-sh/react";
|
|
62
|
+
import { palmerPenguins } from "@ggts-sh/core/data";
|
|
63
|
+
|
|
64
|
+
export default function PenguinChart() {
|
|
65
|
+
return (
|
|
66
|
+
<GGPlot
|
|
67
|
+
data={palmerPenguins}
|
|
68
|
+
aes={{ x: "flipperLengthMm", y: "bodyMassG", color: "species" }}
|
|
69
|
+
height={400}
|
|
70
|
+
>
|
|
71
|
+
<GeomSmooth method="loess" se={false} />
|
|
72
|
+
<GeomPoint alpha={0.8} />
|
|
73
|
+
<ThemeMinimal />
|
|
74
|
+
<Labs title="Penguin size" x="Flipper length (mm)" y="Body mass (g)" />
|
|
75
|
+
</GGPlot>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Interactions and lifecycle
|
|
81
|
+
|
|
82
|
+
Read [Choosing inspect mode and interaction contracts](interactions.md)
|
|
83
|
+
before enabling Inspect, selection, zoom, or linked charts. Its grammar and
|
|
84
|
+
event semantics are shared; translate Svelte template syntax into JSX.
|
|
85
|
+
Pass functions as React props, use React state for application state, and
|
|
86
|
+
use a ref for `resetScales()` and `setZoom()`.
|
|
87
|
+
|
|
88
|
+
- `<Inspect />` declares inspection. Keep its mode, pinning, identity, and
|
|
89
|
+
custom content on this child. Treat Svelte snippets and React render
|
|
90
|
+
callbacks as adapter-specific code.
|
|
91
|
+
- `<GuideLegend channel="color" focus />` and `filter` control legend
|
|
92
|
+
interaction. These are host behavior, not PortableSpec keys.
|
|
93
|
+
- Keep a shared `createPlotInteraction()` controller stable across renders
|
|
94
|
+
with a lazy React state initializer. Match `interactionScope` channels
|
|
95
|
+
between linked plots; separate unrelated datasets.
|
|
96
|
+
- React consumes JSX `key` itself. Use `identity` on Inspect, selection, or
|
|
97
|
+
the interaction controller for durable row keys.
|
|
98
|
+
- Check actual browser hover/pin, keyboard operation, selection, and zoom.
|
|
99
|
+
CLI success verifies the portable chart pipeline, not React event wiring.
|
|
100
|
+
|
|
101
|
+
For lean imports or stat overrides, read [registration](registration.md).
|