test: Added TextObject to CanvasObject union and wired text tool into K…
- "app/src/types/canvas.ts" - "app/src/components/canvas/KonvaStage.tsx" - "app/src/components/canvas/CanvasToolbar.tsx" - "app/src/components/canvas/ObjectPanel.tsx" - "app/src/components/canvas/ShapeProperties.tsx" - "app/src/components/canvas/AlignmentBar.tsx" GSD-Task: S03/T02
This commit is contained in:
parent
ab170d8d20
commit
24fb28d622
6 changed files with 76 additions and 3 deletions
|
|
@ -48,6 +48,10 @@ function toBoundingRect(obj: CanvasObject): BoundingRect {
|
|||
h = Math.max(...ys) - Math.min(...ys);
|
||||
break;
|
||||
}
|
||||
case 'text':
|
||||
w = obj.width;
|
||||
h = obj.fontSize * obj.lineHeight;
|
||||
break;
|
||||
}
|
||||
return { id: obj.id, x: obj.x, y: obj.y, width: w, height: h };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const TOOLS: { tool: CanvasTool; label: string; icon: string }[] = [
|
|||
{ tool: 'circle', label: 'Circle', icon: '○' },
|
||||
{ tool: 'ellipse', label: 'Ellipse', icon: '⬯' },
|
||||
{ tool: 'line', label: 'Line', icon: '╱' },
|
||||
{ tool: 'text', label: 'Text', icon: 'T' },
|
||||
];
|
||||
|
||||
// -- Props --------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
Ellipse,
|
||||
Line,
|
||||
Image as KonvaImage,
|
||||
Text as KonvaText,
|
||||
Transformer,
|
||||
Path,
|
||||
} from 'react-konva';
|
||||
|
|
@ -36,7 +37,7 @@ import { toPx, artboardClipPath } from '../../utils/artboardShapes';
|
|||
|
||||
// -- Types --------------------------------------------------------------------
|
||||
|
||||
export type CanvasTool = 'pointer' | 'rect' | 'circle' | 'ellipse' | 'line';
|
||||
export type CanvasTool = 'pointer' | 'rect' | 'circle' | 'ellipse' | 'line' | 'text';
|
||||
|
||||
export interface KonvaStageProps {
|
||||
width: number;
|
||||
|
|
@ -247,6 +248,9 @@ export default function KonvaStage({
|
|||
} else if (obj.type === 'ellipse') {
|
||||
(changes as Record<string, unknown>).radiusX = Math.max(5, (node.width() * scaleX) / 2);
|
||||
(changes as Record<string, unknown>).radiusY = Math.max(5, (node.height() * scaleY) / 2);
|
||||
} else if (obj.type === 'text') {
|
||||
// Scale width for wrapping, keep fontSize unchanged
|
||||
(changes as Record<string, unknown>).width = Math.max(20, node.width() * scaleX);
|
||||
}
|
||||
|
||||
onUpdateObject(obj.id, changes);
|
||||
|
|
@ -315,6 +319,23 @@ export default function KonvaStage({
|
|||
/>
|
||||
);
|
||||
|
||||
case 'text':
|
||||
return (
|
||||
<KonvaText
|
||||
key={obj.id}
|
||||
{...commonProps}
|
||||
text={obj.text}
|
||||
fontFamily={obj.fontFamily}
|
||||
fontSize={obj.fontSize}
|
||||
fill={obj.fill}
|
||||
stroke={obj.stroke}
|
||||
strokeWidth={obj.strokeWidth}
|
||||
width={obj.width}
|
||||
letterSpacing={obj.letterSpacing}
|
||||
lineHeight={obj.lineHeight}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
@ -426,6 +447,29 @@ export default function KonvaStage({
|
|||
dash: [],
|
||||
};
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
newObj = {
|
||||
id: nextId('text'),
|
||||
type: 'text',
|
||||
name: 'Text',
|
||||
x,
|
||||
y,
|
||||
rotation: 0,
|
||||
visible: true,
|
||||
locked: false,
|
||||
opacity: 1,
|
||||
text: 'Text',
|
||||
fontFamily: 'Roboto',
|
||||
fontSize: 24,
|
||||
letterSpacing: 0,
|
||||
lineHeight: 1.2,
|
||||
fill: '#000000',
|
||||
stroke: 'transparent',
|
||||
strokeWidth: 0,
|
||||
width: 200,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (newObj) {
|
||||
|
|
@ -592,6 +636,8 @@ function getObjWidth(obj: CanvasObject): number {
|
|||
case 'line':
|
||||
return Math.max(...obj.points.filter((_, i) => i % 2 === 0)) -
|
||||
Math.min(...obj.points.filter((_, i) => i % 2 === 0));
|
||||
case 'text':
|
||||
return obj.width || obj.fontSize * obj.text.length * 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -607,6 +653,8 @@ function getObjHeight(obj: CanvasObject): number {
|
|||
case 'line':
|
||||
return Math.max(...obj.points.filter((_, i) => i % 2 === 1)) -
|
||||
Math.min(...obj.points.filter((_, i) => i % 2 === 1));
|
||||
case 'text':
|
||||
return obj.fontSize * obj.lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ const TYPE_ICONS: Record<CanvasObject['type'], string> = {
|
|||
ellipse: '⬯',
|
||||
line: '╱',
|
||||
image: '🖼',
|
||||
text: 'T',
|
||||
};
|
||||
|
||||
// -- Props --------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ function getWidth(obj: CanvasObject): number {
|
|||
const xs = obj.points.filter((_, i) => i % 2 === 0);
|
||||
return Math.round((Math.max(...xs) - Math.min(...xs)) * 100) / 100;
|
||||
}
|
||||
case 'text':
|
||||
return Math.round(obj.width * 100) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +41,8 @@ function getHeight(obj: CanvasObject): number {
|
|||
const ys = obj.points.filter((_, i) => i % 2 === 1);
|
||||
return Math.round((Math.max(...ys) - Math.min(...ys)) * 100) / 100;
|
||||
}
|
||||
case 'text':
|
||||
return Math.round(obj.fontSize * obj.lineHeight * 100) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +66,7 @@ export default function ShapeProperties({
|
|||
onUpdate,
|
||||
}: ShapePropertiesProps) {
|
||||
const hasStroke = object.type !== 'image';
|
||||
const hasFill = object.type === 'rect' || object.type === 'circle' || object.type === 'ellipse';
|
||||
const hasFill = object.type === 'rect' || object.type === 'circle' || object.type === 'ellipse' || object.type === 'text';
|
||||
const isLine = object.type === 'line';
|
||||
|
||||
const handleChange = useCallback(
|
||||
|
|
|
|||
|
|
@ -82,12 +82,27 @@ export interface ImageObject extends BaseCanvasObject {
|
|||
src: string;
|
||||
}
|
||||
|
||||
export interface TextObject extends BaseCanvasObject {
|
||||
type: 'text';
|
||||
text: string;
|
||||
fontFamily: string;
|
||||
fontSize: number;
|
||||
letterSpacing: number;
|
||||
lineHeight: number;
|
||||
fill: string;
|
||||
stroke: string;
|
||||
strokeWidth: number;
|
||||
/** Wrapping width for text layout. */
|
||||
width: number;
|
||||
}
|
||||
|
||||
export type CanvasObject =
|
||||
| RectObject
|
||||
| CircleObject
|
||||
| EllipseObject
|
||||
| LineObject
|
||||
| ImageObject;
|
||||
| ImageObject
|
||||
| TextObject;
|
||||
|
||||
export type CanvasObjectType = CanvasObject['type'];
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue