默认实现总览
教程里,DefaultEditorComponent 一直是个黑盒:把编辑器核心交给它,一个能用的编辑界面就出现了。
这一部分把这个盒子拆开。本页先讲清楚默认实现在整个库里的位置、它为什么被设计成一个可以整体替换的层,
以及那个组件到底替你装配了哪些东西;后面几页分别讲界面、键盘、渲染器工厂、定制手段和印刷侧。
库分成哪两半
库的代码分成两大部分。一部分是核心:概念与节点的定义、编辑器核心、印刷器核心。 它们只关心「文档树是什么样、怎么被修改、怎么被渲染」,不做任何界面上的决定。 核心里没有一个按钮、没有一种配色、没有一条快捷键。
另一部分就是默认实现。它是对「界面应该长什么样」这个问题给出的一整套答案: 一块纸张、一条侧边栏、每个节点上的一排按钮、两个可以拖动的浮动面板、一整套快捷键, 以及印刷侧的一套排版部件。这套答案是有立场的,它假设你在做的是一个偏长文、偏严肃、 需要频繁操作结构的编辑器;如果你的场景不是这样,这一层可以整个不要。
这条分界线的位置解释了一个乍看奇怪的设计:编辑器和印刷器都各自分成了「核心」和「组件」两个对象。
EditorCore 与 EditorComponent 是一对,Printer 与
PrinterComponent 是另一对。核心保存信息但不负责绘制,组件负责绘制但不保存信息。
拆开的好处是同一个核心可以同时被多个组件使用。默认实现里编辑抽象节点的那个浮动窗口就是现成的例子: 它是又一个编辑器组件,和主编辑器共用同一个编辑器核心,因此两边看到的概念定义、渲染器完全一致, 在脚注里插入的定理和在正文里插入的定理走的是同一套代码。
三档深度
对使用者来说,上面这条分界线意味着有三档不同深度的做法,代价和自由度依次增加。
| 做法 | 你要写的 | 适合 |
|---|---|---|
| 直接用默认实现 | 概念定义、渲染器,以及 DefaultEditorComponent 的几个参数 | 绝大多数情况 |
| 在默认实现上改造 | 加上自定义按钮、替换个别渲染器、改配置和主题 | 需要自己的操作或外观时 |
| 不用默认实现 | 基于 EditorComponent 自己搭整套界面 | 界面形态和默认实现差得很远时 |
第三档很少有人需要,但它是可行的:默认实现没有使用任何私有接口,它调用的东西你都能调用。
DefaultEditorComponent 装配了什么
这个组件本身几乎没有业务逻辑,它的价值在于把七八个必须同时存在、且顺序不能错的东西套在了一起。 把它渲染出来的结构从外往里剥,依次是:
| 层 | 作用 |
|---|---|
EditorConfigContext.Provider | 把样式配置(字体、边距、宽度)交给下游所有组件,渲染器里用 useEditorConfig() 读取 |
SnackbarProvider | 提示气泡的容器,来自 notistack |
IdxConflictSolver | 监视文档里是否出现重复的节点编号,出现时自动重新编号 |
KeyEventManager | 全局按键分发器,来自 @ftyyy/mouseless |
EditorComponent | 真正的编辑器组件,也就是那块可以打字的纸 |
EditorGlobalInfo.Provider | 把编辑器实例交给下游,侧边栏、浮动面板、你自己写的按钮都靠它拿到编辑器 |
DefaultSidebar / Areas / AbstractEditorArea | 侧边栏、两个浮动面板、抽象节点的浮动编辑器 |
其中三层值得单独说明。
第一层是 SnackbarProvider。它在这里出现的原因是,默认实现的按钮在操作成功或失败时会弹出提示,
而弹提示需要一个容器。它被放在编辑器内部,意味着你在自定义按钮里可以直接调用 useSnackbar(),
不需要自己再套一层。反过来,如果你想在编辑器之外的地方(比如页面顶栏)弹提示,
那就需要在更外层自己再放一个 SnackbarProvider。
第二层是 IdxConflictSolver。教程第 1 章说过,每个概念节点都有一个全文唯一的编号
idx,交叉引用就是靠它定位的。复制粘贴会天然地破坏这个唯一性:复制一个定理再粘贴,
文档里就出现了两个 idx 相同的节点,引用会指向错误的目标。这个组件在每次文档变化后检查冲突,
发现之后给其中一个重新编号。这是一件必须做、但没有人愿意自己写的事情,所以默认实现替你做了。
第三层是 KeyEventManager,它是整套键盘操作的中枢。所有按键先到它这里,
再按当前状态分发给对应的界面部件;默认编辑器在这里注册了四组界面元素,并登记了一批需要
阻止浏览器默认行为的组合键。这套机制单独一页讲,见键盘操作。
这一部分怎么读
剩下五页各自独立,可以按需要挑着读,顺序大致是从「看得见的」到「改得动的」:
| 页 | 内容 |
|---|---|
| 编辑界面 | 纸张、节点上的八个按钮、参数抽屉、抽象节点、侧边栏、两个浮动面板,以及必须由你摆放的 AreaContainer |
| 键盘操作 | 空间导航的模型、全部组合键、按键提示 |
| 编辑渲染器工厂 | 各类节点的渲染器工厂及其选项、UniversalExtra、渲染器里可用的 hook |
| 定制默认编辑器 | 样式配置、追加按钮、主题,以及替换单个渲染器 |
| 印刷侧的默认实现 | DefaultPrinterComponent、印刷配置、排版部件 |
在线演示的源码(仓库的 demo/ 目录)是这一部分内容的一个完整用例,
读到某处觉得抽象时可以去那里对照。