Undo / redo

There is no built-in undo/redo stack inside <SvGrid>. The grid emits no per-edit event for an external stack to subscribe to, so undo/redo has to be built on top of the data-snapshot approach.

Pattern - snapshot stack

<script lang="ts">
  type Snapshot = Person[]

  let rows = $state<Person[]>(initial)
  let past = $state<Snapshot[]>([])
  let future = $state<Snapshot[]>([])
  const LIMIT = 50

  // Whenever rows change because of an edit, push the previous state onto `past`.
  let last = JSON.stringify(rows)
  $effect(() => {
    const next = JSON.stringify(rows)
    if (next === last) return
    past = [...past.slice(-LIMIT), JSON.parse(last)]
    future = []
    last = next
  })

  function undo() {
    const prev = past.at(-1)
    if (!prev) return
    future = [rows.map((r) => ({ ...r })), ...future].slice(0, LIMIT)
    rows = prev.map((r) => ({ ...r }))
    past = past.slice(0, -1)
    last = JSON.stringify(rows)
  }

  function redo() {
    const next = future[0]
    if (!next) return
    past = [...past, rows.map((r) => ({ ...r }))].slice(-LIMIT)
    rows = next.map((r) => ({ ...r }))
    future = future.slice(1)
    last = JSON.stringify(rows)
  }
</script>

<svelte:window onkeydown={(e) => {
  const meta = e.ctrlKey || e.metaKey
  if (meta && e.key === 'z' && !e.shiftKey) { e.preventDefault(); undo() }
  if (meta && ((e.key === 'z' && e.shiftKey) || e.key === 'y')) { e.preventDefault(); redo() }
}} />

<SvGrid data={rows} {columns} features={features} enableInlineEditing />

Why JSON snapshots

Without a per-edit event from the grid, the safest store of "previous state" is a deep copy of the row array. For a 5,000-row grid the snapshot is a few hundred KB - fine. For a 100,000-row grid, switch to a row-diff stack:

type Diff = { rowId: string; column: string; before: unknown; after: unknown }

…and apply it during undo/redo. The per-edit event needed to compute that cleanly is on the gap list.

Tracked at

Missing features - first-class undo stack with onCellValueChange to drive it.

See also