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);
|
h = Math.max(...ys) - Math.min(...ys);
|
||||||
break;
|
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 };
|
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: 'circle', label: 'Circle', icon: '○' },
|
||||||
{ tool: 'ellipse', label: 'Ellipse', icon: '⬯' },
|
{ tool: 'ellipse', label: 'Ellipse', icon: '⬯' },
|
||||||
{ tool: 'line', label: 'Line', icon: '╱' },
|
{ tool: 'line', label: 'Line', icon: '╱' },
|
||||||
|
{ tool: 'text', label: 'Text', icon: 'T' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// -- Props --------------------------------------------------------------------
|
// -- Props --------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import {
|
||||||
Ellipse,
|
Ellipse,
|
||||||
Line,
|
Line,
|
||||||
Image as KonvaImage,
|
Image as KonvaImage,
|
||||||
|
Text as KonvaText,
|
||||||
Transformer,
|
Transformer,
|
||||||
Path,
|
Path,
|
||||||
} from 'react-konva';
|
} from 'react-konva';
|
||||||
|
|
@ -36,7 +37,7 @@ import { toPx, artboardClipPath } from '../../utils/artboardShapes';
|
||||||
|
|
||||||
// -- Types --------------------------------------------------------------------
|
// -- Types --------------------------------------------------------------------
|
||||||
|
|
||||||
export type CanvasTool = 'pointer' | 'rect' | 'circle' | 'ellipse' | 'line';
|
export type CanvasTool = 'pointer' | 'rect' | 'circle' | 'ellipse' | 'line' | 'text';
|
||||||
|
|
||||||
export interface KonvaStageProps {
|
export interface KonvaStageProps {
|
||||||
width: number;
|
width: number;
|
||||||
|
|
@ -247,6 +248,9 @@ export default function KonvaStage({
|
||||||
} else if (obj.type === 'ellipse') {
|
} else if (obj.type === 'ellipse') {
|
||||||
(changes as Record<string, unknown>).radiusX = Math.max(5, (node.width() * scaleX) / 2);
|
(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);
|
(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);
|
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:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +447,29 @@ export default function KonvaStage({
|
||||||
dash: [],
|
dash: [],
|
||||||
};
|
};
|
||||||
break;
|
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) {
|
if (newObj) {
|
||||||
|
|
@ -592,6 +636,8 @@ function getObjWidth(obj: CanvasObject): number {
|
||||||
case 'line':
|
case 'line':
|
||||||
return Math.max(...obj.points.filter((_, i) => i % 2 === 0)) -
|
return Math.max(...obj.points.filter((_, i) => i % 2 === 0)) -
|
||||||
Math.min(...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':
|
case 'line':
|
||||||
return Math.max(...obj.points.filter((_, i) => i % 2 === 1)) -
|
return Math.max(...obj.points.filter((_, i) => i % 2 === 1)) -
|
||||||
Math.min(...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: '⬯',
|
ellipse: '⬯',
|
||||||
line: '╱',
|
line: '╱',
|
||||||
image: '🖼',
|
image: '🖼',
|
||||||
|
text: 'T',
|
||||||
};
|
};
|
||||||
|
|
||||||
// -- Props --------------------------------------------------------------------
|
// -- Props --------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ function getWidth(obj: CanvasObject): number {
|
||||||
const xs = obj.points.filter((_, i) => i % 2 === 0);
|
const xs = obj.points.filter((_, i) => i % 2 === 0);
|
||||||
return Math.round((Math.max(...xs) - Math.min(...xs)) * 100) / 100;
|
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);
|
const ys = obj.points.filter((_, i) => i % 2 === 1);
|
||||||
return Math.round((Math.max(...ys) - Math.min(...ys)) * 100) / 100;
|
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,
|
onUpdate,
|
||||||
}: ShapePropertiesProps) {
|
}: ShapePropertiesProps) {
|
||||||
const hasStroke = object.type !== 'image';
|
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 isLine = object.type === 'line';
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
|
|
|
||||||
|
|
@ -82,12 +82,27 @@ export interface ImageObject extends BaseCanvasObject {
|
||||||
src: string;
|
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 =
|
export type CanvasObject =
|
||||||
| RectObject
|
| RectObject
|
||||||
| CircleObject
|
| CircleObject
|
||||||
| EllipseObject
|
| EllipseObject
|
||||||
| LineObject
|
| LineObject
|
||||||
| ImageObject;
|
| ImageObject
|
||||||
|
| TextObject;
|
||||||
|
|
||||||
export type CanvasObjectType = CanvasObject['type'];
|
export type CanvasObjectType = CanvasObject['type'];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue