diff --git a/public/shapes/line.svg b/public/shapes/line.svg new file mode 100644 index 00000000..2c40684d --- /dev/null +++ b/public/shapes/line.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/common/components/front-basic-sapes/index.ts b/src/common/components/front-basic-sapes/index.ts index 915b6d11..0d891240 100644 --- a/src/common/components/front-basic-sapes/index.ts +++ b/src/common/components/front-basic-sapes/index.ts @@ -1,2 +1,3 @@ export * from './rectangle-basic-shape'; export * from './diamond-shape'; +export * from './line-basic-shape'; diff --git a/src/common/components/front-basic-sapes/line-basic-shape.tsx b/src/common/components/front-basic-sapes/line-basic-shape.tsx new file mode 100644 index 00000000..ba1e707f --- /dev/null +++ b/src/common/components/front-basic-sapes/line-basic-shape.tsx @@ -0,0 +1,51 @@ +import { forwardRef } from 'react'; +import { Group, Line, Rect } from 'react-konva'; +import { ShapeSizeRestrictions } from '@/core/model'; +import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions'; +import { ShapeProps } from '../front-components/shape.model'; + +const lineShapeRestrictions: ShapeSizeRestrictions = { + minWidth: 50, + minHeight: 10, + maxWidth: -1, + maxHeight: 10, + defaultWidth: 200, + defaultHeight: 10, +}; + +export const getlineShapeRestrictions = (): ShapeSizeRestrictions => + lineShapeRestrictions; + +export const LineShape = forwardRef( + ({ x, y, width, height, id, onSelected, text, ...shapeProps }, ref) => { + const { width: restrictedWidth, height: restrictedHeight } = + fitSizeToShapeSizeRestrictions(lineShapeRestrictions, width, height); + + return ( + onSelected(id, 'line')} + > + {/* Transparent rectangle for applying margin */} + + + + + ); + } +); diff --git a/src/core/model/index.ts b/src/core/model/index.ts index 7a07f20c..78e995c8 100644 --- a/src/core/model/index.ts +++ b/src/core/model/index.ts @@ -27,6 +27,7 @@ export type ShapeType = | 'rectangle' | 'videoPlayer' | 'diamond' + | 'line'; | 'accordion'; /* | "text"| "button" | "radio" | "image"*/ @@ -54,6 +55,7 @@ export interface ShapeModel { height: number; type: ShapeType; allowsInlineEdition: boolean; + hasLateralTransformer: boolean; editType?: EditType; text?: string; } diff --git a/src/pods/basic-shapes-gallery/basic-gallery-data/index.ts b/src/pods/basic-shapes-gallery/basic-gallery-data/index.ts index b9e34aa7..b30f90ee 100644 --- a/src/pods/basic-shapes-gallery/basic-gallery-data/index.ts +++ b/src/pods/basic-shapes-gallery/basic-gallery-data/index.ts @@ -3,4 +3,5 @@ import { ItemInfo } from '@/common/components/gallery/components/model'; export const mockBasicShapesCollection: ItemInfo[] = [ { thumbnailSrc: '/shapes/rectangle.svg', type: 'rectangle' }, { thumbnailSrc: '/shapes/diamond.svg', type: 'diamond' }, + { thumbnailSrc: '/shapes/line.svg', type: 'line' }, ]; diff --git a/src/pods/canvas/canvas.model.ts b/src/pods/canvas/canvas.model.ts index 30aa1f05..ccf95068 100644 --- a/src/pods/canvas/canvas.model.ts +++ b/src/pods/canvas/canvas.model.ts @@ -21,6 +21,7 @@ import { getLabelSizeRestrictions } from '@/common/components/front-components/l import { getDiamondShapeSizeRestrictions, getRectangleShapeSizeRestrictions, + getlineShapeRestrictions, } from '@/common/components/front-basic-sapes'; import { getAccordionShapeSizeRestrictions, @@ -110,6 +111,11 @@ export const getDefaultSizeFromShape = (shapeType: ShapeType): Size => { width: getDiamondShapeSizeRestrictions().defaultWidth, height: getDiamondShapeSizeRestrictions().defaultHeight, }; + case 'line': + return { + width: getlineShapeRestrictions().defaultWidth, + height: getlineShapeRestrictions().defaultHeight, + }; case 'accordion': return { width: getAccordionShapeSizeRestrictions().defaultWidth, @@ -139,6 +145,15 @@ const doesShapeAllowInlineEdition = (shapeType: ShapeType): boolean => { } }; +const doesShapeHaveLateralTransformer = (shapeType: ShapeType): boolean => { + switch (shapeType) { + case 'line': + return true; + default: + return false; + } +}; + const generateDefaultTextValue = (shapeType: ShapeType): string | undefined => { switch (shapeType) { case 'input': @@ -189,6 +204,7 @@ export const createShape = (coord: Coord, shapeType: ShapeType): ShapeModel => { height, type: shapeType, allowsInlineEdition: doesShapeAllowInlineEdition(shapeType), + hasLateralTransformer: doesShapeHaveLateralTransformer(shapeType), text: generateDefaultTextValue(shapeType), editType: getShapeEditInlineType(shapeType), }; diff --git a/src/pods/canvas/canvas.pod.tsx b/src/pods/canvas/canvas.pod.tsx index ffc4c030..d686a462 100644 --- a/src/pods/canvas/canvas.pod.tsx +++ b/src/pods/canvas/canvas.pod.tsx @@ -72,12 +72,7 @@ export const CanvasPod = () => { } = useSnapIn(transformerRef, selectedShapeKonvaId); const { handleTransform, handleTransformerBoundBoxFunc } = useTransform( - updateShapeSizeAndPosition, - { - selectedShapeRef, - selectedShapeId, - selectedShapeType, - } + updateShapeSizeAndPosition ); const handleDragEnd = diff --git a/src/pods/canvas/shape-renderer/index.tsx b/src/pods/canvas/shape-renderer/index.tsx index facedc6c..b1c00ece 100644 --- a/src/pods/canvas/shape-renderer/index.tsx +++ b/src/pods/canvas/shape-renderer/index.tsx @@ -20,11 +20,15 @@ import { renderMobilePhoneContainer, renderTablet, } from './simple-container'; -import { renderRectangle } from './simple-basic-shapes/rectangle.rerender'; import { renderVideoPlayer } from './simple-rich-components'; -import { renderDiamond } from './simple-basic-shapes'; +import { + renderDiamond, + renderRectangle, + renderLine, +} from './simple-basic-shapes'; import { renderAccordion } from './simple-rich-components/accordion.renderer'; + export const renderShapeComponent = ( shape: ShapeModel, shapeRenderedProps: ShapeRendererProps @@ -66,6 +70,8 @@ export const renderShapeComponent = ( return renderVideoPlayer(shape, shapeRenderedProps); case 'diamond': return renderDiamond(shape, shapeRenderedProps); + case 'line': + return renderLine(shape, shapeRenderedProps); case 'accordion': return renderAccordion(shape, shapeRenderedProps); default: diff --git a/src/pods/canvas/shape-renderer/simple-basic-shapes/index.ts b/src/pods/canvas/shape-renderer/simple-basic-shapes/index.ts index 10ef35f9..df9b02af 100644 --- a/src/pods/canvas/shape-renderer/simple-basic-shapes/index.ts +++ b/src/pods/canvas/shape-renderer/simple-basic-shapes/index.ts @@ -1,2 +1,3 @@ export * from './rectangle.rerender'; export * from './diamond.renderer'; +export * from './line.renderer'; diff --git a/src/pods/canvas/shape-renderer/simple-basic-shapes/line.renderer.tsx b/src/pods/canvas/shape-renderer/simple-basic-shapes/line.renderer.tsx new file mode 100644 index 00000000..edb7f647 --- /dev/null +++ b/src/pods/canvas/shape-renderer/simple-basic-shapes/line.renderer.tsx @@ -0,0 +1,30 @@ +import { LineShape } from '@/common/components/front-basic-sapes'; +import { ShapeRendererProps } from '../model'; +import { ShapeModel } from '@/core/model'; + +export const renderLine = ( + shape: ShapeModel, + shapeRenderedProps: ShapeRendererProps +) => { + const { handleSelected, shapeRefs, handleDragEnd, handleTransform } = + shapeRenderedProps; + + return ( + + ); +}; diff --git a/src/pods/canvas/use-transform.hook.ts b/src/pods/canvas/use-transform.hook.ts index 7a3fc66c..9682d974 100644 --- a/src/pods/canvas/use-transform.hook.ts +++ b/src/pods/canvas/use-transform.hook.ts @@ -1,18 +1,35 @@ -import { Node, NodeConfig } from 'konva/lib/Node'; import { Box } from 'konva/lib/shapes/Transformer'; -import { Coord, ShapeType, Size } from '@/core/model'; - -interface TransFormSelectedInfo { - selectedShapeRef: React.MutableRefObject | null>; - selectedShapeId: string; - selectedShapeType: ShapeType | null; -} +import { Coord, Size } from '@/core/model'; +import { useEffect } from 'react'; +import { useCanvasContext } from '@/core/providers'; export const useTransform = ( - updateShapeSizeAndPosition: (id: string, position: Coord, size: Size) => void, - transformSelectedInfo: TransFormSelectedInfo + updateShapeSizeAndPosition: (id: string, position: Coord, size: Size) => void ) => { - const { selectedShapeId, selectedShapeRef } = transformSelectedInfo; + const { selectedShapeId, selectedShapeRef, transformerRef } = + useCanvasContext().selectionInfo; + + useEffect(() => { + const selectedShape = selectedShapeRef.current; + const transformer = transformerRef.current; + if (selectedShape && transformer) { + const hasLateralTransformer = selectedShape.attrs.hasLateralTransformer; + if (hasLateralTransformer) { + transformerRef.current.enabledAnchors(['middle-left', 'middle-right']); + } else { + transformerRef.current.enabledAnchors([ + 'top-left', + 'top-center', + 'top-right', + 'middle-left', + 'middle-right', + 'bottom-left', + 'bottom-center', + 'bottom-right', + ]); + } + } + }, [selectedShapeId]); const handleTransform = () => { const node = selectedShapeRef.current;