Files
tiptap/packages/core/src/extensions/keymap.ts

111 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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({
name: 'keymap',
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(),
])
const handleDelete = () => this.editor.commands.first(({ commands }) => [
() => commands.deleteSelection(),
() => commands.joinForward(),
() => commands.selectNodeForward(),
])
return {
Enter: () => this.editor.commands.first(({ commands }) => [
() => commands.newlineInCode(),
() => commands.createParagraphNear(),
() => commands.liftEmptyBlock(),
() => commands.splitBlock(),
]),
'Mod-Enter': () => this.editor.commands.exitCode(),
Backspace: handleBackspace,
'Mod-Backspace': handleBackspace,
'Shift-Backspace': handleBackspace,
Delete: handleDelete,
'Mod-Delete': handleDelete,
'Mod-a': () => this.editor.commands.selectAll(),
'Ctrl-a': () => this.editor.commands.selectTextblockStart(),
'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),
Home: () => this.editor.commands.selectTextblockStart(),
End: () => this.editor.commands.selectTextblockStart(),
}
},
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 doesnt 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
},
}),
]
},
})