mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
- Add delete-column undo/redo support - Add undo tracking to RowModal (create/edit/delete) - Fix patchUndoRowId to also patch create-rows actions - Extract actual row position from API response (not -1) - Fix Escape key to preserve cell selection when editing - Remove stray conflict marker from modal.tsx
144 lines
4.0 KiB
TypeScript
144 lines
4.0 KiB
TypeScript
/**
|
|
* Zustand store for table undo/redo stacks.
|
|
* Ephemeral — no persistence. Stacks are keyed by tableId.
|
|
*/
|
|
|
|
import { nanoid } from 'nanoid'
|
|
import { create } from 'zustand'
|
|
import { devtools } from 'zustand/middleware'
|
|
import type { TableUndoAction, TableUndoStacks, TableUndoState, UndoEntry } from './types'
|
|
|
|
const STACK_CAPACITY = 100
|
|
const EMPTY_STACKS: TableUndoStacks = { undo: [], redo: [] }
|
|
|
|
let undoRedoInProgress = false
|
|
|
|
/**
|
|
* Run a function without recording undo entries.
|
|
* Used by the hook when executing undo/redo mutations to prevent recursive recording.
|
|
*/
|
|
export function runWithoutRecording<T>(fn: () => T): T {
|
|
undoRedoInProgress = true
|
|
try {
|
|
return fn()
|
|
} finally {
|
|
undoRedoInProgress = false
|
|
}
|
|
}
|
|
|
|
export const useTableUndoStore = create<TableUndoState>()(
|
|
devtools(
|
|
(set, get) => ({
|
|
stacks: {},
|
|
|
|
push: (tableId: string, action: TableUndoAction) => {
|
|
if (undoRedoInProgress) return
|
|
|
|
const entry: UndoEntry = { id: nanoid(), action, timestamp: Date.now() }
|
|
|
|
set((state) => {
|
|
const current = state.stacks[tableId] ?? EMPTY_STACKS
|
|
const undoStack = [entry, ...current.undo].slice(0, STACK_CAPACITY)
|
|
return {
|
|
stacks: {
|
|
...state.stacks,
|
|
[tableId]: { undo: undoStack, redo: [] },
|
|
},
|
|
}
|
|
})
|
|
},
|
|
|
|
popUndo: (tableId: string) => {
|
|
const current = get().stacks[tableId] ?? EMPTY_STACKS
|
|
if (current.undo.length === 0) return null
|
|
|
|
const [entry, ...rest] = current.undo
|
|
set((state) => ({
|
|
stacks: {
|
|
...state.stacks,
|
|
[tableId]: {
|
|
undo: rest,
|
|
redo: [entry, ...current.redo],
|
|
},
|
|
},
|
|
}))
|
|
return entry
|
|
},
|
|
|
|
popRedo: (tableId: string) => {
|
|
const current = get().stacks[tableId] ?? EMPTY_STACKS
|
|
if (current.redo.length === 0) return null
|
|
|
|
const [entry, ...rest] = current.redo
|
|
set((state) => ({
|
|
stacks: {
|
|
...state.stacks,
|
|
[tableId]: {
|
|
undo: [entry, ...current.undo],
|
|
redo: rest,
|
|
},
|
|
},
|
|
}))
|
|
return entry
|
|
},
|
|
|
|
patchRedoRowId: (tableId: string, oldRowId: string, newRowId: string) => {
|
|
const stacks = get().stacks[tableId]
|
|
if (!stacks) return
|
|
|
|
const patchedRedo = stacks.redo.map((entry) => {
|
|
const { action } = entry
|
|
if (action.type === 'delete-rows') {
|
|
const patchedRows = action.rows.map((r) =>
|
|
r.rowId === oldRowId ? { ...r, rowId: newRowId } : r
|
|
)
|
|
return { ...entry, action: { ...action, rows: patchedRows } }
|
|
}
|
|
return entry
|
|
})
|
|
|
|
set((state) => ({
|
|
stacks: {
|
|
...state.stacks,
|
|
[tableId]: { ...stacks, redo: patchedRedo },
|
|
},
|
|
}))
|
|
},
|
|
|
|
patchUndoRowId: (tableId: string, oldRowId: string, newRowId: string) => {
|
|
const stacks = get().stacks[tableId]
|
|
if (!stacks) return
|
|
|
|
const patchedUndo = stacks.undo.map((entry) => {
|
|
const { action } = entry
|
|
if (action.type === 'create-row' && action.rowId === oldRowId) {
|
|
return { ...entry, action: { ...action, rowId: newRowId } }
|
|
}
|
|
if (action.type === 'create-rows') {
|
|
const patchedRows = action.rows.map((r) =>
|
|
r.rowId === oldRowId ? { ...r, rowId: newRowId } : r
|
|
)
|
|
return { ...entry, action: { ...action, rows: patchedRows } }
|
|
}
|
|
return entry
|
|
})
|
|
|
|
set((state) => ({
|
|
stacks: {
|
|
...state.stacks,
|
|
[tableId]: { ...stacks, undo: patchedUndo },
|
|
},
|
|
}))
|
|
},
|
|
|
|
clear: (tableId: string) => {
|
|
set((state) => {
|
|
const { [tableId]: _, ...rest } = state.stacks
|
|
return { stacks: rest }
|
|
})
|
|
},
|
|
}),
|
|
{ name: 'table-undo-store' }
|
|
)
|
|
)
|