Skip to content

Commit 8210d97

Browse files
authored
Merge pull request #567 from Lemoncode/feature/#561-add-note-functionality-to-floating-bar
Feature/#561 add note functionality to floating bar
2 parents bbca525 + 55a0913 commit 8210d97

56 files changed

Lines changed: 1816 additions & 28 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/App.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,19 +308,21 @@ textarea {
308308
width: 100%;
309309
border: none;
310310
outline: none;
311-
311+
font-family: inherit;
312312
border: 1px solid var(--input-border-color);
313313
transition: all 0.2s ease;
314314
}
315315

316316
input:focus,
317-
select:focus {
317+
select:focus,
318+
textarea:focus {
318319
border: 1px solid var(--input-border-color-active);
319320
background-color: var(--hover-input);
320321
}
321322

322323
select:hover,
323-
input:hover {
324+
input:hover,
325+
textarea:hover {
324326
background-color: var(--hover-input);
325327
box-shadow: 0 0 4px var(--hover-checkbox);
326328
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import React, { useState, useCallback } from 'react';
2+
import { Size } from '@/core/model';
3+
import { UpdatePositionFn, UpdatePositionItemInfo } from '@/core/providers';
4+
import { setOffSetZoomToCoords } from '@/common/helpers/set-off-set-zoom-to-coords.helper';
5+
6+
export const useDraggable = (
7+
id: string,
8+
initialX: number,
9+
initialY: number,
10+
updatePosition: UpdatePositionFn,
11+
totalHeight: number,
12+
canvasSize: Size,
13+
viewBoxSize: Size,
14+
zoomFactor: number
15+
) => {
16+
const [isDragging, setIsDragging] = useState(false);
17+
const [startDragPosition, setStartDragPosition] = useState({ x: 0, y: 0 });
18+
const [finalInfoAfterDrag, setFinalInfoAfterDrag] =
19+
useState<UpdatePositionItemInfo | null>(null);
20+
const [node, setNode] = React.useState<SVGElement | null>(null);
21+
const ref = React.useCallback((nodeEle: SVGElement): void => {
22+
setNode(nodeEle);
23+
}, []);
24+
25+
const startDrag = (x: number, y: number) => {
26+
const { x: offsetX, y: offsetY } = setOffSetZoomToCoords(
27+
x,
28+
y,
29+
viewBoxSize,
30+
canvasSize,
31+
zoomFactor
32+
);
33+
setStartDragPosition({
34+
x: offsetX - initialX,
35+
y: offsetY - initialY,
36+
});
37+
setIsDragging(true);
38+
};
39+
40+
const onMouseDown = useCallback(
41+
(event: React.MouseEvent) => {
42+
startDrag(event.clientX, event.clientY);
43+
},
44+
[initialX, initialY, viewBoxSize]
45+
);
46+
47+
const onTouchStart = useCallback(
48+
(event: React.TouchEvent) => {
49+
event.preventDefault();
50+
const touch = event.touches[0];
51+
startDrag(touch.clientX, touch.clientY);
52+
},
53+
[initialX, initialY, viewBoxSize]
54+
);
55+
56+
const updateDrag = (x: number, y: number) => {
57+
if (isDragging) {
58+
const { x: offsetX, y: offsetY } = setOffSetZoomToCoords(
59+
x,
60+
y,
61+
viewBoxSize,
62+
canvasSize,
63+
zoomFactor
64+
);
65+
const newPosition = {
66+
id,
67+
position: {
68+
x: offsetX - startDragPosition.x,
69+
y: offsetY - startDragPosition.y,
70+
},
71+
totalHeight,
72+
canvasSize,
73+
};
74+
75+
updatePosition(newPosition, false);
76+
setFinalInfoAfterDrag(newPosition);
77+
}
78+
};
79+
80+
const onMouseMove = useCallback(
81+
(event: MouseEvent) => {
82+
updateDrag(event.clientX, event.clientY);
83+
},
84+
[id, isDragging, startDragPosition, updatePosition, totalHeight, canvasSize]
85+
);
86+
87+
const onTouchMove = useCallback(
88+
(event: TouchEvent) => {
89+
event.preventDefault();
90+
const touch = event.touches[0];
91+
updateDrag(touch.clientX, touch.clientY);
92+
},
93+
[id, isDragging, startDragPosition, updatePosition, totalHeight, canvasSize]
94+
);
95+
96+
const endDrag = useCallback(() => {
97+
setIsDragging(false);
98+
if (finalInfoAfterDrag) {
99+
updatePosition(finalInfoAfterDrag, true);
100+
}
101+
}, [finalInfoAfterDrag, updatePosition]);
102+
103+
React.useEffect(() => {
104+
if (!node) return;
105+
if (isDragging) {
106+
window.addEventListener('mousemove', onMouseMove);
107+
window.addEventListener('mouseup', endDrag);
108+
node.addEventListener('touchmove', onTouchMove);
109+
node.addEventListener('touchend', endDrag);
110+
} else {
111+
window.removeEventListener('mousemove', onMouseMove);
112+
window.removeEventListener('mouseup', endDrag);
113+
node.removeEventListener('touchmove', onTouchMove);
114+
node.removeEventListener('touchend', endDrag);
115+
}
116+
117+
return () => {
118+
window.removeEventListener('mousemove', onMouseMove);
119+
window.removeEventListener('mouseup', endDrag);
120+
node.removeEventListener('touchmove', onTouchMove);
121+
node.removeEventListener('touchend', endDrag);
122+
};
123+
}, [isDragging, onMouseMove, onTouchMove, endDrag]);
124+
125+
return { onMouseDown, onTouchStart, ref };
126+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './canvas-draggable.hook';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const NoteIcon = () => {
2+
return (
3+
<svg
4+
xmlns="http://www.w3.org/2000/svg"
5+
width="1.2em"
6+
height="1.2em"
7+
viewBox="0 0 256 256"
8+
>
9+
<path
10+
fill="currentColor"
11+
d="M208 34H48a14 14 0 0 0-14 14v160a14 14 0 0 0 14 14h108.69a13.94 13.94 0 0 0 9.9-4.1l51.31-51.31a13.94 13.94 0 0 0 4.1-9.9V48a14 14 0 0 0-14-14M46 208V48a2 2 0 0 1 2-2h160a2 2 0 0 1 2 2v106h-50a6 6 0 0 0-6 6v50H48a2 2 0 0 1-2-2m120-6.49V166h35.52Z"
12+
/>
13+
</svg>
14+
);
15+
};

src/common/components/modal-dialog/modal-dialog.const.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export const ADD_RELATION_TITLE = 'Add Relation';
33
export const EDIT_RELATION_TITLE = 'Edit Relation';
44
export const ADD_COLLECTION_TITLE = 'Add Collection';
55
export const EDIT_COLLECTION_TITLE = 'Edit Collection';
6+
export const ADD_NOTE_TITLE = 'Add Note';
7+
export const EDIT_NOTE_TITLE = 'Edit Note';
68
export const ABOUT_TITLE = 'About us';
79
export const EXPORT_MODEL_TITLE = 'Export Model';
810
export const IMPORT_COLLECTION_TITLE = 'Import JSON Document';

src/common/shortcut/shortcut.hook.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { isMacOS, isWindowsOrLinux } from '@/common/helpers/platform.helpers';
2-
import { useModalDialogContext } from '@/core/providers';
32
import { useEffect } from 'react';
43
import { ModifierType } from './shortcut.model';
54

@@ -25,10 +24,15 @@ const useShortcut = ({
2524
callback,
2625
modifierType = 'system',
2726
}: ShortcutHookProps) => {
28-
const { modalDialog } = useModalDialogContext();
29-
3027
const handleKeyPress = (event: KeyboardEvent) => {
31-
if (modalDialog.isOpen) {
28+
const target = event.target as HTMLElement;
29+
const isTypingInInput =
30+
target.tagName === 'INPUT' ||
31+
target.tagName === 'TEXTAREA' ||
32+
target.isContentEditable;
33+
34+
// Block single-key shortcuts when typing in inputs
35+
if (isTypingInInput && modifierType === 'none') {
3236
return;
3337
}
3438

@@ -45,7 +49,9 @@ const useShortcut = ({
4549
}[modifierType];
4650

4751
if (isValidModifier && targetKey.includes(event.key)) {
48-
event.preventDefault();
52+
if (!isTypingInInput) {
53+
event.preventDefault();
54+
}
4955
callback();
5056
}
5157
};

src/core/autosave/autosave.business.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ describe('saveToLocal', () => {
4848
tables: [{ id: '1', fields: [], tableName: 'tableName', x: 0, y: 0 }],
4949
relations: [],
5050
selectedElementId: null,
51+
notes: [],
5152
},
5253
};
5354
const autosaveError = 0;
@@ -107,6 +108,7 @@ describe('retrieveLocalSchema', () => {
107108
},
108109
],
109110
relations: [],
111+
notes: [],
110112
selectedElementId: null,
111113
};
112114

src/core/autosave/autosave.business.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
DatabaseSchemaVm,
77
createDefaultDatabaseSchemaVm,
88
} from '@/core/providers';
9+
import { mapSchemaToLatestVersion } from '../providers/canvas-schema/canvas-schema.mapper';
910

1011
export interface AutosaveCanvasSchema {
1112
filename: string | undefined;
@@ -47,7 +48,7 @@ export const retrieveLocalSchema = (
4748
if (retrievedValue && retrievedValue.canvasSchema.tables.length !== 0) {
4849
setLoadSample(false);
4950
setFilename(retrievedValue.filename);
50-
return retrievedValue.canvasSchema;
51+
return mapSchemaToLatestVersion(retrievedValue.canvasSchema);
5152
} else {
5253
return defaultSchema(setLoadSample, setFilename);
5354
}

src/core/autosave/autosave.hook.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ const useAutosave = () => {
2323
const autosaveHandler = () => {
2424
if (autosaveError > 1) stopAutosave();
2525

26-
if (canvasSchema.tables.length !== 0 && canvasViewSettings.autoSave) {
26+
if (
27+
(canvasSchema.tables.length !== 0 || canvasSchema.notes.length !== 0) &&
28+
canvasViewSettings.autoSave
29+
) {
2730
saveToLocal(
2831
AUTOSAVE_KEY,
2932
{

src/core/providers/canvas-schema/business-specs/canvas-delete-item.business.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('deleteItemFromCanvasSchema', () => {
99
const dbSchema: DatabaseSchemaVm = {
1010
version: '0.1',
1111
selectedElementId: '1',
12+
notes: [],
1213
relations: [
1314
{
1415
id: '20',
@@ -78,6 +79,7 @@ describe('deleteItemFromCanvasSchema', () => {
7879
const expected = {
7980
version: '0.1',
8081
selectedElementId: null,
82+
notes: [],
8183
relations: [],
8284
tables: [
8385
{
@@ -110,6 +112,7 @@ describe('deleteItemFromCanvasSchema', () => {
110112
const dbSchema: DatabaseSchemaVm = {
111113
version: '0.1',
112114
selectedElementId: '20',
115+
notes: [],
113116
relations: [
114117
{
115118
id: '20',
@@ -179,6 +182,7 @@ describe('deleteItemFromCanvasSchema', () => {
179182
const expected = {
180183
version: '0.1',
181184
selectedElementId: null,
185+
notes: [],
182186
relations: [
183187
{
184188
id: '30',
@@ -240,6 +244,7 @@ describe('deleteItemFromCanvasSchema', () => {
240244
const dbSchema: DatabaseSchemaVm = {
241245
version: '0.1',
242246
selectedElementId: '30',
247+
notes: [],
243248
relations: [
244249
{
245250
id: '30',
@@ -301,6 +306,7 @@ describe('deleteItemFromCanvasSchema', () => {
301306
const expected = {
302307
version: '0.1',
303308
selectedElementId: null,
309+
notes: [],
304310
relations: [],
305311
tables: [
306312
{
@@ -353,6 +359,7 @@ describe('deleteItemFromCanvasSchema', () => {
353359
const dbSchema: DatabaseSchemaVm = {
354360
version: '0.1',
355361
selectedElementId: '1',
362+
notes: [],
356363
relations: [],
357364
tables: [
358365
{
@@ -385,6 +392,7 @@ describe('deleteItemFromCanvasSchema', () => {
385392
const expected = {
386393
version: '0.1',
387394
selectedElementId: null,
395+
notes: [],
388396
relations: [],
389397
tables: [],
390398
};
@@ -396,6 +404,7 @@ describe('deleteItemFromCanvasSchema', () => {
396404
const dbSchema: DatabaseSchemaVm = {
397405
version: '0.1',
398406
selectedElementId: '11',
407+
notes: [],
399408
relations: [
400409
{
401410
id: '20',
@@ -445,6 +454,7 @@ describe('deleteItemFromCanvasSchema', () => {
445454
const expected = {
446455
version: '0.1',
447456
selectedElementId: null,
457+
notes: [],
448458
relations: [
449459
{
450460
id: '20',

0 commit comments

Comments
 (0)