From 4b59e9b4bedadf76b29d33a135cc878e3ccd374d Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Fri, 24 Jan 2025 15:30:05 +0000 Subject: [PATCH] feat: press enter to confirm selection also clean up useEffect(s) and useRef(s) --- src/components/canvas/Canvas.tsx | 153 +++++++++++++------------ src/components/canvas/SelectionBar.tsx | 12 +- src/utils/selection.ts | 17 ++- 3 files changed, 97 insertions(+), 85 deletions(-) diff --git a/src/components/canvas/Canvas.tsx b/src/components/canvas/Canvas.tsx index e0414c0..5bc8df1 100644 --- a/src/components/canvas/Canvas.tsx +++ b/src/components/canvas/Canvas.tsx @@ -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(); - const selectionCoordsRef = useRef(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 (
diff --git a/src/components/canvas/SelectionBar.tsx b/src/components/canvas/SelectionBar.tsx index 8d92abc..b421f57 100644 --- a/src/components/canvas/SelectionBar.tsx +++ b/src/components/canvas/SelectionBar.tsx @@ -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() { Confirm selection? -
diff --git a/src/utils/selection.ts b/src/utils/selection.ts index 838c32c..f99732e 100644 --- a/src/utils/selection.ts +++ b/src/utils/selection.ts @@ -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>, + setLayerBlocks: React.Dispatch> +) { + const combinedBlocks = [...blocks, ...layerBlocks]; + const uniqueBlocks = Array.from(new Map(combinedBlocks.map((block) => [`${block.x},${block.y}`, block])).values()); + + setBlocks(uniqueBlocks); + setLayerBlocks([]); +}