mui-tiptap:一个可定制的Material UI风格的所见即所得富文本编辑器,使用Tiptap。
- :sparkles: 基于您自己的MUI主题样式(颜色、字体、亮暗模式等) - :hammer_and_wrench: 建立在强大的Tiptap和ProseMirror基础之上(可扩展、实时协作编辑、跨平台支持等) **特性:** - :toolbox: 一体化的`RichTextEditor`组件,可立即开始使用(无需其他组件或钩子!),或使用单独的模块化组件以满足您的定制需求 - :sunglasses: 内置Tiptap扩展的样式(文本格式化、列表、表格、类似Google Docs的协作光标等) - :arrow_forward: 标准Tiptap扩展的可组合和可扩展的菜单按钮和控件 - :framed_picture: [`ResizableImage`扩展](#resizableimage),用于直接在编辑器中添加和调整图像大小 - :anchor: [`HeadingWithAnchor`扩展](#headingwithanchor),为添加的每个标题生成类似GitHub的动态锚链接 - :link: [`LinkBubbleMenu`](#components),使添加和编辑链接变得轻而易举 - :1234: [`FontSize`扩展](#fontsize),用于控制文本大小 - :white_square_button: [`TableImproved`扩展](#tableimproved),[修复](https://github.com/ueberdosis/tiptap/issues/2041)[了](https://github.com/ueberdosis/tiptap/issues/2301)底层Tiptap `Table`扩展的问题 - :pencil: [`TableBubbleMenu`](#components),用于交互式编辑富文本表格 - :speech_balloon: 通用的[`ControlledBubbleMenu`](#components),用于构建您自己的自定义菜单,[解决了](https://github.com/ueberdosis/tiptap/issues/2305#issuecomment-1020665146)Tiptap `BubbleMenu`的一些缺陷 - 以及更多!README目录
- [演示](#demo) - [安装](#installation) - [开始使用](#get-started) - [使用一体化组件](#use-the-all-in-one-component) - [自行创建和提供`editor`](#create-and-provide-the-editor-yourself) - [渲染只读富文本内容](#render-read-only-rich-text-content) - [mui-tiptap扩展和组件](#mui-tiptap-extensions-and-components) - [Tiptap扩展](#tiptap-extensions) - [`HeadingWithAnchor`](#headingwithanchor) - [`FontSize`](#fontsize) - [`LinkBubbleMenuHandler`](#linkbubblemenuhandler) - [`ResizableImage`](#resizableimage) - [`TableImproved`](#tableimproved) - [组件](#components) - [控件组件](#controls-components) - [本地化](#localization) - [提示和建议](#tips-and-suggestions) - [选择您的编辑器`extensions`](#choosing-your-editor-extensions) - [扩展优先级和排序](#extension-precedence-and-ordering) - [其他扩展提示](#other-extension-tips) - [图像的拖放和粘贴](#drag-and-drop-and-paste-for-images) - [`content`变化时重新渲染`RichTextEditor`](#re-rendering-richtexteditor-when-content-changes) - [贡献](#contributing)npm install @mui/material @mui/icons-material @emotion/react @emotion/styled @tiptap/react @tiptap/extension-heading @tiptap/extension-image @tiptap/extension-table @tiptap/pm @tiptap/core
或
yarn add @mui/material @mui/icons-material @emotion/react @emotion/styled @tiptap/react @tiptap/extension-heading @tiptap/extension-image @tiptap/extension-table @tiptap/pm @tiptap/core
开始使用
使用一体化组件
渲染富文本编辑器最简单的方法是使用 RichTextEditor
组件:
import { Button } from "@mui/material";
import StarterKit from "@tiptap/starter-kit";
import {
MenuButtonBold,
MenuButtonItalic,
MenuControlsContainer,
MenuDivider,
MenuSelectHeading,
RichTextEditor,
type RichTextEditorRef,
} from "mui-tiptap";
import { useRef } from "react";
function App() {
const rteRef = useRef<RichTextEditorRef>(null);
return (
<div>
<RichTextEditor
ref={rteRef}
extensions={[StarterKit]} // 或任何你想要的 Tiptap 扩展!
content="<p>Hello world</p>" // 编辑器的初始内容
// 可选包含 `renderControls` 以在编辑器顶部显示菜单栏:
renderControls={() => (
<MenuControlsContainer>
<MenuSelectHeading />
<MenuDivider />
<MenuButtonBold />
<MenuButtonItalic />
{/* 在此处添加更多你选择的控件 */}
</MenuControlsContainer>
)}
/>
<Button onClick={() => console.log(rteRef.current?.editor?.getHTML())}>
记录 HTML
</Button>
</div>
);
}
查看下面的 mui-tiptap 扩展和组件 了解更多可以使用的 Tiptap 扩展和组件(比如更多可以包含在 renderControls
中的内容)。查看 src/demo/Editor.tsx
获取使用 RichTextEditor
的更详细示例。
自行创建和提供 editor
如果你需要更多自定义选项,你可以使用 Tiptap 的 useEditor
钩子定义你的编辑器,并使用一系列 mui-tiptap
组件(和/或你自己的组件)来布局你的 UI。
将 editor
传递给组件树顶部的 mui-tiptap
的 RichTextEditorProvider
组件。然后,在提供者内渲染符合你需求的任何子组件。
最简单的选项是 RichTextField
组件,这也是 RichTextEditor
在底层使用的组件:
import { useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import {
MenuButtonBold,
MenuButtonItalic,
MenuControlsContainer,
MenuDivider,
MenuSelectHeading,
RichTextEditorProvider,
RichTextField,
} from "mui-tiptap";
function App() {
const editor = useEditor({
extensions: [StarterKit],
content: "<p>Hello <b>world</b>!</p>",
});
return (
<RichTextEditorProvider editor={editor}>
<RichTextField
controls={
<MenuControlsContainer>
<MenuSelectHeading />
<MenuDivider />
<MenuButtonBold />
<MenuButtonItalic />
{/* 在此处添加更多你选择的控件 */}
</MenuControlsContainer>
}
/>
</RichTextEditorProvider>
);
}
或者如果你想完全控制 UI,你可以自己构建编辑器区域,然后只在你想要显示(经过样式处理的)可编辑富文本内容的地方使用 <RichTextContent />
组件。RichTextContent
是 Tiptap 的 EditorContent
组件的 MUI 主题版本。
渲染只读富文本内容
使用 RichTextReadOnly
组件,只需传入你的 HTML 或 ProseMirror JSON 以及你配置的 Tiptap 扩展,如:
<RichTextReadOnly content="<p>Hello world</p>" extensions={[StarterKit]} />
或者,你可以将 RichTextEditor
的 editable
属性(或 useEditor
的 editable
选项)设置为 false
,以获得更可配置的只读选项。在以下情况下使用 RichTextReadOnly
:
- 你只想直接高效地渲染编辑器 HTML/JSON 内容,而不需要任何轮廓字段样式、控件设置、额外的监听器逻辑、访问
editor
对象等。(如果content
为空,该组件还会跳过创建 Tiptapeditor
,这可以提高性能。) - 你想要一种方便的方式来渲染内容,使其随
content
属性的变化而更新。(相比之下,RichTextEditor
不会在content
变化时自动重新渲染,如下面所述。)
mui-tiptap 扩展和组件
Tiptap 扩展
HeadingWithAnchor
这是 Tiptap 的 Heading
扩展 的修改版本,为每个添加的标题提供动态的 GitHub 风格锚点链接。当 editor
的 editable
设置为 false
时,鼠标悬停在标题上会在其左侧显示一个锚点链接按钮。这允许用户分享链接并跳转到编辑器内容中的特定标题。
FontSize
设置文本字体大小。此扩展需要安装 @tiptap/extension-text-style
包,并在 extensions
中包含其 TextStyle
标记。
可以通过 MenuSelectFontSize
组件 进行控制。
命令
setFontSize()
:设置文本字体大小(使用有效的 CSSfont-size
属性)。例如:"12px"
、"2em"
、"small"
unsetFontSize()
:移除先前设置的字体大小,恢复为给定标记的默认大小。
LinkBubbleMenuHandler
与 LinkBubbleMenu
组件 配合使用,此扩展提供编辑器命令来控制链接气泡菜单的状态。
命令
openLinkBubbleMenu()
:打开/显示链接气泡菜单。如果当前光标选择处没有链接,则创建一个链接;如果已存在链接,则编辑现有链接。editLinkInBubbleMenu()
:在气泡菜单中编辑现有链接,用于已打开的气泡菜单中查看链接时。closeLinkBubbleMenu()
:关闭/隐藏链接气泡菜单,取消任何正在进行的编辑。
ResizableImage
这是 Tiptap 的 Image
扩展 的修改版本,增加了直接在编辑器中调整图片大小的功能。点击图片时,右下角会出现一个拖动手柄,用户可以交互式地更改大小。
TableImproved
这是 Tiptap 的 Table
扩展 的修改版本,修复了与列调整大小和可编辑状态相关的问题。
具体而言,此版本的扩展与 mui-tiptap
CSS 样式相结合,确保:
- 即使编辑器处于
editable=false
状态,列也能保持其调整后的宽度 - 无论初始编辑器状态如何,从
editable=false
切换到editable=true
时,都可以进行列宽调整 (解决了这些已报告的 Tiptap 问题:1、2、3。)
组件
组件 | 描述 |
---|---|
RichTextEditor | 一个直接渲染 MUI 风格 Tiptap 富文本编辑器字段的一体化组件。内部使用了许多下面的组件。参见上面的"开始使用"注意事项。简而言之:<RichTextEditor ref={rteRef} content="<p>Hello world</p>" extensions={[...]} /> |
RichTextReadOnly | 一个直接渲染只读 Tiptap 编辑器内容的一体化组件。虽然 RichTextEditor (或 useEditor 、RichTextEditorProvider 和 RichTextContent )可以通过编辑器的 editable 属性设置为只读,但这是一个更简单和高效的版本,只渲染内容而不做其他操作(例如,不实例化工具栏、气泡菜单等在只读环境中可能不需要的内容,如果没有内容显示,它甚至不会实例化编辑器)。 |
RichTextEditorProvider | 使用 React context 使 Tiptap editor 对任何嵌套组件可用,这样就不需要在每个层级手动传递 editor 。除了一体化的 RichTextEditor 和 RichTextReadOnly 之外,大多数 mui-tiptap 组件都需要它作为父组件。通过 useRichTextEditorContext() 钩子在你自己的组件中使用提供的 editor 。 |
RichTextField | 渲染 Tiptap 富文本编辑器内容和控制菜单栏。使用 "outlined" 变体时,渲染类似于 Material UI TextField 的带边框 UI。"standard" 变体没有轮廓/边框。 |
MenuBar | 一个可折叠的、可选固定的容器,用于在编辑器内容顶部显示编辑器控件。(此组件用于包含 RichTextEditor 的 renderControls 和 RichTextField 的 controls ,但如果你想做一些更自定义的操作,也可以直接使用。) |
RichTextContent | 渲染 Material UI 样式的 Tiptap 富文本编辑器内容。应用所有用于格式化的 CSS 规则,作为 Tiptap 的 <EditorContent /> 组件的样式替代品。(在 RichTextEditor 和 RichTextField 中自动使用。) |
LinkBubbleMenu | 在查看、创建或编辑链接时渲染气泡菜单。需要 Tiptap Link 扩展(@tiptap/extension-link )和 mui-tiptap 的 LinkBubbleMenuHandler 扩展。与 <MenuButtonEditLink /> 组件 配合使用效果很好。如果你使用 RichTextEditor ,通过 RichTextEditor 的 children 渲染属性包含此组件。否则,将 LinkBubbleMenu 作为调用 useEditor 并渲染 RichTextField 或 RichTextContent 的组件的子组件。(只要在 Tiptap editor 强制更新时重新渲染,气泡菜单本身就会被适当定位,如果它是使用 useEditor 的组件的子组件,就会发生这种情况)。参见 src/demo/Editor.tsx 中的示例。 |
TableBubbleMenu | 当用户的光标/选择位于表格内时,渲染一个气泡菜单来操作表格内容(添加或删除列或行、合并单元格等)。用于 mui-tiptap 的 TableImproved 扩展 或 Tiptap 的 @tiptap/extension-table 扩展。如果你使用 RichTextEditor ,通过 RichTextEditor 的 children 渲染属性包含此组件。否则,将 TableBubbleMenu 作为调用 useEditor 并渲染 RichTextField 或 RichTextContent 的组件的子组件。(只要在 Tiptap editor 强制更新时重新渲染,气泡菜单本身就会被适当定位,如果它是使用 useEditor 的组件的子组件,就会发生这种情况)。参见 src/demo/Editor.tsx 中的示例。 |
ControlledBubbleMenu | 用于构建自定义气泡菜单的通用组件,解决了 Tiptap 的 BubbleMenu 的一些缺点。这是 LinkBubbleMenu 和 TableBubbleMenu 在底层使用的组件。 |
ColorPicker | 一个包含色相/饱和度/透明度渐变选择器的颜色选择器(通过 react-colorful),还有一个直接输入颜色的文本输入框,以及可选的 swatchColors 用于颜色预设。在底层被 MenuButtonColorPicker /MenuButtonTextColor /MenuButtonHighlightColor 使用。重要的属性:• swatchColors :显示为预设按钮的颜色数组。• colorToHex :覆盖默认的将给定 CSS 颜色字符串转换为十六进制格式字符串的实现(例如 "#ff0000" )。如果给定的颜色无法解析为有效颜色,应返回 null。详见 ColorPickerProps 定义,如使用更全功能库如 colord 或 tinycolor2 的示例。• disableAlpha :如果为 true,禁用透明度滑块。• value /onChange :受控的颜色值字符串(未设置时为空字符串)及其变更回调。 |
ColorSwatchButton | 渲染一个显示并允许选择颜色预设的按钮。被 ColorPicker 用于其 swatchColors 。 |
控件组件
这些控件组件帮助你快速组装菜单栏,用于你可能想使用的各种 Tiptap 扩展。
你可以覆盖这些组件的所有属性(例如更改 IconComponent
、tooltipLabel
、显示哪个快捷键的 tooltipShortcutKeys
、onClick
行为等)。或者使用基础的 MenuButton
和 MenuSelect
组件轻松创建适用于你自己的扩展和用例的控件。
扩展 | mui-tiptap 组件 |
---|---|
@tiptap/extension-blockquote | MenuButtonBlockquote |
@tiptap/extension-bold | MenuButtonBold |
@tiptap/extension-bullet-list | MenuButtonBulletedList |
@tiptap/extension-color | MenuButtonTextColor (接受可选的 defaultTextColor 属性。其他自定义详情请参见下面的 MenuButtonColorPicker 。) |
@tiptap/extension-code | MenuButtonCode |
@tiptap/extension-code-block | MenuButtonCodeBlock |
@tiptap/extension-font-family | MenuSelectFontFamily (使用 options 属性指定可选择的字体系列,如 [{ label: "等宽字体", value: "monospace" }, ...] ) |
mui-tiptap 的 FontSize | MenuSelectFontSize (使用 options 属性覆盖默认的大小选项) |
mui-tiptap 的 HeadingWithAnchor 或 @tiptap/extension-heading | MenuSelectHeading |
@tiptap/extension-highlight | • MenuButtonHighlightColor :用于Highlight 的multicolor: true 模式。接受可选的defaultMarkColor 。有关其他自定义详情,请参见下面的MenuButtonColorPicker 。• MenuButtonHighlightToggle :用于Highlight 的默认multicolor: false 模式 |
@tiptap/extension-history | MenuButtonRedo ,MenuButtonUndo |
@tiptap/extension-horizontal-rule | MenuButtonHorizontalRule |
mui-tiptap的ResizableImage 或 @tiptap/extension-image | • MenuButtonAddImage :通用按钮。提供自己的onClick 行为(例如,像这样让用户提供图片URL)。• MenuButtonImageUpload :上传图片。提供onUploadFiles 属性来处理文件上传并返回可服务的URL。另请参见mui-tiptap的 insertImages 工具,用于在Tiptap编辑器内容中插入图片。 |
@tiptap/extension-italic | MenuButtonItalic |
@tiptap/extension-link | MenuButtonEditLink (需要mui-tiptap的LinkBubbleMenuHandler 扩展和<LinkBubbleMenu /> 组件) |
@tiptap/extension-list-item | MenuButtonIndent ,MenuButtonUnindent |
@tiptap/extension-ordered-list | MenuButtonOrderedList |
@tiptap/extension-paragraph | MenuSelectHeading |
@tiptap/extension-strike | MenuButtonStrikethrough |
@tiptap/extension-subscript | MenuButtonSubscript |
@tiptap/extension-superscript | MenuButtonSuperscript |
mui-tiptap的TableImproved 或 @tiptap/extension-table | • 插入新表格:MenuButtonAddTable • 编辑表格(添加列、合并单元格等): TableBubbleMenu ,或如果需要替代气泡菜单的UI,则使用TableMenuControls |
@tiptap/extension-task-list | MenuButtonTaskList |
@tiptap/extension-text-align | MenuSelectTextAlign (一体化选择)或 MenuButtonAlignLeft 、MenuButtonAlignCenter 、MenuButtonAlignRight 、MenuButtonAlignJustify (单独按钮) |
@tiptap/extension-underline | MenuButtonUnderline |
其他控件组件: |
MenuButtonColorPicker
:一个用于打开mui-tiptapColorPicker
的按钮,让用户选择颜色。被MenuButtonTextColor
和MenuButtonHighlightColor
使用,它们的属性扩展了MenuButtonColorPicker
。重要属性:swatchColors
:显示为"预设"颜色按钮的颜色选项数组。value
/onChange
:当前颜色(CSS字符串)及其变更回调。这是一个受控组件,因此必须提供这些属性,除非您使用MenuButtonTextColor
或MenuButtonHighlightColor
,它们会处理该逻辑。popperId
:点击此按钮时显示的颜色选择器弹出框的唯一HTML ID(用于无障碍访问的aria-describedby
)。PopperProps
:覆盖容纳颜色选择器的弹出框的属性。ColorPickerProps
:覆盖mui-tiptapColorPicker
的属性,如colorToHex
以自定义颜色解析逻辑。
MenuButtonRemoveFormatting
:一个控制按钮,用于移除所有内联格式标记(调用Tiptap的unsetAllMarks()
)MenuDivider
:渲染一个垂直线分隔符,用于分隔菜单栏的不同部分并隐式地分组不同的控件。MenuControlsContainer
:为作为children
提供的不同编辑器控件组件提供一致的间距。 通常,您将像这样定义控件(用于RichTextEditor
的renderControls
或RichTextField
的controls
):
<MenuControlsContainer>
<MenuSelectHeading />
<MenuDivider />
<MenuButtonBold />
<MenuButtonItalic />
{/* 在这里添加更多您选择的控件 */}
</MenuControlsContainer>
本地化
所有的菜单按钮、选择组件和气泡菜单都允许你通过props覆盖它们的默认标签和内容。以下是一些示例。
按钮
通常情况下,使用 tooltipLabel
:
<MenuButtonBold tooltipLabel="切换粗体" />
MenuButtonTextColor
和 MenuButtonHighlightColor
组件还有一个 labels
prop,用于覆盖颜色选择器弹出窗口的内容。
<MenuButtonTextColor
tooltipLabel="文字颜色"
labels={{
cancelButton: "取消",
removeColorButton: "重置",
removeColorButtonTooltipTitle: "移除颜色",
saveButton: "确定",
textFieldPlaceholder: '例如: "#7cb5ec"',
}}
/>
选择器
<MenuSelectFontFamily
options={[
{ label: "等宽字体", value: "monospace" },
{ label: "衬线字体", value: "serif" },
]}
aria-label="字体系列"
emptyLabel="字体系列"
tooltipTitle="更改字体系列"
unsetOptionLabel="重置"
/>
<MenuSelectFontSize
aria-label="字体大小"
tooltipTitle="更改字体大小"
unsetOptionLabel="重置"
/>
<MenuSelectHeading
aria-label="标题类型"
tooltipTitle="更改标题类型"
labels={{
empty: "更改为…",
paragraph: "普通文本",
heading1: "标题1",
heading2: "标题2",
heading3: "标题3",
heading4: "标题4",
heading5: "标题5",
heading6: "标题6",
}}
/>
<MenuSelectTextAlign
aria-label="文本对齐方式"
tooltipTitle="更改文本对齐方式"
options={[
{
value: "left",
label: "左对齐",
shortcutKeys: ["mod", "Shift", "L"],
IconComponent: MyCustomLeftAlignIcon,
},
{
value: "right",
label: "右对齐",
shortcutKeys: ["mod", "Shift", "R"],
IconComponent: MyCustomRightAlignIcon,
},
]}
/>
气泡菜单
<LinkBubbleMenu
labels={{
viewLinkEditButtonLabel: "编辑链接",
viewLinkRemoveButtonLabel: "移除链接",
editLinkAddTitle: "添加新链接",
editLinkEditTitle: "更新此链接",
editLinkCancelButtonLabel: "取消更改",
editLinkTextInputLabel: "文本内容",
editLinkHrefInputLabel: "URL",
editLinkSaveButtonLabel: "保存更改",
}}
/>
<TableBubbleMenu
labels={{
insertColumnBefore: "在此之前添加新列",
insertColumnAfter: "在此之后添加新列",
deleteColumn: "删除当前列",
// 还有更多选项。详情请查看props类型定义!
}}
/>
选择编辑器扩展
浏览Tiptap官方扩展,并查看mui-tiptap
的额外扩展。最简单的入门方式是安装并使用Tiptap的StarterKit
扩展,它捆绑了几个常用的Tiptap扩展。
要使用扩展,你需要(1)安装其包,(2)在实例化编辑器时将扩展包含在extensions
数组中(通过<RichTextEditor extensions={[]} />
或useEditor({ extensions: [] })
)。
扩展优先级和顺序
需要更高优先级的扩展(用于其键盘快捷键等)应该在你的扩展数组中靠后放置。(参见Tiptap关于扩展插件优先级和顺序的一般说明此处。)例如:
- 将
TableImproved
(或Table
)扩展放在数组的第一位。 - 如底层
prosemirror-tables
包中所述,表格编辑插件应具有最低优先级,因为它依赖于键盘和鼠标事件,但其他插件可能需要先处理这些事件。例如,如果你想在表格内缩进或取消缩进列表项,你应该能够通过按Tab键来实现,只有在不在这样的嵌套节点内时,Tab键才会在表格单元格间移动。 - 将
Blockquote
扩展放在Bold
扩展之后,这样Blockquote
的键盘快捷键优先级更高。 - 否则,
Blockquote
的键盘快捷键(Cmd+Shift+B)会错误地切换粗体标记(由于其"重叠"的Cmd+b快捷键)。(参见相关Tiptap问题这里和这里。) - 将
Mention
扩展放在列表相关扩展(TaskList
、TaskItem
、BulletList
、OrderedList
、ListItem
等)之后,这样在提及建议上按"Enter"键将选择它,而不是创建新的列表项(当试图在现有列表项内@提及某人时)。
其他扩展提示
- 如果你希望
Subscript
和Superscript
扩展互斥,使文本不能同时为上标和下标,请使用excludes
配置参数来相互排除。 - 如此Tiptap问题中所述。例如:
const CustomSubscript = Subscript.extend({
excludes: "superscript",
});
const CustomSuperscript = Superscript.extend({
excludes: "subscript",
});
- 如果你更喜欢能够为内联
Code
标记添加样式(例如,使其加粗、添加链接、更改字体大小),你应该扩展该扩展并覆盖excludes
字段,因为默认情况下它使用"_"
来使其与所有其他标记互斥。例如,要允许你将Code
与任何其他内联标记一起应用,使用excludes: ""
,或要使其与除斜体以外的所有标记一起工作,使用:
Code.extend({ excludes: "italic" });
图片的拖放和粘贴
你可以为RichTextEditor
组件或useEditor
提供editorProps
,并提供handleDrop
和handlePaste
选项,以分别添加对图片文件拖放和粘贴的支持。查看mui-tiptap示例以了解其实际应用。mui-tiptap的insertImages
工具在这方面很有用,可以将上传的文件插入到编辑器内容中。
当content
变化时重新渲染RichTextEditor
默认情况下,RichTextEditor
使用content
的方式与Tiptap的useEditor
相同:它设置编辑器的初始内容,而content
变量的后续更改将_不会_改变渲染的内容。(只有用户的编辑器交互才会改变。)这可以避免在用户主动输入或编辑时覆盖内容的烦恼。
将RichTextEditor
/useEditor
用作完全"受控组件"(即在编辑器的每次onUpdate
调用时更改content
)并不高效,因为编辑器内容必须序列化以获取HTML字符串(getHTML()
)或ProseMirror JSON(getJSON()
)(参见Tiptap文档和这个讨论)。
但如果你在某些情况下需要这种行为,比如你在组件外部改变了content
且与用户的编辑器交互无关,你可以在钩子中调用editor.commands.setContent(content)
(文档)来更新编辑器文档。
例如,你可以使用类似以下的代码,它(1)只在编辑器处于只读状态或未聚焦时调用setContent
(旨在避免丢失用户正在进行的任何更改,但请记住isFocused
本身的变化不会导致重新渲染,因此不会重新运行效果),以及(2)尝试保留用户当前的选择/光标位置:
const editor = rteRef.current?.editor;
useEffect(() => {
if (!editor || editor.isDestroyed) {
return;
}
if (!editor.isFocused || !editor.isEditable) {
// 使用queueMicrotask,参考 https://github.com/ueberdosis/tiptap/issues/3764#issuecomment-1546854730
queueMicrotask(() => {
const currentSelection = editor.state.selection;
editor
.chain()
.setContent(content)
.setTextSelection(currentSelection)
.run();
});
}
}, [content, editor, editor?.isEditable, editor?.isFocused]);
你也可以通过<RichTextEditor … editorDependencies={[content]} />
(或等效地将其包含在你的useEditor
依赖数组中)将content
作为编辑器依赖项传递,这将在值发生变化时强制重新创建整个编辑器。这是一个效率较低的选项,可能会导致编辑器重建时出现视觉"闪烁"。
请注意,如果这些内容更新来自其他用户的更改(例如保存到数据库),使用Yjs的协作编辑功能可能会更好,而不是依赖于content
。
贡献
从这里开始。