diff --git a/components/wysiwyg/Canvas.nudge.test.tsx b/components/wysiwyg/Canvas.nudge.test.tsx index 4d91bee..6a9f2f0 100644 --- a/components/wysiwyg/Canvas.nudge.test.tsx +++ b/components/wysiwyg/Canvas.nudge.test.tsx @@ -41,4 +41,31 @@ describe('WYSIWYG Canvas keyboard nudge with 8px snap', () => { const obj = getByTestId('obj-o1') expect(obj).toHaveStyle({ top: '96px' }) // 100 - 1 => 99 -> snap8 => 96 }) + + it('Shift+ArrowRight nudges by 1px without grid snap', () => { + const frame = makeFrame() + const { getByTestId } = render() + const canvas = getByTestId('wysiwyg-canvas') + fireEvent.keyDown(canvas, { key: 'ArrowRight', shiftKey: true }) + const obj = getByTestId('obj-o1') + expect(obj).toHaveStyle({ left: '102px' }) // 101 + 1 => 102 (no snap) + }) + + it('Shift+ArrowUp nudges by 1px without grid snap', () => { + const frame = makeFrame() + const { getByTestId } = render() + const canvas = getByTestId('wysiwyg-canvas') + fireEvent.keyDown(canvas, { key: 'ArrowUp', shiftKey: true }) + const obj = getByTestId('obj-o1') + expect(obj).toHaveStyle({ top: '99px' }) // 100 - 1 => 99 (no snap) + }) + + it('Alt+ArrowRight nudges by 16px and snaps to grid', () => { + const frame = makeFrame() + const { getByTestId } = render() + const canvas = getByTestId('wysiwyg-canvas') + fireEvent.keyDown(canvas, { key: 'ArrowRight', altKey: true }) + const obj = getByTestId('obj-o1') + expect(obj).toHaveStyle({ left: '120px' }) // 101 + 16 = 117 -> snap8 => 120 + }) }) diff --git a/components/wysiwyg/Canvas.tsx b/components/wysiwyg/Canvas.tsx index fd97b35..32522e6 100644 --- a/components/wysiwyg/Canvas.tsx +++ b/components/wysiwyg/Canvas.tsx @@ -56,15 +56,23 @@ export function Canvas({ frame, onChange }: CanvasProps) { const onKeyDown = useCallback((e: React.KeyboardEvent) => { if (!selected) return let dx = 0, dy = 0 - if (e.key === 'ArrowLeft') dx = -1 - else if (e.key === 'ArrowRight') dx = 1 - else if (e.key === 'ArrowUp') dy = -1 - else if (e.key === 'ArrowDown') dy = 1 + let step = 1 + let useSnap = true + // Shift: 미세 이동(1px, 스냅 없음), Alt: 가속 이동(16px, 스냅 적용) + if (e.shiftKey) { step = 1; useSnap = false } + else if (e.altKey) { step = 16; useSnap = true } + + if (e.key === 'ArrowLeft') dx = -step + else if (e.key === 'ArrowRight') dx = step + else if (e.key === 'ArrowUp') dy = -step + else if (e.key === 'ArrowDown') dy = step else return e.preventDefault() updateObject(selected.id, (o) => { - const nx = snap8(o.x + dx) - const ny = snap8(o.y + dy) + const tx = o.x + dx + const ty = o.y + dy + const nx = useSnap ? snap8(tx) : tx + const ny = useSnap ? snap8(ty) : ty return { ...o, x: nx, y: ny } }) }, [selected, updateObject])