Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5c275b1
feat(#561 note): add NoteVm interface, provider methods and update sc…
gustedeveloper Nov 3, 2025
a3166f4
feat(#561 note): implement draggable note component with title, body …
gustedeveloper Nov 3, 2025
53da018
refactor(#561 use draggable hook): extract draggable logic into reusa…
gustedeveloper Nov 3, 2025
16b1e5f
feat(#561 floating-bar): add AddNote component with NoteIcon and inte…
gustedeveloper Nov 4, 2025
df9b4d8
refactor(#561 note): Move DEFAULT_NOTE_WIDTH and DEFAULT_NOTE_HEIGHT …
gustedeveloper Nov 8, 2025
720ba9e
feat(#561 edit-note): add EditNote component, styles and pod for note…
gustedeveloper Nov 8, 2025
e1a5701
feat(#561 add-note): implement AddNote functionality with modal integ…
gustedeveloper Nov 8, 2025
0eac7f2
feat(#561 canvas-schema): add note deletion support
gustedeveloper Nov 8, 2025
ea5aac2
style(#561 textarea): improve textarea styles for focus and hover states
gustedeveloper Nov 8, 2025
759b83e
feat(#561 canvas): integrate note editing and positioning in canvas
gustedeveloper Nov 8, 2025
503da59
feat(#561 autosave): update schema retrieval to map to latest version…
gustedeveloper Nov 10, 2025
0ec284d
feat(#561 edit-note): integrate note editing functionality in CanvasPod
gustedeveloper Nov 10, 2025
90d4050
test(#561 autosave, canvas-delete-item specs): update test expectatio…
gustedeveloper Nov 10, 2025
f851656
feat(#561 note): implement auto height calculation for notes and upda…
gustedeveloper Nov 10, 2025
7948beb
style(#561 edit-note): update textarea styles to restrict resizing an…
gustedeveloper Nov 10, 2025
a2db4fa
fix(#561 note): Improve note auto-height calculation to respect newlines
gustedeveloper Nov 11, 2025
039bd3e
test(#561 note): add unit tests for calculateNoteAutoHeight function
gustedeveloper Nov 11, 2025
a757abc
style(#561 note): update note component styles and constants for impr…
gustedeveloper Nov 11, 2025
682f6e1
fix(#561 shortcut): fix shortcut hook to allow copy/paste in inputs w…
gustedeveloper Nov 11, 2025
5cc203b
fix(#561 edit-note): add error handling for description field
gustedeveloper Nov 11, 2025
6e52df0
style(#561 note, edit-note): remove unused editIcon styles, change er…
gustedeveloper Nov 11, 2025
6e414db
style(#561 note): add editIcon class
gustedeveloper Nov 12, 2025
8963c4a
feat(#561 note): exporting additional business logic and constants, a…
gustedeveloper Nov 12, 2025
52cbd8a
feat(#561 note): add DatabaseNote component and related styles for no…
gustedeveloper Nov 12, 2025
96a9315
feat(#561 note): implement note export functions with tests and integ…
gustedeveloper Nov 12, 2025
55a0913
Merge branch 'vnext' into feature/#561-add-note-functionality-to-floa…
gustedeveloper Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -308,19 +308,21 @@ textarea {
width: 100%;
border: none;
outline: none;

font-family: inherit;
border: 1px solid var(--input-border-color);
transition: all 0.2s ease;
}

input:focus,
select:focus {
select:focus,
textarea:focus {
border: 1px solid var(--input-border-color-active);
background-color: var(--hover-input);
}

select:hover,
input:hover {
input:hover,
textarea:hover {
background-color: var(--hover-input);
box-shadow: 0 0 4px var(--hover-checkbox);
}
Expand Down
126 changes: 126 additions & 0 deletions src/common/canvas-draggable/canvas-draggable.hook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { useState, useCallback } from 'react';
import { Size } from '@/core/model';
import { UpdatePositionFn, UpdatePositionItemInfo } from '@/core/providers';
import { setOffSetZoomToCoords } from '@/common/helpers/set-off-set-zoom-to-coords.helper';

export const useDraggable = (
id: string,
initialX: number,
initialY: number,
updatePosition: UpdatePositionFn,
totalHeight: number,
canvasSize: Size,
viewBoxSize: Size,
zoomFactor: number
) => {
const [isDragging, setIsDragging] = useState(false);
const [startDragPosition, setStartDragPosition] = useState({ x: 0, y: 0 });
const [finalInfoAfterDrag, setFinalInfoAfterDrag] =
useState<UpdatePositionItemInfo | null>(null);
const [node, setNode] = React.useState<SVGElement | null>(null);
const ref = React.useCallback((nodeEle: SVGElement): void => {
setNode(nodeEle);
}, []);

const startDrag = (x: number, y: number) => {
const { x: offsetX, y: offsetY } = setOffSetZoomToCoords(
x,
y,
viewBoxSize,
canvasSize,
zoomFactor
);
setStartDragPosition({
x: offsetX - initialX,
y: offsetY - initialY,
});
setIsDragging(true);
};

const onMouseDown = useCallback(
(event: React.MouseEvent) => {
startDrag(event.clientX, event.clientY);
},
[initialX, initialY, viewBoxSize]
);

const onTouchStart = useCallback(
(event: React.TouchEvent) => {
event.preventDefault();
const touch = event.touches[0];
startDrag(touch.clientX, touch.clientY);
},
[initialX, initialY, viewBoxSize]
);

const updateDrag = (x: number, y: number) => {
if (isDragging) {
const { x: offsetX, y: offsetY } = setOffSetZoomToCoords(
x,
y,
viewBoxSize,
canvasSize,
zoomFactor
);
const newPosition = {
id,
position: {
x: offsetX - startDragPosition.x,
y: offsetY - startDragPosition.y,
},
totalHeight,
canvasSize,
};

updatePosition(newPosition, false);
setFinalInfoAfterDrag(newPosition);
}
};

const onMouseMove = useCallback(
(event: MouseEvent) => {
updateDrag(event.clientX, event.clientY);
},
[id, isDragging, startDragPosition, updatePosition, totalHeight, canvasSize]
);

const onTouchMove = useCallback(
(event: TouchEvent) => {
event.preventDefault();
const touch = event.touches[0];
updateDrag(touch.clientX, touch.clientY);
},
[id, isDragging, startDragPosition, updatePosition, totalHeight, canvasSize]
);

const endDrag = useCallback(() => {
setIsDragging(false);
if (finalInfoAfterDrag) {
updatePosition(finalInfoAfterDrag, true);
}
}, [finalInfoAfterDrag, updatePosition]);

React.useEffect(() => {
if (!node) return;
if (isDragging) {
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', endDrag);
node.addEventListener('touchmove', onTouchMove);
node.addEventListener('touchend', endDrag);
} else {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', endDrag);
node.removeEventListener('touchmove', onTouchMove);
node.removeEventListener('touchend', endDrag);
}

return () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', endDrag);
node.removeEventListener('touchmove', onTouchMove);
node.removeEventListener('touchend', endDrag);
};
}, [isDragging, onMouseMove, onTouchMove, endDrag]);

return { onMouseDown, onTouchStart, ref };
};
1 change: 1 addition & 0 deletions src/common/canvas-draggable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './canvas-draggable.hook';
15 changes: 15 additions & 0 deletions src/common/components/icons/note-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const NoteIcon = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 256 256"
>
<path
fill="currentColor"
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"
/>
</svg>
);
};
2 changes: 2 additions & 0 deletions src/common/components/modal-dialog/modal-dialog.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export const ADD_RELATION_TITLE = 'Add Relation';
export const EDIT_RELATION_TITLE = 'Edit Relation';
export const ADD_COLLECTION_TITLE = 'Add Collection';
export const EDIT_COLLECTION_TITLE = 'Edit Collection';
export const ADD_NOTE_TITLE = 'Add Note';
export const EDIT_NOTE_TITLE = 'Edit Note';
export const ABOUT_TITLE = 'About us';
export const EXPORT_MODEL_TITLE = 'Export Model';
export const IMPORT_COLLECTION_TITLE = 'Import JSON Document';
16 changes: 11 additions & 5 deletions src/common/shortcut/shortcut.hook.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { isMacOS, isWindowsOrLinux } from '@/common/helpers/platform.helpers';
import { useModalDialogContext } from '@/core/providers';
import { useEffect } from 'react';
import { ModifierType } from './shortcut.model';

Expand All @@ -25,10 +24,15 @@ const useShortcut = ({
callback,
modifierType = 'system',
}: ShortcutHookProps) => {
const { modalDialog } = useModalDialogContext();

const handleKeyPress = (event: KeyboardEvent) => {
if (modalDialog.isOpen) {
const target = event.target as HTMLElement;
const isTypingInInput =
target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.isContentEditable;

// Block single-key shortcuts when typing in inputs
if (isTypingInInput && modifierType === 'none') {
return;
}

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

if (isValidModifier && targetKey.includes(event.key)) {
event.preventDefault();
if (!isTypingInInput) {
event.preventDefault();
}
callback();
}
};
Expand Down
2 changes: 2 additions & 0 deletions src/core/autosave/autosave.business.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('saveToLocal', () => {
tables: [{ id: '1', fields: [], tableName: 'tableName', x: 0, y: 0 }],
relations: [],
selectedElementId: null,
notes: [],
},
};
const autosaveError = 0;
Expand Down Expand Up @@ -107,6 +108,7 @@ describe('retrieveLocalSchema', () => {
},
],
relations: [],
notes: [],
selectedElementId: null,
};

Expand Down
3 changes: 2 additions & 1 deletion src/core/autosave/autosave.business.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DatabaseSchemaVm,
createDefaultDatabaseSchemaVm,
} from '@/core/providers';
import { mapSchemaToLatestVersion } from '../providers/canvas-schema/canvas-schema.mapper';

export interface AutosaveCanvasSchema {
filename: string | undefined;
Expand Down Expand Up @@ -47,7 +48,7 @@ export const retrieveLocalSchema = (
if (retrievedValue && retrievedValue.canvasSchema.tables.length !== 0) {
setLoadSample(false);
setFilename(retrievedValue.filename);
return retrievedValue.canvasSchema;
return mapSchemaToLatestVersion(retrievedValue.canvasSchema);
} else {
return defaultSchema(setLoadSample, setFilename);
}
Expand Down
5 changes: 4 additions & 1 deletion src/core/autosave/autosave.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ const useAutosave = () => {
const autosaveHandler = () => {
if (autosaveError > 1) stopAutosave();

if (canvasSchema.tables.length !== 0 && canvasViewSettings.autoSave) {
if (
(canvasSchema.tables.length !== 0 || canvasSchema.notes.length !== 0) &&
canvasViewSettings.autoSave
) {
saveToLocal(
AUTOSAVE_KEY,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('deleteItemFromCanvasSchema', () => {
const dbSchema: DatabaseSchemaVm = {
version: '0.1',
selectedElementId: '1',
notes: [],
relations: [
{
id: '20',
Expand Down Expand Up @@ -78,6 +79,7 @@ describe('deleteItemFromCanvasSchema', () => {
const expected = {
version: '0.1',
selectedElementId: null,
notes: [],
relations: [],
tables: [
{
Expand Down Expand Up @@ -110,6 +112,7 @@ describe('deleteItemFromCanvasSchema', () => {
const dbSchema: DatabaseSchemaVm = {
version: '0.1',
selectedElementId: '20',
notes: [],
relations: [
{
id: '20',
Expand Down Expand Up @@ -179,6 +182,7 @@ describe('deleteItemFromCanvasSchema', () => {
const expected = {
version: '0.1',
selectedElementId: null,
notes: [],
relations: [
{
id: '30',
Expand Down Expand Up @@ -240,6 +244,7 @@ describe('deleteItemFromCanvasSchema', () => {
const dbSchema: DatabaseSchemaVm = {
version: '0.1',
selectedElementId: '30',
notes: [],
relations: [
{
id: '30',
Expand Down Expand Up @@ -301,6 +306,7 @@ describe('deleteItemFromCanvasSchema', () => {
const expected = {
version: '0.1',
selectedElementId: null,
notes: [],
relations: [],
tables: [
{
Expand Down Expand Up @@ -353,6 +359,7 @@ describe('deleteItemFromCanvasSchema', () => {
const dbSchema: DatabaseSchemaVm = {
version: '0.1',
selectedElementId: '1',
notes: [],
relations: [],
tables: [
{
Expand Down Expand Up @@ -385,6 +392,7 @@ describe('deleteItemFromCanvasSchema', () => {
const expected = {
version: '0.1',
selectedElementId: null,
notes: [],
relations: [],
tables: [],
};
Expand All @@ -396,6 +404,7 @@ describe('deleteItemFromCanvasSchema', () => {
const dbSchema: DatabaseSchemaVm = {
version: '0.1',
selectedElementId: '11',
notes: [],
relations: [
{
id: '20',
Expand Down Expand Up @@ -445,6 +454,7 @@ describe('deleteItemFromCanvasSchema', () => {
const expected = {
version: '0.1',
selectedElementId: null,
notes: [],
relations: [
{
id: '20',
Expand Down
Loading