对于复杂的 DOM 树,我们需要使用一套规则来约束 DOM 树结构,包括节点嵌套、节点属性、以及一些特定的行为。
我们可以对单个节点制定规则,当然也可以针对三种节点类型mark
inline
block
制定全局规则。card
属于我们一种特殊类型,本质上他们也可以归纳为 inline
和 block
类型
如果一个节点不在约束规则中,那么它将会被过滤掉,包括属性、样式,如果你需要这个属性,那么它一定要出现在规则中,否则都不会被保留
单个规则类型:SchemaRule
全局规则类型:SchemaGlobal
一个规则的包含以下属性:
name
DOM 节点名称,可选值type
类型,mark
inline
block
。在不制定节点名称情况下,将根据 type 设置全局规则。必须值attributes
属性,设置节点属性规则,是一个对象。可选值isVoid
是否是空节点,类似 br、img 等标签,是无法设置子节点的,包括文本。可选值例子:
//单个节点规则{name: 'p',type: 'block',},{name: 'span',type: 'mark',attributes: {style: {color: "@color"}}}//按类型全局规则{type: "block",attributes: {id: "*"}}
在通用规则之外,我们还为块级节点额外定制了两个属性
{...allowIn?: Array<string>;canMerge?: boolean;}
allowIn
允许节点可以放入的块级节点名称,默认他们的值为 $root
(编辑器根节点)。这通常在嵌套节点中使用,例如:ul li 无序列表下有 li 子节点,它也是独占一行的属于块级节点。如果一个块级节点没有指定可放入的块级节点,那么它将会被过滤掉canMerge
相邻的两个块级节点是否可以合并。例如:引用插件 blockquote ,在两个 blockquote 节点处于相邻状态时,它们的子节点会被合并到一个 blockquote 节点下,因为它们相邻单独存在是没有意义的,反而还会增加文档的复杂性类型:SchemaBlock
属性值类型 SchemaValue
,它由 SchemaValueObject
和 SchemaValueBase
组成
export type SchemaValueBase =| RegExp| Array<string>| string| ((propValue: string) => boolean)| '@number'| '@length'| '@color'| '@url'| '*';export type SchemaValueObject = {required: boolean;value: SchemaValueBase;};
我们可以看到属性值是可以很灵活配置的,支持:
@number
数量,数字@length
长度,包括像素带单位的像素值,例如:10px@color
可以判断该属性值是否是一个“颜色”。例如:#ffffff rgb(0,0,0,0)@ulr
判断该属性值是否是一个链接*
任意值,包括 undefined、null 等空值都可以通过效验除了值的判定外,默认情况下,这些属性设置后都是可选属性,我们还可能需要为节点制定必要的属性,以区分相通名称节点之间的差别和所属插件类型判别,例如:
表示前景色的样式节点
<span style="color:#ffffff">Hello</span>
{name: "span",type: "mark",attributes: {style:{color: "@color"}}}
表示前景色和背景色的样式节点
<span style="color:#ffffff;background-color:#000000">Hello</span>
{name: "span",type: "mark",attributes: {style:{color: "@color","background-color": "@color"}}}
这两个样式节点名称都是 span 而且都包含 color 样式,因为默认属性都是可选属性,所以我们在判定一个节点时会忽略这些可选属性,剩下的名称也是一样的,这样就会造成逻辑错误,出现很多意外情况。
所以这里我们需要使用 SchemaValueObject
类型的值,来表明这两个节点的唯一性,这些标明的属性也是节点最主要的特征点
{name: "span",type: "mark",attributes: {style:{color: {required: true,value:"@color"}}}}{name: "span",type: "mark",attributes: {style:{color: {required: true,value:"@color"},"background-color": {required: true,value:"@color"}}}}
引擎按照功能和特性对节点进行了划分 mark
inline
block
card
,为了满足这些划分后的节点正常工作以及引擎需要,我们制定了一些默认规则,这些规则会和我们自定义规则合并后一起使用,所以不建议自定义规则去覆盖它们
import { SchemaGlobal, SchemaRule } from '../types';import { CARD_KEY, CARD_TYPE_KEY, CARD_VALUE_KEY } from './card';import { ANCHOR, CURSOR, FOCUS } from './selection';const defualtSchema: Array<SchemaRule | SchemaGlobal> = [{name: 'p',type: 'block',},{name: 'br',type: 'inline',isVoid: true,},{name: ANCHOR,type: 'inline',isVoid: true,},{name: FOCUS,type: 'inline',isVoid: true,},{name: CURSOR,type: 'inline',isVoid: true,},{type: 'block',attributes: {'data-id': '*',},},{name: 'card',type: 'inline',attributes: {name: {required: true,value: /\w+/,},type: {required: true,value: 'inline',},value: '*',},},{name: 'span',type: 'inline',attributes: {[CARD_KEY]: {required: true,value: /\w+/,},[CARD_TYPE_KEY]: {required: true,value: 'inline',},[CARD_VALUE_KEY]: '*',class: '*',contenteditable: '*',},},{name: 'card',type: 'block',attributes: {name: {required: true,value: /\w+/,},type: {required: true,value: 'block',},value: '*',},},{name: 'div',type: 'block',attributes: {[CARD_KEY]: {required: true,value: /\w+/,},[CARD_TYPE_KEY]: {required: true,value: 'block',},[CARD_VALUE_KEY]: '*',class: '*',contenteditable: '*',},},];export default defualtSchema;