Nudge: Arrow keys move selection by 1px, Shift+Arrow by 10px. Consecutive nudges within 300ms are batched into a single undo entry so Cmd+Z undoes the entire sequence at once. Fix duplicate redo creating nodes with new IDs, breaking subsequent undo entries that reference the original IDs. Now snapshotSubtree/restoreSubtree preserve node IDs across redo. Fix delete undo not restoring children — deleteSelected only snapshotted top-level nodes, so undoing a frame delete lost all its children. Now snapshots the full subtree. Fix draw tool not committing resize to undo — shapes created via draw had their final dimensions lost on redo because handleDrawUp never recorded the resize. Now uses beginBatch/commitBatch to combine create+resize into a single undo entry. Clean up use-keyboard.ts — remove shouldPreventDefault and opaque PREVENT_* sets, move tool switch and nudge to dedicated keydown listener, add shift() helper. Closes #182
123 lines
3.4 KiB
TypeScript
123 lines
3.4 KiB
TypeScript
import { describe, test, expect } from 'bun:test'
|
|
|
|
import { createEditor } from '@open-pencil/core/editor'
|
|
|
|
describe('nudgeSelected', () => {
|
|
function setup() {
|
|
const editor = createEditor()
|
|
const pageId = editor.graph.getPages()[0].id
|
|
const rect = editor.graph.createNode('RECTANGLE', pageId, {
|
|
name: 'Rect',
|
|
x: 100,
|
|
y: 200,
|
|
width: 50,
|
|
height: 50,
|
|
})
|
|
editor.select([rect.id])
|
|
return { editor, rect }
|
|
}
|
|
|
|
test('nudge moves selected node by 1px', () => {
|
|
const { editor, rect } = setup()
|
|
|
|
editor.nudgeSelected(1, 0)
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(101)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(200)
|
|
|
|
editor.nudgeSelected(0, -1)
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(101)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(199)
|
|
})
|
|
|
|
test('shift nudge moves by 10px', () => {
|
|
const { editor, rect } = setup()
|
|
|
|
editor.nudgeSelected(10, 0)
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(110)
|
|
|
|
editor.nudgeSelected(0, 10)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(210)
|
|
})
|
|
|
|
test('nudge is undoable as a single entry', () => {
|
|
const { editor, rect } = setup()
|
|
|
|
editor.nudgeSelected(1, 0)
|
|
editor.nudgeSelected(1, 0)
|
|
editor.nudgeSelected(1, 0)
|
|
editor.flushNudge()
|
|
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(103)
|
|
expect(editor.undo.canUndo).toBe(true)
|
|
|
|
editor.undo.undo()
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(100)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(200)
|
|
|
|
editor.undo.redo()
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(103)
|
|
})
|
|
|
|
test('nudge skips locked nodes', () => {
|
|
const { editor, rect } = setup()
|
|
|
|
editor.graph.updateNode(rect.id, { locked: true })
|
|
editor.nudgeSelected(10, 10)
|
|
editor.flushNudge()
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(100)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(200)
|
|
expect(editor.undo.canUndo).toBe(false)
|
|
})
|
|
|
|
test('nudge does nothing with no selection', () => {
|
|
const { editor } = setup()
|
|
|
|
editor.clearSelection()
|
|
editor.nudgeSelected(10, 10)
|
|
editor.flushNudge()
|
|
expect(editor.undo.canUndo).toBe(false)
|
|
})
|
|
|
|
test('nudge moves multiple selected nodes', () => {
|
|
const { editor, rect } = setup()
|
|
const pageId = editor.graph.getPages()[0].id
|
|
const rect2 = editor.graph.createNode('RECTANGLE', pageId, {
|
|
name: 'Rect2',
|
|
x: 300,
|
|
y: 400,
|
|
width: 50,
|
|
height: 50,
|
|
})
|
|
editor.select([rect.id, rect2.id])
|
|
|
|
editor.nudgeSelected(-5, 3)
|
|
editor.flushNudge()
|
|
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(95)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(203)
|
|
expect(editor.graph.getNode(rect2.id)!.x).toBe(295)
|
|
expect(editor.graph.getNode(rect2.id)!.y).toBe(403)
|
|
})
|
|
|
|
test('separate nudge sequences create separate undo entries', () => {
|
|
const { editor, rect } = setup()
|
|
|
|
editor.nudgeSelected(5, 0)
|
|
editor.flushNudge()
|
|
|
|
editor.nudgeSelected(0, 5)
|
|
editor.flushNudge()
|
|
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(105)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(205)
|
|
|
|
editor.undo.undo()
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(105)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(200)
|
|
|
|
editor.undo.undo()
|
|
expect(editor.graph.getNode(rect.id)!.x).toBe(100)
|
|
expect(editor.graph.getNode(rect.id)!.y).toBe(200)
|
|
})
|
|
})
|