am-editor-002/docs/docs/concepts-schema.zh-CN.md

6.3 KiB
Raw Blame History

Schema

对于复杂的 DOM 树,我们需要使用一套规则来约束 DOM 树结构,包括节点嵌套、节点属性、以及一些特定的行为。

我们可以对单个节点制定规则,当然也可以针对三种节点类型mark inline block制定全局规则。card 属于我们一种特殊类型,本质上他们也可以归纳为 inlineblock 类型

如果一个节点不在约束规则中,那么它将会被过滤掉,包括属性、样式,如果你需要这个属性,那么它一定要出现在规则中,否则都不会被保留

设置

单个规则类型: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

attributes 值

属性值类型 SchemaValue,它由 SchemaValueObjectSchemaValueBase 组成

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;