From 125e0d95b21ee2e364ceddc4a7fd6f50c326df8b Mon Sep 17 00:00:00 2001 From: Antonio Contreras Date: Mon, 1 Dec 2025 16:14:45 +0100 Subject: [PATCH 1/4] implement basic component --- .../mock-components/front-components/index.ts | 1 + .../front-components/mouse-cursor-shape.tsx | 67 +++++++++++++++++++ src/core/model/index.ts | 4 +- .../canvas/model/inline-editable.model.ts | 2 + .../canvas/model/shape-other-props.utils.ts | 5 ++ src/pods/canvas/model/shape-size.mapper.ts | 2 + src/pods/canvas/shape-renderer/index.tsx | 3 + .../shape-renderer/simple-component/index.ts | 1 + .../mouse-cursor.renderer.tsx | 33 +++++++++ .../component-gallery-data/index.ts | 1 + 10 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/common/components/mock-components/front-components/mouse-cursor-shape.tsx create mode 100644 src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx diff --git a/src/common/components/mock-components/front-components/index.ts b/src/common/components/mock-components/front-components/index.ts index 26a1223d..a6ad984a 100644 --- a/src/common/components/mock-components/front-components/index.ts +++ b/src/common/components/mock-components/front-components/index.ts @@ -16,3 +16,4 @@ export * from './label-shape'; export * from './tooltip-shape'; export * from './slider-shape'; export * from './chip-shape'; +export * from './mouse-cursor-shape'; diff --git a/src/common/components/mock-components/front-components/mouse-cursor-shape.tsx b/src/common/components/mock-components/front-components/mouse-cursor-shape.tsx new file mode 100644 index 00000000..6194a3ce --- /dev/null +++ b/src/common/components/mock-components/front-components/mouse-cursor-shape.tsx @@ -0,0 +1,67 @@ +import { forwardRef } from 'react'; +import { Group, Path } from 'react-konva'; +import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; +import { ShapeProps } from '../shape.model'; +import { BASIC_SHAPE } from './shape.const'; +import { useShapeProps } from '../../shapes/use-shape-props.hook'; +import { useGroupShapeProps } from '../mock-components.utils'; +import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions'; + +const MouseCursorSizeRestrictions: ShapeSizeRestrictions = { + minWidth: 40, + minHeight: 28, + maxWidth: -1, + maxHeight: 28, + defaultWidth: 56, + defaultHeight: 28, +}; + +export const getMouseCursorShapeSizeRestrictions = (): ShapeSizeRestrictions => + MouseCursorSizeRestrictions; + +const shapeType: ShapeType = 'mousecursor'; + +export const MouseCursorShape = forwardRef((props, ref) => { + const { + x, + y, + width, + height, + id, + onSelected, + text, + otherProps, + ...shapeProps + } = props; + + const restrictedSize = fitSizeToShapeSizeRestrictions( + MouseCursorSizeRestrictions, + width, + height + ); + const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; + + const { fill } = useShapeProps(otherProps, BASIC_SHAPE); + + const commonGroupProps = useGroupShapeProps( + props, + restrictedSize, + shapeType, + ref + ); + + return ( + + + + ); +}); + +export default MouseCursorShape; diff --git a/src/core/model/index.ts b/src/core/model/index.ts index f2dbc4b4..35d3573f 100644 --- a/src/core/model/index.ts +++ b/src/core/model/index.ts @@ -87,7 +87,8 @@ export type ShapeType = | 'textScribbled' | 'paragraphScribbled' | 'fabButton' - | 'fileTree'; + | 'fileTree' + | 'mousecursor'; export const ShapeDisplayName: Record = { multiple: 'multiple', @@ -164,6 +165,7 @@ export const ShapeDisplayName: Record = { paragraphScribbled: 'Paragraph Scribbled', fabButton: 'Fab Button', fileTree: 'File Tree', + mousecursor: 'Mouse Cursor', }; export type EditType = 'input' | 'textarea' | 'imageupload'; diff --git a/src/pods/canvas/model/inline-editable.model.ts b/src/pods/canvas/model/inline-editable.model.ts index cbdf7709..1c293f06 100644 --- a/src/pods/canvas/model/inline-editable.model.ts +++ b/src/pods/canvas/model/inline-editable.model.ts @@ -41,6 +41,7 @@ const inlineEditableShapes = new Set([ 'loading-indicator', 'fileTree', 'input-stepper', + 'mousecursor', ]); // Check if a shape type allows inline editing @@ -86,6 +87,7 @@ const shapeTypesWithDefaultText = new Set([ 'loading-indicator', 'gauge', 'fileTree', + 'mousecursor', ]); // Map of ShapeTypes to their default text values diff --git a/src/pods/canvas/model/shape-other-props.utils.ts b/src/pods/canvas/model/shape-other-props.utils.ts index 8ad7a6d3..1e7dd04d 100644 --- a/src/pods/canvas/model/shape-other-props.utils.ts +++ b/src/pods/canvas/model/shape-other-props.utils.ts @@ -240,6 +240,11 @@ export const generateDefaultOtherProps = ( return { checked: true, }; + case 'mousecursor': + return { + iconSize: 'M', + stroke: BASIC_SHAPE.DEFAULT_STROKE_COLOR, + }; case 'checkbox': case 'radiobutton': return { diff --git a/src/pods/canvas/model/shape-size.mapper.ts b/src/pods/canvas/model/shape-size.mapper.ts index 19dcbf18..56478413 100644 --- a/src/pods/canvas/model/shape-size.mapper.ts +++ b/src/pods/canvas/model/shape-size.mapper.ts @@ -21,6 +21,7 @@ import { getTooltipShapeSizeRestrictions, getVerticalScrollBarShapeSizeRestrictions, getChipShapeSizeRestrictions, + getMouseCursorShapeSizeRestrictions, } from '@/common/components/mock-components/front-components'; import { getBrowserWindowShapeSizeRestrictions, @@ -177,6 +178,7 @@ const shapeSizeMap: Record ShapeSizeRestrictions> = { fabButton: getFabButtonShapeSizeRestrictions, fileTree: getFileTreeShapeSizeRestrictions, paragraphScribbled: getParagraphScribbledShapeRestrictions, + mousecursor: getMouseCursorShapeSizeRestrictions, }; export default shapeSizeMap; diff --git a/src/pods/canvas/shape-renderer/index.tsx b/src/pods/canvas/shape-renderer/index.tsx index 62cdc417..2b63221b 100644 --- a/src/pods/canvas/shape-renderer/index.tsx +++ b/src/pods/canvas/shape-renderer/index.tsx @@ -20,6 +20,7 @@ import { renderTooltip, renderSlider, renderChip, + renderMouseCursor, } from './simple-component'; import { renderBrowserWindow, @@ -224,6 +225,8 @@ export const renderShapeComponent = ( return renderImagePlaceHolder(shape, shapeRenderedProps); case 'chip': return renderChip(shape, shapeRenderedProps); + case 'mousecursor': + return renderMouseCursor(shape, shapeRenderedProps); case 'horizontalLineLow': return renderHorizontalLowLine(shape, shapeRenderedProps); case 'verticalLineLow': diff --git a/src/pods/canvas/shape-renderer/simple-component/index.ts b/src/pods/canvas/shape-renderer/simple-component/index.ts index 9682fd62..ce3a4dd2 100644 --- a/src/pods/canvas/shape-renderer/simple-component/index.ts +++ b/src/pods/canvas/shape-renderer/simple-component/index.ts @@ -17,3 +17,4 @@ export * from './horizontalscrollbar.renderer'; export * from './tooltip.renderer'; export * from './slider.renderer'; export * from './chip.renderer'; +export * from './mouse-cursor.renderer'; diff --git a/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx b/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx new file mode 100644 index 00000000..c5d33a85 --- /dev/null +++ b/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx @@ -0,0 +1,33 @@ +import { MouseCursorShape } from '@/common/components/mock-components/front-components'; +import { ShapeModel } from '@/core/model'; +import { ShapeRendererProps } from '../model'; + +export const renderMouseCursor = ( + shape: ShapeModel, + shapeRenderedProps: ShapeRendererProps +) => { + const { handleSelected, shapeRefs, handleDragEnd, handleTransform } = + shapeRenderedProps; + + console.log(shape); + + return ( + + ); +}; diff --git a/src/pods/galleries/component-gallery/component-gallery-data/index.ts b/src/pods/galleries/component-gallery/component-gallery-data/index.ts index 9a79d859..430a3647 100644 --- a/src/pods/galleries/component-gallery/component-gallery-data/index.ts +++ b/src/pods/galleries/component-gallery/component-gallery-data/index.ts @@ -22,4 +22,5 @@ export const mockWidgetCollection: ItemInfo[] = [ { thumbnailSrc: '/widgets/toggleswitch.svg', type: 'toggleswitch' }, { thumbnailSrc: '/widgets/tooltip.svg', type: 'tooltip' }, { thumbnailSrc: '/widgets/verticalscrollbar.svg', type: 'verticalScrollBar' }, + { thumbnailSrc: '/icons/cursor.svg', type: 'mousecursor' }, ]; From ba8f330e78f657152213491a3c432444e86a0b46 Mon Sep 17 00:00:00 2001 From: Antonio Contreras Date: Mon, 1 Dec 2025 18:06:46 +0100 Subject: [PATCH 2/4] fix component --- .../mock-components/front-components/index.ts | 2 +- .../mouse-cursor/icon-shape.business.ts | 18 +++++++++ .../{ => mouse-cursor}/mouse-cursor-shape.tsx | 38 +++++++++++-------- src/pods/canvas/model/transformer.model.ts | 1 + .../mouse-cursor.renderer.tsx | 3 +- 5 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 src/common/components/mock-components/front-components/mouse-cursor/icon-shape.business.ts rename src/common/components/mock-components/front-components/{ => mouse-cursor}/mouse-cursor-shape.tsx (71%) diff --git a/src/common/components/mock-components/front-components/index.ts b/src/common/components/mock-components/front-components/index.ts index a6ad984a..b278aaa8 100644 --- a/src/common/components/mock-components/front-components/index.ts +++ b/src/common/components/mock-components/front-components/index.ts @@ -16,4 +16,4 @@ export * from './label-shape'; export * from './tooltip-shape'; export * from './slider-shape'; export * from './chip-shape'; -export * from './mouse-cursor-shape'; +export * from './mouse-cursor/mouse-cursor-shape'; diff --git a/src/common/components/mock-components/front-components/mouse-cursor/icon-shape.business.ts b/src/common/components/mock-components/front-components/mouse-cursor/icon-shape.business.ts new file mode 100644 index 00000000..71b7c376 --- /dev/null +++ b/src/common/components/mock-components/front-components/mouse-cursor/icon-shape.business.ts @@ -0,0 +1,18 @@ +import { IconSize } from '@/core/model'; + +export const returnIconSize = (iconSize: IconSize): number[] => { + switch (iconSize) { + case 'XS': + return [25, 25]; + case 'S': + return [50, 50]; + case 'M': + return [100, 100]; + case 'L': + return [125, 125]; + case 'XL': + return [150, 150]; + default: + return [50, 50]; + } +}; diff --git a/src/common/components/mock-components/front-components/mouse-cursor-shape.tsx b/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx similarity index 71% rename from src/common/components/mock-components/front-components/mouse-cursor-shape.tsx rename to src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx index 6194a3ce..1e650df1 100644 --- a/src/common/components/mock-components/front-components/mouse-cursor-shape.tsx +++ b/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx @@ -1,19 +1,20 @@ import { forwardRef } from 'react'; import { Group, Path } from 'react-konva'; import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; -import { ShapeProps } from '../shape.model'; -import { BASIC_SHAPE } from './shape.const'; -import { useShapeProps } from '../../shapes/use-shape-props.hook'; -import { useGroupShapeProps } from '../mock-components.utils'; +import { ShapeProps } from '../../shape.model'; +import { BASIC_SHAPE } from '../shape.const'; +import { useShapeProps } from '../../../shapes/use-shape-props.hook'; +import { useGroupShapeProps } from '../../mock-components.utils'; import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions'; +import { returnIconSize } from './icon-shape.business'; const MouseCursorSizeRestrictions: ShapeSizeRestrictions = { - minWidth: 40, - minHeight: 28, + minWidth: 25, + minHeight: 25, maxWidth: -1, - maxHeight: 28, - defaultWidth: 56, - defaultHeight: 28, + maxHeight: -1, + defaultWidth: 150, + defaultHeight: 150, }; export const getMouseCursorShapeSizeRestrictions = (): ShapeSizeRestrictions => @@ -30,18 +31,24 @@ export const MouseCursorShape = forwardRef((props, ref) => { id, onSelected, text, + iconSize, otherProps, ...shapeProps } = props; + const [iconWidth, iconHeight] = returnIconSize(iconSize); + const restrictedSize = fitSizeToShapeSizeRestrictions( MouseCursorSizeRestrictions, - width, - height + iconWidth, + iconHeight ); + + console.log(iconWidth, iconHeight); + const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - const { fill } = useShapeProps(otherProps, BASIC_SHAPE); + const { stroke } = useShapeProps(otherProps, BASIC_SHAPE); const commonGroupProps = useGroupShapeProps( props, @@ -53,12 +60,13 @@ export const MouseCursorShape = forwardRef((props, ref) => { return ( ); diff --git a/src/pods/canvas/model/transformer.model.ts b/src/pods/canvas/model/transformer.model.ts index e6a71e22..7ef50f2f 100644 --- a/src/pods/canvas/model/transformer.model.ts +++ b/src/pods/canvas/model/transformer.model.ts @@ -87,6 +87,7 @@ export const generateTypeOfTransformer = (shapeType: ShapeType): string[] => { 'bottom-center', ]; case 'icon': + case 'mousecursor': case 'multiple': return []; case 'image': diff --git a/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx b/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx index c5d33a85..6cc37766 100644 --- a/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx +++ b/src/pods/canvas/shape-renderer/simple-component/mouse-cursor.renderer.tsx @@ -9,8 +9,6 @@ export const renderMouseCursor = ( const { handleSelected, shapeRefs, handleDragEnd, handleTransform } = shapeRenderedProps; - console.log(shape); - return ( ); }; From 81aad17b55feae644e3ca05183be6b386c8c7b38 Mon Sep 17 00:00:00 2001 From: Antonio Contreras Date: Mon, 1 Dec 2025 19:08:47 +0100 Subject: [PATCH 3/4] fix component --- .../front-components/mouse-cursor/index.ts | 1 + .../mouse-cursor/mouse-cursor-shape.tsx | 39 ++++++++++++------- .../canvas/model/inline-editable.model.ts | 1 - 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 src/common/components/mock-components/front-components/mouse-cursor/index.ts diff --git a/src/common/components/mock-components/front-components/mouse-cursor/index.ts b/src/common/components/mock-components/front-components/mouse-cursor/index.ts new file mode 100644 index 00000000..071fdea0 --- /dev/null +++ b/src/common/components/mock-components/front-components/mouse-cursor/index.ts @@ -0,0 +1 @@ +export * from './mouse-cursor-shape'; diff --git a/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx b/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx index 1e650df1..c3044229 100644 --- a/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx +++ b/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx @@ -1,12 +1,13 @@ -import { forwardRef } from 'react'; -import { Group, Path } from 'react-konva'; -import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; +import { forwardRef, useEffect, useState, useRef } from 'react'; +import { Group, Image } from 'react-konva'; +import { ShapeSizeRestrictions, ShapeType, BASE_ICONS_URL } from '@/core/model'; import { ShapeProps } from '../../shape.model'; import { BASIC_SHAPE } from '../shape.const'; import { useShapeProps } from '../../../shapes/use-shape-props.hook'; import { useGroupShapeProps } from '../../mock-components.utils'; import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions'; import { returnIconSize } from './icon-shape.business'; +import { loadSvgWithFill } from '@/common/utils/svg.utils'; const MouseCursorSizeRestrictions: ShapeSizeRestrictions = { minWidth: 25, @@ -44,8 +45,6 @@ export const MouseCursorShape = forwardRef((props, ref) => { iconHeight ); - console.log(iconWidth, iconHeight); - const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; const { stroke } = useShapeProps(otherProps, BASIC_SHAPE); @@ -57,17 +56,29 @@ export const MouseCursorShape = forwardRef((props, ref) => { ref ); + const [image, setImage] = useState(null); + const imageRef = useRef(null); + + const fileName = 'cursor.svg'; + + useEffect(() => { + loadSvgWithFill(`${BASE_ICONS_URL}${fileName}`, `${stroke}`).then(img => { + setImage(img); + }); + }, []); + return ( - + {image && ( + + )} ); }); diff --git a/src/pods/canvas/model/inline-editable.model.ts b/src/pods/canvas/model/inline-editable.model.ts index 1c293f06..a2f3e7ea 100644 --- a/src/pods/canvas/model/inline-editable.model.ts +++ b/src/pods/canvas/model/inline-editable.model.ts @@ -41,7 +41,6 @@ const inlineEditableShapes = new Set([ 'loading-indicator', 'fileTree', 'input-stepper', - 'mousecursor', ]); // Check if a shape type allows inline editing From 8f5615f0a1b5acd271c9a3a5a74151195c3de2da Mon Sep 17 00:00:00 2001 From: Antonio Contreras Date: Tue, 2 Dec 2025 10:02:51 +0100 Subject: [PATCH 4/4] add condition to useEffect --- .../front-components/mouse-cursor/mouse-cursor-shape.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx b/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx index c3044229..642b79f5 100644 --- a/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx +++ b/src/common/components/mock-components/front-components/mouse-cursor/mouse-cursor-shape.tsx @@ -65,7 +65,7 @@ export const MouseCursorShape = forwardRef((props, ref) => { loadSvgWithFill(`${BASE_ICONS_URL}${fileName}`, `${stroke}`).then(img => { setImage(img); }); - }, []); + }, [stroke]); return (