Files
docmost/packages/editor-ext/src/lib/table/table.ts
T
Philip Okugbe 657fdf8cb7 feat: Tiptap V3 migration (#1854)
* Tiptap3 migration - WIP

* fix collaboration

* remove unused code

* fix flicker

* disable duplicate extensions

* update tiptap version

* Switch to useEditorState
- Set shouldRerenderOnTransaction to false

* fix editable state

* add tippyoptions for reference

* merge main

* tiptap 3.6.1

* fix bubble menu

* fix converter

* fix menus

* fix collaboration caret css

* fix: Set `isInitialized` to force immediate react node view rendering

* feat: Migrate tippy.js menus to Floating UI

* feat: Update collaboration connection for HocusPocus v3

* fix: Connect/disconnect websocketProvider

* cleanup

* cleanup

* feat: Improved placeholder and upload handling for images

* feat: Improved placeholder and upload handling for videos

* refactor: Image node and view clean-up

* feat: Improved placeholder and upload handling for attachments

* fix: Video view styles

* fix: Transaction handling on asset upload

* fix: Use imageDimensionsFromStream

* feat: Multiple file upload, improved placeholders, local previews

* fix: Drag & drop, paste upload

* fix: Allow media as attachment

* * add skeleton pulse animation
* add translation strings
* fix attachment view responsiveness

* fix collab connection status display

* Tiptap v3.17.0

* fix suggestion menu exit bug

* fix search shortcut

* fix history editor css

* tiptap 3.17.1

---------

Co-authored-by: Arek Nawo <areknawo@areknawo.com>
2026-01-24 20:41:08 +00:00

82 lines
2.1 KiB
TypeScript

import { Table } from "@tiptap/extension-table";
import { Editor } from "@tiptap/core";
import { DOMOutputSpec } from "@tiptap/pm/model";
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();
},
};
},
renderHTML({ node, HTMLAttributes }) {
// https://github.com/ueberdosis/tiptap/issues/4872#issuecomment-2717554498
const originalRender = this.parent?.({ node, HTMLAttributes });
const wrapper: DOMOutputSpec = [
"div",
{ class: "tableWrapper" },
originalRender,
];
return wrapper;
},
});