diff --git a/apps/client/src/features/editor/extensions/extensions.ts b/apps/client/src/features/editor/extensions/extensions.ts index 4558151d..922bcd59 100644 --- a/apps/client/src/features/editor/extensions/extensions.ts +++ b/apps/client/src/features/editor/extensions/extensions.ts @@ -10,7 +10,6 @@ import { Highlight } from "@tiptap/extension-highlight"; import { Typography } from "@tiptap/extension-typography"; import { TextStyle } from "@tiptap/extension-text-style"; import { Color } from "@tiptap/extension-color"; -import Table from "@tiptap/extension-table"; import SlashCommand from "@/features/editor/extensions/slash-command"; import { Collaboration } from "@tiptap/extension-collaboration"; import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor"; @@ -25,6 +24,7 @@ import { TableCell, TableRow, TableHeader, + CustomTable, TrailingNode, TiptapImage, Callout, @@ -160,7 +160,7 @@ export const mainExtensions = [ return ReactNodeViewRenderer(MentionView); }, }), - Table.configure({ + CustomTable.configure({ resizable: true, lastColumnResizable: false, allowTableNodeSelection: true, diff --git a/apps/server/src/collaboration/collaboration.util.ts b/apps/server/src/collaboration/collaboration.util.ts index a766ec91..099d615e 100644 --- a/apps/server/src/collaboration/collaboration.util.ts +++ b/apps/server/src/collaboration/collaboration.util.ts @@ -10,7 +10,6 @@ import { Typography } from '@tiptap/extension-typography'; import { TextStyle } from '@tiptap/extension-text-style'; import { Color } from '@tiptap/extension-color'; import { Youtube } from '@tiptap/extension-youtube'; -import Table from '@tiptap/extension-table'; import { Callout, Comment, @@ -24,6 +23,7 @@ import { TableHeader, TableCell, TableRow, + CustomTable, TiptapImage, TiptapVideo, TrailingNode, @@ -65,7 +65,7 @@ export const tiptapExtensions = [ Details, DetailsContent, DetailsSummary, - Table, + CustomTable, TableCell, TableRow, TableHeader, diff --git a/packages/editor-ext/src/lib/table/cell.ts b/packages/editor-ext/src/lib/table/cell.ts index 0714d69a..25a311b9 100644 --- a/packages/editor-ext/src/lib/table/cell.ts +++ b/packages/editor-ext/src/lib/table/cell.ts @@ -2,7 +2,7 @@ import { TableCell as TiptapTableCell } from "@tiptap/extension-table-cell"; export const TableCell = TiptapTableCell.extend({ name: "tableCell", - content: "paragraph+", + content: "(paragraph | heading | bulletList | orderedList | taskList | blockquote | callout | image | video | attachment | mathBlock | details | codeBlock)+", addAttributes() { return { diff --git a/packages/editor-ext/src/lib/table/header.ts b/packages/editor-ext/src/lib/table/header.ts index 46b1efaf..399a8cf0 100644 --- a/packages/editor-ext/src/lib/table/header.ts +++ b/packages/editor-ext/src/lib/table/header.ts @@ -2,7 +2,7 @@ import { TableHeader as TiptapTableHeader } from "@tiptap/extension-table-header export const TableHeader = TiptapTableHeader.extend({ name: "tableHeader", - content: "paragraph+", + content: "(paragraph | heading | bulletList | orderedList | taskList | blockquote | callout | image | video | attachment | mathBlock | details | codeBlock)+", addAttributes() { return { diff --git a/packages/editor-ext/src/lib/table/index.ts b/packages/editor-ext/src/lib/table/index.ts index 656c1825..fd1d8019 100644 --- a/packages/editor-ext/src/lib/table/index.ts +++ b/packages/editor-ext/src/lib/table/index.ts @@ -1,3 +1,4 @@ export * from "./row"; export * from "./cell"; export * from "./header"; +export * from "./table"; diff --git a/packages/editor-ext/src/lib/table/table.ts b/packages/editor-ext/src/lib/table/table.ts new file mode 100644 index 00000000..549847b8 --- /dev/null +++ b/packages/editor-ext/src/lib/table/table.ts @@ -0,0 +1,65 @@ +import Table from "@tiptap/extension-table"; +import { Editor } from "@tiptap/core"; + +const LIST_TYPES = ["bulletList", "orderedList", "taskList"]; + +function isInList(editor: Editor): boolean { + const { $from } = editor.state.selection; + + for (let depth = $from.depth; depth > 0; depth--) { + const node = $from.node(depth); + if (LIST_TYPES.includes(node.type.name)) { + return true; + } + } + + return false; +} + +function handleListIndent(editor: Editor): boolean { + return editor.commands.sinkListItem("listItem") || + editor.commands.sinkListItem("taskItem"); +} + +function handleListOutdent(editor: Editor): boolean { + return editor.commands.liftListItem("listItem") || + editor.commands.liftListItem("taskItem"); +} + +export const CustomTable = Table.extend({ + addKeyboardShortcuts() { + return { + ...this.parent?.(), + Tab: () => { + // If we're in a list within a table, handle list indentation + if (isInList(this.editor) && this.editor.isActive("table")) { + if (handleListIndent(this.editor)) { + return true; + } + } + + // Otherwise, use default table navigation + if (this.editor.commands.goToNextCell()) { + return true; + } + + if (!this.editor.can().addRowAfter()) { + return false; + } + + return this.editor.chain().addRowAfter().goToNextCell().run(); + }, + "Shift-Tab": () => { + // If we're in a list within a table, handle list outdentation + if (isInList(this.editor) && this.editor.isActive("table")) { + if (handleListOutdent(this.editor)) { + return true; + } + } + + // Otherwise, use default table navigation + return this.editor.commands.goToPreviousCell(); + }, + }; + }, +}); \ No newline at end of file