feat: press enter to confirm selection

also clean up useEffect(s) and useRef(s)
This commit is contained in:
trafficlunar 2025-01-24 15:30:05 +00:00
parent f17b2b9f42
commit 4b59e9b4be
3 changed files with 97 additions and 85 deletions

View file

@ -11,7 +11,7 @@ import { ThemeContext } from "@/context/Theme";
import { ToolContext } from "@/context/Tool";
import { useTextures } from "@/hooks/useTextures";
import { isInSelection } from "@/utils/selection";
import { confirmSelection, isInSelection } from "@/utils/selection";
import Blocks from "./Blocks";
import Cursor from "./Cursor";
@ -51,7 +51,6 @@ function Canvas() {
const holdingAltRef = useRef(false);
const holdingShiftRef = useRef(false);
const oldToolRef = useRef<Tool>();
const selectionCoordsRef = useRef<CoordinateArray>(selectionCoords);
const visibleArea = useMemo(() => {
const blockSize = 16 * scale;
@ -383,76 +382,84 @@ function Canvas() {
}
}, [tool, holdingAltRef, scale, mouseCoords, blocks, setSelectionCoords, setSelectedBlock, zoom]);
const onKeyDown = (e: KeyboardEvent) => {
switch (e.key) {
case " ": // Space
setDragging(true);
oldToolRef.current = tool;
setTool("hand");
setCssCursor("grabbing");
break;
case "Shift":
holdingShiftRef.current = true;
break;
case "Alt":
holdingAltRef.current = true;
if (tool === "zoom") setCssCursor("zoom-out");
break;
case "Delete": {
setBlocks((prev) => prev.filter((b) => !selectionCoordsRef.current.some(([x2, y2]) => x2 === b.x && y2 === b.y)));
break;
const onKeyDown = useCallback(
(e: KeyboardEvent) => {
switch (e.key) {
case "Escape":
setSelectionLayerBlocks([]);
break;
case "Enter":
confirmSelection(blocks, selectionLayerBlocks, setBlocks, setSelectionLayerBlocks);
break;
case " ": // Space
setDragging(true);
oldToolRef.current = tool;
setTool("hand");
setCssCursor("grabbing");
break;
case "Shift":
holdingShiftRef.current = true;
break;
case "Alt":
holdingAltRef.current = true;
if (tool === "zoom") setCssCursor("zoom-out");
break;
case "Delete": {
setBlocks((prev) => prev.filter((b) => !selectionCoords.some(([x2, y2]) => x2 === b.x && y2 === b.y)));
break;
}
case "1":
setTool("hand");
break;
case "2":
setTool("move");
break;
case "3":
setTool("rectangle-select");
break;
case "4":
setTool("lasso");
break;
case "5":
setTool("magic-wand");
break;
case "6":
setTool("pencil");
break;
case "7":
setTool("eraser");
break;
case "8":
setTool("eyedropper");
break;
case "9":
setTool("zoom");
break;
}
case "1":
setTool("hand");
break;
case "2":
setTool("move");
break;
case "3":
setTool("rectangle-select");
break;
case "4":
setTool("lasso");
break;
case "5":
setTool("magic-wand");
break;
case "6":
setTool("pencil");
break;
case "7":
setTool("eraser");
break;
case "8":
setTool("eyedropper");
break;
case "9":
setTool("zoom");
break;
}
};
},
[tool, blocks, selectionCoords, selectionLayerBlocks, setBlocks, setCssCursor, setSelectionLayerBlocks, setTool]
);
const onKeyUp = (e: KeyboardEvent) => {
switch (e.key) {
case " ": // Space
if (!oldToolRef.current) return;
setDragging(false);
setCssCursor("grab");
setTool(oldToolRef.current);
break;
case "Shift":
holdingShiftRef.current = false;
break;
case "Alt":
holdingAltRef.current = false;
setCssCursor("zoom-in");
break;
}
};
useEffect(() => {
selectionCoordsRef.current = selectionCoords;
}, [selectionCoords]);
const onKeyUp = useCallback(
(e: KeyboardEvent) => {
switch (e.key) {
case " ": // Space
if (!oldToolRef.current) return;
setDragging(false);
setCssCursor("grab");
setTool(oldToolRef.current);
break;
case "Shift":
holdingShiftRef.current = false;
break;
case "Alt":
holdingAltRef.current = false;
setCssCursor("zoom-in");
break;
}
},
[setCssCursor, setTool]
);
useEffect(() => {
const container = stageContainerRef.current;
@ -470,8 +477,7 @@ function Canvas() {
resizeCanvas();
return () => resizeObserver.disconnect();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stageContainerRef]);
}, [stageContainerRef, setStageSize]);
useEffect(() => {
window.addEventListener("keydown", onKeyDown);
@ -484,8 +490,7 @@ function Canvas() {
window.removeEventListener("keydown", onKeyDown);
window.removeEventListener("keyup", onKeyUp);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}, [onKeyDown, onKeyUp]);
return (
<div ref={stageContainerRef} style={{ cursor: cssCursor }} className="relative w-full h-full bg-zinc-200 dark:bg-black">

View file

@ -4,6 +4,8 @@ import { CheckIcon, XIcon } from "lucide-react";
import { CanvasContext } from "@/context/Canvas";
import { SelectionContext } from "@/context/Selection";
import { confirmSelection } from "@/utils/selection";
import { Button } from "@/components/ui/button";
function SelectionBar() {
@ -12,14 +14,6 @@ function SelectionBar() {
const [isVisible, setIsVisible] = useState(false);
const confirmSelection = () => {
const combinedBlocks = [...blocks, ...layerBlocks];
const uniqueBlocks = Array.from(new Map(combinedBlocks.map((block) => [`${block.x},${block.y}`, block])).values());
setBlocks(uniqueBlocks);
setLayerBlocks([]);
};
useEffect(() => {
setIsVisible(layerBlocks.length !== 0);
}, [layerBlocks]);
@ -35,7 +29,7 @@ function SelectionBar() {
<XIcon />
</Button>
<span className="mx-2 text-[0.85rem]">Confirm selection?</span>
<Button variant="ghost" className="w-8 h-8" onClick={confirmSelection}>
<Button variant="ghost" className="w-8 h-8" onClick={() => confirmSelection(blocks, layerBlocks, setBlocks, setLayerBlocks)}>
<CheckIcon />
</Button>
</div>

View file

@ -1,7 +1,20 @@
// Check if a block is within the selection
export const isInSelection = (selection: CoordinateArray, x: number, y: number): boolean => {
export function isInSelection(selection: CoordinateArray, x: number, y: number): boolean {
if (selection.length !== 0) {
return selection.some(([x2, y2]) => x2 === x && y2 === y);
}
return true;
};
}
export function confirmSelection(
blocks: Block[],
layerBlocks: Block[],
setBlocks: React.Dispatch<React.SetStateAction<Block[]>>,
setLayerBlocks: React.Dispatch<React.SetStateAction<Block[]>>
) {
const combinedBlocks = [...blocks, ...layerBlocks];
const uniqueBlocks = Array.from(new Map(combinedBlocks.map((block) => [`${block.x},${block.y}`, block])).values());
setBlocks(uniqueBlocks);
setLayerBlocks([]);
}