diff --git a/src/components/PatternCanvas/PatternCanvas.tsx b/src/components/PatternCanvas/PatternCanvas.tsx index 665fb69..88c9c5f 100644 --- a/src/components/PatternCanvas/PatternCanvas.tsx +++ b/src/components/PatternCanvas/PatternCanvas.tsx @@ -5,17 +5,10 @@ import { usePatternUploaded, } from "../../stores/useMachineStore"; import { usePatternStore } from "../../stores/usePatternStore"; -import { Stage, Layer, Group, Transformer } from "react-konva"; +import { Stage, Layer } from "react-konva"; import Konva from "konva"; import { PhotoIcon } from "@heroicons/react/24/solid"; -import { - Grid, - Origin, - Hoop, - Stitches, - PatternBounds, - CurrentPosition, -} from "../KonvaComponents"; +import { Grid, Origin, Hoop } from "../KonvaComponents"; import { Card, CardHeader, @@ -23,13 +16,10 @@ import { CardDescription, CardContent, } from "@/components/ui/card"; -import { - calculatePatternCenter, - convertPenStitchesToPesFormat, -} from "./patternCanvasHelpers"; import { ThreadLegend } from "./ThreadLegend"; import { PatternPositionIndicator } from "./PatternPositionIndicator"; import { ZoomControls } from "./ZoomControls"; +import { PatternLayer } from "./PatternLayer"; import { useCanvasViewport } from "../../hooks/useCanvasViewport"; import { usePatternTransform } from "../../hooks/usePatternTransform"; @@ -201,141 +191,34 @@ export function PatternCanvas() { {/* Original pattern layer: draggable with transformer (shown before upload starts) */} - {pesData && - (() => { - const originalCenter = calculatePatternCenter( - pesData.bounds, - ); - console.log("[Canvas] Rendering original pattern:", { - position: localPatternOffset, - rotation: localPatternRotation, - center: originalCenter, - bounds: pesData.bounds, - }); - return ( - <> - { - patternGroupRef.current = node; - // Set initial rotation from state - if (node) { - node.rotation(localPatternRotation); - // Try to attach transformer when group is mounted - attachTransformer(); - } - }} - draggable={!isUploading} - x={localPatternOffset.x} - y={localPatternOffset.y} - offsetX={originalCenter.x} - offsetY={originalCenter.y} - onDragEnd={handlePatternDragEnd} - onTransformEnd={handleTransformEnd} - onMouseEnter={(e) => { - const stage = e.target.getStage(); - if (stage && !isUploading) - stage.container().style.cursor = "move"; - }} - onMouseLeave={(e) => { - const stage = e.target.getStage(); - if (stage && !isUploading) - stage.container().style.cursor = "grab"; - }} - > - - - - { - transformerRef.current = node; - // Try to attach transformer when transformer is mounted - if (node) { - attachTransformer(); - } - }} - enabledAnchors={[]} - rotateEnabled={true} - borderEnabled={true} - borderStroke="#FF6B6B" - borderStrokeWidth={2} - rotationSnaps={[0, 45, 90, 135, 180, 225, 270, 315]} - ignoreStroke={true} - rotateAnchorOffset={20} - /> - - ); - })()} + {pesData && ( + + )} {/* Uploaded pattern layer: locked, rotation baked in (shown during and after upload) */} - {uploadedPesData && - (() => { - const uploadedCenter = calculatePatternCenter( - uploadedPesData.bounds, - ); - console.log("[Canvas] Rendering uploaded pattern:", { - position: initialUploadedPatternOffset, - center: uploadedCenter, - bounds: uploadedPesData.bounds, - }); - return ( - - - - - ); - })()} - - - {/* Current position layer (for uploaded pattern during sewing) */} - - {uploadedPesData && - sewingProgress && - sewingProgress.currentStitch > 0 && - (() => { - const center = calculatePatternCenter( - uploadedPesData.bounds, - ); - return ( - - - - ); - })()} + {uploadedPesData && ( + + )} )} diff --git a/src/components/PatternCanvas/PatternLayer.tsx b/src/components/PatternCanvas/PatternLayer.tsx new file mode 100644 index 0000000..7d1f4ff --- /dev/null +++ b/src/components/PatternCanvas/PatternLayer.tsx @@ -0,0 +1,156 @@ +/** + * PatternLayer Component + * + * Unified component for rendering pattern layers (both original and uploaded) + * Handles both interactive (draggable/rotatable) and locked states + */ + +import { useMemo, type RefObject } from "react"; +import { Group, Transformer } from "react-konva"; +import type Konva from "konva"; +import type { KonvaEventObject } from "konva/lib/Node"; +import type { PesPatternData } from "../../formats/import/pesImporter"; +import { + calculatePatternCenter, + convertPenStitchesToPesFormat, +} from "./patternCanvasHelpers"; +import { Stitches, PatternBounds, CurrentPosition } from "../KonvaComponents"; + +interface PatternLayerProps { + pesData: PesPatternData; + offset: { x: number; y: number }; + rotation?: number; + isInteractive: boolean; + showProgress?: boolean; + currentStitchIndex?: number; + patternGroupRef?: RefObject; + transformerRef?: RefObject; + onDragEnd?: (e: Konva.KonvaEventObject) => void; + onTransformEnd?: (e: KonvaEventObject) => void; + attachTransformer?: () => void; +} + +export function PatternLayer({ + pesData, + offset, + rotation = 0, + isInteractive, + showProgress = false, + currentStitchIndex = 0, + patternGroupRef, + transformerRef, + onDragEnd, + onTransformEnd, + attachTransformer, +}: PatternLayerProps) { + const center = useMemo( + () => calculatePatternCenter(pesData.bounds), + [pesData.bounds], + ); + + const stitches = useMemo( + () => convertPenStitchesToPesFormat(pesData.penStitches), + [pesData.penStitches], + ); + + const groupName = isInteractive ? "pattern-group" : "uploaded-pattern-group"; + + console.log( + `[PatternLayer] Rendering ${isInteractive ? "original" : "uploaded"} pattern:`, + { + position: offset, + rotation: isInteractive ? rotation : "n/a", + center, + bounds: pesData.bounds, + }, + ); + + return ( + <> + { + if (patternGroupRef) { + patternGroupRef.current = node; + } + // Set initial rotation from state + if (node && isInteractive) { + node.rotation(rotation); + // Try to attach transformer when group is mounted + if (attachTransformer) { + attachTransformer(); + } + } + } + : undefined + } + draggable={isInteractive} + x={offset.x} + y={offset.y} + offsetX={center.x} + offsetY={center.y} + onDragEnd={isInteractive ? onDragEnd : undefined} + onTransformEnd={isInteractive ? onTransformEnd : undefined} + onMouseEnter={ + isInteractive + ? (e) => { + const stage = e.target.getStage(); + if (stage) stage.container().style.cursor = "move"; + } + : undefined + } + onMouseLeave={ + isInteractive + ? (e) => { + const stage = e.target.getStage(); + if (stage) stage.container().style.cursor = "grab"; + } + : undefined + } + > + + + + + {/* Transformer only for interactive layer */} + {isInteractive && transformerRef && ( + { + if (transformerRef) { + transformerRef.current = node; + } + // Try to attach transformer when transformer is mounted + if (node && attachTransformer) { + attachTransformer(); + } + }} + enabledAnchors={[]} + rotateEnabled={true} + borderEnabled={true} + borderStroke="#FF6B6B" + borderStrokeWidth={2} + rotationSnaps={[0, 45, 90, 135, 180, 225, 270, 315]} + ignoreStroke={true} + rotateAnchorOffset={20} + /> + )} + + {/* Current position indicator (only for uploaded pattern with progress) */} + {!isInteractive && showProgress && currentStitchIndex > 0 && ( + + + + )} + + ); +}