mirror of https://github.com/fantasticit/think.git
tiptap: ensure only one title node, do not create paragraph if next sibling is exists
This commit is contained in:
parent
1b99a0876b
commit
a7e787d758
|
@ -2,7 +2,7 @@ import { mergeAttributes, Node } from '@tiptap/core';
|
|||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view';
|
||||
import { getDatasetAttribute, isInTitle, nodeAttrsToDataset } from 'tiptap/prose-utils';
|
||||
import { getDatasetAttribute, getNodeAtPos, isInTitle, nodeAttrsToDataset } from 'tiptap/prose-utils';
|
||||
|
||||
import { TitleWrapper } from '../wrappers/title';
|
||||
|
||||
|
@ -78,15 +78,29 @@ export const Title = Node.create<TitleOptions>({
|
|||
addProseMirrorPlugins() {
|
||||
const { editor } = this;
|
||||
let shouldSelectTitleNode = true;
|
||||
let editorView;
|
||||
|
||||
const closeSelectTitleNode = () => {
|
||||
shouldSelectTitleNode = false;
|
||||
return;
|
||||
};
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
key: TitlePluginKey,
|
||||
view: (view) => {
|
||||
editorView = view;
|
||||
|
||||
return {
|
||||
update(view) {
|
||||
editorView = view;
|
||||
},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
decorations: (state) => {
|
||||
const { doc } = state;
|
||||
const decorations = [];
|
||||
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.type.name !== this.name) return;
|
||||
|
||||
|
@ -96,34 +110,22 @@ export const Title = Node.create<TitleOptions>({
|
|||
})
|
||||
);
|
||||
});
|
||||
|
||||
return DecorationSet.create(doc, decorations);
|
||||
},
|
||||
handleClick() {
|
||||
shouldSelectTitleNode = false;
|
||||
closeSelectTitleNode();
|
||||
return;
|
||||
},
|
||||
handleDOMEvents: {
|
||||
click() {
|
||||
shouldSelectTitleNode = false;
|
||||
return;
|
||||
},
|
||||
mousedown() {
|
||||
shouldSelectTitleNode = false;
|
||||
return;
|
||||
},
|
||||
pointerdown() {
|
||||
shouldSelectTitleNode = false;
|
||||
return;
|
||||
},
|
||||
touchstart() {
|
||||
shouldSelectTitleNode = false;
|
||||
return;
|
||||
},
|
||||
click: closeSelectTitleNode,
|
||||
mousedown: closeSelectTitleNode,
|
||||
pointerdown: closeSelectTitleNode,
|
||||
touchstart: closeSelectTitleNode,
|
||||
},
|
||||
handleKeyDown(view, evt) {
|
||||
const { state, dispatch } = view;
|
||||
shouldSelectTitleNode = false;
|
||||
|
||||
closeSelectTitleNode();
|
||||
|
||||
if (isInTitle(view.state) && evt.code === 'Enter') {
|
||||
evt.preventDefault();
|
||||
|
@ -136,10 +138,13 @@ export const Title = Node.create<TitleOptions>({
|
|||
|
||||
const $head = state.selection.$head;
|
||||
const titleNode = $head.node($head.depth);
|
||||
|
||||
const endPos = ((titleNode.firstChild && titleNode.firstChild.nodeSize) || 0) + 1;
|
||||
|
||||
dispatch(state.tr.insert(endPos, paragraph.create()));
|
||||
const nextNode = getNodeAtPos(state, endPos + 2);
|
||||
|
||||
if (!nextNode) {
|
||||
dispatch(state.tr.insert(endPos, paragraph.create()));
|
||||
}
|
||||
|
||||
const newState = view.state;
|
||||
const next = new TextSelection(newState.doc.resolve(endPos + 2));
|
||||
|
@ -152,20 +157,45 @@ export const Title = Node.create<TitleOptions>({
|
|||
appendTransaction: (transactions, oldState, newState) => {
|
||||
if (!editor.isEditable) return;
|
||||
|
||||
if (!shouldSelectTitleNode) return;
|
||||
|
||||
const tr = newState.tr;
|
||||
let shouldReturnTr = false;
|
||||
|
||||
const firstNode = newState?.doc?.content?.content?.[0];
|
||||
|
||||
if (firstNode && firstNode.type.name === this.name && firstNode.nodeSize === 2) {
|
||||
const selection = new TextSelection(newState.tr.doc.resolve(firstNode?.attrs?.cover ? 1 : 0));
|
||||
tr.setSelection(selection).scrollIntoView();
|
||||
tr.setMeta('addToHistory', false);
|
||||
return tr;
|
||||
if (shouldSelectTitleNode) {
|
||||
const firstNode = newState?.doc?.content?.content?.[0];
|
||||
if (firstNode && firstNode.type.name === this.name && firstNode.nodeSize === 2) {
|
||||
const selection = new TextSelection(newState.tr.doc.resolve(firstNode?.attrs?.cover ? 1 : 0));
|
||||
tr.setSelection(selection).scrollIntoView();
|
||||
tr.setMeta('addToHistory', false);
|
||||
shouldReturnTr = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
const newTitleNodes = (newState.tr.doc.content.content || []).filter((item) => item.type.name === this.name);
|
||||
|
||||
if (newTitleNodes.length > 1) {
|
||||
const oldTitleNode = (oldState.tr.doc.content.content || []).filter((item) => item.type.name === this.name);
|
||||
|
||||
const otherNewNodes = (newState.tr.doc.content.content || []).filter(
|
||||
(item) => item.type.name !== this.name
|
||||
);
|
||||
|
||||
const fixedDoc = {
|
||||
...newState.tr.doc.toJSON(),
|
||||
content: [].concat(
|
||||
((oldTitleNode && oldTitleNode[0]) || newTitleNodes[0]).toJSON(),
|
||||
otherNewNodes.map((node) => node.toJSON())
|
||||
),
|
||||
};
|
||||
|
||||
tr.replaceWith(0, newState.doc.content.size, newState.schema.nodeFromJSON(fixedDoc));
|
||||
|
||||
if (tr.docChanged) {
|
||||
shouldReturnTr = true;
|
||||
tr.setMeta('addToHistory', false);
|
||||
}
|
||||
}
|
||||
|
||||
return shouldReturnTr ? tr : undefined;
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue