-
Notifications
You must be signed in to change notification settings - Fork 2
feat(sheet): kollaboratives Undo/Redo #377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ import { DomSheetView } from './sheetView'; | |
| import { SheetPresence, effectiveCells, type PresenceFrame } from './sheetPresence'; | ||
| import { rangeToTSV, rangeToCSV, parseTSV, parseCSV, pasteOps, fillOps } from './sheetClipboard'; | ||
| import { normalize, selCells, selIsSingle, type Selection } from './sheetSelection'; | ||
| import { createToolbar, type ToolbarCallbacks } from './sheetToolbar'; | ||
| import { createToolbar, type ToolbarCallbacks, type ToolbarElement } from './sheetToolbar'; | ||
| import { createSheetTabs } from './sheetTabs'; | ||
| import { sortRangeOps, distinctValues, hiddenRowsForFilter } from './sheetSortFilter'; | ||
| import { createFormulaBar, type FormulaBarHandle } from './sheetFormulaBar'; | ||
|
|
@@ -73,6 +73,7 @@ export function startSheetEditor(root: HTMLElement): void { | |
| // Client-local filter state (per active sheet, reset on switch — not collaborative). | ||
| let hiddenRows = new Set<number>(); | ||
| let tabs: { el: HTMLElement; refresh: () => void } | null = null; | ||
| let toolbarEl: ToolbarElement | null = null; | ||
|
|
||
| const transport = { | ||
| send: (op: Op) => | ||
|
|
@@ -233,6 +234,7 @@ export function startSheetEditor(root: HTMLElement): void { | |
| } | ||
| view?.render(); | ||
| tabs?.refresh(); | ||
| toolbarEl?.refreshHistory(); | ||
| if (formulaBar) { | ||
| const { r0, c0, r1, c1 } = normalize(selection); | ||
| formulaBar.setActive(rangeRefA1(r0, c0, r1, c1), rawValue(selection.focus.row, selection.focus.col)); | ||
|
|
@@ -389,6 +391,9 @@ export function startSheetEditor(root: HTMLElement): void { | |
| if (!readOnly) formulaBar?.beginFormula(`=${fn}(`); | ||
| }, | ||
| fill: doFill, | ||
| undo: () => doHistory('undo'), | ||
| redo: () => doHistory('redo'), | ||
| history: () => ({ canUndo: collab?.canUndo() ?? false, canRedo: collab?.canRedo() ?? false }), | ||
| clear: (what: 'all' | 'formats' | 'contents') => { | ||
| if (readOnly || !collab) return; | ||
| blurActiveCell(); | ||
|
|
@@ -436,6 +441,7 @@ export function startSheetEditor(root: HTMLElement): void { | |
| }, | ||
| }; | ||
| const toolbar = createToolbar(actions); | ||
| toolbarEl = toolbar; | ||
| formulaBar = createFormulaBar({ | ||
| readOnly: data.readonly, | ||
| getFunctionNames: () => engine.functionNames(), | ||
|
|
@@ -644,6 +650,15 @@ export function startSheetEditor(root: HTMLElement): void { | |
| for (const op of pasteOps(grid, { row: r0, col: c0 }, activeSheetId, collab.rev)) collab.applyLocal(op); | ||
| }); | ||
| }; | ||
| // Undo/redo this client's own edits. Blur first so a half-typed cell does not | ||
| // get committed over the restored value by the blur handler. | ||
| const doHistory = (which: 'undo' | 'redo'): void => { | ||
| if (readOnly || !collab) return; | ||
| blurActiveCell(); | ||
| if (which === 'undo') collab.undo(); | ||
| else collab.redo(); | ||
|
Comment on lines
+658
to
+659
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 5. Undo leaves invalid active sheet Undoing an added sheet deletes it without updating activeSheetId. The editor remains pointed at a nonexistent sheet, producing no active tab and an empty grid until another sheet is selected. Agent Prompt
|
||
| }; | ||
|
|
||
| // Fill the selection from its first row (down) or first column (right). | ||
| // fillOps adjusts relative references, so formulas fill like in Excel. | ||
| const doFill = (dir: 'down' | 'right'): void => { | ||
|
|
@@ -671,6 +686,21 @@ export function startSheetEditor(root: HTMLElement): void { | |
| doPaste(); | ||
| return; | ||
| } | ||
| // Ctrl+Z / Ctrl+Y (and Ctrl+Shift+Z) — only outside cell editing, where the | ||
| // browser's own text undo still owns the keystroke. | ||
| if (mod && !editingNow() && !readOnly) { | ||
| const k = e.key.toLowerCase(); | ||
|
Comment on lines
+691
to
+692
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4. Formula undo triggers sheet history The global undo/redo shortcut only checks whether the grid is editing, not whether the formula-bar input is focused. Pressing Ctrl/Meta+Z while editing a formula therefore invokes workbook history instead of the input's native text undo. Agent Prompt
|
||
| if (k === 'z' && !e.shiftKey) { | ||
| e.preventDefault(); | ||
| doHistory('undo'); | ||
| return; | ||
| } | ||
| if (k === 'y' || (k === 'z' && e.shiftKey)) { | ||
| e.preventDefault(); | ||
| doHistory('redo'); | ||
| return; | ||
| } | ||
| } | ||
| // Ctrl+D / Ctrl+R fill the selection from its first row / column, like Excel. | ||
| if (mod && !editingNow() && !readOnly && (e.key === 'd' || e.key === 'D' || e.key === 'r' || e.key === 'R')) { | ||
| e.preventDefault(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2. Deleted-row undo clobbers cells
🐞 Bug≡ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools