diff --git a/public/rich-components/input-stepper.svg b/public/rich-components/input-stepper.svg
new file mode 100644
index 00000000..22ca7b92
--- /dev/null
+++ b/public/rich-components/input-stepper.svg
@@ -0,0 +1,15 @@
+
diff --git a/src/common/components/mock-components/front-rich-components/index.ts b/src/common/components/mock-components/front-rich-components/index.ts
index b1e5f180..c29428d6 100644
--- a/src/common/components/mock-components/front-rich-components/index.ts
+++ b/src/common/components/mock-components/front-rich-components/index.ts
@@ -20,3 +20,4 @@ export * from './togglelightdark-shape';
export * from './gauge/gauge';
export * from './fab-button/fab-button';
export * from './file-tree/file-tree';
+export * from './input-stepper';
diff --git a/src/common/components/mock-components/front-rich-components/input-stepper.tsx b/src/common/components/mock-components/front-rich-components/input-stepper.tsx
new file mode 100644
index 00000000..a10facfd
--- /dev/null
+++ b/src/common/components/mock-components/front-rich-components/input-stepper.tsx
@@ -0,0 +1,144 @@
+import { forwardRef } from 'react';
+import { Group, Rect, Text } from 'react-konva';
+import { ShapeSizeRestrictions, ShapeType } from '@/core/model';
+import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions';
+import { ShapeProps } from '../shape.model';
+import { useGroupShapeProps } from '../mock-components.utils';
+import { useShapeProps } from '@/common/components/shapes/use-shape-props.hook';
+import { INPUT_SHAPE } from '../front-components/shape.const';
+
+const InputStepperShapeSizeRestrictions: ShapeSizeRestrictions = {
+ minWidth: 60,
+ minHeight: 35,
+ maxWidth: 500,
+ maxHeight: 35,
+ defaultWidth: 100,
+ defaultHeight: 35,
+};
+
+export const getInputStepperShapeSizeRestrictions = (): ShapeSizeRestrictions =>
+ InputStepperShapeSizeRestrictions;
+
+const shapeType: ShapeType = 'inputStepper';
+
+export const InputStepperShape = forwardRef((props, ref) => {
+ const {
+ x,
+ y,
+ width,
+ height,
+ id,
+ text,
+ onSelected,
+ otherProps,
+ ...shapeProps
+ } = props;
+
+ const restrictedSize = fitSizeToShapeSizeRestrictions(
+ InputStepperShapeSizeRestrictions,
+ width,
+ height
+ );
+
+ const { width: restrictedWidth, height: restrictedHeight } = restrictedSize;
+
+ const getButtonWidth = (restrictedWidth: number): number => {
+ const buttonWidth = restrictedWidth * 0.3;
+ const minButtonWidth = 30;
+ const maxButtonWidth = 70;
+
+ if (buttonWidth < minButtonWidth) return minButtonWidth;
+ if (buttonWidth > maxButtonWidth) return maxButtonWidth;
+ return buttonWidth;
+ };
+
+ const buttonWidth = getButtonWidth(restrictedWidth);
+ const buttonHeight = restrictedHeight / 2;
+
+ const commonGroupProps = useGroupShapeProps(
+ props,
+ restrictedSize,
+ shapeType,
+ ref
+ );
+
+ const { stroke, strokeStyle, fill, textColor } = useShapeProps(
+ otherProps,
+ INPUT_SHAPE
+ );
+
+ return (
+
+
+
+ {/* Texto del input */}
+
+
+ {/* Botón de incremento (flecha arriba) */}
+
+
+
+
+
+ {/* Botón de decremento (flecha abajo) */}
+
+
+
+
+
+ );
+});
+
+export default InputStepperShape;
diff --git a/src/core/model/index.ts b/src/core/model/index.ts
index bf189305..8c13a80e 100644
--- a/src/core/model/index.ts
+++ b/src/core/model/index.ts
@@ -86,7 +86,8 @@ export type ShapeType =
| 'textScribbled'
| 'paragraphScribbled'
| 'fabButton'
- | 'fileTree';
+ | 'fileTree'
+ | 'inputStepper';
export const ShapeDisplayName: Record = {
multiple: 'multiple',
@@ -162,6 +163,7 @@ export const ShapeDisplayName: Record = {
paragraphScribbled: 'Paragraph Scribbled',
fabButton: 'Fab Button',
fileTree: 'File Tree',
+ inputStepper: 'Input Stepper',
};
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 bc99eecc..e4a06a8a 100644
--- a/src/pods/canvas/model/inline-editable.model.ts
+++ b/src/pods/canvas/model/inline-editable.model.ts
@@ -84,6 +84,7 @@ const shapeTypesWithDefaultText = new Set([
'loading-indicator',
'gauge',
'fileTree',
+ 'inputStepper',
]);
// Map of ShapeTypes to their default text values
@@ -125,6 +126,7 @@ const defaultTextValueMap: Partial> = {
browser: 'https://example.com',
modalDialog: 'Title here...',
'loading-indicator': 'Loading...',
+ inputStepper: '0',
};
export const generateDefaultTextValue = (
diff --git a/src/pods/canvas/model/shape-other-props.utils.ts b/src/pods/canvas/model/shape-other-props.utils.ts
index f2c1007f..1483182f 100644
--- a/src/pods/canvas/model/shape-other-props.utils.ts
+++ b/src/pods/canvas/model/shape-other-props.utils.ts
@@ -287,6 +287,13 @@ export const generateDefaultOtherProps = (
textColor: '#000000',
strokeStyle: [],
};
+ case 'inputStepper':
+ return {
+ stroke: INPUT_SHAPE.DEFAULT_STROKE_COLOR,
+ backgroundColor: INPUT_SHAPE.DEFAULT_FILL_BACKGROUND,
+ textColor: INPUT_SHAPE.DEFAULT_FILL_TEXT,
+ strokeStyle: [],
+ };
default:
return undefined;
}
diff --git a/src/pods/canvas/model/shape-size.mapper.ts b/src/pods/canvas/model/shape-size.mapper.ts
index 3c4ea531..d73980d2 100644
--- a/src/pods/canvas/model/shape-size.mapper.ts
+++ b/src/pods/canvas/model/shape-size.mapper.ts
@@ -66,6 +66,7 @@ import {
getGaugeShapeSizeRestrictions,
getFabButtonShapeSizeRestrictions,
getFileTreeShapeSizeRestrictions,
+ getInputStepperShapeSizeRestrictions,
// other imports
} from '@/common/components/mock-components/front-rich-components';
import {
@@ -175,6 +176,7 @@ const shapeSizeMap: Record ShapeSizeRestrictions> = {
paragraphScribbled: getParagraphScribbledShapeRestrictions,
fabButton: getFabButtonShapeSizeRestrictions,
fileTree: getFileTreeShapeSizeRestrictions,
+ inputStepper: getInputStepperShapeSizeRestrictions,
};
export default shapeSizeMap;
diff --git a/src/pods/canvas/model/transformer.model.ts b/src/pods/canvas/model/transformer.model.ts
index bf601b42..3cc690cc 100644
--- a/src/pods/canvas/model/transformer.model.ts
+++ b/src/pods/canvas/model/transformer.model.ts
@@ -71,6 +71,7 @@ export const generateTypeOfTransformer = (shapeType: ShapeType): string[] => {
case 'buttonBar':
case 'slider':
case 'chip':
+ case 'inputStepper':
return ['middle-left', 'middle-right'];
case 'verticalLine':
case 'verticalScrollBar':
diff --git a/src/pods/canvas/shape-renderer/index.tsx b/src/pods/canvas/shape-renderer/index.tsx
index 37a59949..c7fb0a3d 100644
--- a/src/pods/canvas/shape-renderer/index.tsx
+++ b/src/pods/canvas/shape-renderer/index.tsx
@@ -50,6 +50,7 @@ import {
renderLoadingIndicator,
renderFabButton,
renderFileTree,
+ renderInputStepper,
} from './simple-rich-components';
import {
renderDiamond,
@@ -235,6 +236,8 @@ export const renderShapeComponent = (
return renderTextScribbled(shape, shapeRenderedProps);
case 'paragraphScribbled':
return renderParagraphScribbled(shape, shapeRenderedProps);
+ case 'inputStepper':
+ return renderInputStepper(shape, shapeRenderedProps);
default:
return renderNotFound(shape, shapeRenderedProps);
}
diff --git a/src/pods/canvas/shape-renderer/simple-rich-components/index.ts b/src/pods/canvas/shape-renderer/simple-rich-components/index.ts
index 2a32d3d1..d00fb7c2 100644
--- a/src/pods/canvas/shape-renderer/simple-rich-components/index.ts
+++ b/src/pods/canvas/shape-renderer/simple-rich-components/index.ts
@@ -22,3 +22,4 @@ export * from './loading-indicator.renderer';
export * from './videoconference.renderer';
export * from './fab-button.renderer';
export * from './file-tree.renderer';
+export * from './input-stepper.renderer';
diff --git a/src/pods/canvas/shape-renderer/simple-rich-components/input-stepper.renderer.tsx b/src/pods/canvas/shape-renderer/simple-rich-components/input-stepper.renderer.tsx
new file mode 100644
index 00000000..26b456b3
--- /dev/null
+++ b/src/pods/canvas/shape-renderer/simple-rich-components/input-stepper.renderer.tsx
@@ -0,0 +1,34 @@
+import { InputStepperShape } from '@/common/components/mock-components/front-rich-components';
+import { ShapeRendererProps } from '../model';
+import { ShapeModel } from '@/core/model';
+
+export const renderInputStepper = (
+ shape: ShapeModel,
+ shapeRenderedProps: ShapeRendererProps
+) => {
+ const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
+ shapeRenderedProps;
+
+ return (
+
+ );
+};
diff --git a/src/pods/galleries/rich-components-gallery/rich-components-gallery-data/index.ts b/src/pods/galleries/rich-components-gallery/rich-components-gallery-data/index.ts
index b44eee90..3a26cb76 100644
--- a/src/pods/galleries/rich-components-gallery/rich-components-gallery-data/index.ts
+++ b/src/pods/galleries/rich-components-gallery/rich-components-gallery-data/index.ts
@@ -44,4 +44,8 @@ export const mockRichComponentsCollection: ItemInfo[] = [
thumbnailSrc: '/rich-components/file-tree.svg',
type: 'fileTree',
},
+ {
+ thumbnailSrc: '/rich-components/input-stepper.svg',
+ type: 'inputStepper',
+ },
];