diff --git a/public/assets/iria-carballo.jpeg b/public/assets/iria-carballo.jpeg
new file mode 100644
index 00000000..a0e0bb4d
Binary files /dev/null and b/public/assets/iria-carballo.jpeg differ
diff --git a/public/rich-components/loading-indicator.svg b/public/rich-components/loading-indicator.svg
new file mode 100644
index 00000000..3a245919
--- /dev/null
+++ b/public/rich-components/loading-indicator.svg
@@ -0,0 +1,10 @@
+
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 e1ca7951..d3097d8d 100644
--- a/src/common/components/mock-components/front-rich-components/index.ts
+++ b/src/common/components/mock-components/front-rich-components/index.ts
@@ -14,5 +14,6 @@ export * from './appBar';
export * from './buttonBar/buttonBar';
export * from './tabsbar';
export * from './audio-player';
+export * from './loading-indicator';
export * from './videoconference';
export * from './togglelightdark-shape';
diff --git a/src/common/components/mock-components/front-rich-components/loading-indicator.tsx b/src/common/components/mock-components/front-rich-components/loading-indicator.tsx
new file mode 100644
index 00000000..5319ac8a
--- /dev/null
+++ b/src/common/components/mock-components/front-rich-components/loading-indicator.tsx
@@ -0,0 +1,91 @@
+import { useRef, forwardRef } from 'react';
+import { Group, Rect, Text, Circle } from 'react-konva';
+import Konva from 'konva';
+import { ShapeProps } from '../shape.model';
+import { ShapeSizeRestrictions, ShapeType } from '@/core/model';
+import { BASIC_SHAPE } from '../front-components/shape.const';
+import { useShapeProps } from '../../shapes/use-shape-props.hook';
+import { useGroupShapeProps } from '../mock-components.utils';
+
+const LoadIndicatorSizeRestrictions: ShapeSizeRestrictions = {
+ minWidth: 200,
+ minHeight: 100,
+ maxWidth: -1,
+ maxHeight: -1,
+ defaultWidth: 200,
+ defaultHeight: 100,
+};
+
+const shapeType: ShapeType = 'loading-indicator';
+
+export const getLoadIndicatorSizeRestrictions = (): ShapeSizeRestrictions =>
+ LoadIndicatorSizeRestrictions;
+
+export const LoadIndicator = forwardRef((props, ref) => {
+ const { x, y, width, height, otherProps, ...shapeProps } = props;
+
+ const restrictedSize = {
+ width: width || LoadIndicatorSizeRestrictions.defaultWidth,
+ height: height || LoadIndicatorSizeRestrictions.defaultHeight,
+ };
+
+ const { width: restrictedWidth, height: restrictedHeight } = restrictedSize;
+
+ const colors = ['#666', '#888', '#aaa', '#ccc'];
+ const circlesRef = useRef>([]);
+
+ const { textColor } = useShapeProps(otherProps, BASIC_SHAPE);
+
+ const commonGroupProps = useGroupShapeProps(
+ props,
+ restrictedSize,
+ shapeType,
+ ref
+ );
+
+ const circleRadius = Math.min(restrictedWidth / 10, 15);
+ const circleSpacing = restrictedWidth / (colors.length + 1);
+
+ return (
+
+ {/* Load Indicator Background */}
+
+
+ {/* Animated Circles */}
+ {colors.map((color, index) => (
+ (circlesRef.current[index] = el)}
+ x={circleSpacing * (index + 1)}
+ y={restrictedHeight / 2}
+ radius={circleRadius}
+ fill={color}
+ stroke="#000"
+ strokeWidth={2}
+ />
+ ))}
+
+ {/* Loading Text */}
+
+
+ );
+});
+
+export default LoadIndicator;
diff --git a/src/core/model/index.ts b/src/core/model/index.ts
index 7a51b45d..af039047 100644
--- a/src/core/model/index.ts
+++ b/src/core/model/index.ts
@@ -71,6 +71,8 @@ export type ShapeType =
| 'slider'
| 'link'
| 'cilinder'
+ | 'richtext'
+ | 'loading-indicator'
| 'videoconference'
| 'richtext';
@@ -134,6 +136,7 @@ export const ShapeDisplayName: Record = {
slider: 'Slider',
richtext: 'Rich Text',
cilinder: 'Cilinder',
+ 'loading-indicator': 'Loading',
videoconference: 'Videoconference',
};
diff --git a/src/pods/about/members.ts b/src/pods/about/members.ts
index 8b86fd66..12f03175 100644
--- a/src/pods/about/members.ts
+++ b/src/pods/about/members.ts
@@ -136,9 +136,15 @@ export const memberList: Member[] = [
urlLinkedin: 'https://www.linkedin.com/in/sergioelmoreno/',
image: './assets/sergio-del-campo.jpg',
},
-
{
id: '18',
+ name: 'Iria',
+ surname: 'Carballo',
+ urlLinkedin: 'https://www.linkedin.com/in/iria-carballo/',
+ image: './assets/iria-carballo.jpeg',
+ },
+ {
+ id: '19',
name: 'Gabriel',
surname: 'Ionut',
urlLinkedin: 'https://www.linkedin.com/in/gabriel-ionut-birsan-b14816307/',
@@ -146,7 +152,7 @@ export const memberList: Member[] = [
},
{
- id: '19',
+ id: '20',
name: 'Antonio',
surname: 'Contreras',
urlLinkedin:
@@ -155,7 +161,7 @@ export const memberList: Member[] = [
},
{
- id: '20',
+ id: '21',
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 a4f0cef0..7843b8b8 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',
+ 'loading-indicator',
]);
// Check if a shape type allows inline editing
@@ -76,6 +77,7 @@ const shapeTypesWithDefaultText = new Set([
'datepickerinput',
'browser',
'modalDialog',
+ 'loading-indicator',
]);
// Map of ShapeTypes to their default text values
@@ -113,6 +115,7 @@ const defaultTextValueMap: Partial> = {
datepickerinput: new Date().toLocaleDateString(),
browser: 'https://example.com',
modalDialog: 'Title here...',
+ 'loading-indicator': 'Loading...',
};
export const generateDefaultTextValue = (
diff --git a/src/pods/canvas/model/shape-size.mapper.ts b/src/pods/canvas/model/shape-size.mapper.ts
index e362f27f..f6a5c993 100644
--- a/src/pods/canvas/model/shape-size.mapper.ts
+++ b/src/pods/canvas/model/shape-size.mapper.ts
@@ -52,6 +52,7 @@ import {
getCalendarShapeSizeRestrictions,
getHorizontalMenuShapeSizeRestrictions,
getLineChartShapeSizeRestrictions,
+ getLoadIndicatorSizeRestrictions,
getMapChartShapeSizeRestrictions,
getModalShapeSizeRestrictions,
getPieChartShapeSizeRestrictions,
@@ -145,6 +146,7 @@ const shapeSizeMap: Record ShapeSizeRestrictions> = {
slider: getSliderShapeSizeRestrictions,
audioPlayer: getAudioPlayerShapeSizeRestrictions,
cilinder: getCilinderShapeSizeRestrictions,
+ 'loading-indicator': getLoadIndicatorSizeRestrictions,
videoconference: getVideoconferenceShapeSizeRestrictions,
};
diff --git a/src/pods/canvas/model/transformer.model.ts b/src/pods/canvas/model/transformer.model.ts
index 59185373..092380f1 100644
--- a/src/pods/canvas/model/transformer.model.ts
+++ b/src/pods/canvas/model/transformer.model.ts
@@ -64,6 +64,7 @@ export const generateTypeOfTransformer = (shapeType: ShapeType): string[] => {
case 'link':
case 'horizontalScrollBar':
case 'appBar':
+ case 'loading-indicator':
case 'buttonBar':
case 'slider':
return ['middle-left', 'middle-right'];
diff --git a/src/pods/canvas/shape-renderer/index.tsx b/src/pods/canvas/shape-renderer/index.tsx
index d72062fe..25ee3cb6 100644
--- a/src/pods/canvas/shape-renderer/index.tsx
+++ b/src/pods/canvas/shape-renderer/index.tsx
@@ -70,6 +70,7 @@ import { renderSmalltext } from './simple-text-components/smalltext.renderer';
import { renderImage } from './simple-basic-shapes/image.renderer';
import { renderCalendar } from './simple-rich-components/calendar.renderer';
import { renderAppBar } from './simple-rich-components/appBar.renderer';
+import { renderLoadingIndicator } from './simple-rich-components/loading-indicator.renderer';
export const renderShapeComponent = (
shape: ShapeModel,
@@ -192,6 +193,8 @@ export const renderShapeComponent = (
return renderSlider(shape, shapeRenderedProps);
case 'cilinder':
return renderCilinder(shape, shapeRenderedProps);
+ case 'loading-indicator':
+ return renderLoadingIndicator(shape, shapeRenderedProps);
case 'videoconference':
return renderVideoconference(shape, shapeRenderedProps);
default:
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 f0dad515..9690dbd1 100644
--- a/src/pods/canvas/shape-renderer/simple-rich-components/index.ts
+++ b/src/pods/canvas/shape-renderer/simple-rich-components/index.ts
@@ -15,4 +15,5 @@ export * from './appBar.renderer';
export * from './button-bar.renderer';
export * from './tabsbar.renderer';
export * from './audio-player.renderer';
+export * from './loading-indicator.renderer';
export * from './videoconference.renderer';
diff --git a/src/pods/canvas/shape-renderer/simple-rich-components/loading-indicator.renderer.tsx b/src/pods/canvas/shape-renderer/simple-rich-components/loading-indicator.renderer.tsx
new file mode 100644
index 00000000..39610f09
--- /dev/null
+++ b/src/pods/canvas/shape-renderer/simple-rich-components/loading-indicator.renderer.tsx
@@ -0,0 +1,33 @@
+import { LoadIndicator } from '@/common/components/mock-components/front-rich-components/loading-indicator';
+import { ShapeRendererProps } from '../model';
+import { ShapeModel } from '@/core/model';
+
+export const renderLoadingIndicator = (
+ 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 cd6d16c4..37e2042e 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
@@ -13,6 +13,10 @@ export const mockRichComponentsCollection: ItemInfo[] = [
type: 'horizontal-menu',
},
{ thumbnailSrc: '/rich-components/line-chart.svg', type: 'linechart' },
+ {
+ thumbnailSrc: '/rich-components/loading-indicator.svg',
+ type: 'loading-indicator',
+ },
{ thumbnailSrc: '/rich-components/map.svg', type: 'map' },
{ thumbnailSrc: '/rich-components/modal.svg', type: 'modal' },
{ thumbnailSrc: '/rich-components/pie.svg', type: 'pie' },