feat: place back blocks when canceling selection and create history entry
This commit is contained in:
parent
0fc7497f10
commit
a68f995699
4 changed files with 62 additions and 25 deletions
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue