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 @@
+
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'],
},
{