diff --git a/public/low-wireframes/paragraphScribbled.svg b/public/low-wireframes/paragraphScribbled.svg new file mode 100644 index 00000000..a3f11e0c --- /dev/null +++ b/public/low-wireframes/paragraphScribbled.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/index.ts b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/index.ts new file mode 100644 index 00000000..7ab23d52 --- /dev/null +++ b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/index.ts @@ -0,0 +1 @@ +export * from './paragraph-scribbled-shape'; diff --git a/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled-shape.tsx b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled-shape.tsx new file mode 100644 index 00000000..ff6da889 --- /dev/null +++ b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled-shape.tsx @@ -0,0 +1,69 @@ +import { forwardRef, useMemo } from 'react'; +import { Group, Path, Rect } from 'react-konva'; +import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; +import { ShapeProps } from '../../shape.model'; +import { useShapeProps } from '../../../shapes/use-shape-props.hook'; +import { BASIC_SHAPE } from '../../front-components/shape.const'; +import { useGroupShapeProps } from '../../mock-components.utils'; +import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes'; +import { MIN_LINE_HEIGHT } from './paragraph-scribbled.const'; +import { calculateParagraphPaths } from './paragraph-scribbled.business'; + +const paragraphScribbledShapeRestrictions: ShapeSizeRestrictions = { + minWidth: 100, + minHeight: MIN_LINE_HEIGHT, + maxWidth: -1, + maxHeight: -1, + defaultWidth: 300, + defaultHeight: 150, +}; + +export const getParagraphScribbledShapeRestrictions = + (): ShapeSizeRestrictions => paragraphScribbledShapeRestrictions; + +const shapeType: ShapeType = 'paragraphScribbled'; + +export const ParagraphScribbled = forwardRef((props, ref) => { + const { width, height, id, otherProps, ...shapeProps } = props; + + const { stroke } = useShapeProps(otherProps, BASIC_SHAPE); + const commonGroupProps = useGroupShapeProps( + props, + { width, height }, + shapeType, + ref + ); + + const restrictedSize = fitSizeToShapeSizeRestrictions( + paragraphScribbledShapeRestrictions, + width, + height + ); + + const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; + + const paths = useMemo(() => { + return calculateParagraphPaths(restrictedWidth, restrictedHeight, id); + }, [restrictedWidth, restrictedHeight, id]); + + return ( + + {paths.map((path, idx) => ( + + ))} + + + ); +}); diff --git a/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled.business.ts b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled.business.ts new file mode 100644 index 00000000..7256ebb4 --- /dev/null +++ b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled.business.ts @@ -0,0 +1,36 @@ +import { calculatePath } from '../text-scribbled-shape/text-scribbled.business'; +import { MIN_LINE_HEIGHT } from './paragraph-scribbled.const'; + +export const calculateParagraphPaths = ( + restrictedWidth: number, + restrictedHeight: number, + id: string +): string[] => { + // Calculate how many lines fit based on the height + const numLines = Math.max(1, Math.trunc(restrictedHeight / MIN_LINE_HEIGHT)); + + return Array.from({ length: numLines }).map((_, i) => { + const lineY = i * MIN_LINE_HEIGHT; + const lineId = `${id}-${i}`; + const rawPath = calculatePath(restrictedWidth, MIN_LINE_HEIGHT, lineId); + + // Adjust the path to shift Y coordinate for each line + // 🔍 Step by step: + // The path assumes the text is vertically centered in a block of given height (e.g., 25px). + // If you just drew this path multiple times, all lines would overlap. + // To fix that, we shift the Y coordinate for each point in the path. + // + // Regular expression: /\d+,\d+/g + // Finds all x,y coordinates in the path string (e.g., "10,12", "15,11"). + // We split each coordinate, convert y to number, add vertical offset (lineY), + // then reassemble the coordinate string. + const shiftedPath = rawPath.replace(/\d+,\d+/g, match => { + const [xStr, yStr] = match.split(','); + const x = parseFloat(xStr); + const y = parseFloat(yStr) + lineY; + return `${x},${y}`; + }); + + return shiftedPath; + }); +}; diff --git a/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled.const.ts b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled.const.ts new file mode 100644 index 00000000..9667baf5 --- /dev/null +++ b/src/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape/paragraph-scribbled.const.ts @@ -0,0 +1 @@ +export const MIN_LINE_HEIGHT = 25; diff --git a/src/core/model/index.ts b/src/core/model/index.ts index db8001e4..97dd6e3f 100644 --- a/src/core/model/index.ts +++ b/src/core/model/index.ts @@ -83,7 +83,8 @@ export type ShapeType = | 'ellipseLow' | 'rectangleLow' | 'circleLow' - | 'textScribbled'; + | 'textScribbled' + | 'paragraphScribbled'; export const ShapeDisplayName: Record = { multiple: 'multiple', @@ -156,6 +157,7 @@ export const ShapeDisplayName: Record = { rectangleLow: 'Rectangle Placeholder', circleLow: 'Circle', textScribbled: 'Text Scribbled', + paragraphScribbled: 'Paragraph Scribbled', }; export type EditType = 'input' | 'textarea' | 'imageupload'; diff --git a/src/pods/canvas/model/shape-size.mapper.ts b/src/pods/canvas/model/shape-size.mapper.ts index 1c3f0052..e4f8fd74 100644 --- a/src/pods/canvas/model/shape-size.mapper.ts +++ b/src/pods/canvas/model/shape-size.mapper.ts @@ -87,6 +87,7 @@ import { getCircleLowShapeSizeRestrictions, getTextScribbledShapeRestrictions, } from '@/common/components/mock-components/front-low-wireframes-components'; +import { getParagraphScribbledShapeRestrictions } from '@/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape'; const getMultipleNodeSizeRestrictions = (): ShapeSizeRestrictions => ({ minWidth: 0, @@ -169,6 +170,7 @@ const shapeSizeMap: Record ShapeSizeRestrictions> = { rectangleLow: getRectangleLowShapeRestrictions, circleLow: getCircleLowShapeSizeRestrictions, textScribbled: getTextScribbledShapeRestrictions, + paragraphScribbled: getParagraphScribbledShapeRestrictions, }; export default shapeSizeMap; diff --git a/src/pods/canvas/shape-renderer/index.tsx b/src/pods/canvas/shape-renderer/index.tsx index 1ca9a691..7b06e31c 100644 --- a/src/pods/canvas/shape-renderer/index.tsx +++ b/src/pods/canvas/shape-renderer/index.tsx @@ -81,6 +81,7 @@ import { renderEllipseLow, renderRectangleLow, renderTextScribbled, + renderParagraphScribbled, } from './simple-low-wireframes-components'; export const renderShapeComponent = ( @@ -226,6 +227,8 @@ export const renderShapeComponent = ( return renderCircleLow(shape, shapeRenderedProps); case 'textScribbled': return renderTextScribbled(shape, shapeRenderedProps); + case 'paragraphScribbled': + return renderParagraphScribbled(shape, shapeRenderedProps); default: return renderNotFound(shape, shapeRenderedProps); } diff --git a/src/pods/canvas/shape-renderer/simple-low-wireframes-components/index.ts b/src/pods/canvas/shape-renderer/simple-low-wireframes-components/index.ts index 714172a3..83c76451 100644 --- a/src/pods/canvas/shape-renderer/simple-low-wireframes-components/index.ts +++ b/src/pods/canvas/shape-renderer/simple-low-wireframes-components/index.ts @@ -5,3 +5,4 @@ export * from './low-vertical-line.renderer'; export * from './rectangle-low.renderer'; export * from './circle-low.renderer'; export * from './text-scribbled.renderer'; +export * from './paragraph-scribbled.renderer'; diff --git a/src/pods/canvas/shape-renderer/simple-low-wireframes-components/paragraph-scribbled.renderer.tsx b/src/pods/canvas/shape-renderer/simple-low-wireframes-components/paragraph-scribbled.renderer.tsx new file mode 100644 index 00000000..85ca8d04 --- /dev/null +++ b/src/pods/canvas/shape-renderer/simple-low-wireframes-components/paragraph-scribbled.renderer.tsx @@ -0,0 +1,32 @@ +import { ShapeRendererProps } from '../model'; +import { ShapeModel } from '@/core/model'; +import { ParagraphScribbled } from '@/common/components/mock-components/front-low-wireframes-components/paragraph-scribbled-shape'; + +export const renderParagraphScribbled = ( + shape: ShapeModel, + shapeRenderedProps: ShapeRendererProps +) => { + const { handleSelected, shapeRefs, handleDragEnd, handleTransform } = + shapeRenderedProps; + + return ( + + ); +}; diff --git a/src/pods/galleries/low-wireframe-gallery/low-wireframe-gallery-data/index.ts b/src/pods/galleries/low-wireframe-gallery/low-wireframe-gallery-data/index.ts index f309ac20..ce07b4a5 100644 --- a/src/pods/galleries/low-wireframe-gallery/low-wireframe-gallery-data/index.ts +++ b/src/pods/galleries/low-wireframe-gallery/low-wireframe-gallery-data/index.ts @@ -29,4 +29,8 @@ export const mockLowWireframeCollection: ItemInfo[] = [ thumbnailSrc: '/low-wireframes/textScribbled.svg', type: 'textScribbled', }, + { + thumbnailSrc: '/low-wireframes/paragraphScribbled.svg', + type: 'paragraphScribbled', + }, ];