refactor: move clipboard util script into custom hook

This commit is contained in:
trafficlunar 2025-02-08 13:16:46 +00:00
parent 14c026b81a
commit 4687330f02
4 changed files with 52 additions and 48 deletions

View file

@ -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,
] ]

View file

@ -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
View 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 };
}

View file

@ -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);
}
}