Tiptap:Headingブロック先頭のdeleteでparagraphにしたい
皆さん Tiptap 使ってますか?
Tiptap には様々な機能がデフォルトで用意されていますが、その中の1つに Heading があります。
用意されているのはありがたいのですが、細かい挙動で気になるところがありました。
それがタイトルです。Heading ブロック先頭の delete で paragraph にしたい。
前の行が空行だと、前の行を削除して Heading ブロック全体を上げる処理になります。
また、Headingが文章の先頭にある場合は何も起きません。
前の行が文字ありの paragraph だと、結合処理になって Heading が消えます。
意図する挙動としては、先頭で delete をすると前の行に関わらず中身のテキストを保持しながら paragraph にして欲しいです。
Notion はこのようになっており、キーボード上で直感的に Heading をオフにできるので使いやすいです。
今回の実装を試せるサイト
実装タイム
Heading を拡張して実装します。
import TiptapHeading from "@tiptap/extension-heading";
const Heading = TiptapHeading.extend({
addKeyboardShortcuts() {
const baseShortcuts =
TiptapHeading.config.addKeyboardShortcuts?.call(this) || {};
return {
...baseShortcuts,
Backspace: () => {
const { selection } = this.editor.state;
const { $from } = selection;
if ($from.node().type.name !== this.name) return false;
// ブロックの先頭で削除か
if (!selection.empty || $from.start() !== $from.pos) return false;
return this.editor.commands.setParagraph();
},
};
},
});
export default Heading;
さらっと解説すると、まずは Backspace が呼ばれた時に Heading 内 && ブロックの先頭にいるかチェック。その後に、setParagraph を呼ぶだけです。
baseShortcuts は Heading 元々のショートカットを継承するようにしています。
他にいい書き方ありそう。
おまけ
他にも、Heading 途中で Enter を押すと、Heading が2つに分かれるという挙動があります。
僕的には、先頭以外で Enter を押すと後続のテキストは paragraph として分割されて欲しいです。
なのでそのコードも追加します。
import TiptapHeading from "@tiptap/extension-heading";
import { splitBlockAs } from "@tiptap/pm/commands";
const Heading = TiptapHeading.extend({
addKeyboardShortcuts() {
const baseShortcuts =
TiptapHeading.config.addKeyboardShortcuts?.call(this) || {};
return {
...baseShortcuts,
Enter: () => {
const { selection } = this.editor.state;
const { $from } = selection;
if ($from.node().type.name !== this.name) return false;
if ($from.start() === $from.pos) return false;
return splitBlockAs(() => {
return {
type: this.editor.schema.nodes.paragraph,
};
})(this.editor.state, this.editor.view.dispatch);
},
};
},
});
export default Heading;
Tiptap 側に良いコマンドがなかったので、ProseMirror の splitBlockAs を採用しました。
引数で分割された後続のノードを指定することができます。
おわりに
意外とこういう細かい挙動の解説なかったので、書いてみました。参考になれば幸いです。
追記
parentを使えば継承を綺麗にかけた
Discussion