feat: place back blocks when canceling selection and create history entry

This commit is contained in:
trafficlunar 2025-02-09 12:29:13 +00:00
parent 0fc7497f10
commit a68f995699
4 changed files with 62 additions and 25 deletions

View file

@ -41,7 +41,7 @@ PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
function Canvas() { function Canvas() {
const { stageSize, canvasSize, blocks, coords, scale, version, setStageSize, setBlocks, setCoords, setScale } = useContext(CanvasContext); const { stageSize, canvasSize, blocks, coords, scale, version, setStageSize, setBlocks, setCoords, setScale } = useContext(CanvasContext);
const { addHistory, undo, redo } = useContext(HistoryContext); const { addHistory, undo, redo } = useContext(HistoryContext);
const { selectionCoords, selectionLayerBlocks, setSelectionCoords, setSelectionLayerBlocks, confirmSelection } = useContext(SelectionContext); const { selectionCoords, selectionLayerBlocks, setSelectionCoords, setSelectionLayerBlocks } = useContext(SelectionContext);
const { settings } = useContext(SettingsContext); const { settings } = useContext(SettingsContext);
const { missingTexture } = useContext(TexturesContext); const { missingTexture } = useContext(TexturesContext);
const { isDark } = useContext(ThemeContext); const { isDark } = useContext(ThemeContext);
@ -245,11 +245,23 @@ function Canvas() {
async (e: React.KeyboardEvent) => { async (e: React.KeyboardEvent) => {
switch (e.key) { switch (e.key) {
case "Escape": case "Escape":
setBlocks(startBlocksRef.current);
setSelectionLayerBlocks([]); setSelectionLayerBlocks([]);
break; break;
case "Enter": case "Enter": {
confirmSelection(); const combinedBlocks = [...blocks, ...selectionLayerBlocks];
const uniqueBlocks = Array.from(new Map(combinedBlocks.map((block) => [`${block.x},${block.y}`, block])).values());
setBlocks(uniqueBlocks);
setSelectionLayerBlocks([]);
addHistory(
"Move Selection",
() => setBlocks(uniqueBlocks),
() => setBlocks(startBlocksRef.current)
);
break; break;
}
case " ": // Space case " ": // Space
setDragging(true); setDragging(true);
oldToolRef.current = tool; oldToolRef.current = tool;
@ -364,11 +376,11 @@ function Canvas() {
}, },
[ [
tool, tool,
blocks,
selectionCoords, selectionCoords,
selectionLayerBlocks,
canvasSize, canvasSize,
blockData, blockData,
blocks,
selectionLayerBlocks,
clipboard, clipboard,
setBlocks, setBlocks,
setSelectionCoords, setSelectionCoords,
@ -487,7 +499,7 @@ function Canvas() {
<CursorInformation mouseCoords={mouseCoords} /> <CursorInformation mouseCoords={mouseCoords} />
<CanvasInformation /> <CanvasInformation />
<SelectionBar /> <SelectionBar startBlocks={startBlocksRef.current} startSelectionCoords={startSelectionCoordsRef.current} />
</div> </div>
); );
} }

View file

@ -1,15 +1,52 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { CheckIcon, XIcon } from "lucide-react"; import { CheckIcon, XIcon } from "lucide-react";
import { HistoryContext } from "@/context/History";
import { SelectionContext } from "@/context/Selection"; import { SelectionContext } from "@/context/Selection";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { CanvasContext } from "@/context/Canvas";
function SelectionBar() { interface Props {
const { selectionLayerBlocks, setSelectionLayerBlocks, confirmSelection } = useContext(SelectionContext); startBlocks: Block[];
startSelectionCoords: CoordinateArray;
}
function SelectionBar({ startBlocks, startSelectionCoords }: Props) {
const { blocks, setBlocks } = useContext(CanvasContext);
const { addHistory } = useContext(HistoryContext);
const { selectionCoords, selectionLayerBlocks, setSelectionCoords, setSelectionLayerBlocks } = useContext(SelectionContext);
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const confirm = () => {
const oldSelectionCoords = [...selectionCoords];
const combinedBlocks = [...blocks, ...selectionLayerBlocks];
const uniqueBlocks = Array.from(new Map(combinedBlocks.map((block) => [`${block.x},${block.y}`, block])).values());
setBlocks(uniqueBlocks);
setSelectionLayerBlocks([]);
addHistory(
"Move Selection",
() => {
setBlocks(uniqueBlocks);
setSelectionCoords(oldSelectionCoords);
},
() => {
setBlocks(startBlocks);
setSelectionCoords(startSelectionCoords);
}
);
};
const cancel = () => {
setBlocks(startBlocks);
setSelectionLayerBlocks([]);
setSelectionCoords(startSelectionCoords);
};
useEffect(() => { useEffect(() => {
setIsVisible(selectionLayerBlocks.length !== 0); setIsVisible(selectionLayerBlocks.length !== 0);
}, [selectionLayerBlocks]); }, [selectionLayerBlocks]);
@ -20,12 +57,11 @@ function SelectionBar() {
${isVisible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6 pointer-events-none"} ${isVisible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-6 pointer-events-none"}
`} `}
> >
{/* todo: place back blocks removed */} <Button variant="ghost" className="w-8 h-8" onClick={cancel}>
<Button variant="ghost" className="w-8 h-8" onClick={() => setSelectionLayerBlocks([])}>
<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={confirm}>
<CheckIcon /> <CheckIcon />
</Button> </Button>
</div> </div>

View file

@ -5,6 +5,7 @@ import {
FileIcon, FileIcon,
ImageIcon, ImageIcon,
LassoIcon, LassoIcon,
MoveIcon,
PaintBucketIcon, PaintBucketIcon,
PencilIcon, PencilIcon,
PresentationIcon, PresentationIcon,
@ -24,6 +25,7 @@ const iconMap = {
Eraser: EraserIcon, Eraser: EraserIcon,
Lasso: LassoIcon, Lasso: LassoIcon,
"Magic Wand": WandIcon, "Magic Wand": WandIcon,
"Move Selection": MoveIcon,
"New Canvas": PresentationIcon, "New Canvas": PresentationIcon,
"Open Image": ImageIcon, "Open Image": ImageIcon,
"Open Schematic": FileIcon, "Open Schematic": FileIcon,

View file

@ -1,5 +1,4 @@
import { createContext, ReactNode, useContext, useState } from "react"; import { createContext, ReactNode, useState } from "react";
import { CanvasContext } from "./Canvas";
interface Context { interface Context {
selectionCoords: CoordinateArray; selectionCoords: CoordinateArray;
@ -7,7 +6,6 @@ interface Context {
setSelectionCoords: React.Dispatch<React.SetStateAction<CoordinateArray>>; setSelectionCoords: React.Dispatch<React.SetStateAction<CoordinateArray>>;
setSelectionLayerBlocks: React.Dispatch<React.SetStateAction<Block[]>>; setSelectionLayerBlocks: React.Dispatch<React.SetStateAction<Block[]>>;
isInSelection: (x: number, y: number) => boolean; isInSelection: (x: number, y: number) => boolean;
confirmSelection: () => void;
} }
interface Props { interface Props {
@ -17,8 +15,6 @@ interface Props {
export const SelectionContext = createContext<Context>({} as Context); export const SelectionContext = createContext<Context>({} as Context);
export const SelectionProvider = ({ children }: Props) => { export const SelectionProvider = ({ children }: Props) => {
const { blocks, setBlocks } = useContext(CanvasContext);
const [selectionCoords, setSelectionCoords] = useState<CoordinateArray>([]); const [selectionCoords, setSelectionCoords] = useState<CoordinateArray>([]);
const [selectionLayerBlocks, setSelectionLayerBlocks] = useState<Block[]>([]); const [selectionLayerBlocks, setSelectionLayerBlocks] = useState<Block[]>([]);
@ -29,14 +25,6 @@ export const SelectionProvider = ({ children }: Props) => {
return true; return true;
}; };
const confirmSelection = () => {
const combinedBlocks = [...blocks, ...selectionLayerBlocks];
const uniqueBlocks = Array.from(new Map(combinedBlocks.map((block) => [`${block.x},${block.y}`, block])).values());
setBlocks(uniqueBlocks);
setSelectionLayerBlocks([]);
};
return ( return (
<SelectionContext.Provider <SelectionContext.Provider
value={{ value={{
@ -45,7 +33,6 @@ export const SelectionProvider = ({ children }: Props) => {
setSelectionCoords, setSelectionCoords,
setSelectionLayerBlocks, setSelectionLayerBlocks,
isInSelection, isInSelection,
confirmSelection,
}} }}
> >
{children} {children}