Interaction reference

Searchable interaction contract. Chart-local state and callbacks by default. createPlotInteraction() for shared semantic state across plots and UI.

Static default

No capture layer, tooltip, selection, or zoom until a capability is enabled. Page scroll is not hijacked by unused tools.

Capability props

inspect

Enables inspection, the default HTML tooltip, semantic crosshair, keyboard traversal, and optional pinning. Inputs are true or options with mode, pin, maxDistance, content, and contentMode.

Point selection

select={{ type: "point", multiple: true }} stores stable semantic keys. Supply key for every row.

Interval selection

select={{ type: "interval", mode: "x" | "y" | "xy", persistent: true }} enables an explicit Select area tool and emits domain and pixel bounds. In facets, add preset: "independent" | "union" | "cross-panel" to replace one panel, combine panel selections, or project one domain through compatible panels.

zoom

zoom={{ mode: "x" | "y" | "xy" }} enables the explicit Zoom area tool. Reset zoom and double-click return to the natural domains.

legendFocus

legendFocus={true} enables discrete legend preview and committed focus. Use legendFocus={{ preview: false }} to disable hover/focus preview while retaining click, touch, Enter, Space, Escape, and arrow-key controls. It requires stable row key values and does not make continuous ramps interactive.

legendFilter

legendFilter={true} adds native Show-group checkboxes to discrete color and fill legends. It changes the rows supplied to facets, statistics, scales, and rendering while preserving the full legend catalog and categorical color identity. Configure mode: "exclude" | "include" and multiple; receive typed clauses through onlegendfilter. It is independent of presentation-only legendFocus.

Controlled tool

tool and ontoolchange control the active Inspect, Select area, or Zoom area mode. Keep the value in Svelte state when application controls and the plot tool rail must stay synchronized:

<script lang="ts">
  import type { InteractionTool } from "@ggsvelte/svelte";

  let activeTool = $state<InteractionTool>("inspect");
</script>

<GGPlot
  inspect={true}
  select={{ type: "interval" }}
  tool={activeTool}
  ontoolchange={(next) => (activeTool = next)}
/>

A controlled unavailable tool requests a change and emits a diagnostic; it does not silently arm a different drag behavior. The active tool remains local to one chart; shared controllers coordinate data semantics, not UI modes.

Shared controller

createPlotInteraction<Key>({ onchange? }) returns a reactive PlotInteractionController<Key>. Pass it through the interaction prop and name the semantic channels with the required interactionScope={{ keys, x?, y? }}; controlled plots never fall back to a generic scope or infer x/y channel names from encodings. Controlled zoom requires an explicit scope for every active channel (x, y, or both).

  • Reads: selected(scope), emphasized(scope), intervals(scope), isSelected(key, scope), zoom(scope), snapshot, and revision.
  • Selection: setSelection, toggleSelection, and clearSelection.
  • Lightweight presentation: setEmphasis and clearEmphasis.
  • Facet intervals: setInterval, clearInterval, and clearIntervals.
  • Domains: setZoom and resetZoom for finite numeric x/y pairs.
  • Data replacement: reconcileKeys(validKeys, { scope }) explicitly removes selected or emphasized keys that no longer exist.

Scopes are application-level names. Reuse a key scope only where keys mean the same thing, and reuse x/y scopes only where their data domains are compatible. Every mutation returns one immutable transition or null for a no-op. Passive consumers never republish controller state, preventing linked-view feedback loops. Do not mutate the controller inside its synchronous onchange callback; schedule a later Svelte application update instead. See the linked views example.

Identity

key is a field name or accessor returning a unique stable PropertyKey. Public events expose semantic keys, aggregate sourceKeys, and lineageCount, never renderer indices.

Events

oninspect

Receives PlotInspection: change with transient or pinned focus and members, or clear.

onselect

Receives PlotSelection. Point selection emits end and clear. Interval selection emits start, change, end, and clear.

onzoom

Receives ZoomEvent: end with explicit domains or clear with null domains.

onlegendfocus

Receives LegendFocusEvent: a transient or committed change carrying the raw encoded value, formatted label, scale channel, and stable row keys, or a small clear event. The same object is included in oninteraction.

onlegendfilter

Receives LegendFilterEvent: a change with one typed LegendFilterClause, or clear with clause: null. Legend filtering changes pipeline input and is not folded into the presentation interaction union.

oninteraction

Receives the same discriminated PlotInteractionEvent union emitted by the focused callbacks. Narrow on type and phase.

ondiagnostic

Receives structured PlotDiagnostic objects (InteractionDiagnostic or DeprecationDiagnostic) with severity, code, message, prop, suggestions, and docUrl. Deprecation advisories also carry since and removeIn.

<GGPlot
  ondiagnostic={(diagnostic) =>
    console.warn(diagnostic.code, diagnostic.message, diagnostic.suggestions)}
/>

Every event has a source: pointer, keyboard, touch, or programmatic.

Diagnostics

INTERACTION_INTERVAL_FACET_UNSUPPORTED

Brush zoom currently requires one unfaceted panel.

INTERACTION_INVALID_MAX_DISTANCE

inspect.maxDistance must be a finite non-negative CSS-pixel distance.

INTERACTION_POINT_REQUIRES_KEY

Durable point selection requires a stable key field or accessor.

INTERACTION_INTERVAL_PRESET_REQUIRES_KEY

Coordinated interval presets (union, cross-panel) require a stable key field or accessor; without one they combine no rows.

INTERACTION_INVALID_KEY

A key accessor returned null, undefined, or a non-PropertyKey value.

INTERACTION_DUPLICATE_KEY

The key accessor returned a duplicate value; durable interaction is disabled for that value.

INTERACTION_UNSTABLE_KEY

The key accessor returned a different value for the same source row.

INTERACTION_MISSING_LINEAGE

A synthetic or aggregate mark did not expose source-row lineage.

INTERACTION_LEGEND_REQUIRES_KEY

Legend focus requires stable row keys so encoded legend values never become identities.

INTERACTION_LEGEND_DISCRETE_ONLY

Legend focus currently applies to discrete color and fill legends; continuous ramps remain static.

INTERACTION_INTERVAL_SCALE_UNSUPPORTED

Interval domains and brush zoom require continuous linear, log, or time scales.

INTERACTION_TOOL_UNAVAILABLE

The requested interaction tool is unavailable for the enabled capabilities.

INTERACTION_SCOPE_WITHOUT_CONTROLLER

interactionScope is ignored without an interaction controller; chart-local scope is derived from key and aes.

INTERACTION_HANDLER_WITHOUT_CAPABILITY

An interaction handler is set but its capability prop is not enabled, so the handler never fires.

INTERACTION_INSPECT_X_ON_COL

inspect.mode draws a vertical guide through column marks; columns already encode x as a filled band, so the guide cuts the bar body and rarely adds information.

INTERACTION_INSPECT_X_ON_BAR

inspect.mode draws a vertical guide through bar marks; bars already encode the band axis as a filled region, so the guide cuts the bar body and rarely adds information.

INTERACTION_INSPECT_X_BISECTS_COL_LABELS

inspect.mode draws a vertical guide through GeomCol marks that also carry GeomText/GeomLabel values; the guide bisects the on-bar totals and makes them hard to read.

INTERACTION_INSPECT_X_BISECTS_BAR_LABELS

inspect.mode draws a vertical guide through GeomBar marks that also carry GeomText/GeomLabel values; the guide bisects the on-bar totals and makes them hard to read.

Accessibility

The plot surface is named and keyboard focusable when interaction is enabled. Arrow keys or brackets traverse data; Enter or Space pins or commits the active tool; Escape dismisses. A polite live region announces concise state while pinned HTML remains labelled, navigable DOM. Area tools remain explicit so ordinary page scrolling is available until a user chooses a drag mode.

Committed interval and zoom state exposes precise Edit-bounds buttons in the tool rail. Their inline form uses labelled native inputs, stages drafts until Apply, validates log/time/category constraints, restores trigger focus after Apply or Cancel, and supports Escape. Linear and reversed domains use ascending data values; time uses ISO 8601 text; band intervals use inclusive native selects. Clear panel selection, Clear all selections, Reset zoom, and Reset legend filters remain separate operations.