33 lines
1.3 KiB
TypeScript
33 lines
1.3 KiB
TypeScript
import { Extension } from '@tiptap/core';
|
|
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
|
|
|
// Keep an empty paragraph at the end of the document when the last block is a
|
|
// table. A table is an isolating block, so if it sits at the very end there is
|
|
// nowhere to click or type below it — the cursor gets trapped inside the table
|
|
// and the user can't continue the document. When the doc ends with a table we
|
|
// append a paragraph; once it's there the last child is a paragraph, so the
|
|
// check no-ops on the next round (no loop).
|
|
const trailingNodeKey = new PluginKey('trailingNode');
|
|
|
|
// Block types that can't sit at the document end without trapping the cursor.
|
|
const NEEDS_TRAILING_AFTER = new Set(['table']);
|
|
|
|
export const TrailingNode = Extension.create({
|
|
name: 'trailingNode',
|
|
|
|
addProseMirrorPlugins() {
|
|
return [
|
|
new Plugin({
|
|
key: trailingNodeKey,
|
|
appendTransaction(_transactions, _oldState, newState) {
|
|
const last = newState.doc.lastChild;
|
|
if (!last || !NEEDS_TRAILING_AFTER.has(last.type.name)) return null;
|
|
const paragraph = newState.schema.nodes.paragraph;
|
|
if (!paragraph) return null;
|
|
return newState.tr.insert(newState.doc.content.size, paragraph.create());
|
|
},
|
|
}),
|
|
];
|
|
},
|
|
});
|