feat(interactions): interaction_spec for chart interactions - #124
Merged
Merged
Conversation
Adds the JSON shape for declarative interactions beside chart_spec and
theme_spec: a list of preset entries { type, id?, options }, retained
updates applied at mount, and the surface policies. The four policy
interfaces move to core so the contract needs no runtime import;
flint-chart/interactive re-exports them under their old names. Includes
the design document that records the decisions.
INTERACTION_PRESETS maps each preset name to the factory code calls today, with the capability it needs and the gesture it uses. resolveInteractionSpec turns interaction_spec entries into the same CanvasInteractionDef values, tagged origin 'spec', with the type name as the default id. It rejects an unknown type, a flat option outside "options", a missing required option, and a duplicate id, and names the entry in every message.
The checks addVegaLiteInteractions made inline move to admitInteractions. A code definition still throws with the same message. A spec entry the chart cannot honour is dropped and reported as a ChartWarning; in a pan-versus-drag conflict the later entry yields. The plan carries the admitted list and the warnings, the Vega mount runs the admitted list, and the renderer exposes the warnings.
composeInteractiveOptions merges the spec with the code options: the spec comes first, an id shared by both sources is an error, the code wins on the surface policies, and a backend that runs no interactions ignores the spec with one info warning. The surface exposes surface.warnings, resolved after ready, and logs the list once.
…nteraction_spec The Test cases lab gains a `source` and a second route, spec-test-cases, that mounts every case from `input.interaction_spec` instead of factory calls. `modeSpec()` mirrors `modeInteractions()` entry for entry, with the surface policies in the spec. Each card shows the JSON it mounted from in a foldable, token-coloured panel with a copy button, reports dropped entries in a callout, and the page tallies ready and dropped cards. A headless comparison of both tabs found no status difference across 23 modes and 837 cards.
Contributor
|
Let's move update / dismiss out of the interaction API? |
Retained state leaves the spec. An update says what the chart shows now and arrives from outside: a host seeds a view, a story scrolls to a sentence, an agent answers a question. The code already treats it that way through options.updates, applyUpdate, setUpdates, and dispatch, so the spec keeps interactions and the surface policies and nothing else. The design document records the decision and notes the next step: a per-interaction reset list that replaces the global dismiss policy.
… state Step 1 of replacing the global dismiss policy with a per-interaction reset. Three gestures, defined by what the reader does and nothing else: click-none (a click whose hit resolves to no element), double-click (anywhere), and escape. Every preset that retains state accepts `reset` and normalises it onto its definition; presets that retain nothing have no such option. The registry records each preset's supported and default lists, the resolver rejects an unknown or unsupported gesture by name, and navigate renames click-background to click-none (a margin click now resets too). The runtime still honours only navigate's list; the dispatcher that reads the others is step 2.
…r interaction Step 2 of replacing the global dismiss policy. A click that hits nothing, a double-click, or Escape now resets only the interactions whose reset list holds that gesture, each by its own id. Host updates and the other interactions keep their state; legend-toggle drops its closure through onReset(). navigate flies home through its navigation path and the stateful brush clears through the gesture's own reset(), so the special double-click and Escape paths in the two gesture modules go away. A chart with an escape reset becomes focusable and takes focus on a pointer press, so Escape reaches the chart the reader touched last and no other. double-activate next to a double-click reset is an admission conflict. options.dismiss survives as a deprecated code option that maps onto every interaction that resets by default.
…t lists own it The global dismiss policy is gone from interaction_spec, from buildInteractiveChart, the surface, and the Vega renderer options, and the InteractionDismissPolicy type with it. Each interaction's reset list already decides what a click on nothing, a double-click, or Escape clears. The resolver rejects a spec that still carries dismiss or updates and points at the replacement. The two callers in the repo needed nothing in exchange: the Test cases lab passed the values the presets default to, and the you-draw-it demo mounts a hand-built definition with no reset list plus host updates, which no gesture resets.
Collaborator
Author
|
The
|
xavier (xavier-shaw)
added this pull request to stack #128
September 16, 2026 00:56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A chart spec can now request interactions. The new top-level field
interaction_speclists presets by name with their options. Each name maps to the factory that code already calls, so a spec entry and a factory call produce the same interaction. When a chart cannot support an entry, the mount drops it with a warning and the chart still renders.Design notes:
docs/design-interaction-spec.md.What changed
core/interaction-spec.ts: theInteractionSpectype andinteraction_speconChartAssemblyInput.resolveInteractionSpec(). The resolver rejects unknown types, options outsideoptions, missing required options, and duplicate ids.admitInteractions(), extracted fromaddVegaLiteInteractions(). Code definitions throw as before. Spec entries drop with aChartWarning.composeInteractiveOptions()merges spec and code entries. Code wins on shared keys. A shared id is an error.reset: every stateful preset accepts a list ofclick-none,double-click,escape. One dispatcher in the Vega runtime replaces the global dismiss policy.interaction_spec.Verification
Per-chart-type admission,
validateChart(), and the MCP schema follow in #127.🤖 Generated with Claude Code