diff --git a/public/widgets/timepicker.svg b/public/widgets/timepicker.svg index d71771ce..1a262986 100644 --- a/public/widgets/timepicker.svg +++ b/public/widgets/timepicker.svg @@ -1,13 +1,24 @@ - - - - - - - : - - - : - + + + + + Time + + + + hh:mm + + + + + + + + + + + + + + diff --git a/src/common/components/mock-components/front-components/datepickerinput-shape.tsx b/src/common/components/mock-components/front-components/datepickerinput-shape.tsx index 17d64b21..e3b1d90a 100644 --- a/src/common/components/mock-components/front-components/datepickerinput-shape.tsx +++ b/src/common/components/mock-components/front-components/datepickerinput-shape.tsx @@ -111,6 +111,16 @@ export const DatepickerInputShape = forwardRef( ellipsis={true} wrap="none" /> + + {/* Calendar Icon */} timepickerInputShapeRestrictions; - -export const TimepickerInputShape = forwardRef( - (props, ref) => { - const { x, y, width, height, id, onSelected, otherProps, ...shapeProps } = - props; - const restrictedSize = fitSizeToShapeSizeRestrictions( - timepickerInputShapeRestrictions, - width, - height - ); - const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; - - const separatorPadding = 3; // Extra padding for spacers - const separator1X = restrictedWidth / 3; - const separator2X = (2 * restrictedWidth) / 3; - - const { stroke, strokeStyle, fill, borderRadius } = useShapeProps( - otherProps, - BASIC_SHAPE - ); - - const commonGroupProps = useGroupShapeProps( - props, - restrictedSize, - shapeType, - ref - ); - - return ( - - {/* input frame */} - - - {/* Separators : */} - - - - ); - } -); diff --git a/src/common/components/mock-components/front-components/timepickerinput/index.ts b/src/common/components/mock-components/front-components/timepickerinput/index.ts new file mode 100644 index 00000000..b2663481 --- /dev/null +++ b/src/common/components/mock-components/front-components/timepickerinput/index.ts @@ -0,0 +1 @@ +export * from './timepickerinput-shape.tsx'; diff --git a/src/common/components/mock-components/front-components/timepickerinput/timepickerinput-shape.business.ts b/src/common/components/mock-components/front-components/timepickerinput/timepickerinput-shape.business.ts new file mode 100644 index 00000000..a257c096 --- /dev/null +++ b/src/common/components/mock-components/front-components/timepickerinput/timepickerinput-shape.business.ts @@ -0,0 +1,33 @@ +const MAX_DIGITS = 2; +const MAX_HOURS = '23'; +const MAX_MINUTES = '59'; +const HOUR_MASK = 'hh'; +const MINUTES_MASK = 'mm'; + +export const splitCSVContent = (csvContent: string): string[] => { + const splitedCsvContent = csvContent + .trim() + .split(/[:|,]/) + .map(el => el.trim()); + return splitedCsvContent; +}; + +export const setTime = (csvData: string[]) => { + let [hour, minutes] = csvData; + if (csvData.length < 2) { + return true; + } + if (csvData[0] !== HOUR_MASK || csvData[1] !== MINUTES_MASK) { + if ( + csvData.length > MAX_DIGITS || + hour.length !== MAX_DIGITS || + hour === '' || + hour > MAX_HOURS || + minutes.length !== MAX_DIGITS || + minutes === '' || + minutes > MAX_MINUTES + ) { + return true; + } + } +}; diff --git a/src/common/components/mock-components/front-components/timepickerinput/timepickerinput-shape.tsx b/src/common/components/mock-components/front-components/timepickerinput/timepickerinput-shape.tsx new file mode 100644 index 00000000..a0ec43c8 --- /dev/null +++ b/src/common/components/mock-components/front-components/timepickerinput/timepickerinput-shape.tsx @@ -0,0 +1,148 @@ +import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; +import { forwardRef } from 'react'; +import { ShapeProps } from '../../shape.model'; +import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions'; +import { Group, Rect, Text, Image } from 'react-konva'; +import { BASIC_SHAPE } from '../shape.const'; +import { useShapeProps } from '../../../shapes/use-shape-props.hook'; +import { useGroupShapeProps } from '../../mock-components.utils'; +import { splitCSVContent, setTime } from './timepickerinput-shape.business'; + +import clockIconSrc from '/icons/clock.svg'; + +const timepickerInputShapeRestrictions: ShapeSizeRestrictions = { + minWidth: 100, + minHeight: 50, + maxWidth: -1, + maxHeight: 50, + defaultWidth: 220, + defaultHeight: 50, +}; + +const shapeType: ShapeType = 'timepickerinput'; + +export const getTimepickerInputShapeSizeRestrictions = + (): ShapeSizeRestrictions => timepickerInputShapeRestrictions; + +export const TimepickerInputShape = forwardRef( + (props, ref) => { + const { + x, + y, + width, + height, + id, + text, + onSelected, + otherProps, + ...shapeProps + } = props; + + const restrictedSize = fitSizeToShapeSizeRestrictions( + timepickerInputShapeRestrictions, + width, + height + ); + const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; + + const { stroke, strokeStyle, fill, borderRadius } = useShapeProps( + otherProps, + BASIC_SHAPE + ); + + const commonGroupProps = useGroupShapeProps( + props, + restrictedSize, + shapeType, + ref + ); + + const csvData = splitCSVContent(text); + let isError = setTime(csvData); + + const iconWidth = 25; + const availableWidth = restrictedWidth - iconWidth - 20; + const fontSize = Math.min( + availableWidth * 0.2, + restrictedHeight * 0.35, + 20 + ); + const labelFontSize = Math.min(restrictedHeight * 0.3, 12); + + const clockIcon = new window.Image(); + clockIcon.src = clockIconSrc; + + return ( + + {/* External Rectangle */} + + {/* Background of Time Label */} + + {/* Label "Time" */} + + {/* Main Text */} + + {/* Error Text */} + {isError && ( + + )} + + {/* Clock Icon */} + + + ); + } +); diff --git a/src/pods/canvas/model/inline-editable.model.ts b/src/pods/canvas/model/inline-editable.model.ts index 946498f4..69877b36 100644 --- a/src/pods/canvas/model/inline-editable.model.ts +++ b/src/pods/canvas/model/inline-editable.model.ts @@ -30,6 +30,7 @@ const inlineEditableShapes = new Set([ 'buttonBar', 'tabsBar', 'tooltip', + 'timepickerinput', 'datepickerinput', 'browser', ]); @@ -66,6 +67,7 @@ const shapeTypesWithDefaultText = new Set([ 'appBar', 'buttonBar', 'tabsBar', + 'timepickerinput', 'datepickerinput', 'browser', ]); @@ -99,6 +101,7 @@ const defaultTextValueMap: Partial> = { appBar: 'AppBar', buttonBar: 'Button 1, Button 2, Button 3', tabsBar: 'Tab 1, Tab 2, Tab 3', + timepickerinput: 'hh:mm', datepickerinput: new Date().toLocaleDateString(), browser: 'https://example.com', }; diff --git a/src/pods/canvas/shape-renderer/simple-component/timepickerinput.renderer.tsx b/src/pods/canvas/shape-renderer/simple-component/timepickerinput.renderer.tsx index 99bc8425..6476b6b6 100644 --- a/src/pods/canvas/shape-renderer/simple-component/timepickerinput.renderer.tsx +++ b/src/pods/canvas/shape-renderer/simple-component/timepickerinput.renderer.tsx @@ -25,6 +25,8 @@ export const renderTimepickerinput = ( onDragEnd={handleDragEnd(shape.id)} onTransform={handleTransform} onTransformEnd={handleTransform} + isEditable={shape.allowsInlineEdition} + text={shape.text} otherProps={shape.otherProps} /> ); diff --git a/src/pods/properties/components/active-element-selector/active-element-selector.component.tsx b/src/pods/properties/components/active-element-selector/active-element-selector.component.tsx index dd827294..9b0c6b71 100644 --- a/src/pods/properties/components/active-element-selector/active-element-selector.component.tsx +++ b/src/pods/properties/components/active-element-selector/active-element-selector.component.tsx @@ -27,7 +27,7 @@ export const ActiveElementSelector: React.FC = ({ // Checking whether the type is tabsBar and parsing the text const isElementTypeSupported = - type === 'tabsBar' || 'buttonBar' || 'horizontal-menu'; + type === 'tabsBar' || 'buttonBar' || 'horizontal-menu' || 'timepickerinput'; const elementNames = isElementTypeSupported && text ? extractElementNames(text) : [];