Undo and Redo
Undo returns cards to their earlier state; Redo reapplies the change. Use this after a move or a successful editor change rather than manually reconstructing source rows.
Undo and redo a move
History keeps accepted card changes.
Try it: Move research, then choose Undo and Redo in the toolbar.
What to notice: Undo returns research to To do; Redo moves it back to In progress.
TypeScript source
Install the packages from Getting Started, then use this source in your app.
import type { KanbanPlugin } from '@revolist/kanban';import type { WorkCard } from './first-board';
export function showHighPriority(kanban: KanbanPlugin<WorkCard>) { kanban.setFilter({ predicate: card => card.priority === 'high' });}export function searchResearch(kanban: KanbanPlugin<WorkCard>) { kanban.setFilter({ query: 'research', fields: ['title'] });}export function clearFilters(kanban: KanbanPlugin<WorkCard>) { kanban.setFilter(); }
// An app-owned snapshot of serializable data, not a Kanban remote-store API.export function captureBoard(kanban: KanbanPlugin<WorkCard>) { return { cards: kanban.getCards().map(card => ({ ...card })), columns: [...kanban.getColumnOrder()], collapsed: [...kanban.getCollapsedColumnProps()], selected: [...kanban.getSelectedCardIds()] };}export async function restoreBoard(grid: HTMLRevoGridElement, kanban: KanbanPlugin<WorkCard>, saved: ReturnType<typeof captureBoard>) { grid.source = saved.cards.map(card => ({ ...card })); await new Promise<void>(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))); kanban.setColumnOrder(saved.columns); for (const prop of kanban.getColumnOrder()) kanban.setColumnCollapsed(prop, saved.collapsed.includes(prop)); kanban.setSelectedCardIds(saved.selected);}// Persist JSON.stringify(captureBoard(kanban)) in your application's storage.// Restore only data validated by your application; the live example stays in memory.import { defineCustomElements } from '@revolist/revogrid/loader';import { KanbanPlugin, type KanbanConfig } from '@revolist/kanban';import '@revolist/revogrid-pro/styles.css';import '@revolist/kanban/styles.css';
export interface WorkCard { id: string; title: string; status: string; order: number; description?: string; team?: string; owner?: string; priority?: string; tags?: string[]; progress?: number; startDate?: string; endDate?: string;}export const demoCards: WorkCard[] = [ { id: 'task-1', title: 'Research customer needs', description: 'Collect feedback for the next release.', status: 'todo', team: 'product', owner: 'Ana', priority: 'normal', order: 1000 }, { id: 'task-2', title: 'Build onboarding', description: 'Make the first session useful.', status: 'doing', team: 'product', owner: 'Mia', priority: 'high', order: 1000 }, { id: 'task-3', title: 'Improve API errors', description: 'Return actionable validation messages.', status: 'todo', team: 'platform', owner: 'Rui', priority: 'normal', order: 2000 }, { id: 'task-4', title: 'Release checklist', description: 'Ready for the next deployment.', status: 'done', team: 'platform', owner: 'Sam', priority: 'normal', order: 1000 },];export const boardConfig: KanbanConfig<WorkCard> = { columns: [{ prop: 'todo', name: 'To do', size: 210 }, { prop: 'doing', name: 'In progress', size: 210 }, { prop: 'done', name: 'Done', size: 210 }], card: { titleField: 'title' }, cardRowHeight: 88, addCard: false, swimlaneColumn: false,};
export async function configureBoard( grid: HTMLRevoGridElement, config: KanbanConfig<WorkCard> = boardConfig, cards: readonly WorkCard[] = demoCards, extraPlugins: HTMLRevoGridElement['plugins'] = [],) { await defineCustomElements(); grid.plugins = [KanbanPlugin, ...extraPlugins]; // The packaged editor and history install automatically. grid.columns = [{ prop: 'title', name: 'Title' }, { prop: 'status', name: 'Status' }, { prop: 'owner', name: 'Owner' }]; // Cached remote data can hydrate immediately: finish initial grid setup first. grid.kanban = config.remote ? { ...config, remote: false } : config; grid.source = cards.map(card => ({ ...card })); await grid.componentOnReady(); const plugins = await grid.getPlugins(); const kanban = plugins.find((plugin): plugin is KanbanPlugin<WorkCard> => plugin instanceof KanbanPlugin); if (!kanban) throw new Error('Kanban could not start.'); if (config.remote) grid.kanban = config; return kanban;}
// Give your container a width and mount once after it exists in the DOM.export async function createKanbanBoard(container: HTMLElement) { await defineCustomElements(); const grid = document.createElement('revo-grid'); grid.style.cssText = 'display:block;width:100%;height:400px'; container.append(grid); try { const kanban = await configureBoard(grid); return { grid, kanban, destroy() { grid.remove(); } }; } catch (error) { grid.remove(); throw error; }}Try it: move a card, choose Undo, then Redo. Watch both its column and its position.
grid.kanbanToolbar = { actions: ['undo', 'redo'], search: false, workflowFilter: false, viewSwitcher: false,};Undo and redo
Section titled “Undo and redo”Move the first card, then undo and redo it. This is the same fixture with only history actions enabled: actions: ['undo', 'redo'], search: false, workflowFilter: false, viewSwitcher: false.
Kanban coordinates with EventManagerPlugin and HistoryPlugin. Each successful move or editor CRUD action contributes normal stable row patches plus typed card changes to the shared gridedit stream. Replay-only card state is kept in separate keyed history metadata. With EventManager collection enabled, consecutive operations merge by card ID into one transaction and one history step while preserving each card’s earliest prior state and latest committed state. Undo/redo is replayed through Kanban rather than against projected lane rows, including source-length changes.
History is not cleared merely because filtering, sorting, or board projection refreshes. Replacing grid.source with an externally authored dataset clears Kanban history so an old move cannot overwrite newer host data.
Saving history to your backend
Section titled “Saving history to your backend”A local undo is not a server rollback by itself. If your application owns writes, save replayed card changes through the same committed event flow. The optional managed adapter coordinates accepted-only history and backend conflict decisions for a complete dataset.
Next steps
Section titled “Next steps”Continue with save board state, application-owned saving and events. See the Kanban API for exact types and signatures.