Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions packages/core/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export const EditorNodeTypes = {
TableHeader: 'tableHeader',
TableCell: 'tableCell',
TableRow: 'tableRow',
ToggleList: 'toggleList',
ToggleItem: 'toggleItem',
};

export type SortDirection = 'asc' | 'desc';
Expand Down
161 changes: 75 additions & 86 deletions packages/ui/src/components/documents/document-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
useEditor,
} from '@tiptap/react';
import { debounce, isEqual } from 'lodash-es';
import { Fragment, useEffect, useMemo, useRef } from 'react';
import { Fragment, useCallback, useEffect, useMemo, useRef } from 'react';
import { toast } from 'sonner';

import {
Expand Down Expand Up @@ -39,6 +39,7 @@ import {
ParagraphCommand,
TableCommand,
TodoCommand,
ToggleListCommand,
DatabaseCommand,
DatabaseInlineCommand,
} from '@colanode/ui/editor/commands';
Expand Down Expand Up @@ -77,6 +78,8 @@ import {
TableCellNode,
TaskItemNode,
TaskListNode,
ToggleItemNode,
ToggleListNode,
TextNode,
TrailingNode,
UnderlineMark,
Expand Down Expand Up @@ -107,84 +110,6 @@ const buildYDoc = (
return ydoc;
};

interface UndoRedoParams {
editor: ReturnType<typeof useEditor>;
ydoc: YDoc;
nodeId: string;
userId: string;
}

const performUndo = async ({
editor,
ydoc,
nodeId,
userId,
}: UndoRedoParams) => {
const beforeContent = ydoc.getObject<RichTextContent>();
const update = ydoc.undo();

if (!update) {
return;
}

const afterContent = ydoc.getObject<RichTextContent>();

if (isEqual(beforeContent, afterContent)) {
return;
}

const editorContent = buildEditorContent(nodeId, afterContent);
editor.chain().setContent(editorContent).run();

const result = await window.colanode.executeMutation({
type: 'document.update',
userId,
documentId: nodeId,
update: encodeState(update),
});

if (!result.success) {
toast.error(result.error.message);
}
};

const performRedo = async ({
editor,
ydoc,
nodeId,
userId,
}: UndoRedoParams) => {
const beforeContent = ydoc.getObject<RichTextContent>();
console.log('beforeContent', beforeContent);
const update = ydoc.redo();
console.log('afterContent', ydoc.getObject<RichTextContent>());
console.log('update', update);

if (!update) {
return;
}

const afterContent = ydoc.getObject<RichTextContent>();

if (isEqual(beforeContent, afterContent)) {
return;
}

const editorContent = buildEditorContent(nodeId, afterContent);
editor.chain().setContent(editorContent).run();

const result = await window.colanode.executeMutation({
type: 'document.update',
userId,
documentId: nodeId,
update: encodeState(update),
});

if (!result.success) {
toast.error(result.error.message);
}
};

export const DocumentEditor = ({
node,
state,
Expand All @@ -197,7 +122,6 @@ export const DocumentEditor = ({
const hasPendingChanges = useRef(false);
const revisionRef = useRef(state?.revision ?? 0);
const ydocRef = useRef<YDoc>(buildYDoc(state, updates));
const editorRef = useRef<ReturnType<typeof useEditor>>(null);

const debouncedSave = useMemo(
() =>
Expand Down Expand Up @@ -283,6 +207,8 @@ export const DocumentEditor = ({
}),
TaskListNode,
TaskItemNode,
ToggleListNode,
ToggleItemNode,
TableNode,
TableRowNode,
TableCellNode,
Expand Down Expand Up @@ -310,6 +236,7 @@ export const DocumentEditor = ({
DatabaseCommand,
DividerCommand,
TodoCommand,
ToggleListCommand,
FileCommand,
FolderCommand,
],
Expand Down Expand Up @@ -339,7 +266,6 @@ export const DocumentEditor = ({
if (!editorRef.current) {
return false;
}

if (event.key === 'z' && event.metaKey && !event.shiftKey) {
event.preventDefault();
performUndo({
Expand All @@ -348,7 +274,6 @@ export const DocumentEditor = ({
nodeId: node.id,
userId: workspace.userId,
});
return true;
}
if (event.key === 'z' && event.metaKey && event.shiftKey) {
event.preventDefault();
Expand All @@ -358,7 +283,6 @@ export const DocumentEditor = ({
nodeId: node.id,
userId: workspace.userId,
});
return true;
}
if (event.key === 'y' && event.metaKey) {
event.preventDefault();
Expand All @@ -368,7 +292,6 @@ export const DocumentEditor = ({
nodeId: node.id,
userId: workspace.userId,
});
return true;
}
},
},
Expand Down Expand Up @@ -430,9 +353,75 @@ export const DocumentEditor = ({
}
}, [state, updates, editor]);

// Keep editorRef updated so handleKeyDown can access the current editor
// Keep editorRef updated so handleKeyDown can access the current editor
editorRef.current = editor;

const undo = useCallback(async () => {
if (!editor) {
return;
}

const beforeContent = ydocRef.current.getObject<RichTextContent>();
const update = ydocRef.current.undo();

if (!update) {
return;
}

const afterContent = ydocRef.current.getObject<RichTextContent>();

if (isEqual(beforeContent, afterContent)) {
return;
}

const editorContent = buildEditorContent(node.id, afterContent);
editor.chain().setContent(editorContent).run();

const result = await window.colanode.executeMutation({
type: 'document.update',
userId: workspace.userId,
documentId: node.id,
update: encodeState(update),
});

if (!result.success) {
toast.error(result.error.message);
}
}, [node.id, editor]);

const redo = useCallback(async () => {
if (!editor) {
return;
}

const beforeContent = ydocRef.current.getObject<RichTextContent>();
const update = ydocRef.current.redo();

if (!update) {
return;
}

const afterContent = ydocRef.current.getObject<RichTextContent>();

if (isEqual(beforeContent, afterContent)) {
return;
}

const editorContent = buildEditorContent(node.id, afterContent);
editor.chain().setContent(editorContent).run();

const result = await window.colanode.executeMutation({
type: 'document.update',
userId: workspace.userId,
documentId: node.id,
update: encodeState(update),
});

if (!result.success) {
toast.error(result.error.message);
}
}, [node.id, editor]);

return (
<>
{editor && canEdit && (
Expand All @@ -444,4 +433,4 @@ export const DocumentEditor = ({
<EditorContent editor={editor} />
</>
);
};
};
4 changes: 3 additions & 1 deletion packages/ui/src/editor/classes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ export const defaultClasses = {
tableCell: 'flex p-1 px-2 items-center',
tableHeaderWrapper: 'border',
tableHeader: 'flex p-1 px-2 items-center font-semibold bg-muted',
};
toggleList: 'list-none pl-0 my-1',
toggleItem: 'my-0.5',
};
4 changes: 3 additions & 1 deletion packages/ui/src/editor/commands/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PageCommand } from '@colanode/ui/editor/commands/page';
import { ParagraphCommand } from '@colanode/ui/editor/commands/paragraph';
import { TableCommand } from '@colanode/ui/editor/commands/table';
import { TodoCommand } from '@colanode/ui/editor/commands/todo';
import { ToggleListCommand } from '@colanode/ui/editor/commands/toggle-list';

export type { EditorCommand, EditorCommandProps };

Expand All @@ -33,6 +34,7 @@ export {
ParagraphCommand,
TableCommand,
TodoCommand,
ToggleListCommand,
DatabaseCommand,
DatabaseInlineCommand,
};
};
29 changes: 29 additions & 0 deletions packages/ui/src/editor/commands/toggle-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ChevronRight } from 'lucide-react';

import { EditorCommand } from '@colanode/client/types';

export const ToggleListCommand: EditorCommand = {
key: 'toggle-list',
name: 'Toggle List',
description: 'Insert a toggle list',
keywords: ['toggle', 'collapsible', 'expand', 'collapse'],
icon: ChevronRight,
disabled: false,
handler: ({ editor, range }) => {
editor
.chain()
.focus()
.deleteRange(range)
.insertContent({
type: 'toggleList',
content: [
{
type: 'toggleItem',
attrs: { expanded: false },
content: [{ type: 'paragraph' }],
},
],
})
.run();
},
};
6 changes: 5 additions & 1 deletion packages/ui/src/editor/extensions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import { TableHeaderNode } from '@colanode/ui/editor/extensions/table-header';
import { TableRowNode } from '@colanode/ui/editor/extensions/table-row';
import { TaskItemNode } from '@colanode/ui/editor/extensions/task-item';
import { TaskListNode } from '@colanode/ui/editor/extensions/task-list';
import { ToggleItemNode } from '@colanode/ui/editor/extensions/toggle-item';
import { ToggleListNode } from '@colanode/ui/editor/extensions/toggle-list';
import { TempFileNode } from '@colanode/ui/editor/extensions/temp-file';
import { TrailingNode } from '@colanode/ui/editor/extensions/trailing-node';

Expand Down Expand Up @@ -82,6 +84,8 @@ export {
TableCellNode,
TaskItemNode,
TaskListNode,
ToggleItemNode,
ToggleListNode,
TextNode,
TrailingNode,
UnderlineMark,
Expand All @@ -91,4 +95,4 @@ export {
HardBreakNode,
ParserExtension,
Markdown,
};
};
6 changes: 5 additions & 1 deletion packages/ui/src/editor/extensions/list-keymap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@ export const ListKeymapExtension = ListKeymap.configure({
itemName: 'taskItem',
wrapperNames: ['taskList'],
},
{
itemName: 'toggleItem',
wrapperNames: ['toggleList'],
},
],
});
});
Loading