【element-tiptap】Tiptap编辑器核心概念----结构篇

core-concepts
前言:这篇文章来介绍一下 Tiptap 编辑器的一些核心概念

(一)结构

1、 Schemas

定义文档组成方式。一个文档就是标题、段落以及其他的节点组成的一棵树。
每一个 ProseMirror 的文档都有一个与之相关联的 schema,它规定了可以出现在文档中的节点的类型,以及它们的嵌套方式。例如,它可能规定,顶层节点可以包含一个或多个块(block),段落节点可以包括任意数量的拥有各种标记的行内节点。ProseMirror 给出了基本的 schema,并且允许自定义 schema
一个简单的 schema 示例:

// 底层 ProseMirror schema
{nodes: {doc: {content: 'block+',},paragraph: {content: 'inline*',group: 'block',parseDOM: [{ tag: 'p' }],toDOM: () => ['p', 0],},text: {group: 'inline',},},
}

在上面的 schema 的定义中,我们定义了三个节点

  • doc,它是文档的根节点,它的内容由 content 属性定义,可以是一个或多个块级元素,block+ 是正则表达式的写法,小加号表示一个或多个。
  • paragraph 节点,它自身是块级元素,由 group 属性指定,由于定义的节点只有它是块级元素,所以根节点的子节点只允许出现 paragraph 节点。它的内容 inline* 指的是0个或多个行内节点,所以它只能包含 text 节点。parseDOM 定义了如何从粘贴的 HTML 中解析节点。 toDOM 定义了 paragraph 节点如何在 DOM 中渲染。
  • text 节点,这个节点就很简单了,行内,纯文本
    在 Tiptap 中,节点、标记和扩展都在各自的文件中独立定义,便于拆分逻辑,最终引擎会将所有的定义合并在一起。

schema 是严格的,文档中不能出现任何 schema 不允许的节点。
例如,如果你想编辑器中粘贴了 This is <strong>important</strong> ,但是没有扩展可以解析 strong 标签,那么文档中会直接显示 This is important
可以通过监听器可以监听解析失败的事件:
contentError
在这个事件中还会接收到一个参数 disableCollaboration,它是一个函数,调用这个函数可以重新初始化编辑器,并且不会将出错的内容同步给其他用户。不过协同编辑的功能是收费的,大概率还是需要自己的团队重新开发。
这个方法的调用有以下两种:

  • 直接在创建编辑器的时候设置监听器
new Editor({enableContentCheck: true,content: invalidContent,onContentError({ editor, error, disableCollaboration }) {// your handler here},...options,
})
  • 通过 on() 方法监听
const editor = new Editor({enableContentCheck: true,content: invalidContent,...options,
})editor.on('contentError', ({ editor, error, disableCollaboration }) => {// your handler here
})
2、Marks

标记可以附加到每一个节点上,用来给节点的某些特殊的部分增加样式或者注释文本。
在 schema 中,必须指定可以允许的 marks 的类型,和上面的 schema 中指定 nodes 的写法类似。
默认情况下,带有行内内容的节点会允许所有的在 schema 中定义的 marks 应用于它们的子节点,但是可以在节点的 marks 属性中进行自定义。
例如下面这个简单的 schema,允许 strong 和 em 标记应用于 paragraphs 中的文本,但是不允许应用于 headings 中的文本:

const markSchema = new Schema({nodes: {doc: {content: "block+"},paragraph: {group: "block", content: "text*", marks: "_"},heading: {group: "block", content: "text*", marks: ""},text: {inline: true}},marks: {strong: {},em: {}}
})

标记集合会被解析成标记的所有名称以空格分隔的字符串的形式,_ 会作为一个通配符,匹配所有的标记;而空字符串表示不允许任何的标记。
Tiptap 中提供了一系列 marks 扩展

3、commands

Commands 是以编程的方式改变编辑器内容。编辑器提供了很多很多的命令,以编程的方式添加、改变编辑器内容或者更改选区。

① 执行 command

通过编辑器实例上的 commands 属性,调用 command

editor.commands.setBold()

像酱紫执行命令就可以让文本加粗

② 链式执行 command

大多数的命令都可以合并到一次调用中,这会比单独调用函数更加高效。下面的例子是让选中的文本加粗

editor.chain().focus().toggleBold().run()

chain() 用来开启新的执行链,run() 方法用来实际执行所有的命令。这些命令很可能都是通过点击按钮触发的,但是按钮通常不在编辑器内部,所有可能会需要先执行 focus 聚焦于编辑器,然后再执行 toggleBold 方法加粗选中的文本。所以大部分命令在执行前都会先链式调用 focus(),这样用户就可以继续进行编辑操作。
执行链上的方法是排着队执行的。一个执行链上的方法会被合并到一个 transaction 中,一个执行链只会触发一次更新监听器。
默认情况下,ProseMirror 是不支持链式操作的,我们需要通过 Transaction mapping 在命令执行链中更新位置。
下面是一个例子,链式执行删除和插入命令:

// 添加两个自定义命令演示两个 transaction 步骤之间的映射
addCommands() {return {delete: () => ({ tr }) => {const { $from, $to } = tr.selection// 使用 tr.mapping.map 在 transaction 步骤之间映射位置const from = tr.mapping.map($from.pos)const to = tr.mapping.map($to.pos)tr.delete(from, to)return true},insert: (content: string) => ({ tr }) => {const { $from } = tr.selection// 使用 tr.mapping.map 在 transaction 步骤之间映射位置const pos = tr.mapping.map($from.pos)tr.insertText(content, pos)return true},}
}

现在就可以执行下面的操作,确保插入内容时位置不会错误

editor.chain().delete().insert('foo').run()
③ 自定义命令中的链

当链接到一个命令的时候,事务会处于保留状态。如果你想链式调用自定义命令,你需要使用当前的事务,并且将你的自定义命令添加到当前的调用链上,如以下代码:

addCommands() {return {customCommand: attributes => ({ chain }) => {// Doesn’t work:// return editor.chain() …// Does work:return chain().insertContent('foo!').insertContent('bar!').run()},}
}
④ 行内命令

如果命令中执行的代码比较简单,可以直接写成行内命令的形式:

editor.chain().focus().command(({ tr }) => {// manipulate the transactiontr.insertText('hey, that’s cool!')return true}).run()
⑤ 空运行命令

在执行某些命令之前,可以使用 can() 方法,来判断这个命令能不能执行,例如在菜单中的按钮能不能显示等。这个方法不会执行任何修改而只是会判断后面跟的命令是否可以执行。

editor.can().toggleBold()

can() 方法也可以和 chain() 一起使用,来判断是否执行链上所有的方法都可以执行

editor.can().chain().toggleBold().toggleItalic().run()

如果链式操作中的所有命令都能执行,can() 方法才会返回 true。如果其中有自定义命令,切记要返回布尔值。
就是说上面一连串的方法,最后返回的是个布尔值,不会有任何的修改。

⑥ 尝试命令

如果有一连串的命令,运行一个命令成功后,就不再往后执行,就可以使用 first 命令。好像有 if~else~ 的作用,或者是替代 can() 判断的作用。
例如下面的例子,backspace 键会首先去尝试撤销一个输入规则;如果成功的话就执行这个操作,如果失败的话就执行下一个命令,删除选区内容

editor.first(({ commands }) => [() => commands.undoInputRule(),() => commands.deleteSelection(),// …
])

下面的写法作用相同:

export default () =>({ commands }) => {return commands.first([() => commands.undoInputRule(),() => commands.deleteSelection(),// …])}

就是说如果当前焦点是一个列表,点击删除键,会列表输入规则删除,变成普通的文本输入
在这里插入图片描述

⑦ 关键命令列表
内容
命令描述
clearContent()删除整个文档
insertContent()在当前位置插入一个节点或者HTML字符串
insertContentAt()在指定位置插入一个节点或者HTML字符串
setContent()用新内容替代整篇文档的内容
节点&标记
命令描述
clearNodes()将节点变成简单的段落
createParagraphNear()在当前位置的附近创建一个段落
deleteNode()删除节点
extendMarkRange()将文本选择范围扩展到当前标记。
exitCode()停止代码编辑
joinBackward()和后一个节点合并
joinForward()和前一个节点合并
lift()提升当前的选区到上一个层级,例如将二层的列表项变成一层的列表项
liftEmptyBlock()提升空块的层,例如空的列表项点回车时会将当前列表项提升为单独的一行;空引用点击回车会退出引用
newlineInCode()在代码中添加换行符
resetAttributes()将一些节点或标记的属性重置为默认值
setMark()给标记添加一个新属性
setNode()将一个指定范围的内容替换为新节点
splitBlock()在光标处分割当前元素,派生一个新节点
toggleMark()切换标记
toggleWrap()切换指定的包裹标签,例如 toggleWrap(‘bulletList’) 切换当前元素是否放在列表中
undoInputRule()撤销输入规则,即变成普通文本
unsetAllMarks()删除当前选区的所有标记
unsetMark()删除当前选区的指定的标记
updateAttributes()更新节点或标记的属性
列表
命令描述
liftListItem()提升列表项等级
sinkListItem()降低列表项等级
splitListItem()将一个列表项拆分为两个列表项。
toggleList()切换列表类型;切换普通文本和列表
wrapInList()将一个节点包装在一个列表中
选区
命令描述
blur()从编辑器中移除焦点
deleteRange()删除指定 range
deleteSelection()删除 selection
enter()触发回车行为 例如分割p标签、创建新的一行等
focus()聚焦编辑器到指定的位置
keyboardShortcut()触发指定的键盘快捷键
scrollIntoView()滚动视图到选区位置
selectAll()选中整个文档
selectNodeBackward()向后选中一个节点
selectNodeForward()向前选中一个节点
selectParentNode()选中父节点
setNodeSelection()创建一个 NodeSelection
setTextSelection()创建一个 TextSelection

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/59939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024.6使用 UMLS 集成的基于 CNN 的文本索引增强医学图像检索

Enhancing Medical Image Retrieval with UMLS-Integrated CNN-Based Text Indexing 问题 医疗图像检索中&#xff0c;图像与相关文本的一致性问题&#xff0c;如患者有病症但影像可能无明显异常&#xff0c;影响图像检索系统准确性。传统的基于文本的医学图像检索&#xff0…

初识Linux · 信号处理 · 续

目录 前言&#xff1a; 可重入函数 重谈进程等待和优化 前言&#xff1a; 在前文&#xff0c;我们已经介绍了信号产生&#xff0c;信号保存&#xff0c;信号处理的主题内容&#xff0c;本文作为信号处理的续篇&#xff0c;主要是介绍一些不那么重要的内容&#xff0c;第一个…

微信小程序 最新获取用户头像以及用户名

一.在小程序改版为了安全起见 使用用户填写来获取头像以及用户名 二.代码实现 <view class"login_box"><!-- 头像 --><view class"avator_box"><button wx:if"{{ !userInfo.avatarUrl }}" class"avatorbtn" op…

WPF MVVM框架

一、MVVM简介 MVC Model View Control MVP MVVM即Model-View-ViewModel&#xff0c;MVVM模式与MVP&#xff08;Model-View-Presenter&#xff09;模式相似&#xff0c;主要目的是分离视图&#xff08;View&#xff09;和模型&#xff08;Model&#xff09;&#xff0c;具有低…

【算法】【优选算法】前缀和(下)

目录 一、560.和为K的⼦数组1.1 前缀和1.2 暴力枚举 二、974.和可被K整除的⼦数组2.1 前缀和2.2 暴力枚举 三、525.连续数组3.1 前缀和3.2 暴力枚举 四、1314.矩阵区域和4.1 前缀和4.2 暴力枚举 一、560.和为K的⼦数组 题目链接&#xff1a;560.和为K的⼦数组 题目描述&#x…

两大新兴开发语言大比拼:Move PK Rust

了解 Move 和 Rust 的差异有助于开发者根据项目的具体需求选择最合适的语言。选择不恰当的语言可能会导致项目后期出现技术债务。不同语言有其独特的优势。了解 Move 和 Rust 的差异可以帮助开发者拓展技术视野&#xff0c;发现不同语言在不同领域的应用潜力。 咱们直奔主题&a…

Scaling Law的“终结“还是新起点?——开源实践者的深度思考

作者&#xff1a;宋大宝&#xff0c;与大宝同学因那篇《回顾总结展望「融合RL与LLM思想&#xff0c;探寻世界模型以迈向AGI」》结识于今年春天&#xff0c;虽我们当时某些思想观念有些出入&#xff0c;也碰撞出了很多火花与共鸣&#xff0c;并持续地相互启发的走到了现在。他是…

“fc-async”提供了基本的异步处理能力

在开发中,异步处理已经成为提升系统性能和用户体验的常用方式。然而,传统的@Async注解和基础的异步处理工具在面对复杂的任务场景时,存在局限性。这些局限性包括但不限于高并发环境下的稳定性、任务失败后的恢复机制、以及任务的监控和管理。 开源项目“fc-async”提供了基…

Ubuntu 的 ROS 操作系统 turtlebot3 导航仿真

引言 导航仿真是机器人自动化系统中不可或缺的一部分&#xff0c;能够帮助开发者在虚拟环境中测试机器人在复杂场景下的运动与路径规划。 在 Gazebo 仿真环境中&#xff0c;TurtleBot3 配合 ROS 操作系统提供了强大的导航功能。在进行导航仿真时&#xff0c;首先需要准备地图&…

FFmpeg 4.3 音视频-多路H265监控录放C++开发十三.2:avpacket中包含多个 NALU如何解析头部分析

前提&#xff1a; 注意的是&#xff1a;我们这里是从avframe转换成avpacket 后&#xff0c;从avpacket中查看NALU。 在实际开发中&#xff0c;我们有可能是从摄像头中拿到 RGB 或者 PCM&#xff0c;然后将pcm打包成avframe&#xff0c;然后将avframe转换成avpacket&#xff0…

java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程

文章目录 PC Register堆虚拟机栈方法区(Metaspace元空间双亲委派机制类加载器 类装载的执行过程 PC Register 程序计数器&#xff08;Program Counter Register&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;中的一个组件&#xff0c;它在 JVM 的内存模型中扮演着非常…

使用Web Animations API实现复杂的网页动画效果

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Web Animations API实现复杂的网页动画效果 使用Web Animations API实现复杂的网页动画效果 使用Web Animations API实现复杂…

本草纲目数字化:Spring Boot在中药实验管理中的应用

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理中药实验管理系统的相关信息成为必然。开发…

无人机挂载超细干粉灭火装置技术详解

无人机挂载超细干粉灭火装置技术是一种创新的灭火方式&#xff0c;结合了无人机的远程操控能力和超细干粉灭火剂的高效灭火性能。以下是对该技术的详细解析&#xff1a; 一、技术背景与原理 背景&#xff1a;高层建筑灭火救援困难一直是公认的世界性难题。无人机技术的发展为…

Linux下MySQL的简单使用

Linux下MySQL的简单使用 导语MySQL安装与配置MySQL安装密码设置 MySQL管理命令myisamchkmysql其他 常见操作 C语言访问MYSQL连接例程错误处理使用SQL 总结参考文献 导语 这一章是MySQL的使用&#xff0c;一些常用的MySQL语句属于本科阶段内容&#xff0c;然后是C语言和MySQl之…

多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码

社团活动与组织 信息发布&#xff1a;系统支持社团发布活动信息、招募新成员等&#xff0c;方便社团进行线上线下活动的组织和管理。 增强凝聚力&#xff1a;通过系统&#xff0c;社团成员可以更好地交流和互动&#xff0c;增强社团的凝聚力和影响力。 生活服务功能 二手市场…

androidstudio入门到放弃配置

b站视频讲解传送门 android_studio安装包&#xff1a;https://developer.android.google.cn/studio?hlzh-cn 下载安装 开始创建hello-world 1.删除缓存 文件 下载gradle文件压缩&#xff1a;gradle-8.9用自己创建项目时自动生成的版本即可&#xff0c;不用和我一样 https://…

深入理解 Redis跳跃表 Skip List 原理|图解查询、插入

1. 简介 跳跃表 ( skip list ) 是一种有序数据结构&#xff0c;通过在每个节点中维持多个指向其他节点的指针&#xff0c;从而达到快速访问节点的目的。 在 Redis 中&#xff0c;跳跃表是有序集合键的底层实现之一&#xff0c;那么这篇文章我们就来讲讲跳跃表的实现原理。 2. …

如何在算家云搭建Peach-9B-8k-Roleplay(文本生成)

一、Peach-9B-8k-Roleplay简介 Peach-9B-8k-Roleplay 是一种聊天大型语言模型&#xff0c;它是通过我们的数据合成方法创建的超过 100K 的对话中微调 01-ai/Yi-1.5-9B 模型而获得的。 也许是 34B 以下参数最好的 LLM。 二、模型搭建流程 1. 创建容器镜像 进入算家云平台的“…

Flutter中的Material Theme完全指南:从入门到实战

Flutter作为一款热门的跨平台开发框架&#xff0c;其UI组件库Material Design深受开发者喜爱。本文将深入探讨Flutter Material Theme的使用&#xff0c;包括如何借助Material Theme Builder创建符合产品需求的主题风格。通过多个场景和代码实例&#xff0c;让你轻松掌握这一工…