From 8ed485ba53e809207a23ca6871aab2eb0806f0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20K=C3=BChn?= Date: Thu, 16 Dec 2021 13:55:32 +0100 Subject: [PATCH] fix: Improve backspace handling (#2284), fix #2281 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve backspace handling * revert codeblock changes * revert codeblock changes * fix tests Co-authored-by: Philipp Kühn --- packages/core/src/extensions/keymap.ts | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/packages/core/src/extensions/keymap.ts b/packages/core/src/extensions/keymap.ts index 72117b10730..51cdd2761ff 100644 --- a/packages/core/src/extensions/keymap.ts +++ b/packages/core/src/extensions/keymap.ts @@ -1,3 +1,6 @@ +import { Plugin, PluginKey, Selection } from 'prosemirror-state' +import { createChainableState } from '../helpers/createChainableState' +import { CommandManager } from '../CommandManager' import { Extension } from '../Extension' export const Keymap = Extension.create({ @@ -6,6 +9,24 @@ export const Keymap = Extension.create({ addKeyboardShortcuts() { const handleBackspace = () => this.editor.commands.first(({ commands }) => [ () => commands.undoInputRule(), + // maybe convert first text block node to default node + () => commands.command(({ tr }) => { + const { selection, doc } = tr + const { empty, $anchor } = selection + const { pos, parent } = $anchor + const isAtStart = Selection.atStart(doc).from === pos + + if ( + !empty + || !isAtStart + || !parent.type.isTextblock + || parent.textContent.length + ) { + return false + } + + return commands.clearNodes() + }), () => commands.deleteSelection(), () => commands.joinBackward(), () => commands.selectNodeBackward(), @@ -33,4 +54,53 @@ export const Keymap = Extension.create({ 'Mod-a': () => this.editor.commands.selectAll(), } }, + + addProseMirrorPlugins() { + return [ + // With this plugin we check if the whole document was selected and deleted. + // In this case we will additionally call `clearNodes()` to convert e.g. a heading + // to a paragraph if necessary. + // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well + // with many other commands. + new Plugin({ + key: new PluginKey('clearDocument'), + appendTransaction: (transactions, oldState, newState) => { + const docChanges = transactions.some(transaction => transaction.docChanged) + && !oldState.doc.eq(newState.doc) + + if (!docChanges) { + return + } + + const { empty, from, to } = oldState.selection + const allFrom = Selection.atStart(oldState.doc).from + const allEnd = Selection.atEnd(oldState.doc).to + const allWasSelected = from === allFrom && to === allEnd + const isEmpty = newState.doc.textBetween(0, newState.doc.content.size, ' ', ' ').length === 0 + + if (empty || !allWasSelected || !isEmpty) { + return + } + + const tr = newState.tr + const state = createChainableState({ + state: newState, + transaction: tr, + }) + const { commands } = new CommandManager({ + editor: this.editor, + state, + }) + + commands.clearNodes() + + if (!tr.steps.length) { + return + } + + return tr + }, + }), + ] + }, })