mirror of
https://github.com/docmost/docmost.git
synced 2025-11-13 08:32:38 +10:00
feat: enhance table cells with rich content support (#1409)
- Support multiple content types in table cells and headers: paragraphs, headings, lists (bullet/ordered/task), blockquotes, callouts, images, videos, attachments, math blocks, toggles, and code blocks - Add custom table extension with smart Tab key handling for list indentation within tables - Preserve default table navigation when not in lists
This commit is contained in:
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export * from "./row";
|
||||
export * from "./cell";
|
||||
export * from "./header";
|
||||
export * from "./table";
|
||||
|
||||
65
packages/editor-ext/src/lib/table/table.ts
Normal file
65
packages/editor-ext/src/lib/table/table.ts
Normal file
@ -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();
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user