Phase 2: Canvas Editor Core
Implements the core canvas editor with react-konva: - Added dependencies: react-konva, konva, use-image - DesignCanvas component: 300×300px Stage with T-shirt SVG overlay - TShirtSVG component: Visual t-shirt outline with print zone indicator - ImageElement: Draggable/resizable image with Transformer handles - TextElement: Draggable/resizable text with Transformer handles - useDesignEditor hook: Element CRUD, selection, reordering - Keyboard shortcut: Delete/Backspace removes selected element - Test image added on mount for Phase 2 verification Canvas info bar shows: "Design Area: 15" × 15" • Export: 4500 × 4500px @ 300 DPI" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
59
client/src/hooks/useDesignEditor.js
Normal file
59
client/src/hooks/useDesignEditor.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
export function useDesignEditor() {
|
||||
const [elements, setElements] = useState([]);
|
||||
const [selectedId, setSelectedId] = useState(null);
|
||||
|
||||
const addElement = useCallback((element) => {
|
||||
const newElement = {
|
||||
...element,
|
||||
id: `element-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
};
|
||||
setElements((prev) => [...prev, newElement]);
|
||||
setSelectedId(newElement.id);
|
||||
return newElement.id;
|
||||
}, []);
|
||||
|
||||
const updateElement = useCallback((id, attrs) => {
|
||||
setElements((prev) =>
|
||||
prev.map((el) => (el.id === id ? { ...el, ...attrs } : el))
|
||||
);
|
||||
}, []);
|
||||
|
||||
const deleteElement = useCallback((id) => {
|
||||
setElements((prev) => prev.filter((el) => el.id !== id));
|
||||
if (selectedId === id) {
|
||||
setSelectedId(null);
|
||||
}
|
||||
}, [selectedId]);
|
||||
|
||||
const selectElement = useCallback((id) => {
|
||||
setSelectedId(id);
|
||||
}, []);
|
||||
|
||||
const deselectAll = useCallback(() => {
|
||||
setSelectedId(null);
|
||||
}, []);
|
||||
|
||||
const reorderElement = useCallback((id, newOrder) => {
|
||||
setElements((prev) => {
|
||||
const index = prev.findIndex((el) => el.id === id);
|
||||
if (index === -1 || index === newOrder) return prev;
|
||||
const newElements = [...prev];
|
||||
const [removed] = newElements.splice(index, 1);
|
||||
newElements.splice(newOrder, 0, removed);
|
||||
return newElements;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
elements,
|
||||
selectedId,
|
||||
addElement,
|
||||
updateElement,
|
||||
deleteElement,
|
||||
selectElement,
|
||||
deselectAll,
|
||||
reorderElement,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user