feat: add sticky notes (#967)

* feat: add sticky notes

* feat: add sticky notes
This commit is contained in:
Guy Ben-Aharon
2025-11-02 15:42:18 +02:00
committed by GitHub
parent 68412f90a7
commit 6d38ebe3ec
56 changed files with 3007 additions and 105 deletions

View File

@@ -24,6 +24,7 @@ import { defaultSchemas } from '@/lib/data/default-schemas';
import { useEventEmitter } from 'ahooks';
import type { DBDependency } from '@/lib/domain/db-dependency';
import type { Area } from '@/lib/domain/area';
import type { Note } from '@/lib/domain/note';
import { storageInitialValue } from '../storage-context/storage-context';
import { useDiff } from '../diff-context/use-diff';
import type { DiffCalculatedEvent } from '../diff-context/diff-context';
@@ -67,6 +68,7 @@ export const ChartDBProvider: React.FC<
const [customTypes, setCustomTypes] = useState<DBCustomType[]>(
diagram?.customTypes ?? []
);
const [notes, setNotes] = useState<Note[]>(diagram?.notes ?? []);
const { events: diffEvents } = useDiff();
@@ -147,6 +149,7 @@ export const ChartDBProvider: React.FC<
dependencies,
areas,
customTypes,
notes,
}),
[
diagramId,
@@ -158,6 +161,7 @@ export const ChartDBProvider: React.FC<
dependencies,
areas,
customTypes,
notes,
diagramCreatedAt,
diagramUpdatedAt,
]
@@ -171,6 +175,7 @@ export const ChartDBProvider: React.FC<
setDependencies([]);
setAreas([]);
setCustomTypes([]);
setNotes([]);
setDiagramUpdatedAt(updatedAt);
resetRedoStack();
@@ -183,6 +188,7 @@ export const ChartDBProvider: React.FC<
db.deleteDiagramDependencies(diagramId),
db.deleteDiagramAreas(diagramId),
db.deleteDiagramCustomTypes(diagramId),
db.deleteDiagramNotes(diagramId),
]);
}, [db, diagramId, resetRedoStack, resetUndoStack]);
@@ -197,6 +203,7 @@ export const ChartDBProvider: React.FC<
setDependencies([]);
setAreas([]);
setCustomTypes([]);
setNotes([]);
resetRedoStack();
resetUndoStack();
@@ -207,6 +214,7 @@ export const ChartDBProvider: React.FC<
db.deleteDiagramDependencies(diagramId),
db.deleteDiagramAreas(diagramId),
db.deleteDiagramCustomTypes(diagramId),
db.deleteDiagramNotes(diagramId),
]);
}, [db, diagramId, resetRedoStack, resetUndoStack]);
@@ -1528,6 +1536,130 @@ export const ChartDBProvider: React.FC<
[db, diagramId, setAreas, getArea, addUndoAction, resetRedoStack]
);
// Note operations
const addNotes: ChartDBContext['addNotes'] = useCallback(
async (notes: Note[], options = { updateHistory: true }) => {
setNotes((currentNotes) => [...currentNotes, ...notes]);
const updatedAt = new Date();
setDiagramUpdatedAt(updatedAt);
await Promise.all([
...notes.map((note) => db.addNote({ diagramId, note })),
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
]);
if (options.updateHistory) {
addUndoAction({
action: 'addNotes',
redoData: { notes },
undoData: { noteIds: notes.map((n) => n.id) },
});
resetRedoStack();
}
},
[db, diagramId, setNotes, addUndoAction, resetRedoStack]
);
const addNote: ChartDBContext['addNote'] = useCallback(
async (note: Note, options = { updateHistory: true }) => {
return addNotes([note], options);
},
[addNotes]
);
const createNote: ChartDBContext['createNote'] = useCallback(
async (attributes) => {
const note: Note = {
id: generateId(),
content: '',
x: 0,
y: 0,
width: 200,
height: 150,
color: '#ffe374', // Default warm yellow
...attributes,
};
await addNote(note);
return note;
},
[addNote]
);
const getNote: ChartDBContext['getNote'] = useCallback(
(id: string) => notes.find((note) => note.id === id) ?? null,
[notes]
);
const removeNotes: ChartDBContext['removeNotes'] = useCallback(
async (ids: string[], options = { updateHistory: true }) => {
const prevNotes = [
...notes.filter((note) => ids.includes(note.id)),
];
setNotes((notes) => notes.filter((note) => !ids.includes(note.id)));
const updatedAt = new Date();
setDiagramUpdatedAt(updatedAt);
await Promise.all([
...ids.map((id) => db.deleteNote({ diagramId, id })),
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
]);
if (prevNotes.length > 0 && options.updateHistory) {
addUndoAction({
action: 'removeNotes',
redoData: { noteIds: ids },
undoData: { notes: prevNotes },
});
resetRedoStack();
}
},
[db, diagramId, setNotes, notes, addUndoAction, resetRedoStack]
);
const removeNote: ChartDBContext['removeNote'] = useCallback(
async (id: string, options = { updateHistory: true }) => {
return removeNotes([id], options);
},
[removeNotes]
);
const updateNote: ChartDBContext['updateNote'] = useCallback(
async (
id: string,
note: Partial<Note>,
options = { updateHistory: true }
) => {
const prevNote = getNote(id);
setNotes((notes) =>
notes.map((n) => (n.id === id ? { ...n, ...note } : n))
);
const updatedAt = new Date();
setDiagramUpdatedAt(updatedAt);
await Promise.all([
db.updateDiagram({ id: diagramId, attributes: { updatedAt } }),
db.updateNote({ id, attributes: note }),
]);
if (!!prevNote && options.updateHistory) {
addUndoAction({
action: 'updateNote',
redoData: { noteId: id, note },
undoData: { noteId: id, note: prevNote },
});
resetRedoStack();
}
},
[db, diagramId, setNotes, getNote, addUndoAction, resetRedoStack]
);
const highlightCustomTypeId = useCallback(
(id?: string) => setHighlightedCustomTypeId(id),
[setHighlightedCustomTypeId]
@@ -1554,6 +1686,7 @@ export const ChartDBProvider: React.FC<
setDiagramCreatedAt(diagram.createdAt);
setDiagramUpdatedAt(diagram.updatedAt);
setHighlightedCustomTypeId(undefined);
setNotes(diagram.notes ?? []);
events.emit({ action: 'load_diagram', data: { diagram } });
@@ -1574,6 +1707,7 @@ export const ChartDBProvider: React.FC<
setDiagramUpdatedAt,
setHighlightedCustomTypeId,
events,
setNotes,
resetRedoStack,
resetUndoStack,
]
@@ -1597,6 +1731,7 @@ export const ChartDBProvider: React.FC<
includeDependencies: true,
includeAreas: true,
includeCustomTypes: true,
includeNotes: true,
});
if (diagram) {
@@ -1762,6 +1897,7 @@ export const ChartDBProvider: React.FC<
relationships,
dependencies,
areas,
notes,
currentDiagram,
schemas,
events,
@@ -1825,6 +1961,13 @@ export const ChartDBProvider: React.FC<
updateCustomType,
highlightCustomTypeId,
highlightedCustomType,
createNote,
addNote,
addNotes,
getNote,
removeNote,
removeNotes,
updateNote,
}}
>
{children}