mirror of
https://github.com/docmost/docmost.git
synced 2026-08-31 02:46:27 +08:00
feat(editor-ext): block typing/paste from replacing a selected base embed
Add a ProseMirror plugin with handleTextInput and handlePaste that return true (handled, no-op) when the current selection is a NodeSelection of the base embed. Pairs with the existing Backspace/ Delete keyboard guard — between them the two accidental-deletion paths (focus + delete key, focus + type a character) are blocked. Other deletion routes still work: range selections covering the node, programmatic deletes, and so on. Pressing an arrow key deselects the node so the user can type elsewhere.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Node, mergeAttributes } from '@tiptap/core';
|
import { Node, mergeAttributes } from '@tiptap/core';
|
||||||
import { NodeSelection } from '@tiptap/pm/state';
|
import { EditorState, NodeSelection, Plugin } from '@tiptap/pm/state';
|
||||||
|
|
||||||
export interface BaseEmbedOptions {
|
export interface BaseEmbedOptions {
|
||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
@@ -62,11 +62,11 @@ export const BaseEmbed = Node.create<BaseEmbedOptions>({
|
|||||||
|
|
||||||
addKeyboardShortcuts() {
|
addKeyboardShortcuts() {
|
||||||
// Block Backspace / Delete when the base embed itself is the
|
// Block Backspace / Delete when the base embed itself is the
|
||||||
// current selection — that's the "click on the embed and hit
|
// current selection — the "click on the embed and hit delete"
|
||||||
// delete" accidental-delete path. Returning true tells TipTap
|
// accidental-delete path. Returning true tells TipTap we've
|
||||||
// we've handled the key, preventing the default removal.
|
// handled the key, preventing the default removal. Range
|
||||||
// Other deletion paths (range selections covering the node,
|
// selections covering the node and programmatic deletes still
|
||||||
// programmatic transactions) still go through.
|
// work normally.
|
||||||
const isThisNodeSelected = (): boolean => {
|
const isThisNodeSelected = (): boolean => {
|
||||||
const { selection } = this.editor.state;
|
const { selection } = this.editor.state;
|
||||||
return (
|
return (
|
||||||
@@ -79,4 +79,28 @@ export const BaseEmbed = Node.create<BaseEmbedOptions>({
|
|||||||
Delete: () => isThisNodeSelected(),
|
Delete: () => isThisNodeSelected(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addProseMirrorPlugins() {
|
||||||
|
// Same idea as the Backspace/Delete shortcuts above, but for the
|
||||||
|
// other accidental-delete path: when the embed is the selection,
|
||||||
|
// a typed character or paste would replace the whole node. These
|
||||||
|
// hooks return true (handled, no-op) so the node stays put. The
|
||||||
|
// user can still press an arrow key to deselect and then type.
|
||||||
|
const nodeName = this.name;
|
||||||
|
const isThisNodeSelected = (state: EditorState): boolean => {
|
||||||
|
const { selection } = state;
|
||||||
|
return (
|
||||||
|
selection instanceof NodeSelection &&
|
||||||
|
selection.node.type.name === nodeName
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return [
|
||||||
|
new Plugin({
|
||||||
|
props: {
|
||||||
|
handleTextInput: (view) => isThisNodeSelected(view.state),
|
||||||
|
handlePaste: (view) => isThisNodeSelected(view.state),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user