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 { ToolContext } from "@/context/Tool";
import { useTextures } from "@/hooks/useTextures"; import { useTextures } from "@/hooks/useTextures";
import { isInSelection } from "@/utils/selection"; import { confirmSelection, isInSelection } from "@/utils/selection";
import Blocks from "./Blocks"; import Blocks from "./Blocks";
import Cursor from "./Cursor"; import Cursor from "./Cursor";
@ -51,7 +51,6 @@ function Canvas() {
const holdingAltRef = useRef(false); const holdingAltRef = useRef(false);
const holdingShiftRef = useRef(false); const holdingShiftRef = useRef(false);
const oldToolRef = useRef<Tool>(); const oldToolRef = useRef<Tool>();
const selectionCoordsRef = useRef<CoordinateArray>(selectionCoords);
const visibleArea = useMemo(() => { const visibleArea = useMemo(() => {
const blockSize = 16 * scale; const blockSize = 16 * scale;
@ -383,8 +382,15 @@ function Canvas() {
} }
}, [tool, holdingAltRef, scale, mouseCoords, blocks, setSelectionCoords, setSelectedBlock, zoom]); }, [tool, holdingAltRef, scale, mouseCoords, blocks, setSelectionCoords, setSelectedBlock, zoom]);
const onKeyDown = (e: KeyboardEvent) => { const onKeyDown = useCallback(
(e: KeyboardEvent) => {
switch (e.key) { switch (e.key) {
case "Escape":
setSelectionLayerBlocks([]);
break;
case "Enter":
confirmSelection(blocks, selectionLayerBlocks, setBlocks, setSelectionLayerBlocks);
break;
case " ": // Space case " ": // Space
setDragging(true); setDragging(true);
oldToolRef.current = tool; oldToolRef.current = tool;
@ -399,7 +405,7 @@ function Canvas() {
if (tool === "zoom") setCssCursor("zoom-out"); if (tool === "zoom") setCssCursor("zoom-out");
break; break;
case "Delete": { case "Delete": {
setBlocks((prev) => prev.filter((b) => !selectionCoordsRef.current.some(([x2, y2]) => x2 === b.x && y2 === b.y))); setBlocks((prev) => prev.filter((b) => !selectionCoords.some(([x2, y2]) => x2 === b.x && y2 === b.y)));
break; break;
} }
case "1": case "1":
@ -430,9 +436,12 @@ function Canvas() {
setTool("zoom"); setTool("zoom");
break; break;
} }
}; },
[tool, blocks, selectionCoords, selectionLayerBlocks, setBlocks, setCssCursor, setSelectionLayerBlocks, setTool]
);
const onKeyUp = (e: KeyboardEvent) => { const onKeyUp = useCallback(
(e: KeyboardEvent) => {
switch (e.key) { switch (e.key) {
case " ": // Space case " ": // Space
if (!oldToolRef.current) return; if (!oldToolRef.current) return;
@ -448,11 +457,9 @@ function Canvas() {
setCssCursor("zoom-in"); setCssCursor("zoom-in");
break; break;
} }
}; },
[setCssCursor, setTool]
useEffect(() => { );
selectionCoordsRef.current = selectionCoords;
}, [selectionCoords]);
useEffect(() => { useEffect(() => {
const container = stageContainerRef.current; const container = stageContainerRef.current;
@ -470,8 +477,7 @@ function Canvas() {
resizeCanvas(); resizeCanvas();
return () => resizeObserver.disconnect(); return () => resizeObserver.disconnect();
// eslint-disable-next-line react-hooks/exhaustive-deps }, [stageContainerRef, setStageSize]);
}, [stageContainerRef]);
useEffect(() => { useEffect(() => {
window.addEventListener("keydown", onKeyDown); window.addEventListener("keydown", onKeyDown);
@ -484,8 +490,7 @@ function Canvas() {
window.removeEventListener("keydown", onKeyDown); window.removeEventListener("keydown", onKeyDown);
window.removeEventListener("keyup", onKeyUp); window.removeEventListener("keyup", onKeyUp);
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps }, [onKeyDown, onKeyUp]);
}, []);
return ( return (
<div ref={stageContainerRef} style={{ cursor: cssCursor }} className="relative w-full h-full bg-zinc-200 dark:bg-black"> <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 { CanvasContext } from "@/context/Canvas";
import { SelectionContext } from "@/context/Selection"; import { SelectionContext } from "@/context/Selection";
import { confirmSelection } from "@/utils/selection";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
function SelectionBar() { function SelectionBar() {
@ -12,14 +14,6 @@ function SelectionBar() {
const [isVisible, setIsVisible] = useState(false); 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(() => { useEffect(() => {
setIsVisible(layerBlocks.length !== 0); setIsVisible(layerBlocks.length !== 0);
}, [layerBlocks]); }, [layerBlocks]);
@ -35,7 +29,7 @@ function SelectionBar() {
<XIcon /> <XIcon />
</Button> </Button>
<span className="mx-2 text-[0.85rem]">Confirm selection?</span> <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 /> <CheckIcon />
</Button> </Button>
</div> </div>

View file

@ -1,7 +1,20 @@
// Check if a block is within the selection // 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) { if (selection.length !== 0) {
return selection.some(([x2, y2]) => x2 === x && y2 === y); return selection.some(([x2, y2]) => x2 === x && y2 === y);
} }
return true; 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([]);
}