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:
parent
6c8c31e13b
commit
62c866be84
2 changed files with 70 additions and 25 deletions
|
|
@ -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 type { TraceMetadata } from './types/engine';
|
||||||
|
import { useCanvasState } from './hooks/useCanvasState';
|
||||||
import ImportConvert from './views/ImportConvert';
|
import ImportConvert from './views/ImportConvert';
|
||||||
import DesignCanvas from './views/DesignCanvas';
|
import DesignCanvas from './views/DesignCanvas';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
@ -10,6 +12,13 @@ function App() {
|
||||||
const [view, setView] = useState<ViewState>('import');
|
const [view, setView] = useState<ViewState>('import');
|
||||||
const [svgResult, setSvgResult] = useState<string | null>(null);
|
const [svgResult, setSvgResult] = useState<string | null>(null);
|
||||||
const [traceMetadata, setTraceMetadata] = useState<TraceMetadata | 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) => {
|
const handleUseThis = (svgOutput: string, metadata: TraceMetadata) => {
|
||||||
setSvgResult(svgOutput);
|
setSvgResult(svgOutput);
|
||||||
|
|
@ -17,13 +26,40 @@ function App() {
|
||||||
setView('canvas');
|
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 (
|
return (
|
||||||
<div id="app">
|
<div id="app">
|
||||||
{view === 'import' && <ImportConvert onUseThis={handleUseThis} />}
|
{view === 'import' && <ImportConvert onUseThis={handleUseThis} />}
|
||||||
{view === 'canvas' && (
|
{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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@
|
||||||
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import type Konva from 'konva';
|
import type Konva from 'konva';
|
||||||
import type { TraceMetadata } from '../types/engine';
|
|
||||||
import type { ArtboardConfig, CanvasObject, ImageObject } from '../types/canvas';
|
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 ArtboardSetup from '../components/canvas/ArtboardSetup';
|
||||||
import KonvaStage from '../components/canvas/KonvaStage';
|
import KonvaStage from '../components/canvas/KonvaStage';
|
||||||
import type { CanvasTool } from '../components/canvas/KonvaStage';
|
import type { CanvasTool } from '../components/canvas/KonvaStage';
|
||||||
|
|
@ -21,16 +20,16 @@ import ShapeProperties from '../components/canvas/ShapeProperties';
|
||||||
import { toPx } from '../utils/artboardShapes';
|
import { toPx } from '../utils/artboardShapes';
|
||||||
import styles from './DesignCanvas.module.css';
|
import styles from './DesignCanvas.module.css';
|
||||||
|
|
||||||
interface DesignCanvasProps {
|
export interface DesignCanvasProps extends UseCanvasStateReturn {
|
||||||
svgData: string | null;
|
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({
|
export default function DesignCanvas({
|
||||||
svgData,
|
svgData,
|
||||||
traceMetadata,
|
|
||||||
}: DesignCanvasProps) {
|
|
||||||
const {
|
|
||||||
state,
|
state,
|
||||||
addObject,
|
addObject,
|
||||||
removeObject,
|
removeObject,
|
||||||
|
|
@ -45,7 +44,9 @@ export default function DesignCanvas({
|
||||||
redo,
|
redo,
|
||||||
canUndo,
|
canUndo,
|
||||||
canRedo,
|
canRedo,
|
||||||
} = useCanvasState(traceMetadata);
|
stageRef,
|
||||||
|
onExport,
|
||||||
|
}: DesignCanvasProps) {
|
||||||
|
|
||||||
const [activeTool, setActiveTool] = useState<CanvasTool>('pointer');
|
const [activeTool, setActiveTool] = useState<CanvasTool>('pointer');
|
||||||
const [showArtboardSetup, setShowArtboardSetup] = useState(true);
|
const [showArtboardSetup, setShowArtboardSetup] = useState(true);
|
||||||
|
|
@ -53,7 +54,6 @@ export default function DesignCanvas({
|
||||||
const [showGrid, setShowGrid] = useState(false);
|
const [showGrid, setShowGrid] = useState(false);
|
||||||
const [zoomLevel, setZoomLevel] = useState(1);
|
const [zoomLevel, setZoomLevel] = useState(1);
|
||||||
|
|
||||||
const stageRef = useRef<Konva.Stage | null>(null);
|
|
||||||
const canvasContainerRef = useRef<HTMLDivElement | null>(null);
|
const canvasContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [stageSize, setStageSize] = useState({ width: 800, height: 600 });
|
const [stageSize, setStageSize] = useState({ width: 800, height: 600 });
|
||||||
|
|
||||||
|
|
@ -265,6 +265,15 @@ export default function DesignCanvas({
|
||||||
onZoomOut={handleZoomOut}
|
onZoomOut={handleZoomOut}
|
||||||
onZoomFit={handleZoomFit}
|
onZoomFit={handleZoomFit}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="canvas-tool-btn"
|
||||||
|
onClick={onExport}
|
||||||
|
title="Export design"
|
||||||
|
data-testid="export-btn"
|
||||||
|
>
|
||||||
|
⤓ Export
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main area: canvas + right panel */}
|
{/* Main area: canvas + right panel */}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue