Skip to content

Commit 47d27d3

Browse files
committed
Merge branch 'dev' into feature/#666-changin-canvas-size-on-settings
2 parents 5e5807e + fb9e063 commit 47d27d3

17 files changed

Lines changed: 262 additions & 68 deletions

File tree

editor.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,5 @@
3737
<body>
3838
<div id="root"></div>
3939
<script type="module" src="/src/main.tsx"></script>
40-
<script>
41-
window.onbeforeunload = function () {
42-
return 'You are about to leave this page. All progress in QuickMock will be lost. Are you sure you want to proceed?';
43-
};
44-
</script>
4540
</body>
4641
</html>

index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ <h1>
357357
</p>
358358
</div>
359359
</div>
360-
<a href="editor.html" class="link">Launch QuickMock</a>
360+
<a href="editor.html" class="link hide-mobile">START DESIGN</a>
361+
<p class="mobile-text mobile-only">Now, only available on desktop.</p>
361362
</div>
362363
</main>
363364
</body>

public/shapes/cilinder.svg

Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { ShapeSizeRestrictions } from '@/core/model';
2+
import { forwardRef } from 'react';
3+
import { ShapeProps } from '../shape.model';
4+
import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-restrictions';
5+
import { Group, Rect, Ellipse, Line } from 'react-konva';
6+
import { BASIC_SHAPE } from '../front-components/shape.const';
7+
import { useShapeProps } from '../../shapes/use-shape-props.hook';
8+
import { useGroupShapeProps } from '../mock-components.utils';
9+
10+
const cilinderShapeRestrictions: ShapeSizeRestrictions = {
11+
minWidth: 10,
12+
minHeight: 10,
13+
maxWidth: -1,
14+
maxHeight: -1,
15+
defaultWidth: 160,
16+
defaultHeight: 110,
17+
};
18+
19+
export const getCilinderShapeSizeRestrictions = (): ShapeSizeRestrictions =>
20+
cilinderShapeRestrictions;
21+
22+
const shapeType = 'cilinder';
23+
24+
export const CilinderShape = forwardRef<any, ShapeProps>((props, ref) => {
25+
const {
26+
x,
27+
y,
28+
width,
29+
height,
30+
id,
31+
onSelected,
32+
text,
33+
otherProps,
34+
...shapeProps
35+
} = props;
36+
37+
const restrictedSize = fitSizeToShapeSizeRestrictions(
38+
cilinderShapeRestrictions,
39+
width,
40+
height
41+
);
42+
43+
const { width: restrictedWidth, height: restrictedHeight } = restrictedSize;
44+
45+
const { strokeStyle } = useShapeProps(otherProps, BASIC_SHAPE);
46+
47+
const commonGroupProps = useGroupShapeProps(
48+
props,
49+
restrictedSize,
50+
shapeType,
51+
ref
52+
);
53+
54+
return (
55+
<Group {...commonGroupProps} {...shapeProps}>
56+
<Ellipse
57+
x={restrictedWidth / 2}
58+
y={restrictedHeight}
59+
radiusX={restrictedWidth / 2}
60+
radiusY={restrictedWidth / 8}
61+
fill="#B0B0B0"
62+
stroke={BASIC_SHAPE.DEFAULT_STROKE_COLOR}
63+
strokeWidth={BASIC_SHAPE.DEFAULT_STROKE_WIDTH}
64+
/>
65+
<Rect
66+
x={0}
67+
y={0}
68+
width={restrictedWidth}
69+
height={restrictedHeight}
70+
strokeWidth={BASIC_SHAPE.DEFAULT_STROKE_WIDTH}
71+
fill="#B0B0B0"
72+
dash={strokeStyle}
73+
/>
74+
<Line
75+
points={[0, 0, 0, restrictedHeight]}
76+
stroke={BASIC_SHAPE.DEFAULT_STROKE_COLOR}
77+
strokeWidth={BASIC_SHAPE.DEFAULT_STROKE_WIDTH}
78+
/>
79+
<Line
80+
points={[restrictedWidth, 0, restrictedWidth, restrictedHeight]}
81+
stroke={BASIC_SHAPE.DEFAULT_STROKE_COLOR}
82+
strokeWidth={BASIC_SHAPE.DEFAULT_STROKE_WIDTH}
83+
/>
84+
<Ellipse
85+
x={restrictedWidth / 2}
86+
y={0}
87+
radiusX={restrictedWidth / 2}
88+
radiusY={restrictedWidth / 8}
89+
fill="#CFCFCF"
90+
stroke={BASIC_SHAPE.DEFAULT_STROKE_COLOR}
91+
strokeWidth={BASIC_SHAPE.DEFAULT_STROKE_WIDTH}
92+
/>
93+
</Group>
94+
);
95+
});

src/common/components/mock-components/front-basic-shapes/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export * from './star-shape';
99
export * from './large-arrow-shape';
1010
export * from './image-shape';
1111
export * from './modal-cover-shape';
12+
export * from './cilinder-basic-shape';

src/common/components/mock-components/front-rich-components/table/table.utils.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
//Example:'"Adri, Doe", "24,4", Paraguay' => ['"Adri, Doe"','"24,4"',' Paraguay']
2+
const divideIntoColumns = (row: string): string[] =>
3+
row.match(/\s*"([^"]*)"|\s*([^,]+)/g) || [];
14
export const knowMaxColumns = (rows: string[]): number => {
25
return rows.reduce((maxColumns, row) => {
3-
const columns = row.split(',').length;
6+
const columns = divideIntoColumns(row).length;
47
return columns > maxColumns ? columns : maxColumns;
58
}, 0);
69
};
@@ -10,7 +13,7 @@ export const parseCSVRowsIntoArray = (csvContent: string): string[] => {
1013
const maxColumns = knowMaxColumns(arrayRows);
1114

1215
arrayRows = arrayRows.map(row => {
13-
const currentColumnCount = row.split(',').length;
16+
const currentColumnCount = divideIntoColumns(row).length;
1417

1518
// If a row is empty, return a string of commas
1619
if (currentColumnCount === 0) {
@@ -23,7 +26,7 @@ export const parseCSVRowsIntoArray = (csvContent: string): string[] => {
2326
}
2427

2528
// If a row has less columns than maxColumns, add commas at the end
26-
if (currentColumnCount < maxColumns) {
29+
if (currentColumnCount <= maxColumns) {
2730
return row + ','.repeat(maxColumns - currentColumnCount); // Añadir comas al final
2831
}
2932

@@ -71,10 +74,14 @@ export const extractDataRows = (
7174
return widthRow
7275
? rows
7376
.slice(1, rows.length - 1)
74-
.map(row => row.split(',').map(cell => cell.trim()))
77+
.map(row =>
78+
divideIntoColumns(row).map(value => value.replace(/"/g, '').trim())
79+
)
7580
: rows
7681
.slice(1, rows.length)
77-
.map(row => row.split(',').map(cell => cell.trim()));
82+
.map(row =>
83+
divideIntoColumns(row).map(value => value.replace(/"/g, '').trim())
84+
);
7885
};
7986

8087
export const extractWidthRow = (

src/core/model/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export type ShapeType =
6969
| 'tooltip'
7070
| 'slider'
7171
| 'link'
72+
| 'cilinder'
7273
| 'richtext';
7374

7475
export const ShapeDisplayName: Record<ShapeType, string> = {
@@ -129,6 +130,7 @@ export const ShapeDisplayName: Record<ShapeType, string> = {
129130
tooltip: 'Tooltip',
130131
slider: 'Slider',
131132
richtext: 'Rich Text',
133+
cilinder: 'Cilinder',
132134
};
133135

134136
export type EditType = 'input' | 'textarea' | 'imageupload';

src/core/providers/canvas/canvas.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export interface CanvasContextModel {
134134
updateColorSlot: (color: string, index: number) => void;
135135
dropRef: React.MutableRefObject<HTMLDivElement | null>;
136136
setDropRef: (dropRef: React.MutableRefObject<HTMLDivElement | null>) => void;
137+
setIsDirty: (dirty: boolean) => void;
137138
loadSampleDocument: boolean;
138139
setLoadSampleDocument: React.Dispatch<React.SetStateAction<boolean>>;
139140
}

src/core/providers/canvas/canvas.provider.tsx

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,34 @@ export const CanvasProvider: React.FC<Props> = props => {
4848
addSnapshot
4949
);
5050

51-
const selectionInfo = useSelection(document, setDocument);
51+
const [isDirty, setIsDirty] = React.useState(false);
52+
53+
const setDocumentAndMarkDirtyState = (
54+
updater: DocumentModel | ((prev: DocumentModel) => DocumentModel),
55+
isDirty = true
56+
) => {
57+
setDocument(updater);
58+
setIsDirty(isDirty);
59+
};
60+
61+
const selectionInfo = useSelection(
62+
document,
63+
setDocument,
64+
setDocumentAndMarkDirtyState
65+
);
66+
67+
React.useEffect(() => {
68+
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
69+
if (isDirty) {
70+
e.preventDefault();
71+
}
72+
};
73+
window.addEventListener('beforeunload', handleBeforeUnload);
74+
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
75+
}, [isDirty]);
5276

5377
const addNewPage = () => {
54-
setDocument(lastDocument =>
78+
setDocumentAndMarkDirtyState(lastDocument =>
5579
produce(lastDocument, draft => {
5680
const newActiveIndex = draft.pages.length;
5781
draft.pages.push({
@@ -73,7 +97,7 @@ export const CanvasProvider: React.FC<Props> = props => {
7397
}
7498
);
7599

76-
setDocument(lastDocument =>
100+
setDocumentAndMarkDirtyState(lastDocument =>
77101
produce(lastDocument, draft => {
78102
const newPage = {
79103
id: uuidv4(),
@@ -93,7 +117,7 @@ export const CanvasProvider: React.FC<Props> = props => {
93117
? document.pages[pageIndex + 1].id // If it's not the last page, select the next one
94118
: document.pages[pageIndex - 1].id; // Otherwise, select the previous one
95119

96-
setDocument(lastDocument =>
120+
setDocumentAndMarkDirtyState(lastDocument =>
97121
produce(lastDocument, draft => {
98122
draft.pages = draft.pages.filter(
99123
currentPage => document.pages[pageIndex].id !== currentPage.id
@@ -116,7 +140,7 @@ export const CanvasProvider: React.FC<Props> = props => {
116140
selectionInfo.clearSelection();
117141
selectionInfo.shapeRefs.current = {};
118142

119-
setDocument(lastDocument =>
143+
setDocumentAndMarkDirtyState(lastDocument =>
120144
produce(lastDocument, draft => {
121145
const pageIndex = draft.pages.findIndex(page => page.id === pageId);
122146
if (pageIndex !== -1) {
@@ -127,15 +151,15 @@ export const CanvasProvider: React.FC<Props> = props => {
127151
};
128152

129153
const editPageTitle = (pageIndex: number, newName: string) => {
130-
setDocument(lastDocument =>
154+
setDocumentAndMarkDirtyState(lastDocument =>
131155
produce(lastDocument, draft => {
132156
draft.pages[pageIndex].name = newName;
133157
})
134158
);
135159
};
136160

137161
const swapPages = (id1: string, id2: string) => {
138-
setDocument(lastDocument =>
162+
setDocumentAndMarkDirtyState(lastDocument =>
139163
produce(lastDocument, draft => {
140164
const index1 = draft.pages.findIndex(page => page.id === id1);
141165
const index2 = draft.pages.findIndex(page => page.id === id2);
@@ -156,7 +180,7 @@ export const CanvasProvider: React.FC<Props> = props => {
156180
});
157181

158182
if (isPageIndexValid(document)) {
159-
setDocument(lastDocument =>
183+
setDocumentAndMarkDirtyState(lastDocument =>
160184
produce(lastDocument, draft => {
161185
draft.pages[lastDocument.activePageIndex].shapes.push(...newShapes);
162186
})
@@ -197,12 +221,13 @@ export const CanvasProvider: React.FC<Props> = props => {
197221
const createNewFullDocument = () => {
198222
setDocument(createDefaultDocumentModel());
199223
setCanvasSize(createDefaultCanvasSize());
224+
setDocumentAndMarkDirtyState(createDefaultDocumentModel(), false);
200225
setFileName('');
201226
};
202227

203228
const deleteSelectedShapes = () => {
204229
if (isPageIndexValid(document)) {
205-
setDocument(lastDocument =>
230+
setDocumentAndMarkDirtyState(lastDocument =>
206231
produce(lastDocument, draft => {
207232
draft.pages[lastDocument.activePageIndex].shapes =
208233
removeShapesFromList(
@@ -228,7 +253,7 @@ export const CanvasProvider: React.FC<Props> = props => {
228253

229254
const newShape = createShape({ x, y }, type, otherProps);
230255

231-
setDocument(lastDocument =>
256+
setDocumentAndMarkDirtyState(lastDocument =>
232257
produce(lastDocument, draft => {
233258
draft.pages[lastDocument.activePageIndex].shapes.push(newShape);
234259
})
@@ -258,7 +283,7 @@ export const CanvasProvider: React.FC<Props> = props => {
258283
});
259284
});
260285
} else {
261-
setDocument(fullDocument => {
286+
setDocumentAndMarkDirtyState(fullDocument => {
262287
return produce(fullDocument, draft => {
263288
draft.pages[document.activePageIndex].shapes = draft.pages[
264289
document.activePageIndex
@@ -272,7 +297,7 @@ export const CanvasProvider: React.FC<Props> = props => {
272297

273298
const updateShapePosition = (id: string, { x, y }: Coord) => {
274299
if (isPageIndexValid(document)) {
275-
setDocument(fullDocument => {
300+
setDocumentAndMarkDirtyState(fullDocument => {
276301
return produce(fullDocument, draft => {
277302
draft.pages[document.activePageIndex].shapes = draft.pages[
278303
document.activePageIndex
@@ -305,6 +330,7 @@ export const CanvasProvider: React.FC<Props> = props => {
305330
};
306331

307332
const loadDocument = (document: DocumentModel) => {
333+
setDocumentAndMarkDirtyState(document, false);
308334
loadSampleDocument && setLoadSampleDocument(false);
309335
setDocument(document);
310336
setHowManyLoadedDocuments(numberOfDocuments => numberOfDocuments + 1);
@@ -373,6 +399,7 @@ export const CanvasProvider: React.FC<Props> = props => {
373399
updateColorSlot,
374400
dropRef,
375401
setDropRef,
402+
setIsDirty,
376403
loadSampleDocument,
377404
setLoadSampleDocument,
378405
}}

src/core/providers/canvas/use-selection.hook.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import { produce } from 'immer';
88

99
export const useSelection = (
1010
document: DocumentModel,
11-
setDocument: React.Dispatch<React.SetStateAction<DocumentModel>>
11+
setDocument: React.Dispatch<React.SetStateAction<DocumentModel>>,
12+
setDocumentAndMarkDirtyState: (
13+
updater: DocumentModel | ((prev: DocumentModel) => DocumentModel),
14+
isDirty?: boolean
15+
) => void
1216
): SelectionInfo => {
1317
const transformerRef = useRef<Konva.Transformer>(null);
1418
const shapeRefs = useRef<ShapeRefs>({});
@@ -165,7 +169,7 @@ export const useSelection = (
165169
}
166170

167171
const selectedShapeId = selectedShapesIds[0];
168-
setDocument(prevDocument =>
172+
setDocumentAndMarkDirtyState(prevDocument =>
169173
produce(prevDocument, draft => {
170174
draft.pages[prevDocument.activePageIndex].shapes = draft.pages[
171175
prevDocument.activePageIndex
@@ -181,7 +185,7 @@ export const useSelection = (
181185
key: K,
182186
value: OtherProps[K]
183187
) => {
184-
setDocument(prevDocument =>
188+
setDocumentAndMarkDirtyState(prevDocument =>
185189
produce(prevDocument, draft => {
186190
draft.pages[prevDocument.activePageIndex].shapes = draft.pages[
187191
prevDocument.activePageIndex
@@ -198,7 +202,7 @@ export const useSelection = (
198202
key: K,
199203
value: OtherProps[K]
200204
) => {
201-
setDocument(prevDocument =>
205+
setDocumentAndMarkDirtyState(prevDocument =>
202206
produce(prevDocument, draft => {
203207
draft.pages[prevDocument.activePageIndex].shapes = draft.pages[
204208
prevDocument.activePageIndex
@@ -225,7 +229,6 @@ export const useSelection = (
225229
if (selectedShapesIds.length === 1) {
226230
const selectedShapeId = selectedShapesIds[0];
227231
updateOtherPropsOnSelectedSingleShape(selectedShapeId, key, value);
228-
229232
return;
230233
}
231234

0 commit comments

Comments
 (0)