Closed12

prosemirror-viewのNodeViewで作成したDOMに対して、nodeにselectionがある時だけ表示を変更したい

Hirotaka MiyagiHirotaka Miyagi

divに対してHTML classを付けたり外したりできれば良い

わからないこと

  • nodeが選択された時・選択を外された時をどのように検知する?
  • HTML classはどのように付与すれば良い?decorationsを介する?
Hirotaka MiyagiHirotaka Miyagi

うーん、 view.state.selection の様子を見ていると、テーブルセル内の文章を選択した場合はTextSelection になり、テーブルセル自体を選択した場合は CellSelection になるみたいだ
tableNodeの NodeSelection にならないと発火しないのかもしれない

Hirotaka MiyagiHirotaka Miyagi

今ある情報

  • constructorでProsemirrorNode, EditorView, getPos関数, decorationsが手に入る
  • nodeに変更が起こるとupdate関数が呼ばれる
  • EditorStateさえあればisInTable()を使ってテーブルを選択しているかは判別できる
  • table nodeの子要素を選択したら setSelection() が呼ばれる
    • しかし選択が外れたことを検知することはできない
Hirotaka MiyagiHirotaka Miyagi

こんな感じで一旦通知することはできた

// 今回のplugin
export const tablePlugin = () => {
  return new Plugin({
    key: new PluginKey("tablePlugin"),
    props: {
      nodeViews: {
        table: (node, _, _, _, innerDecorations) => {
          return new TableNodeView(node, innerDecorations);
        },
      },
      decorations: (state) => {
        const decorations: Decoration[] = [];

        // selectionがtable node内にあるか判定する
        if (isInTable(state)) {
          const resolved = state.doc.resolve(state.selection.from);
          // attrsは空で、specでtableを編集中ということを伝える
          const decoration = Decoration.node(resolved.before(), resolved.after(), {}, { "tableEditing": true });
          decorations.push(decoration);
        }
        return DecorationSet.create(state.doc, decorations);
      },
    },
  });
};


// NodeViewに渡すclass
export class TableNodeView implements NodeView {
  readonly node: ProsemirrorNode;
  readonly dom: HTMLDivElement;
  readonly contentDOM: HTMLElement;
  readonly table: HTMLTableElement;

  constructor(
    node: ProsemirrorNode,
    innerDecorations: DecorationSet,
  ) {
    this.node = node;
    this.dom = document.createElement("div");
    this.dom.className = "tableWrapper";
    this.table = this.dom.appendChild(document.createElement("table"));
    this.contentDOM = this.table.appendChild(document.createElement("tbody"));
    
    // specにtableEditingがあるdecorationがあるかを探す
    const decorations = innerDecorations.find(undefined, undefined, (deco) => deco["tableEditing"] != null);
    if (decorations.length !== 0) {
      this.dom.classList.add("tableEditing");
    }
  }

  update(node: ProsemirrorNode) {
    return node.type.name === this.node.type.name;
  }
}
Hirotaka MiyagiHirotaka Miyagi

しかしこのままだと以下の問題がある

Hirotaka MiyagiHirotaka Miyagi

結論としては別のアプローチを取った方が良さそうなので一旦Close.
NodeViewで追加したDOMはProseMirrorのstateやmodelの管理下にないので扱いづらい。NodeViewでDOMを追加するのではなくDecoration.widgetなどで追加した方が都合が良さそう

このスクラップは2022/01/05にクローズされました