Skip to main content
A command is an undoable edit to a chart’s spec, such as hiding an axis or moving the legend. Dispatch it through the provider to apply the change, recompile the chart and record an undo step. Import command classes from @graphysdk/react/editable and hooks from @graphysdk/react.
The edit menu, custom controls and commands received from an agent all share the same history.

Dispatching

useGraphCommands returns dispatch(command, options?) and commit(). It must be called from inside a <GraphProvider>. Construct a command with its parameters, then dispatch it:
Most layer commands accept an optional layerId and default to the first compatible layer. Specify it for a combo chart:
Set the layer’s id when building the spec to use the same name in commands, highlights and annotation anchors:
If omitted, an ID is assigned at compile time. SetLineStyleCommand, SetPointSizeCommand, SetPointSymbolCommand, SetBarCornerRadiusCommand and RemoveLayerCommand require layerId. Commands return a new spec without mutating the input. The provider reports committed changes through onSpecChange; see Saving the chart.

Live gestures

For a slider or color picker, dispatch updates with { transient: true } and call commit() when the gesture ends. The chart updates live, but the whole gesture creates one undo step and one onSpecChange callback:
A transient run combines consecutive updates of the same command type and target. Undo restores the spec from before the run began. Changing the command type or target closes the run. So does a committed dispatch, undo, redo, or an external change to spec, data or color scheme. Always call commit() at the end of a gesture: without it, onSpecChange is delayed and later updates to the same target can merge into that undo step. For canvas dragging, you can update the position locally and dispatch once on drop. Use transient dispatch when live feedback needs a recompiled spec.

Undo & redo

Call useGraphHistory inside the provider to read history and undo or redo changes. It re-renders the component when history changes:
boolean
Whether a step is available in each direction.
string | null
Human-readable label for the next step, such as "Set line width to 3", for a button tooltip or a menu item. null when no step is available.
CommandMetadata[]
Full history entries with id, timestamp, description and author. undoStack is oldest first; redoStack follows the order of undo operations. In both stacks, the last entry is the next step.

Keyboard shortcuts

Call useGraphHistoryShortcuts(handleRef) to enable keyboard shortcuts. The handle lets the hook run outside the provider:
It binds ⌘/Ctrl+Z, ⌘/Ctrl+Shift+Z and Ctrl+Y. It ignores shortcuts already handled by the app and those entered in text fields. Pass { target } with an element or ref to scope the listener to that subtree instead of window. A null target or { enabled: false } disables the listener. The hook prevents the default key action only when the chart successfully undoes or redoes a step. Otherwise, your app can handle it. For controls outside the provider, handleRef.current also exposes commands.dispatch, commands.commit, undo, redo, getScene and subscribe. Both undo() and redo() return false if no step is available or the restored spec fails to compile.

When a command doesn’t apply

Two outcomes leave the chart and history unchanged:
  • No-op: the value already matches, or the target layer or scale is missing. Nothing recompiles.
  • Rejected: the edited spec fails to compile. The last good chart stays visible and the provider reports diagnostics through onError.
When calling command.apply(spec) directly, custom geoms require the chart’s compiler lookup: command.apply(spec, { findGeom: (name) => compiler.findGeom(name) }). The provider supplies this automatically. Undo and redo also require a valid spec. If new data lacks a column an older spec needs, that step returns false and stays on the stack. It blocks earlier steps until the data supports it again.

History lifecycle

History follows the provider’s current chart:
  • A new spec reference clears history after successful compilation. Keep it stable across renders. Passing back the exact value from onSpecChange is an exception and preserves history.
  • A spec that fails to compile preserves the last good chart’s history.
  • Changes to data, color scheme or renderer mode preserve history.
  • Remounting the provider starts a new history. Changing the theme or plugins requires a remount with a new React key.
  • History is capped at 100 steps. Further edits drop the oldest step.

Saving the chart

Use the provider’s onSpecChange to save edits. It receives the current spec after each committed change, undo or redo, and once when a transient run closes. It does not fire on mount or when you supply a new spec. This browser example saves to localStorage and restores the spec on the next visit:
A spec is plain JSON, so JSON.stringify and JSON.parse are enough to save and restore it. Store the chart’s data separately. Passing the exact onSpecChange value back as spec preserves history without recompiling. A spec restored from storage starts a new history; saving the spec does not save the undo stack.

Available commands

Each command takes parameters as its first constructor argument and optional metadata as its second.
  • Scale commands identify the scale with scaledAesthetic: 'x', 'y', 'color', etc.
  • Axis commands use axis: 'x' | 'y'. SetAxisLabelCommand, SetAxisVisibilityCommand and SetAxisTickModeCommand also accept 'ySecondary'; omitting its value removes the override so it follows y. On y, use keepSecondaryAxis: true to preserve the secondary axis’s current setting.
  • SetGridLineStyleCommand accepts axis: 'both' to style the whole grid in one undo step.
AddLayerCommand, AddHighlightCommand and AddAnnotationCommand generate an id if omitted. Serialization preserves that ID so replaying the command uses the same identity.SequenceCommand({ commands }) applies command instances in order as one undo step, using the first command’s target and description. It skips no-ops and records nothing if the final spec is unchanged. It serializes the nested commands; deserializing an empty sequence fails with INVALID_SEQUENCE.Chart typeSetGraphTypeCommand changes the coordinate system, data layers and required scales as one undo step. Reference lines and fitted trends are preserved. readGraphType(spec) reads the current type for a picker, returning null for combinations it cannot represent.SetMappingCommand maps a chart-wide aesthetic to a column and infers its scale. Pass null to unmap it. Layer-specific aes mappings stay unchanged.Titles & contentAxes & gridLegend & headlineAppearance & formattingFor the chart’s background, border, corner radius or text scale, use SetStyleRuleCommand with style.graph({ … }). See Styling.CoordinatesLayersAddLayerCommand takes a complete layer. Toggles that remove layers (ToggleLinePointsCommand, ToggleGoalLineCommand, SetStatLineCommand) preserve a removedLayer so undo restores its settings and identity.SetLineStyleCommand, SetPointSizeCommand, SetPointSymbolCommand, SetBarWidthCommand and SetBarCornerRadiusCommand update layer-scoped style rules while preserving unrelated properties. Line style, point size and point symbol also accept where to target one group. Without it, they update the layer and remove conflicting group overrides, except overrides that keep points hidden.ToggleLinePointsCommand also accepts where. It hides a group’s points with size: 0 and removes the point layer when no groups remain visible. Showing points without where clears all hidden-point overrides. Changing point size preserves hidden groups; if the layer itself has size: 0, only explicitly visible groups are resized.SetLineStyleCommand changes only the fields you pass, as one undo step. lineStyle accepts 'solid', 'dashed' or 'dotted'; strokeWidth: null restores the stylesheet default; showFill toggles fill beneath a line and is ignored by areas.
StylesSetStyleRuleCommand edits widths, dashes, opacity and colors. It identifies a rule by its select and when, not its ID. To give the rule an ID, pass it through the builder: style.geom.line({ strokeWidth: 3 }, { id: 'emphasis' }).
Updating a matching rule replaces its declarations while preserving its cascade position. declarations: null removes all matching rules and does nothing if none match. See Styling for supported targets and properties.SetObservationsColorCommand colors a selection in one undo step. It takes target: { kind: 'observations', layerId, scope, predicate } and a hex color, or null to remove the color override.ScalesSetScaleRangeCommand assigns values to a discrete scale’s groups in order, or the colormap stops of a continuous color scale. For a color scale, range: null restores the theme’s list for its class: categorical, sequential or diverging.HighlightsAnnotationsAnnotation IDs are unique across kinds. UpdateAnnotationCommand patches only the fields you supply. MoveAnnotationCommand moves by a relative translation in panel fractions, clamped to the panel; undo restores the previous position.SetDifferenceArrowStyleCommand edits a difference arrow’s line (arrow) and label (label) style overrides in one step. Pass null to remove an entry; omit it to leave it unchanged.

Commands over the wire

Use commandRegistry to serialize a command as { type, params, metadata } and deserialize it back into an instance. Commands received from an agent, collaborator or session log use the same dispatch and history as local edits:
deserialize throws for unknown command types. Check that sender and receiver use compatible SDK versions. Serialized parameters include generated IDs so replay preserves them. Use readObservationsTargetData to read the selected observations as Data. Values come from the layer after its transforms and stat, before stacking. The payload includes each mapped variable once and omits unmapped columns and non-stat internal variables. Send the data beside the target so a receiver can reply with a command on the same observations target:
Column keys follow the spec’s mapping. A sum of sales returns { quarter: 'Q1', sales: 550 }; a count has no source variable, so its key is count, and a summary’s interval goes out as summaryLower and summaryUpper. Reshaped variables keep the names assigned by the reshape. If keys collide, stat outputs use their own names (sum, count, summaryEstimate, summaryLower, summaryUpper), with a numeric suffix if needed, such as count_2. Columns include their source variables’ labels and value formats. Lookup formats, which vary by observation, are omitted. Dates and value formats are copied so editing the payload leaves the scene unchanged. The function returns null if the layer is missing or cannot evaluate the predicate. A valid predicate that matches nothing returns the columns with rows: [].