Day 11. slate × Interfaces × Document-Model

https://ithelp.ithome.com.tw/upload/images/20210926/20139359tu5MSeiweV.png

接下来的篇章我们会把目光聚焦於 interfaces/ 这个目录底下的内容,想确认 slate package 完整的 src directory 的读者们可以回到 上一篇 做确认。

slate Interfaces


我们首先来为它底下的 files 做点简单的分类与介绍:

https://ithelp.ithome.com.tw/upload/images/20210926/20139359by7pwbU5II.png

除了 custom-types.ts 是专门定义与处理 type extension 之外,其他的 file 都各自代表着一组 concept ,也就是上图画了红线的部分。它们各自同时拥有:定义这组 concept 相关的 types 、这组 concept 提供给开发者使用的 method apis。

我们用一个名叫 Example 的范例来讲解 concept file 里基本的 code-structure :

export type ExampleType = {
	// type declaration, use "interface" to declare it if it's extendable
}

export interface ExampleInterface {
	// Concept method apis interface declaration
}

export const Example: ExampleInterface = {
	// Concept methods implementation
}

如果 type 被定义为 extendable 的话,我们则能看到额外引用 custom-types.ts 里的 ExtendedType type utility 去扩充其基本 BaseType 的 type 。

type 来 type 去的是想乱死谁,直接上个范例最清楚,一样用 Example 来介绍。

import { ExtendedType } from './custom-types';

export interface BaseExample {
  // type declaration
}

export type Example = ExtendedType<'Example', BaseExmaple>

要注意其实上面的范例放进 ExtendedType 是不过关的,因为 ExtendedType 吃得第一个 string generic 是有用一个 Union type 限制住的,详情我们後续在 custom-type 的篇章会再深入讲解,目前只是先让读者们有个概念而已。


接着我们看到有被黄框的 files ,这些是我们在建立 editor 与操作 editor value 时主要会使用到的概念,我们接下来会花不少的篇幅,拿官方提供的注解当温开水配着服用,依序介绍里面的 types ,而各个 files 里提供的 method apis 因为数量众多又杂又乱,一条一条介绍完另一个 30 天就又过去了所以我们就不在这边细说,笔者的时间跟心态都算充裕的话再另外整理成 reference 提供给各位吧~

那麽就如标题所言,我们首先会针对 slate 的 document model 所需要用到的 concepts 做介绍,分别是: text 、 element。

https://ithelp.ithome.com.tw/upload/images/20210926/20139359GBkAbKAyk2.png

text.ts


这里面就只有一个 Extendable 的 Text type 而已,光看名字应该也能很直觉的反应出它代表的就是 editor 里的『文字资料』。

/**
 * `Text` objects represent the nodes that contain the actual text content of a
 * Slate document along with any formatting properties. They are always leaf
 * nodes in the document tree as they cannot contain any children.
 */

export interface BaseText {
  text: string
}

export type Text = ExtendedType<'Text', BaseText>

Text type 只规定了其中的内容物必须要有一个类别为 string 的 text property ,开发者可以依照自己的喜好定义其他的 properties ,就拿我们在 Day3 举过官方范例来看看。

下图只要有被红圈圈到的 Object 都各为一组 Text

https://ithelp.ithome.com.tw/upload/images/20210926/20139359zAomlQFvmw.png

再来看看它的 module declaration

type CustomText = {
  bold?: boolean
  italic?: boolean
  code?: boolean
  text: string
}

// slate module "Text" declaration
declare module 'slate' {
  interface CustomTypes {
    Text: CustomText
  }
}

把定义好的 module 引入就能使用自定义的 CustomText 随意开发了,就是这麽轻松写意。


Text 提供的 method apis 里有一个特别值得提到的 method 是 decorations()

/**
 * Get the leaves for a text node given decorations.
 */
decorations(node: Text, decorations: Range[]): Text[] {
    ... // method implementation
}

Range 的概念我们会在 下一篇 介绍,目前读者先知道它是 Slate 中描述一段可以横跨不同的 Text 甚至 Element 的『字元集范围』就好,一个 Range 里面会同时有 anchor 与 focus 这两个 properties 代表这个 range 的起始字元位置以及结束字元位置。

当我们丢给 decorations 合法的 Text 以及 range list ,也就是这些 Ranges 的范围是正确的位於该 Text 内时,它会动态地帮我们依照这些 Range 拆成一组 Text list。

const text = { text: 'This is a text example.' };
const ranges = [{
    anchor: { path: [0, 0], offset: 5 },
    focus: { path: [0, 0], offset: 7 },
}];

/**
returns: [
    { text: 'This ' },
    { text: 'is' },
    { text: ' a text example.' },
] */
Text.decorations(text, ranges);

当然你也可以放上自定义的 Text property

const text = { text: 'This is text example2.' };
const ranges = [{
    anchor: { path: [0, 0], offset: 5 },
    focus: { path: [0, 0], offset: 7 },
    bold: true,
}];

/**
returns: [
    { text: 'This ' },
    { text: 'is', bold: true },
    { text: ' text example2.' },
] */
Text.decorations(text, ranges);

嗯?干嘛多此一举?直接修改 document model 里的 value 不就好了吗?


笔者一开始也很困惑於这个功能存在的意义,确实直接修改 value 就能达到一模一样的效果了,後来在官方提供的 Search Highlighting example 里找到了答案:

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a2cd3927-6885-4be1-b0cd-b7f025568edc/ezgif.com-gif-maker拷贝.gif

它很适合用於开发 「修改的频率高,幅度大,却只修正 Text properties 而不会更改到文字的内容或其他 value 的功能」。正如其名 decorations ,它的工用就像是在整个工程的尾端轻轻的装饰文字的属性,因为不会去更动到 editor value ,所以可以省略掉许多更新整个 value tree 庞杂的运算,因此把它放在 view layer 的浅层,动态地计算 decorate 後的 Text ,会让速度整个快上非常非常多。(读者可以自己去实验看看 XD


element.ts


里面主要定义的 type 也只有一个 Extendable 的 Element type 而已,slate 的 document model 就是由 ElementText 这两种 types 所组成的,相对於 Text 所代表的文字资料, Element 代表 editor 里的元件资料

/**
 * `Element` objects are a type of node in a Slate document that contain other
 * element nodes or text nodes. They can be either "blocks" or "inlines"
 * depending on the Slate editor's configuration.
 */

export interface BaseElement {
  children: Descendant[]
}

export type Element = ExtendedType<'Element', BaseElement>

Element type 只规定了其中的内容物必须要有一个类别为 children 的 Descendant[] property ,开发者一样可以依照自己的喜好定义其他的 properties 。

Descendant type 象徵着 slate document tree 里的子节点,我们会在之後的 node concept 章节 详细介绍,我们现在先偷瞄一眼 Descendant type 的定义

/**
 * The `Descendant` union type represents nodes that are descendants in the
 * tree. It is returned as a convenience in certain cases to narrow a value
 * further than the more generic `Node` union.
 */

export type Descendant = Element | Text

可以看到 ElementDescendant 这两个 type 是彼此关联的,在 Elementchildren property 里再放上另一个 Element 也是没问题的,这也是为什麽开发者可以很直观地透过 slate 建立拥有巢状结构的 feature ,只要照着 type 的规范走 slate 完全不在乎你的 document tree 长什麽样子,你爱怎麽巢就怎麽巢。

另外我们也可以在 Element type 的注解里看到,开发者也能针对各个不同的 elements 定义 blocksinlines 属性,这部分因为牵涉到 slate editor 的 config 定义,我们就放到後面介绍 editor concept 的篇章吧!

element.ts 里还有一个附加的 type 是 ElementEntry ,它主要是用在进行 iterate 相关操作的用途上,关於 iterate 我们之後会在整理出一篇文章跟各位分享分享。

哇今天的内容不少诶!来稍微整理一下:我们首先对 interfaces/ 这个目录底下的 files 做了个简单的分类 → 定义画了 红线 的 concept files 里的 code structure → 分别介绍了 Document model 里会出现的 Text type 以及 decorations methods 、 Element type 以及它与 Descendant type 的关系


感谢你的整理,今天的篇幅有那麽一点长,请读者花些时间消化一下,紧接着到下一篇我们要介绍 slate 是如何实现 document value 的『定位』这件事的。

咱们一样下一篇见啦~


<<:  D-4.Line_pay_api 串接(一)

>>:  Day20 感谢祭

RISC-V on Rust 从零开始(6) - 使用Spike模拟器

其实RISC-V官方也有开发了一个instruction accurate等级的模拟器Spike,只...

桌面端 YouTube 影片下载器--〖2022亲测〗

接下来为您介绍 10 款不错的第三方 YouTube 下载软件!让我们来看看哪个软件才是 2022 ...

Day10 Overlapping Example

昨天已经看过我们在实务上可能会遇到的需求,利用多个可能重复范围的配对池,当作匹配搜寻条件,今天让我们...

Day29:今天来聊一下CEH中讲的Cryptography

随着Internet越来越多地使用在商业和个人通讯,保护敏感讯息(例如:信用卡和 个人识别码(PIN...

[20] [烧瓶里的部落格] 10. 心得

这个部落格练习难度明显比前一份的难度要提升不少 也使用到一些共用的概念 首先介绍了工厂模式 [烧瓶里...