refactor: move clipboard util script into custom hook
This commit is contained in:
parent
14c026b81a
commit
4687330f02
4 changed files with 52 additions and 48 deletions
|
|
@ -13,6 +13,7 @@ import { ToolContext } from "@/context/Tool";
|
||||||
|
|
||||||
import { useTextures } from "@/hooks/useTextures";
|
import { useTextures } from "@/hooks/useTextures";
|
||||||
import { useBlockData } from "@/hooks/useBlockData";
|
import { useBlockData } from "@/hooks/useBlockData";
|
||||||
|
import { useClipboard } from "@/hooks/useClipboard";
|
||||||
|
|
||||||
import { useMoveTool } from "@/hooks/tools/move";
|
import { useMoveTool } from "@/hooks/tools/move";
|
||||||
import { useRectangleSelectTool } from "@/hooks/tools/rectangle-select";
|
import { useRectangleSelectTool } from "@/hooks/tools/rectangle-select";
|
||||||
|
|
@ -25,7 +26,6 @@ import { useEyedropperTool } from "@/hooks/tools/eyedropper";
|
||||||
import { useZoomTool } from "@/hooks/tools/zoom";
|
import { useZoomTool } from "@/hooks/tools/zoom";
|
||||||
|
|
||||||
import * as selection from "@/utils/selection";
|
import * as selection from "@/utils/selection";
|
||||||
import * as clipboard from "@/utils/clipboard";
|
|
||||||
|
|
||||||
import Blocks from "./Blocks";
|
import Blocks from "./Blocks";
|
||||||
import Cursor from "./Cursor";
|
import Cursor from "./Cursor";
|
||||||
|
|
@ -67,6 +67,8 @@ function Canvas() {
|
||||||
const startBlocksRef = useRef<Block[]>([]);
|
const startBlocksRef = useRef<Block[]>([]);
|
||||||
const startSelectionCoordsRef = useRef<CoordinateArray>([]);
|
const startSelectionCoordsRef = useRef<CoordinateArray>([]);
|
||||||
|
|
||||||
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
const zoom = useCallback(
|
const zoom = useCallback(
|
||||||
(newScale: number) => {
|
(newScale: number) => {
|
||||||
setScale(newScale);
|
setScale(newScale);
|
||||||
|
|
@ -300,11 +302,11 @@ function Canvas() {
|
||||||
break;
|
break;
|
||||||
case "c":
|
case "c":
|
||||||
if (!e.ctrlKey) return;
|
if (!e.ctrlKey) return;
|
||||||
clipboard.copy(selectionCoords, blocks);
|
clipboard.copy();
|
||||||
break;
|
break;
|
||||||
case "v":
|
case "v":
|
||||||
if (!e.ctrlKey) return;
|
if (!e.ctrlKey) return;
|
||||||
clipboard.paste(setSelectionLayerBlocks, setSelectionCoords, setTool);
|
clipboard.paste();
|
||||||
break;
|
break;
|
||||||
case "1":
|
case "1":
|
||||||
setTool("hand");
|
setTool("hand");
|
||||||
|
|
@ -360,11 +362,12 @@ function Canvas() {
|
||||||
selectionLayerBlocks,
|
selectionLayerBlocks,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
blockData,
|
blockData,
|
||||||
|
clipboard,
|
||||||
setBlocks,
|
setBlocks,
|
||||||
setCssCursor,
|
|
||||||
setSelectionCoords,
|
setSelectionCoords,
|
||||||
setSelectionLayerBlocks,
|
setSelectionLayerBlocks,
|
||||||
setTool,
|
setTool,
|
||||||
|
addHistory,
|
||||||
redo,
|
redo,
|
||||||
undo,
|
undo,
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ import { useContext } from "react";
|
||||||
import { CanvasContext } from "@/context/Canvas";
|
import { CanvasContext } from "@/context/Canvas";
|
||||||
import { HistoryContext } from "@/context/History";
|
import { HistoryContext } from "@/context/History";
|
||||||
import { SelectionContext } from "@/context/Selection";
|
import { SelectionContext } from "@/context/Selection";
|
||||||
import { ToolContext } from "@/context/Tool";
|
|
||||||
|
|
||||||
import * as clipboard from "@/utils/clipboard";
|
import { useClipboard } from "@/hooks/useClipboard";
|
||||||
|
|
||||||
import { MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger } from "@/components/ui/menubar";
|
import { MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger } from "@/components/ui/menubar";
|
||||||
|
|
||||||
function EditMenu() {
|
function EditMenu() {
|
||||||
const { blocks, setBlocks } = useContext(CanvasContext);
|
const { setBlocks } = useContext(CanvasContext);
|
||||||
const { undo, redo, isUndoAvailable, isRedoAvailable } = useContext(HistoryContext);
|
const { undo, redo, isUndoAvailable, isRedoAvailable } = useContext(HistoryContext);
|
||||||
const { selectionCoords, setSelectionCoords, setSelectionLayerBlocks } = useContext(SelectionContext);
|
const { selectionCoords } = useContext(SelectionContext);
|
||||||
const { setTool } = useContext(ToolContext);
|
|
||||||
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
const cut = () => {
|
const cut = () => {
|
||||||
setBlocks((prev) => prev.filter((b) => !selectionCoords.some(([x2, y2]) => x2 === b.x && y2 === b.y)));
|
setBlocks((prev) => prev.filter((b) => !selectionCoords.some(([x2, y2]) => x2 === b.x && y2 === b.y)));
|
||||||
|
|
@ -33,11 +33,11 @@ function EditMenu() {
|
||||||
</MenubarItem>
|
</MenubarItem>
|
||||||
<MenubarSeparator />
|
<MenubarSeparator />
|
||||||
|
|
||||||
<MenubarItem onClick={() => clipboard.copy(selectionCoords, blocks)}>
|
<MenubarItem onClick={clipboard.copy}>
|
||||||
Copy
|
Copy
|
||||||
<MenubarShortcut>Ctrl C</MenubarShortcut>
|
<MenubarShortcut>Ctrl C</MenubarShortcut>
|
||||||
</MenubarItem>
|
</MenubarItem>
|
||||||
<MenubarItem onClick={() => clipboard.paste(setSelectionLayerBlocks, setSelectionCoords, setTool)}>
|
<MenubarItem onClick={clipboard.paste}>
|
||||||
Paste
|
Paste
|
||||||
<MenubarShortcut>Ctrl V</MenubarShortcut>
|
<MenubarShortcut>Ctrl V</MenubarShortcut>
|
||||||
</MenubarItem>
|
</MenubarItem>
|
||||||
|
|
|
||||||
38
src/hooks/useClipboard.ts
Normal file
38
src/hooks/useClipboard.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
import { CanvasContext } from "@/context/Canvas";
|
||||||
|
import { SelectionContext } from "@/context/Selection";
|
||||||
|
import { ToolContext } from "@/context/Tool";
|
||||||
|
|
||||||
|
export function useClipboard() {
|
||||||
|
const { blocks } = useContext(CanvasContext);
|
||||||
|
const { selectionCoords, setSelectionCoords, setSelectionLayerBlocks } = useContext(SelectionContext);
|
||||||
|
const { setTool } = useContext(ToolContext);
|
||||||
|
|
||||||
|
function copy() {
|
||||||
|
const selectorBlocks = selectionCoords.map(([x, y]) => blocks.find((block) => block.x === x && block.y === y)).filter(Boolean);
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(selectorBlocks));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function paste() {
|
||||||
|
try {
|
||||||
|
const clipboardText = await navigator.clipboard.readText();
|
||||||
|
const clipboardBlocks = JSON.parse(clipboardText);
|
||||||
|
|
||||||
|
// Check if pasted object is of type Block[]
|
||||||
|
if (
|
||||||
|
!Array.isArray(clipboardBlocks) ||
|
||||||
|
!clipboardBlocks.every((block) => typeof block.x === "number" && typeof block.y === "number" && typeof block.name === "string")
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setSelectionLayerBlocks(clipboardBlocks);
|
||||||
|
setSelectionCoords(clipboardBlocks.map(({ x, y }) => [x, y]));
|
||||||
|
setTool("move");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to read/parse clipboard:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { copy, paste };
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
export function copy(selectionCoords: CoordinateArray, blocks: Block[]) {
|
|
||||||
// Get all blocks within selection
|
|
||||||
const selectorBlocks = selectionCoords
|
|
||||||
.map((coord) => {
|
|
||||||
const [x, y] = coord;
|
|
||||||
return blocks.find((block) => block.x === x && block.y === y);
|
|
||||||
})
|
|
||||||
.filter((block) => block !== undefined);
|
|
||||||
|
|
||||||
// Write to clipboard
|
|
||||||
navigator.clipboard.writeText(JSON.stringify(selectorBlocks));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function paste(
|
|
||||||
setSelectionLayerBlocks: React.Dispatch<React.SetStateAction<Block[]>>,
|
|
||||||
setSelectionCoords: React.Dispatch<React.SetStateAction<CoordinateArray>>,
|
|
||||||
setTool: React.Dispatch<React.SetStateAction<Tool>>
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// Read clipboard then parse it
|
|
||||||
const clipboardText = await navigator.clipboard.readText();
|
|
||||||
const clipboardBlocks = JSON.parse(clipboardText);
|
|
||||||
|
|
||||||
// Check if pasted object is of type Block[]
|
|
||||||
if (
|
|
||||||
!Array.isArray(clipboardBlocks) ||
|
|
||||||
!clipboardBlocks.every((block) => typeof block.x === "number" && typeof block.y === "number" && typeof block.name === "string")
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
setSelectionLayerBlocks(clipboardBlocks);
|
|
||||||
setSelectionCoords(clipboardBlocks.map((block) => [block.x, block.y]));
|
|
||||||
setTool("move");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to read/parse clipboard:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue