@opncanvas/history
v0.1.0
Published
Undo/redo system for design schemas using command pattern
Downloads
47
Maintainers
Readme
@opncanvas/history
Undo/redo system for design schemas using the command pattern. Works with any state management or framework.
Installation
npm install @opncanvas/history @opncanvas/schema @opncanvas/engineUsage with React
import { useHistory } from '@opncanvas/history'
import { updateNodeProperty } from '@opncanvas/engine'
import { initialFormSchema } from '@opncanvas/templates'
function Editor() {
const { schema, setSchema, undo, redo, canUndo, canRedo } = useHistory(initialFormSchema)
const handleLabelChange = (nodeId: string, newLabel: string) => {
setSchema(
updateNodeProperty(nodeId, 'label', newLabel),
'Update button label' // Optional description
)
}
return (
<div>
<button onClick={undo} disabled={!canUndo}>Undo (Ctrl+Z)</button>
<button onClick={redo} disabled={!canRedo}>Redo (Ctrl+Y)</button>
{/* Your editor UI */}
</div>
)
}Features
- Keyboard Shortcuts: Automatic Ctrl+Z (undo) and Ctrl+Y/Ctrl+Shift+Z (redo)
- Command Descriptions: Track what each change does
- Configurable History Size: Limit memory usage
- Batch Commands: Group multiple changes into one undo step
- Framework Agnostic: Use the
HistoryManagerclass directly without React
API
useHistory(initialSchema, maxHistorySize?)
React hook that returns:
schema- Current schema statesetSchema(updater, description?)- Apply a change with history trackingundo()- Undo last changeredo()- Redo last undone changecanUndo- Whether undo is availablecanRedo- Whether redo is availableclear()- Clear all historyundoSize- Number of undo stepsredoSize- Number of redo stepsundoDescription- Description of last undoredoDescription- Description of last redo
HistoryManager
Class for managing history without React:
import { HistoryManager, createCommand } from '@opncanvas/history'
import { updateNodeProperty } from '@opncanvas/engine'
const history = new HistoryManager(50) // Max 50 undo steps
let schema = initialSchema
// Execute a command
const command = createCommand(
updateNodeProperty('button_1', 'label', 'New Label'),
'Update button label'
)
schema = history.execute(command, schema)
// Undo
const undoneSchema = history.undo(schema)
if (undoneSchema) schema = undoneSchema
// Redo
const redoneSchema = history.redo(schema)
if (redoneSchema) schema = redoneSchemacreateCommand(updater, description?)
Create a command from an updater function.
createBatchCommand(commands, description?)
Group multiple commands into one:
import { createBatchCommand, createCommand } from '@opncanvas/history'
import { updateNodeProperty } from '@opncanvas/engine'
const batch = createBatchCommand([
createCommand(updateNodeProperty('button_1', 'label', 'Submit')),
createCommand(updateNodeProperty('button_1', 'color', '#000'))
], 'Style button')Examples
Custom Keyboard Shortcuts
import { useHistory } from '@opncanvas/history'
import { useEffect } from 'react'
function Editor() {
const history = useHistory(initialSchema)
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === 'u') {
e.preventDefault()
history.undo()
}
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [history.undo])
return <div>...</div>
}History UI
function HistoryPanel() {
const { undoSize, redoSize, undoDescription, canUndo, canRedo, undo, redo } = useHistory(initialSchema)
return (
<div>
<div>Undo Stack: {undoSize}</div>
<div>Redo Stack: {redoSize}</div>
<button onClick={undo} disabled={!canUndo}>
Undo {undoDescription ? `(${undoDescription})` : ''}
</button>
<button onClick={redo} disabled={!canRedo}>
Redo {redoDescription ? `(${redoDescription})` : ''}
</button>
</div>
)
}License
MIT
