Closed14
TiptapのisMarkActiveの動作を見る

storedmarksは次の入力につけられるマークらしい。何か入力するとなくなる。
2文字目以降にマークがついているのは、そのマーク内の範囲で入力しているから

toggleMarkがデフォルトであるの嬉しいね。Nodeにもくれ

マークのアクティブ判定、単一ノード内限定だとこんな感じになる?
(これ色々ダメ)
import { MarkType, Node } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
export default function isMarkActive(
state: EditorState,
markType: MarkType
): boolean {
const { empty } = state.selection;
if (empty) {
const storedMarks = state.storedMarks ?? [];
return !!markType.isInSet([
...storedMarks,
...state.selection.$from.marks(),
]);
}
const nodes: Node[] = [];
const { from, to } = state.selection;
state.doc.nodesBetween(from, to, (node) => {
if (node.isText || !node.marks.length) return;
nodes.push(node);
});
// TODO: 複数ノードに跨る場合
if (nodes.length !== 1) return false;
const node = nodes[0];
return !!markType.isInSet(node.marks);
}

Tiptapのを参考

FireFoxはテキストの複数選択が可能?えぇ。。
Chromeでも拡張使えばできるっぽいのか?Tiptapでは対応してそう

あ〜storedMarksを強制OFFにした場合を考えてなかった。
- 何もしていない => null
- ONに => [Mark]
- OFFに => []
空配列とnullに違いがある。

tiptapみたいに, storedMarkが存在しなかった場合のみ$from.nodes()
を見るようにしないと

if (empty) {
return !!markType.isInSet(
state.storedMarks ?? state.selection.$from.marks()
);
}

TiptapのrelativeFromって何かと思ったけど、選択範囲の最初と最後のノードは途中までの選択の可能性があるから、それを考慮してるってことか。
const relativeFrom = Math.max(from, pos)
const relativeTo = Math.min(to, pos + node.nodeSize)

MEMO: マークが異なるテキストは、異なるテキストノードになる

テキストノードだけ対応した範囲選択はこんな感じか?
const { from, to } = state.selection;
const range = to - from;
let markRange = 0;
// テキスト以外のノードにも対応
state.doc.nodesBetween(from, to, (node, pos) => {
if (!node.isText) return;
const relativeFrom = Math.max(from, pos);
const relativeTo = Math.min(to, pos + node.nodeSize);
if (markType.isInSet(node.marks)) markRange += relativeTo - relativeFrom;
});
return markRange === range;

これだとブロックノードを跨ぐとダメや。
rangeをテキストのサイズにしよう。

テキストノードのみ対応
import { MarkType } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
export default function isMarkActive(
state: EditorState,
markType: MarkType
): boolean {
const { empty } = state.selection;
if (empty) {
return !!markType.isInSet(
state.storedMarks ?? state.selection.$from.marks()
);
}
const { from, to } = state.selection;
let selectionRange = 0;
let markRange = 0;
// テキスト以外のノードにも対応
state.doc.nodesBetween(from, to, (node, pos) => {
if (!node.isText) return;
const relativeFrom = Math.max(from, pos);
const relativeTo = Math.min(to, pos + node.nodeSize);
selectionRange += relativeTo - relativeFrom;
if (markType.isInSet(node.marks)) markRange += relativeTo - relativeFrom;
});
return markRange === selectionRange;
}

他にも対応しないといけないことあるけど、必要になった時に考える。
このスクラップは2025/01/07にクローズされました