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 { Typography } from "@tiptap/extension-typography";
|
||||||
import { TextStyle } from "@tiptap/extension-text-style";
|
import { TextStyle } from "@tiptap/extension-text-style";
|
||||||
import { Color } from "@tiptap/extension-color";
|
import { Color } from "@tiptap/extension-color";
|
||||||
import Table from "@tiptap/extension-table";
|
|
||||||
import SlashCommand from "@/features/editor/extensions/slash-command";
|
import SlashCommand from "@/features/editor/extensions/slash-command";
|
||||||
import { Collaboration } from "@tiptap/extension-collaboration";
|
import { Collaboration } from "@tiptap/extension-collaboration";
|
||||||
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
import { CollaborationCursor } from "@tiptap/extension-collaboration-cursor";
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
|
CustomTable,
|
||||||
TrailingNode,
|
TrailingNode,
|
||||||
TiptapImage,
|
TiptapImage,
|
||||||
Callout,
|
Callout,
|
||||||
@ -160,7 +160,7 @@ export const mainExtensions = [
|
|||||||
return ReactNodeViewRenderer(MentionView);
|
return ReactNodeViewRenderer(MentionView);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Table.configure({
|
CustomTable.configure({
|
||||||
resizable: true,
|
resizable: true,
|
||||||
lastColumnResizable: false,
|
lastColumnResizable: false,
|
||||||
allowTableNodeSelection: true,
|
allowTableNodeSelection: true,
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { Typography } from '@tiptap/extension-typography';
|
|||||||
import { TextStyle } from '@tiptap/extension-text-style';
|
import { TextStyle } from '@tiptap/extension-text-style';
|
||||||
import { Color } from '@tiptap/extension-color';
|
import { Color } from '@tiptap/extension-color';
|
||||||
import { Youtube } from '@tiptap/extension-youtube';
|
import { Youtube } from '@tiptap/extension-youtube';
|
||||||
import Table from '@tiptap/extension-table';
|
|
||||||
import {
|
import {
|
||||||
Callout,
|
Callout,
|
||||||
Comment,
|
Comment,
|
||||||
@ -24,6 +23,7 @@ import {
|
|||||||
TableHeader,
|
TableHeader,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
CustomTable,
|
||||||
TiptapImage,
|
TiptapImage,
|
||||||
TiptapVideo,
|
TiptapVideo,
|
||||||
TrailingNode,
|
TrailingNode,
|
||||||
@ -65,7 +65,7 @@ export const tiptapExtensions = [
|
|||||||
Details,
|
Details,
|
||||||
DetailsContent,
|
DetailsContent,
|
||||||
DetailsSummary,
|
DetailsSummary,
|
||||||
Table,
|
CustomTable,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { TableCell as TiptapTableCell } from "@tiptap/extension-table-cell";
|
|||||||
|
|
||||||
export const TableCell = TiptapTableCell.extend({
|
export const TableCell = TiptapTableCell.extend({
|
||||||
name: "tableCell",
|
name: "tableCell",
|
||||||
content: "paragraph+",
|
content: "(paragraph | heading | bulletList | orderedList | taskList | blockquote | callout | image | video | attachment | mathBlock | details | codeBlock)+",
|
||||||
|
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { TableHeader as TiptapTableHeader } from "@tiptap/extension-table-header
|
|||||||
|
|
||||||
export const TableHeader = TiptapTableHeader.extend({
|
export const TableHeader = TiptapTableHeader.extend({
|
||||||
name: "tableHeader",
|
name: "tableHeader",
|
||||||
content: "paragraph+",
|
content: "(paragraph | heading | bulletList | orderedList | taskList | blockquote | callout | image | video | attachment | mathBlock | details | codeBlock)+",
|
||||||
|
|
||||||
addAttributes() {
|
addAttributes() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export * from "./row";
|
export * from "./row";
|
||||||
export * from "./cell";
|
export * from "./cell";
|
||||||
export * from "./header";
|
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