diff --git a/index.html b/index.html index d8affb1c..74734100 100644 --- a/index.html +++ b/index.html @@ -335,7 +335,6 @@

Share your ideas as low-fidelity mocks in minutes, perfect for getting started.

-

Community preview

diff --git a/package-lock.json b/package-lock.json index ad6019ab..c8a6ac69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4675,9 +4675,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -4685,6 +4685,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5943,10 +5944,11 @@ } }, "node_modules/vite": { - "version": "5.4.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", - "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", diff --git a/public/assets/omar-lorenzo.jpeg b/public/assets/omar-lorenzo.jpeg new file mode 100644 index 00000000..e6390ea4 Binary files /dev/null and b/public/assets/omar-lorenzo.jpeg differ diff --git a/public/rich-components/gauge.svg b/public/rich-components/gauge.svg new file mode 100644 index 00000000..56bcdaae --- /dev/null +++ b/public/rich-components/gauge.svg @@ -0,0 +1,32 @@ + + + + + + 10% + diff --git a/src/common/components/mock-components/front-rich-components/gauge/gauge.tsx b/src/common/components/mock-components/front-rich-components/gauge/gauge.tsx new file mode 100644 index 00000000..056566ae --- /dev/null +++ b/src/common/components/mock-components/front-rich-components/gauge/gauge.tsx @@ -0,0 +1,109 @@ +import { Circle, Group, Path, Text } from 'react-konva'; +import { ShapeSizeRestrictions, ShapeType } from '@/core/model'; +import { forwardRef } from 'react'; +import { ShapeProps } from '../../shape.model'; +import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions'; +import { useShapeProps } from '../../../shapes/use-shape-props.hook'; + +import { BASIC_SHAPE } from '@/common/components/mock-components/front-components/shape.const'; +import { useGroupShapeProps } from '../../mock-components.utils'; +import { + endsWhithPercentageSymbol, + extractNumbersAsTwoDigitString, +} from './gauge.utils'; + +const gaugeShapeSizeRestrictions: ShapeSizeRestrictions = { + minWidth: 70, + minHeight: 70, + maxWidth: -1, + maxHeight: -1, + defaultWidth: 150, + defaultHeight: 150, +}; + +export const getGaugeShapeSizeRestrictions = (): ShapeSizeRestrictions => + gaugeShapeSizeRestrictions; + +const shapeType: ShapeType = 'gauge'; + +export const Gauge = forwardRef((props, ref) => { + const { + x, + y, + width, + height, + id, + onSelected, + text, + otherProps, + ...shapeProps + } = props; + const restrictedSize = fitSizeToShapeSizeRestrictions( + gaugeShapeSizeRestrictions, + width, + height + ); + + const { width: restrictedWidth, height: restrictedHeight } = restrictedSize; + const { fontSize } = useShapeProps(otherProps, BASIC_SHAPE); + const commonGroupProps = useGroupShapeProps( + props, + restrictedSize, + shapeType, + ref + ); + const { stroke, fill, textColor } = useShapeProps(otherProps, BASIC_SHAPE); + + const numericPart = extractNumbersAsTwoDigitString(text); + + const progress = Number(numericPart); + const displayValue = endsWhithPercentageSymbol(text) + ? `${numericPart}%` + : numericPart; + + const size = Math.min(restrictedWidth, restrictedHeight); + const strokeWidth = Math.min(restrictedWidth, restrictedHeight) / 10; + const radius = (size - strokeWidth) / 2; + const center = size / 2; + const angle = (progress / 100.01) * 360; + const fontSizeScaled = fontSize * (size / 80); + const arcPath = () => { + const startAngle = -90; + const endAngle = startAngle + angle; + const largeArcFlag = angle > 180 ? 1 : 0; + const startX = center + radius * Math.cos((Math.PI * startAngle) / 180); + const startY = center + radius * Math.sin((Math.PI * startAngle) / 180); + const endX = center + radius * Math.cos((Math.PI * endAngle) / 180); + const endY = center + radius * Math.sin((Math.PI * endAngle) / 180); + return `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY}`; + }; + return ( + + {/* Background */} + + + {/* Moving Arc */} + + + {/* Percent */} + + + ); +}); diff --git a/src/common/components/mock-components/front-rich-components/gauge/gauge.utils.ts b/src/common/components/mock-components/front-rich-components/gauge/gauge.utils.ts new file mode 100644 index 00000000..0d23307c --- /dev/null +++ b/src/common/components/mock-components/front-rich-components/gauge/gauge.utils.ts @@ -0,0 +1,8 @@ +export const extractNumbersAsTwoDigitString = (text: string): string => { + const numbersAsString = text.replace(/\D/g, ''); + return numbersAsString === '100' ? '100' : numbersAsString.slice(0, 2) || '0'; +}; + +export const endsWhithPercentageSymbol = (text: string): boolean => { + return text.trim().endsWith('%'); +}; diff --git a/src/common/components/mock-components/front-rich-components/gauge/index.ts b/src/common/components/mock-components/front-rich-components/gauge/index.ts new file mode 100644 index 00000000..72f8d539 --- /dev/null +++ b/src/common/components/mock-components/front-rich-components/gauge/index.ts @@ -0,0 +1 @@ +export * from './gauge'; 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 d3097d8d..6664c0ae 100644 --- a/src/common/components/mock-components/front-rich-components/index.ts +++ b/src/common/components/mock-components/front-rich-components/index.ts @@ -17,3 +17,4 @@ export * from './audio-player'; export * from './loading-indicator'; export * from './videoconference'; export * from './togglelightdark-shape'; +export * from './gauge/gauge'; diff --git a/src/core/model/index.ts b/src/core/model/index.ts index 1495f85f..8e083d3f 100644 --- a/src/core/model/index.ts +++ b/src/core/model/index.ts @@ -74,7 +74,8 @@ export type ShapeType = | 'richtext' | 'loading-indicator' | 'videoconference' - | 'richtext'; + | 'richtext' + | 'gauge'; export const ShapeDisplayName: Record = { multiple: 'multiple', @@ -138,6 +139,7 @@ export const ShapeDisplayName: Record = { cilinder: 'Cilinder', 'loading-indicator': 'Loading', videoconference: 'Videoconference', + gauge: 'Gauge', }; export type EditType = 'input' | 'textarea' | 'imageupload'; diff --git a/src/pods/about/about.pod.tsx b/src/pods/about/about.pod.tsx index e2504672..702d43aa 100644 --- a/src/pods/about/about.pod.tsx +++ b/src/pods/about/about.pod.tsx @@ -9,8 +9,7 @@ export const AboutPod = () => {

Quickmock

-

Version 0.0

-

Community preview

+

Version 1.0

); diff --git a/src/pods/about/members.ts b/src/pods/about/members.ts index 12f03175..955dd70c 100644 --- a/src/pods/about/members.ts +++ b/src/pods/about/members.ts @@ -145,6 +145,13 @@ export const memberList: Member[] = [ }, { id: '19', + name: 'Omar', + surname: 'Lorenzo', + urlLinkedin: 'https://www.linkedin.com/in/omar-lorenzo-montelongo/', + image: './assets/omar-lorenzo.jpeg', + }, + { + id: '20', name: 'Gabriel', surname: 'Ionut', urlLinkedin: 'https://www.linkedin.com/in/gabriel-ionut-birsan-b14816307/', @@ -152,7 +159,7 @@ export const memberList: Member[] = [ }, { - id: '20', + id: '21', name: 'Antonio', surname: 'Contreras', urlLinkedin: @@ -161,7 +168,7 @@ export const memberList: Member[] = [ }, { - id: '21', + id: '22', name: 'Braulio', surname: 'Diez', urlLinkedin: 'https://www.linkedin.com/in/brauliodiez/', diff --git a/src/pods/canvas/model/inline-editable.model.ts b/src/pods/canvas/model/inline-editable.model.ts index 7843b8b8..b08668c4 100644 --- a/src/pods/canvas/model/inline-editable.model.ts +++ b/src/pods/canvas/model/inline-editable.model.ts @@ -36,6 +36,7 @@ const inlineEditableShapes = new Set([ 'datepickerinput', 'browser', 'modalDialog', + 'gauge', 'loading-indicator', ]); @@ -78,6 +79,7 @@ const shapeTypesWithDefaultText = new Set([ 'browser', 'modalDialog', 'loading-indicator', + 'gauge', ]); // Map of ShapeTypes to their default text values @@ -108,6 +110,7 @@ const defaultTextValueMap: Partial> = { modal: 'Alert\nWarning: The action you are about to perform may affect existing data. Are you sure you want to proceed? Once confirmed, this action cannot be undone.\nConfirm,Cancel', appBar: 'AppBar', + gauge: '10%', buttonBar: 'Button 1, Button 2, Button 3', tabsBar: 'Tab 1, Tab 2, Tab 3', link: 'Link', diff --git a/src/pods/canvas/model/shape-other-props.utils.ts b/src/pods/canvas/model/shape-other-props.utils.ts index 84247133..43c03e1a 100644 --- a/src/pods/canvas/model/shape-other-props.utils.ts +++ b/src/pods/canvas/model/shape-other-props.utils.ts @@ -68,6 +68,12 @@ export const generateDefaultOtherProps = ( disabled: BASIC_SHAPE.DEFAULT_DISABLED, }; case 'modal': + case 'gauge': + return { + backgroundColor: '#d3d3d3', + stroke: '#808080', + textColor: BASIC_SHAPE.DEFAULT_FILL_TEXT, + }; case 'buttonBar': return { stroke: BASIC_SHAPE.DEFAULT_STROKE_COLOR, diff --git a/src/pods/canvas/model/shape-size.mapper.ts b/src/pods/canvas/model/shape-size.mapper.ts index f6a5c993..8dd5d064 100644 --- a/src/pods/canvas/model/shape-size.mapper.ts +++ b/src/pods/canvas/model/shape-size.mapper.ts @@ -62,6 +62,7 @@ import { getVerticalMenuShapeSizeRestrictions, getVideoPlayerShapeSizeRestrictions, getVideoconferenceShapeSizeRestrictions, + getGaugeShapeSizeRestrictions, // other imports } from '@/common/components/mock-components/front-rich-components'; import { @@ -148,6 +149,7 @@ const shapeSizeMap: Record ShapeSizeRestrictions> = { cilinder: getCilinderShapeSizeRestrictions, 'loading-indicator': getLoadIndicatorSizeRestrictions, videoconference: getVideoconferenceShapeSizeRestrictions, + gauge: getGaugeShapeSizeRestrictions, }; export default shapeSizeMap; diff --git a/src/pods/canvas/shape-renderer/index.tsx b/src/pods/canvas/shape-renderer/index.tsx index 25ee3cb6..bad6cca6 100644 --- a/src/pods/canvas/shape-renderer/index.tsx +++ b/src/pods/canvas/shape-renderer/index.tsx @@ -43,6 +43,7 @@ import { renderTabsBar, renderToggleLightDark, renderVideoconference, + renderGauge, } from './simple-rich-components'; import { renderDiamond, @@ -197,6 +198,9 @@ export const renderShapeComponent = ( return renderLoadingIndicator(shape, shapeRenderedProps); case 'videoconference': return renderVideoconference(shape, shapeRenderedProps); + case 'gauge': + return renderGauge(shape, shapeRenderedProps); + default: return renderNotFound(shape, shapeRenderedProps); } diff --git a/src/pods/canvas/shape-renderer/simple-rich-components/gauge.renderer.tsx b/src/pods/canvas/shape-renderer/simple-rich-components/gauge.renderer.tsx new file mode 100644 index 00000000..07412cfb --- /dev/null +++ b/src/pods/canvas/shape-renderer/simple-rich-components/gauge.renderer.tsx @@ -0,0 +1,34 @@ +import { Gauge } from '@/common/components/mock-components/front-rich-components'; +import { ShapeRendererProps } from '../model'; +import { ShapeModel } from '@/core/model'; + +export const renderGauge = ( + shape: ShapeModel, + shapeRenderedProps: ShapeRendererProps +) => { + const { handleSelected, shapeRefs, handleDragEnd, handleTransform } = + shapeRenderedProps; + + return ( + + ); +}; 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 9690dbd1..81078779 100644 --- a/src/pods/canvas/shape-renderer/simple-rich-components/index.ts +++ b/src/pods/canvas/shape-renderer/simple-rich-components/index.ts @@ -1,19 +1,22 @@ -export * from './video-player.renderer'; export * from './accordion.renderer'; -export * from './pie-chart.renderer'; -export * from './horizontal-menu.renderer'; -export * from './map-chart.renderer'; -export * from './breadcrumb.renderer'; +export * from './appBar.renderer'; +export * from './audio-player.renderer'; export * from './bar-chart.renderer'; -export * from './line-chart.renderer'; -export * from './vertical-menu.renderer'; +export * from './breadcrumb.renderer'; +export * from './button-bar.renderer'; export * from './calendar.renderer'; +export * from './horizontal-menu.renderer'; +export * from './line-chart.renderer'; +export * from './map-chart.renderer'; export * from './table.renderer'; export * from './togglelightdark.renderer'; export * from './modal.renderer'; -export * from './appBar.renderer'; -export * from './button-bar.renderer'; +export * from './pie-chart.renderer'; +export * from './gauge.renderer'; +export * from './table.renderer'; export * from './tabsbar.renderer'; +export * from './vertical-menu.renderer'; +export * from './video-player.renderer'; export * from './audio-player.renderer'; export * from './loading-indicator.renderer'; export * from './videoconference.renderer'; 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 37e2042e..55dd5c28 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 @@ -19,6 +19,10 @@ export const mockRichComponentsCollection: ItemInfo[] = [ }, { thumbnailSrc: '/rich-components/map.svg', type: 'map' }, { thumbnailSrc: '/rich-components/modal.svg', type: 'modal' }, + { + thumbnailSrc: '/rich-components/gauge.svg', + type: 'gauge', + }, { thumbnailSrc: '/rich-components/pie.svg', type: 'pie' }, { thumbnailSrc: '/rich-components/table.svg', type: 'table' }, { thumbnailSrc: '/rich-components/tabsbar.svg', type: 'tabsBar' }, diff --git a/src/pods/properties/components/icon-selector/modal/icons.ts b/src/pods/properties/components/icon-selector/modal/icons.ts index f04f6c85..51f83027 100644 --- a/src/pods/properties/components/icon-selector/modal/icons.ts +++ b/src/pods/properties/components/icon-selector/modal/icons.ts @@ -1877,15 +1877,15 @@ export const iconCollection: IconInfo[] = [ categories: ['IT'], }, { - name: 'Spinner', - filename: 'spinner.svg', - searchTerms: ['spinner', 'loading', 'wait', 'progress'], + name: 'Gauge', + filename: 'gauge.svg', + searchTerms: ['gauge', 'loading', 'wait', 'progress'], categories: ['IT'], }, { - name: 'Spinner gap', - filename: 'spinnergap.svg', - searchTerms: ['spinner', 'loading', 'wait', 'progress'], + name: 'Gauge gap', + filename: 'gaugegap.svg', + searchTerms: ['gauge', 'loading', 'wait', 'progress'], categories: ['IT'], }, {