How to prevent block splitting in custom nodes? #5785
-
I'm working on implementing custom block nodes, and I'm encountering an issue with node splitting. Specifically, I want to prevent certain block nodes from being split when the user presses Enter at the middle of the node, but I also need to maintain the ability to exit the node at the end or remove it with backspace. Important note: I specifically want to avoid using Example of the issue: Screen.Recording.2024-10-30.at.10.38.01.movCurrently, I have a custom node defined like this: import { mergeAttributes, Node } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import NoActionGroupComponent from './GroupComponent.vue';
export default Node.create({
name: 'group',
group: 'block',
content: 'block*',
defining: true,
addAttributes() {
return {
name: {
default: '',
},
};
},
parseHTML() {
return [
{
tag: 'group',
},
];
},
renderHTML({ HTMLAttributes }) {
return ['group', mergeAttributes(HTMLAttributes), 0];
},
addNodeView() {
return VueNodeViewRenderer(NoActionGroupComponent);
},
}); I've considered using Has anyone successfully implemented this kind of behavior for custom nodes without using |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
To anyone having the same senario, here is the solution: import { mergeAttributes, Node } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import { TextSelection } from '@tiptap/pm/state';
import NoActionGroupComponent from './GroupComponent.vue';
export default Node.create({
name: 'group',
group: 'block',
content: 'block*',
defining: true,
addAttributes() {
return {
name: { default: '' },
};
},
parseHTML() {
return [{ tag: 'group' }];
},
renderHTML({ HTMLAttributes }) {
return ['group', mergeAttributes(HTMLAttributes), 0];
},
addKeyboardShortcuts() {
return {
Enter: ({ editor }) => {
const { state, view } = editor;
const { selection } = state;
const { $anchor } = selection;
const currentDepth = $anchor.depth - 1;
const parentNode = $anchor.node(currentDepth);
// Check if in a group level
if (parentNode.type.name !== 'group') return false;
const endPos = $anchor.end(currentDepth);
const isAnchorAtEndOfGroup = $anchor.pos === endPos - 1;
if (isAnchorAtEndOfGroup) return false;
const isAnchorAtEndOfLine = selection.$from.parentOffset === selection.$from.parent.nodeSize - 2;
if (!isAnchorAtEndOfLine) return false;
// Insert a new paragraph node
const paragraphNode = state.schema.nodes.paragraph.create();
const transaction = state.tr.insert($anchor.pos, paragraphNode);
// Set new selection
const newSelection = TextSelection.create(transaction.doc, $anchor.pos + 1);
transaction.setSelection(newSelection);
view.dispatch(transaction);
view.focus();
return true;
},
};
},
addNodeView() {
return VueNodeViewRenderer(NoActionGroupComponent);
},
}); |
Beta Was this translation helpful? Give feedback.
To anyone having the same senario, here is the solution: