feat: Lifted useCanvasState() from DesignCanvas to App.tsx, threaded al…

- "app/src/App.tsx"
- "app/src/views/DesignCanvas.tsx"

GSD-Task: S01/T02
This commit is contained in:
jlightner 2026-03-26 06:19:30 +00:00
parent 6c8c31e13b
commit 62c866be84
2 changed files with 70 additions and 25 deletions

View file

@ -1,5 +1,7 @@
import { useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import type Konva from 'konva';
import type { TraceMetadata } from './types/engine';
import { useCanvasState } from './hooks/useCanvasState';
import ImportConvert from './views/ImportConvert';
import DesignCanvas from './views/DesignCanvas';
import './App.css';
@ -10,6 +12,13 @@ function App() {
const [view, setView] = useState<ViewState>('import');
const [svgResult, setSvgResult] = useState<string | null>(null);
const [traceMetadata, setTraceMetadata] = useState<TraceMetadata | null>(null);
const [pngDataUrl, setPngDataUrl] = useState<string | null>(null);
// Lifted canvas state — shared between View 2 (DesignCanvas) and View 3 (ExportView)
const canvasState = useCanvasState(traceMetadata);
// Stage ref created here so View 3 can capture PNG from View 2's Konva stage
const stageRef = useRef<Konva.Stage | null>(null);
const handleUseThis = (svgOutput: string, metadata: TraceMetadata) => {
setSvgResult(svgOutput);
@ -17,13 +26,40 @@ function App() {
setView('canvas');
};
const handleExport = useCallback(() => {
// Capture PNG data URL from the Konva stage before navigating away
if (stageRef.current) {
const dataUrl = stageRef.current.toDataURL({ pixelRatio: 2 });
setPngDataUrl(dataUrl);
}
setView('export');
}, []);
const handleBackToCanvas = useCallback(() => {
setView('canvas');
}, []);
return (
<div id="app">
{view === 'import' && <ImportConvert onUseThis={handleUseThis} />}
{view === 'canvas' && (
<DesignCanvas svgData={svgResult} traceMetadata={traceMetadata} />
<DesignCanvas
svgData={svgResult}
stageRef={stageRef}
onExport={handleExport}
{...canvasState}
/>
)}
{view === 'export' && (
<div className="placeholder-view" data-testid="export-view">
<p>View 3: Export (placeholder)</p>
<p>PNG preview captured: {pngDataUrl ? 'Yes' : 'No'}</p>
<p>Objects: {canvasState.state.objects.length}</p>
<button type="button" onClick={handleBackToCanvas}>
Back to Design
</button>
</div>
)}
{view === 'export' && <div className="placeholder-view">View 3: Export</div>}
</div>
);
}

View file

@ -8,9 +8,8 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type Konva from 'konva';
import type { TraceMetadata } from '../types/engine';
import type { ArtboardConfig, CanvasObject, ImageObject } from '../types/canvas';
import { useCanvasState } from '../hooks/useCanvasState';
import type { UseCanvasStateReturn } from '../hooks/useCanvasState';
import ArtboardSetup from '../components/canvas/ArtboardSetup';
import KonvaStage from '../components/canvas/KonvaStage';
import type { CanvasTool } from '../components/canvas/KonvaStage';
@ -21,31 +20,33 @@ import ShapeProperties from '../components/canvas/ShapeProperties';
import { toPx } from '../utils/artboardShapes';
import styles from './DesignCanvas.module.css';
interface DesignCanvasProps {
export interface DesignCanvasProps extends UseCanvasStateReturn {
svgData: string | null;
traceMetadata: TraceMetadata | null;
/** Ref to the Konva Stage, created in App.tsx for cross-view access (PNG export). */
stageRef: React.RefObject<Konva.Stage | null>;
/** Called when user clicks Export — App.tsx navigates to View 3. */
onExport: () => void;
}
export default function DesignCanvas({
svgData,
traceMetadata,
state,
addObject,
removeObject,
updateObject,
selectObjects,
deselectAll,
reorderObject,
toggleVisibility,
toggleLock,
setArtboard,
undo,
redo,
canUndo,
canRedo,
stageRef,
onExport,
}: DesignCanvasProps) {
const {
state,
addObject,
removeObject,
updateObject,
selectObjects,
deselectAll,
reorderObject,
toggleVisibility,
toggleLock,
setArtboard,
undo,
redo,
canUndo,
canRedo,
} = useCanvasState(traceMetadata);
const [activeTool, setActiveTool] = useState<CanvasTool>('pointer');
const [showArtboardSetup, setShowArtboardSetup] = useState(true);
@ -53,7 +54,6 @@ export default function DesignCanvas({
const [showGrid, setShowGrid] = useState(false);
const [zoomLevel, setZoomLevel] = useState(1);
const stageRef = useRef<Konva.Stage | null>(null);
const canvasContainerRef = useRef<HTMLDivElement | null>(null);
const [stageSize, setStageSize] = useState({ width: 800, height: 600 });
@ -265,6 +265,15 @@ export default function DesignCanvas({
onZoomOut={handleZoomOut}
onZoomFit={handleZoomFit}
/>
<button
type="button"
className="canvas-tool-btn"
onClick={onExport}
title="Export design"
data-testid="export-btn"
>
Export
</button>
</div>
{/* Main area: canvas + right panel */}