• prosemirror 学习记录(四)decoration


    使用 decorations

    使用 props - decorations() 添加 decorations

    写一个简单的插件:高亮所有 apple 节点

    export const MyHighlightApplesPlugin = new Plugin({
      props: {
      	// view 每次变化都会执行 decorations 方法
        decorations(state) {
          let arrs = [];
          state.doc.descendants((node, pos) => {
            if (node.type.name === "apple") {
              const deco = Decoration.inline(pos, pos + 1, { class: "highlight" });
              arrs.push(deco);
            }
          });
          return DecorationSet.create(state.doc, arrs);	// 返回 DecorationSet
        },
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    css

    span[custom-node-type="apple"].highlight::before {
      background: yellow;
      color: red;
    }
    
    • 1
    • 2
    • 3
    • 4

    效果:

    在这里插入图片描述

    用 Plugin - state 存储数据

    Plugin 中的 state 字段可以存储数据

    • init: (config, state) => T
    • apply: (tr, value:T, oldState, newState) => T

    plugin.getState(state) 可以获取这个数据

    将 DecorationSet 存在 state 中,改写上面的插件:

    export const MyHighlightApplesPlugin = new Plugin({
      state: {
        init(_, state) {
          return createAppleDecos(state.doc);
        },
        apply(tr) {
          return createAppleDecos(tr.doc);
        },
      },
      props: {
        decorations(state) {
          const set = MyHighlightApplesPlugin.getState(state);
          return set;
        },
      },
    });
    
    function createAppleDecos(doc) {
      let arrs = [];
      doc.descendants((node, pos) => {
        if (node.type.name === "apple") {
          const deco = Decoration.inline(pos, pos + 1, { class: "highlight" });
          arrs.push(deco);
        }
      });
      return DecorationSet.create(doc, arrs); // 返回 DecorationSet
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    setMeta

    给高亮效果添加开关,点击按钮随时切换是否高亮

    plugin 中存储 enable 字段(以前只存储 set,现在是 {enable,set}

    export const MyHighlightApplesPlugin = new Plugin({
      state: {
        init(_, state) {
          return {
            enable: true,
            set: createAppleDecos(state.doc),
          };
        },
        apply(tr, value) {
          const { enable } = value;
          let set;
          if (enable) {
            set = createAppleDecos(tr.doc);
          } else {
            set = DecorationSet.empty;
          }
          return { enable, set };
        },
      },
      props: {
        decorations(state) {
          const { set } = MyHighlightApplesPlugin.getState(state);
          return set;
        },
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    enable 字段添加上了,接下来要在点击按钮时修改它的值

    
    function toggleHighlightApples() {
      // 通过 getState 获取数据
      const state = MyHighlightApplesPlugin.getState(editorView.value.state);
      // 直接修改!
      state.enable = !state.enable;
      // 然后 dispatch 一个 tr
      const tr = editorView.value.state.tr;
      editorView.value.dispatch(tr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面的代码在插件外部直接修改 state.enable ,能实现效果,但是不好。
    正确的做法是发一个信号,让 plugin 内部自己修改。

    tr.setMeta 发信号:

    function toggleHighlightApples() {
      const state = MyHighlightApplesPlugin.getState(editorView.value.state);
      const tr = editorView.value.state.tr;
      tr.setMeta(MyHighlightApplesPlugin, !state.enable);
      editorView.value.dispatch(tr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    applygetMeta 接收信号

        apply(tr, value) {
          let { enable } = value;
          const meta = tr.getMeta(MyHighlightApplesPlugin);
          if (meta !== undefined) {
            enable = meta;
          }
          let set;
          if (enable) {
            set = createAppleDecos(tr.doc);
          } else {
            set = DecorationSet.empty;
          }
          return { enable, set };
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    效果

    在这里插入图片描述

    set.map(tr.mapping, tr.doc) 的作用

    官网示例 image placeholder example 中用到 set.map(tr.mapping, tr.doc) ,作用看不懂,写代码测试一下

    写一个简单的插件:给粗体文字自动添加红色背景

    export const TestPlugin = new Plugin({
      state: {
        init(_, state) {
          return addRedBg(state.doc);
        },
        apply(tr, set) {
          return set;
        },
      },
      props: {
        decorations(state) {
          const set = TestPlugin.getState(state);
          return set;
        },
      },
    });
    
    function addRedBg(doc) {
      let arrs = [];
      doc.descendants((node, pos) => {
        if (node.marks.find((mark) => mark.type.name === "strong")) {
          const deco = Decoration.inline(pos, pos + node.nodeSize, { style: "background:red" });
          arrs.push(deco);
        }
      });
      return DecorationSet.create(doc, arrs);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    文档初始内容为:

    <p>你好,今天天气不错。<strong>中午strong>吃什么p>
    
    • 1

    效果:

    在这里插入图片描述

    因为只在 init 中设置 deco,在 apply 中什么都没做。所以插件不会响应后续视图变化

    新增 bold 文本,不会变红:

    在这里插入图片描述

    移除 bold,不会消失:

    在这里插入图片描述

    修改文档内容,致使bold文字位置变动,红色不跟着挪位置:

    在这里插入图片描述
    这些都是意料之中的效果。现在在 apply 中添加上这一句:

        apply(tr, set) {
          set = set.map(tr.mapping, tr.doc);	// 新增
          return set;
        },
    
    • 1
    • 2
    • 3
    • 4

    新增 bold 文本、移除 bold 的效果不变,不放图了。

    修改文档内容,致使bold文字位置变动,效果和之前不同了:

    在这里插入图片描述

    transform Guide 中讲到了 Mapping 的用法

    let tr = new Transform(myDoc)
    tr.split(10)    // split a node, +2 tokens at 10
    tr.delete(2, 5) // -3 tokens at 2
    console.log(tr.mapping.map(15)) // → 14
    console.log(tr.mapping.map(6))  // → 3
    console.log(tr.mapping.map(10)) // → 9
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    结合 DecorationSet.map 的说明:
    在这里插入图片描述
    set.map(tr.mapping, tr.doc) 的含义就明白了 —— 所有 deco 都应用 tr.mapping 找到新位置。

    但是对于新增的 bold 文本(需要新增 deco),或者已经有的 bold 文本去掉 bold 效果(需要移除 deco),set.map(tr.mapping, tr.doc) 就无能为力了(剪切、复制、拖拽等同样处理不了)

  • 相关阅读:
    分库分表真的适合你的系统吗?聊聊分库分表和NewSQL如何选择
    leetcode 43.字符串相乘
    CrossOver软件2023官方破解版本下载
    企业云成本管控,你真的做对了吗?
    【Effective Python】读书笔记-06元类与属性
    渗透测试学习day2
    docker push image harbor http 镜像
    XML解析是一种常见的任务,它允许我们从XML文档中提取数据并进行处理
    RStudion | 基础使用教程(初学者详细) | 基本设置 | 快捷操作 | 脚本运行 | 画图
    【附源码】计算机毕业设计JAVA车辆调度管理系统
  • 原文地址:https://blog.csdn.net/tangran0526/article/details/134036296