Tiptap是一個(gè)基于ProseMirror的現(xiàn)代化無(wú)渲染富文本編輯器框架,其強(qiáng)大的可擴(kuò)展性允許開(kāi)發(fā)者創(chuàng)建自定義插件來(lái)滿足特定需求。下面我將詳細(xì)介紹如何在Tiptap中開(kāi)發(fā)自定義插件。
## 1. Tiptap插件基礎(chǔ)概念
Tiptap的內(nèi)容由三大核心組件構(gòu)成:
- **節(jié)點(diǎn)(Node)**:文檔的基本結(jié)構(gòu)單元,對(duì)應(yīng)DOM節(jié)點(diǎn)
- **標(biāo)記(Mark)**:用于給選中的文本添加特殊樣式或語(yǔ)義
- **擴(kuò)展(Extension)**:功能擴(kuò)展的基礎(chǔ)類,可以擴(kuò)展編輯器功能
自定義插件通常通過(guò)繼承`Extension`類來(lái)實(shí)現(xiàn)。
## 2. 創(chuàng)建自定義插件的基本步驟
### 2.1 插件基本結(jié)構(gòu)
```javascript
import { Extension } from '@tiptap/core'
const CustomExtension = Extension.create({
name: 'customExtension', // 插件名稱
// 添加配置選項(xiàng)
addOptions() {
return {
// 你的配置項(xiàng)
}
},
// 添加全局屬性
addGlobalAttributes() {
return [
{
types: ['paragraph', 'heading'], // 應(yīng)用到的節(jié)點(diǎn)類型
attributes: {
// 屬性定義
}
}
]
},
// 添加編輯器命令
addCommands() {
return {
customCommand: () => ({ commands }) => {
// 命令實(shí)現(xiàn)
}
}
}
})
```
### 2.2 實(shí)際案例:字體大小插件
以下是一個(gè)完整的字體大小插件實(shí)現(xiàn):
```javascript
import { Extension } from "@tiptap/core";
import "@tiptap/extension-text-style";
// 聲明類型擴(kuò)展
declare module "@tiptap/core" {
interface Commands<ReturnType> {
fontSize: {
setFontSize: (fontSize: string) => ReturnType;
unsetFontSize: () => ReturnType;
};
}
}
export const FontSizeExtension = Extension.create({
name: "fontSize",
addOptions() {
return {
types: ["textStyle"],
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
fontSize: {
default: null,
parseHTML: (element) => element.style.fontSize,
renderHTML: (attributes) => {
if (!attributes.fontSize) return {};
return { style: `font-size: ${attributes.fontSize};` };
},
},
},
},
];
},
addCommands() {
return {
setFontSize: (fontSize) => ({ chain }) => {
return chain()
.setMark("textStyle", { fontSize })
.run();
},
unsetFontSize: () => ({ chain }) => {
return chain()
.setMark("textStyle", { fontSize: null })
.removeEmptyTextStyle()
.run();
},
};
},
});
```
### 2.3 行高插件示例
另一個(gè)實(shí)用的行高插件實(shí)現(xiàn):
```javascript
import { Extension } from "@tiptap/core";
declare module "@tiptap/core" {
interface Commands<ReturnType> {
lineHeight: {
setLineHeight: (lineHeight: string) => ReturnType;
unsetLineHeight: () => ReturnType;
};
}
}
export const LineHeightExtension = Extension.create({
name: "lineHeight",
addOptions() {
return {
types: ["paragraph", "heading"],
defaultLineHeight: null,
};
},
addGlobalAttributes() {
return [
{
types: this.options.types,
attributes: {
lineHeight: {
default: this.options.defaultLineHeight,
renderHTML: (attributes) => {
if (!attributes.lineHeight) return {};
return { style: `line-height: ${attributes.lineHeight};` };
},
parseHTML: (element) => ({
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 }) => {
// 類似setLineHeight,但重置行高
}
};
},
});
```
## 3. 插件核心API詳解
### 3.1 addOptions()
用于定義插件的配置選項(xiàng),這些選項(xiàng)可以通過(guò)`this.options`在插件內(nèi)部訪問(wèn)。
```javascript
addOptions() {
return {
types: ['paragraph'],
defaultColor: 'red'
}
}
```
### 3.2 addGlobalAttributes()
定義全局屬性,可以應(yīng)用到指定的節(jié)點(diǎn)類型上。
```javascript
addGlobalAttributes() {
return [{
types: this.options.types,
attributes: {
color: {
default: this.options.defaultColor,
renderHTML: attributes => ({
style: `color: ${attributes.color}`
}),
parseHTML: element => element.style.color
}
}
}]
}
```
### 3.3 addCommands()
添加編輯器命令,可以通過(guò)`editor.commands`調(diào)用。
```javascript
addCommands() {
return {
setColor: color => ({ chain }) => {
return chain()
.setMark('textStyle', { color })
.run()
},
unsetColor: () => ({ chain }) => {
return chain()
.setMark('textStyle', { color: null })
.removeEmptyTextStyle()
.run()
}
}
}
```
### 3.4 renderHTML()和parseHTML()
控制插件如何渲染到HTML和從HTML解析。
```javascript
// 在addGlobalAttributes或addAttributes中使用
attributes: {
align: {
default: 'left',
renderHTML: attributes => ({
style: `text-align: ${attributes.align}`
}),
parseHTML: element => element.style.textAlign || 'left'
}
}
```
## 4. 插件使用方式
創(chuàng)建插件后,需要在編輯器初始化時(shí)注冊(cè):
```javascript
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { FontSizeExtension } from './font-size-extension'
const editor = new Editor({
extensions: [
StarterKit,
FontSizeExtension,
// 其他擴(kuò)展
],
content: '<p>Hello World!</p>',
})
```
## 5. 高級(jí)技巧
### 5.1 繼承現(xiàn)有擴(kuò)展
可以通過(guò)`.extend()`方法擴(kuò)展現(xiàn)有功能:
```javascript
import Heading from "@tiptap/extension-heading";
const CustomHeading = Heading.extend({
addOptions() {
return {
...this.parent?.(), // 保留原有選項(xiàng)
levels: [1, 2, 3], // 修改選項(xiàng)
};
},
});
```
### 5.2 處理節(jié)點(diǎn)選擇
可以使用`addNodeView()`創(chuàng)建復(fù)雜的節(jié)點(diǎn)視圖。
### 5.3 使用ProseMirror API
Tiptap基于ProseMirror,可以直接使用ProseMirror的API進(jìn)行更底層的操作。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者





暫無(wú)評(píng)論,快來(lái)評(píng)論吧!