背景
项目电子病历技术选型采用 slate
, 使用 slate-plugins
加快开发效率。本文主要叙述如何采用 slate-plugins
的形式开发自定义插件。以分割线组件举例。
组件开发
目录结构如下
├── plugins
│ ├── line 逻辑组件
│ │ ├── createLinePlugin.ts
│ │ ├── defaults.ts
│ │ ├── index.ts
│ │ └── withLine.ts
│ └── line-ui 样式组件
│ ├── LineElement
│ ├── ToolBarLine
│ └── index.ts
插件采用逻辑和样式分离的开发方式。
逻辑组件 line
line/
文件夹下存放组件逻辑行为的代码。 核心是返回一个 SlatePlugin
函数。
export interface SlatePlugin<T extends SPEditor = SPEditor> extends Partial<DOMHandlers<T>> {
/**
* @see {@link Decorate}
*/
decorate?: Decorate<T>;
/**
* @see {@link DeserializeHtml}
*/
deserialize?: Deserialize<T>;
/**
* Inline element types.
*/
inlineTypes?: (editor: T) => string[];
/**
* @see {@link OnChange}
*/
onChange?: OnChange<T>;
/**
* Plugin keys to support configuration.
*/
pluginKeys?: string | string[];
/**
* @see {@link RenderElement}
*/
renderElement?: RenderElement<T>;
/**
* @see {@link RenderLeaf}
*/
renderLeaf?: RenderLeaf<T>;
/**
* @see {@link SerializeHtml}
*/
serialize?: Serialize;
/**
* Void element types.
*/
voidTypes?: (editor: T) => string[];
/**
* Editor method overriders.
*/
withOverrides?: WithOverride | WithOverride[];
}
组件的所有行为逻辑都可以使用单个文件的形式开发,最后由 createLinePlugin.ts
方法导出。
// createLinePlugin.ts
import {
getRenderElement,
// eslint-disable-next-line no-unused-vars
getSlatePluginTypes,
SlatePlugin
} from '@udecode/slate-plugins-core'
import { ELEMENT_LINE } from './defaults'
import { withLine } from './withLine'
export const createLinePlugin = (): SlatePlugin => ({
pluginKeys: ELEMENT_LINE,
renderElement: getRenderElement(ELEMENT_LINE),
voidTypes: getSlatePluginTypes(ELEMENT_LINE),
withOverrides: withLine()
})
方法指定了组件的 type
、渲染函数、对 editor
的方法重写、serialize
、deserialize
等等。
其中 index.ts
文件为 barrelsby 自动生成, 可以方便的导出所有方法和变量,易于引用。
barrelsby -d "you dirPath" -n -q -S
/**
* @file Automatically generated by barrelsby.
*/
export * from './createLinePlugin'
export * from './defaults'
export * from './withLine'
样式组件 line-ui
样式组件主要分为两块
组件渲染函数
即为 render 函数,渲染出想要的组件即可,样式内容在 LineElement.styles.ts
中定义。使用的样式框架为 Component-Styling
// LineElement.tsx
import * as React from 'react'
import {
ClassName,
getRootClassNames,
RootStyleSet,
StyledElementProps
} from '@udecode/slate-plugins-ui-fluent'
import { styled } from '@uifabric/utilities'
import { getLineElementStyles } from './LineElement.styles'
const getClassNames = getRootClassNames()
export const LineElementBase = ({
attributes,
children,
className,
styles
}: StyledElementProps) => {
const classNames = getClassNames(styles, {
className
// Other style props
})
return (
<div {...attributes} className={classNames.root}>
{children}
</div>
)
}
/**
* BlockElement
*/
export const LineElement = styled<
StyledElementProps,
ClassName,
RootStyleSet
>(LineElementBase, getLineElementStyles, undefined, {
scope: 'BlockElement'
})
toolbar渲染函数
核心代码为 ToolbarLink.tsx
文件,导出一个放在 Toolbar
上的组件,此处可以定义工具条的交互,比如点击插入、高亮状态等等。
// ToolbarLink.tsx
import * as React from 'react'
import {
insertNodes
} from '@udecode/slate-plugins-common'
import {
getSlatePluginType,
useEventEditorId,
TElement,
useStoreEditorState
} from '@udecode/slate-plugins-core'
import {
ToolbarButton,
ToolbarButtonProps
} from '@udecode/slate-plugins-toolbar'
import { Editor, Transforms } from 'slate'
import { ELEMENT_LINE } from '../../line'
export interface ToolbarLineProps extends ToolbarButtonProps {}
export const ToolbarLine = ({ getLinkUrl, ...props }: ToolbarLineProps) => {
const editor = useStoreEditorState(useEventEditorId('focus'))
return (
<ToolbarButton
onMouseDown={async (event) => {
if (!editor) return
// 跳到前一个元素
const afterPath = Editor.after(editor, editor.selection, { unit: 'block' })
event.preventDefault()
// 插入一个line 元素 光标移动到下一行
insertNodes<TElement>(editor, {
type: ELEMENT_LINE,
children: [{ text: '' }],
at: afterPath
})
const pPath = Editor.after(editor, editor.selection)
insertNodes<TElement>(editor, {
type: 'p',
children: [{ text: '' }],
at: pPath
})
Transforms.select(editor, pPath)
Transforms.collapse(editor, {
edge: 'start'
})
}}
{...props}
/>
)
}
引入组件
逻辑组件引入
完成逻辑组件和样式组件的开发,一个组件就算开发完成,应该如何引入改组件呢。 方法和引入 slate-plugins
自身组件一样。
在初始化 SlatePlugins
的时候,在 plugins
中调用导出函数即可。
...
const pluginsMemo = useMemo(() => {
const plugins = [
...
createLinePlugin()
...
]
return plugins
}, [])
...
<SlatePlugins
plugins={pluginsMemo}
components={components}
...
>
....
</SlatePlugins>
完成以上操作,即可把逻辑组件加入到编辑器当中。
样式组件引入
组件样式引入
在初始化 SlatePlugins
时,还有一个 components
参数,这个参数用于正确渲染元素。
import { ELEMENT_LINE } from './plugins/line'
const components = withStyledPlaceHolders(
createSlatePluginsComponents({
[ELEMENT_H1]: withProps(StyledElement, {
styles: {
root: {
color: '#000'
}
}
}),
[ELEMENT_LINE]: LineElement // 加入分割线组件
})
)
<SlatePlugins
plugins={pluginsMemo}
components={components}
...
>
....
</SlatePlugins>
以上完成了组件渲染。
toolbar组件引入
import { ToolbarLine } from './plugins/line-ui'
export const ToolbarButtons = () => {
return (
<>
<ToolbarElement type={getSlatePluginType(editor, ELEMENT_H1)} icon={<LooksOne />} />
<ToolbarLine icon={<FileBreak />} />
</>
)
}
只需要简单的在工具条组件中引入我们开发好的组件即可。
完
下次在分享