扩展 API
Tiptap 的强大之处在于其灵活性。您可以从头开始创建自己的扩展,并构建一个独特的编辑器体验,以满足您的需求。
所有扩展的基本结构都是相同的,无论您是创建节点、标记还是功能更改。而且,Tiptap 中的一切都是基于扩展的。
创建扩展
扩展为 Tiptap 添加了新功能。在文档中,您会经常看到“扩展”这个词,甚至在节点和标记中也是如此。但也有字面意义上的扩展。这些扩展不能添加到架构中(就像标记和节点那样),但它们可以添加功能或改变编辑器的行为。
一个很好的例子是监听编辑器事件并处理它们的内容。像这样:
import { Extension } from '@tiptap/core'
const CustomExtension = Extension.create({
name: 'customExtension',
onUpdate() {
console.log(this.editor.getJSON())
},
})您也可以使用回调函数创建扩展。这在您想封装扩展逻辑时非常有用,例如,当您想定义事件处理程序或其他自定义逻辑时。
import { Extension } from '@tiptap/core'
const CustomExtension = Extension.create(() => {
// 定义扩展内部使用的变量或函数
const customVariable = 'foo'
function onCreate() {}
function onUpdate() {}
return {
name: 'customExtension',
onCreate,
onUpdate,
// 在这里编写您的代码。
}
})此扩展监听编辑器的 update 事件,并将编辑器当前的 JSON 表示日志输出到控制台。
它这样安装到编辑器中:
import { Editor } from '@tiptap/core'
const editor = new Editor({
extensions: [CustomExtension],
})
// 或者在使用 React 或 Vue 时
const editor = useEditor({
extensions: [CustomExtension],
})此扩展数组可以包含任意数量的扩展。它们将按列出的顺序安装,或按它们的 priority 属性排序。
现在我们已经看到扩展的基本结构,让我们深入了解您可以用来创建自己扩展的所有扩展选项。
扩展选项
在创建扩展时,您可以定义一组用户可配置的选项。这些选项可以用于自定义扩展的行为,或提供额外的功能。
name
扩展的名称。这用于在编辑器的扩展管理器中识别扩展。
const CustomExtension = Extension.create({
name: 'customExtension',
})如果扩展是节点或标记,则名称用于在编辑器的架构中识别节点或标记,并因此保存在编辑器内容的 JSON 表示中。有关更多信息,请参见 存储您的内容为 JSON。
priority
优先级定义扩展注册的顺序。默认优先级为 100,大多数扩展都是这个值。具有更高优先级的扩展将更早加载。
import Link from '@tiptap/extension-link'
const CustomLink = Link.extend({
priority: 1000,
})扩展加载的顺序影响两个方面:
- 插件顺序(优先级更高的扩展的 ProseMirror 插件将先运行。)
- 架构顺序
例如,Link 标记具有更高的优先级,这意味着它将被渲染为 <a href="…"><strong>Example</strong></a> 而不是 <strong><a href="…">Example</a></strong>。
addOptions
addOptions 方法用于定义扩展的选项。此方法应返回一个对象,其中包含用户可配置的选项。
type CustomExtensionOptions = {
customOption: string
}
declare module '@tiptap/core' {
interface ExtensionOptions {
customOption: CustomExtensionOptions
}
}
const CustomExtension = Extension.create<CustomExtensionOptions>({
name: 'customExtension',
addOptions() {
return {
customOption: 'default value',
}
},
})这会暴露在安装扩展时可以设置的配置:
const editor = new Editor({
extensions: [CustomExtension.configure({ customOption: 'new value' })],
})addStorage
addStorage 方法用于定义扩展的存储(本质上是一个简单的状态管理器)。此方法应返回一个对象,其中包含扩展可以使用的存储。
type CustomExtensionStorage = {
customValue: string
}
declare module '@tiptap/core' {
interface ExtensionStorage {
customExtension: CustomExtensionStorage
}
}
const CustomExtension = Extension.create<any, CustomExtensionStorage>({
name: 'customExtension',
addStorage() {
return {
customValue: 'default value',
}
},
})这会暴露在扩展内部可以访问的存储:
const CustomExtension = Extension.create<any, CustomExtensionStorage>({
name: 'customExtension',
addStorage() {
return {
customValue: 'default value',
}
},
onUpdate() {
console.log(this.storage.customValue) // 'default value'
},
})或者通过编辑器:
const editor = new Editor({
extensions: [CustomExtension],
})
editor.storage.customExtension.customValue // 'default value'注意
editor.storage 由扩展的名称命名空间。
addCommands
addCommands 方法用于定义扩展的命令。此方法应返回一个对象,其中包含用户可以执行的命令。
declare module '@tiptap/core' {
interface Commands<ReturnType> {
customExtension: {
customCommand: () => ReturnType
}
}
}
const CustomExtension = Extension.create({
name: 'customExtension',
addCommands() {
return {
customCommand:
() =>
({ commands }) => {
return commands.setContent('Custom command executed')
},
}
},
})在 addCommands 内部使用 commands 参数
要在 addCommands 内部访问其他命令,请使用传递给它的 commands 参数。
这会暴露用户可以执行的命令:
const editor = new Editor({
extensions: [CustomExtension],
})
editor.commands.customCommand() // 'Custom command executed'
editor.chain().customCommand().run() // 'Custom command executed'addKeyboardShortcuts
addKeyboardShortcuts 方法用于定义键盘快捷键。此方法应返回一个对象,其中包含用户可以使用的键盘快捷键。
const CustomExtension = Extension.create({
name: 'customExtension',
addKeyboardShortcuts() {
return {
'Mod-k': () => {
console.log('Keyboard shortcut executed')
},
}
},
})这会暴露可供用户使用的键盘快捷键。
addInputRules
使用输入规则,您可以定义正则表达式以监听用户输入。它们用于 Markdown 快捷方式,例如将文本 (c) 转换为 ©(还有许多其他用途)与 Typography 扩展一起使用。对于标记,使用 markInputRule 辅助函数,对于节点,使用 nodeInputRule。
默认情况下,位于两侧的波浪号之间的文本会转换为 删除线文本。如果您认为在每边只需一个波浪号就足够了,可以像这样覆盖输入规则:
import { Extension } from '@tiptap/core'
import { markInputRule } from '@tiptap/core'
const CustomExtension = Extension.create({
name: 'customExtension',
addInputRules() {
return [
markInputRule({
find: /(?:~)((?:[^~]+))(?:~)$/,
type: this.editor.schema.marks.strike,
}),
]
},
})现在,当您输入 ~striked text~ 时,它将被转换为 删除线文本。
addPasteRules
粘贴规则的工作方式与输入规则(见上文)相似。但不同之处在于,它们应用于粘贴的内容,而不是监听用户输入。
正则表达式有一个微小的区别。输入规则通常以 $ 符号结束(这意味着“在行尾断言位置”),而粘贴规则通常浏览所有内容,不带该 $ 符号。
将上述示例应用于粘贴规则将如下所示:
import { Extension } from '@tiptap/core'
import { markPasteRule } from '@tiptap/core'
const CustomExtension = Extension.create({
name: 'customExtension',
addPasteRules() {
return [
markPasteRule({
find: /(?:~)((?:[^~]+))(?:~)/g,
type: this.editor.schema.marks.strike,
}),
]
},
})事件
您甚至可以将您的 事件监听器 移动到一个单独的扩展。以下是一个涉及所有事件的示例:
import { Extension } from '@tiptap/core'
const CustomExtension = Extension.create({
onBeforeCreate() {
// 编辑器即将被创建。
},
onCreate() {
// 编辑器已准备就绪。
},
onUpdate() {
// 内容已更改。
},
onSelectionUpdate({ editor }) {
// 选择已更改。
},
onTransaction({ transaction }) {
// 编辑器状态已更改。
},
onFocus({ event }) {
// 编辑器已获得焦点。
},
onBlur({ event }) {
// 编辑器不再获得焦点。
},
onDestroy() {
// 编辑器正在被销毁。
},
})addProseMirrorPlugins
您可以向扩展添加 ProseMirror 插件。如果您想使用 ProseMirror 插件扩展编辑器,这非常有用。
使用现有 ProseMirror 插件
您可以将现有的 ProseMirror 插件包装在 Tiptap 扩展中,如下面的示例所示。
import { history } from '@tiptap/pm/history'
const History = Extension.create({
addProseMirrorPlugins() {
return [
history(),
// …
]
},
})创建 ProseMirror 插件
您也可以创建自定义的 ProseMirror 插件。以下是一个将消息记录到控制台的自定义 ProseMirror 插件示例。
import { Plugin, PluginKey } from '@tiptap/pm/state'
import { Extension } from '@tiptap/core'
const CustomExtension = Extension.create({
name: 'customExtension',
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('customPlugin'),
view() {
return {
update() {
console.log('Custom plugin updated')
},
}
},
}),
]
},
})要了解有关 ProseMirror 插件的更多信息,请查阅 ProseMirror 文档。
addExtensions
您可以向扩展添加更多扩展。如果您想创建一组属于同一类别的扩展,这非常有用。
import { Extension } from '@tiptap/core'
import CustomExtension1 from './CustomExtension1'
const CustomExtension = Extension.create({
name: 'customExtension',
addExtensions() {
return [
CustomExtension1.configure({
name: 'customExtension1',
}),
]
},
})extendNodeSchema
您可以使用 extendNodeSchema 方法扩展编辑器的 NodeConfig。如果您想向节点架构添加额外属性,这将非常有用。
import { Extension } from '@tiptap/core'
declare module '@tiptap/core' {
// 这将向 NodeConfig 添加一个新的配置选项
interface NodeConfig {
customAttribute: {
default: null
}
}
}
const CustomExtension = Extension.create({
name: 'customExtension',
extendNodeSchema() {
return {
customAttribute: {
default: null,
},
}
},
})extendMarkSchema
您可以使用 extendMarkSchema 方法扩展编辑器的 MarkConfig。如果您想向标记架构添加额外属性,这将非常有用。
import { Extension } from '@tiptap/core'
declare module '@tiptap/core' {
// 这将向 MarkConfig 添加一个新的配置选项
interface MarkConfig {
customAttribute: {
default: null
}
}
}
const CustomExtension = Extension.create({
name: 'customExtension',
extendMarkSchema() {
return {
customAttribute: {
default: null,
},
}
},
})这有什么可用的?
这些扩展不是类,但您在扩展中的 this 处仍然可以访问一些重要内容。
// 扩展的名称,例如 'bulletList'
this.name
// 编辑器实例
this.editor
// ProseMirror 类型(如果是节点或标记)
this.type
// 包含所有设置的对象
this.options
// 所有扩展的内容
this.parent
// 存储对象
this.storage