当前位置: 首页 > news >正文

自定义tiptap插件

本文为开发开源项目的真实开发经历,感兴趣的可以来给我的项目点个star,谢谢啦~

具体博文介绍:
开源|Documind协同文档(接入deepseek-r1、支持实时聊天)Documind 🚀 一个支持实时聊天和接入 - 掘金

我干了什么

我定义了两个插件:

  • font-size:支持通过setFontSize设置tiptap编辑器字体大小,通过unsetFontSize重置为默认大小。
  • line-height:支持通过设置setLineHeight设置tiptap编辑器行高,通过unsetLineHeight重置为默认行高。

源码参考

这里就不一点一点讲解了,注释看不懂的话可以叫AI帮你解析。

font-size插件:
import { Extension } from "@tiptap/core";
import "@tiptap/extension-text-style";

// 声明类型
declare module "@tiptap/core" {
  interface Commands<ReturnType> {
    fontSize: {
      /** 设置字体大小(支持CSS单位如12px/1.2rem等) */
      setFontSize: (fontSize: string) => ReturnType;
      /** 清除字体大小设置 */
      unsetFontSize: () => ReturnType;
    };
  }
}

// 创建扩展
export const FontSizeExtension = Extension.create({
  name: "fontSize",

  // 扩展配置项
  addOptions() {
    return {
      types: ["textStyle"], // 作用对象为文本样式标记
    };
  },

  // 注册全局属性
  addGlobalAttributes() {
    return [
      {
        types: this.options.types, // 应用范围(textStyle类型)
        attributes: {
          fontSize: {
            default: null, // 默认无字体大小
            // 从DOM解析字体大小(读取style属性)
            parseHTML: (element) => element.style.fontSize,
            // 渲染到DOM时生成样式
            renderHTML: (attributes) => {
              if (!attributes.fontSize) {
                return {}; // 无设置时返回空对象
              }
              return {
                style: `font-size: ${attributes.fontSize};`, // 生成内联样式
              };
            },
          },
        },
      },
    ];
  },

  // 注册编辑器命令
  addCommands() {
    return {
      /** 设置字体大小命令 */
      setFontSize:
        (fontSize: string) =>
        ({ chain }) => {
          return chain()
            .setMark("textStyle", { fontSize }) // 更新文本样式标记
            .run();
        },
      /** 清除字体大小命令 */
      unsetFontSize:
        () =>
        ({ chain }) => {
          return chain()
            .setMark("textStyle", { fontSize: null }) // 清除字体大小属性
            .removeEmptyTextStyle() // 移除空文本样式标记
            .run();
        },
    };
  },
});

line-height插件:
import { Extension } from "@tiptap/core";

// 类型声明:扩展Tiptap的命令接口
declare module "@tiptap/core" {
  interface Commands<ReturnType> {
    lineHeight: {
      /** 设置行高(支持CSS单位如1.5/2/24px等) */
      setLineHeight: (lineHeight: string) => ReturnType;
      /** 重置为默认行高 */
      unsetLineHeight: () => ReturnType;
    };
  }
}

export const LineHeightExtension = Extension.create({
  name: "lineHeight",

  // 扩展配置项
  addOptions() {
    return {
      types: ["paragraph", "heading"], // 应用行高样式的节点类型
      defaultLineHeight: null, // 默认行高(null表示不设置)
    };
  },

  // 添加全局属性处理
  addGlobalAttributes() {
    return [
      {
        types: this.options.types, // 应用到的节点类型
        attributes: {
          lineHeight: {
            // 默认值(从配置项获取)
            default: this.options.defaultLineHeight,

            // 渲染到HTML时的处理
            renderHTML: (attributes) => {
              if (!attributes.lineHeight) {
                return {};
              }
              // 将行高转换为行内样式
              return {
                style: `line-height: ${attributes.lineHeight};`,
              };
            },

            // 从HTML解析时的处理
            parseHTML: (element) => {
              return {
                // 获取行高样式或使用默认值
                lineHeight: element.style.lineHeight || this.options.defaultLineHeight,
              };
            },
          },
        },
      },
    ];
  },

  // 添加自定义命令
  addCommands() {
    return {
      setLineHeight:
        (lineHeight) =>
        ({ tr, state, dispatch }) => {
          // 创建事务副本以保持不可变性
          tr = tr.setSelection(state.selection);
          // 遍历选区内的所有节点
          state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos) => {
            // 只处理配置的类型节点
            if (this.options.types.includes(node.type.name)) {
              tr = tr.setNodeMarkup(pos, undefined, {
                ...node.attrs,
                lineHeight, // 更新行高属性
              });
            }
          });

          // 提交事务更新
          if (dispatch) {
            dispatch(tr);
          }
          return true;
        },

      unsetLineHeight:
        () =>
        ({ tr, state, dispatch }) => {
          tr = tr.setSelection(state.selection);

          // 遍历选区节点重置行高
          state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos) => {
            if (this.options.types.includes(node.type.name)) {
              tr = tr.setNodeMarkup(pos, undefined, {
                ...node.attrs,
                lineHeight: this.options.defaultLineHeight, // 重置为默认值
              });
            }
          });

          if (dispatch) {
            dispatch(tr);
          }
          return true;
        },
    };
  },
});

使用案例

首先我们在extensions中添加扩展以激活

extensions: [
  /*......*/
  FontSizeExtension,
  LineHeightExtension.configure({
    types: ["paragraph", "heading"],
  }),
  /*......*/
];

相关文章:

  • obsidian中Text Generate的使用教程(以DeepSeek为例)
  • TTS语音模型调用出错
  • 【前端实战】一文掌握响应式布局 - 多设备完美适配方案详解
  • Vuex 高级技巧与最佳实践
  • IMX6ULL学习整理篇——Linux驱动开发的基础3:向新框架迁移
  • LabVIEW棉花穴播器排种自动监测系统
  • Linux 命令学习记录
  • 垃圾收集算法
  • Linux 用户和用户组管理
  • SFT数据处理部分的思考
  • 本周行情——20250315
  • 实时系统优先级设置与修改
  • system V信号量
  • [文献阅读] 可变形卷积DCN - Deformable Convolutional Networks
  • IMX6ULL学习整理篇——UBoot的一些基础知识(2. 启动流程)
  • 嵌入式C语言中堆栈管理与数据存储的精髓
  • 华为机试牛客刷题之HJ5 进制转换
  • /proc/sys/kernel/yama/ptrace_scope的作用
  • 网络爬虫【简介】
  • TMS320F28P550SJ9学习笔记13: 软件I2C_驱动AT24Cxx存储芯片
  • wordpress publisher/广州网站seo
  • 网站编辑是什么/引流推广
  • 网站建设的规划方案/seo图片优化的方法
  • 做爰午夜福利全过程视频网站/企业营销
  • 网站后台搭建图文/站长之家seo概况查询
  • 做服装店网站的素材/百度指数官方下载