undo/redo
This commit is contained in:
parent
8e6abb95db
commit
a258592054
@ -1,5 +1,12 @@
|
||||
import { useMouse, useWindowSize } from '@uidotdev/usehooks';
|
||||
import { FC, MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
FC,
|
||||
KeyboardEvent,
|
||||
MouseEvent,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import './grid.scss';
|
||||
|
||||
@ -51,7 +58,6 @@ function useMouseInRange(range: Range): Point {
|
||||
const Grid: FC = () => {
|
||||
const GAP = 20;
|
||||
|
||||
// const [offset, setOffset] = useState<Point>({ x: GAP / 2, y: GAP / 2 });
|
||||
const [offset, setOffset] = useState<Point>({ x: 10, y: 10 });
|
||||
const range = useRange(offset);
|
||||
const mousePoint = useMouseInRange(range);
|
||||
@ -71,8 +77,68 @@ const Grid: FC = () => {
|
||||
}, [range, GAP]);
|
||||
|
||||
const [userLines, setUserLines] = useState<Range[]>([]);
|
||||
const [userRedoLines, setUserRedoLines] = useState<Range[]>([]);
|
||||
const [userStartPoint, setUserStartPoint] = useState<Point | null>(null);
|
||||
|
||||
// mouse clicks create lines
|
||||
const onGridClick = useCallback(
|
||||
(event: MouseEvent<SVGSVGElement>) => {
|
||||
const point = snapToGrid(mousePoint, GAP);
|
||||
|
||||
setUserRedoLines([]);
|
||||
|
||||
if (lodash.isEqual(userStartPoint, point)) return;
|
||||
if (lodash.isNull(userStartPoint)) {
|
||||
setUserStartPoint(point);
|
||||
return;
|
||||
}
|
||||
|
||||
const line = pointsToRange(userStartPoint, point);
|
||||
setUserLines([...userLines, line]);
|
||||
|
||||
if (event.shiftKey == false) {
|
||||
setUserStartPoint(null);
|
||||
} else {
|
||||
setUserStartPoint(point);
|
||||
}
|
||||
},
|
||||
[userStartPoint, setUserStartPoint, setUserLines, GAP, mousePoint]
|
||||
);
|
||||
|
||||
const onGridRightClick = useCallback(
|
||||
(event: MouseEvent<SVGSVGElement>) => {
|
||||
event.preventDefault(); // disable default context menu
|
||||
|
||||
setUserStartPoint(null);
|
||||
},
|
||||
[setUserStartPoint]
|
||||
);
|
||||
|
||||
// ctrl+z/ctrl+y for undo/redo
|
||||
const onUndo = useCallback(() => {
|
||||
const line = userLines.at(-1);
|
||||
if (!line) return;
|
||||
|
||||
setUserLines(userLines.slice(0, -1));
|
||||
setUserRedoLines([...userRedoLines, line]);
|
||||
}, [userLines, setUserLines, userRedoLines, setUserRedoLines]);
|
||||
|
||||
const onRedo = useCallback(() => {
|
||||
const line = userRedoLines.at(-1);
|
||||
if (!line) return;
|
||||
|
||||
setUserRedoLines(userRedoLines.slice(0, -1));
|
||||
setUserLines([...userLines, line]);
|
||||
}, [userLines, setUserLines, userRedoLines, setUserRedoLines]);
|
||||
|
||||
const onGridKeyDown = useCallback(
|
||||
(event: KeyboardEvent<SVGSVGElement>) => {
|
||||
if (event.key == 'z' && event.ctrlKey) onUndo();
|
||||
if (event.key == 'y' && event.ctrlKey) onRedo();
|
||||
},
|
||||
[onUndo, onRedo]
|
||||
);
|
||||
|
||||
const gridLineElements = useMemo(() => {
|
||||
return gridLines.map((line, idx) => (
|
||||
<line
|
||||
@ -130,28 +196,6 @@ const Grid: FC = () => {
|
||||
));
|
||||
}, [userLines]);
|
||||
|
||||
const onGridClick = useCallback(
|
||||
(event: MouseEvent<SVGSVGElement>) => {
|
||||
const point = snapToGrid(mousePoint, GAP);
|
||||
|
||||
if (lodash.isEqual(userStartPoint, point)) return;
|
||||
if (lodash.isNull(userStartPoint)) {
|
||||
setUserStartPoint(point);
|
||||
return;
|
||||
}
|
||||
|
||||
const line = pointsToRange(userStartPoint, point);
|
||||
setUserLines([...userLines, line]);
|
||||
|
||||
if (event.shiftKey == false) {
|
||||
setUserStartPoint(null);
|
||||
} else {
|
||||
setUserStartPoint(point);
|
||||
}
|
||||
},
|
||||
[userStartPoint, setUserStartPoint, setUserLines, GAP, mousePoint]
|
||||
);
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="grid"
|
||||
@ -159,7 +203,10 @@ const Grid: FC = () => {
|
||||
viewBox={`${range.x0} ${range.y0} ${range.x1 - range.x0} ${
|
||||
range.y1 - range.y0
|
||||
}`}
|
||||
onClick={onGridClick}
|
||||
tabIndex={0} // to support onKeyDown event
|
||||
onMouseDown={onGridClick}
|
||||
onContextMenu={onGridRightClick}
|
||||
onKeyDown={onGridKeyDown}
|
||||
>
|
||||
{gridLineElements}
|
||||
{userLineElements}
|
||||
|
Loading…
Reference in New Issue
Block a user