Closed14

TiptapのisMarkActiveの動作を見る

かりんとうかりんとう

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

かりんとうかりんとう

マークのアクティブ判定、単一ノード内限定だとこんな感じになる?

(これ色々ダメ)

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);
}

かりんとうかりんとう

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にクローズされました