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:
Khalid A
2026-04-21 01:03:30 -05:00
parent 1af0e6152d
commit e67017b259
10 changed files with 476 additions and 28 deletions

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