This commit is contained in:
hufeixiong
2021-07-10 23:19:56 +08:00
parent c27b4eb8e0
commit 7f7a839aef
20 changed files with 418 additions and 21 deletions

View File

@@ -2,11 +2,11 @@
* @Author: yehuozhili
* @Date: 2021-06-29 11:14:15
* @LastEditors: yehuozhili
* @LastEditTime: 2021-07-01 22:13:09
* @FilePath: \my-app\src\app.html
* @LastEditTime: 2021-07-10 20:30:13
* @FilePath: \dooringx\packages\dooringx-doc\src\app.html
-->
<!DOCTYPE html>
<html lang="en">
<html lang="zh">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.png" />

View File

@@ -3,11 +3,9 @@ title: dooringx-lib 如何工作?
sTitle: 介绍
order: 2
---
dooringx-lib 在运行时维护一套数据流主要分为json数据部分左侧组件部分右侧配置项部分快捷键部分弹窗部分事件与函数部分数据源部分。
dooringx-lib 在载入后会进行实例化,如果有插件需要传递给 config
其除了提供基础的拖拽、移动、缩放、全选等功能外,还可以使用暴露的组件。如果觉得组件不够定制化,可以调整样式或者自己重新写
开发者通过调用 api 来获取想要的数据,来开发出自己想要的功能。
对于概念部分请参考 dooringx-lib 基础,对于 api 部分请参考 api。
建议先学习 dooringx-lib 基础和 dooringx-lib 插件开发注意事项再去看 api

View File

@@ -1,7 +1,7 @@
---
title: store
sTitle: dooringx-lib基础
order: 3
order: 4
---
store 类似于 redux 的概念,它内部实现了 redo、undo、发布订阅、置换数据、强制刷新等功能。
@@ -10,8 +10,10 @@ store 可以在 config 中获取。
在最开始时,需要通过 useStoreState 与 react 结合,此时可以在任意位置使用 store.forceUpdate 强刷,也可以使用 state 获取 store 中的数据。
store 的主要数据是保存着每次修改 jsonSchema 队列。
store 的最重要功能是保存着每次修改 jsonSchema 队列。
如果你需要更新数据,在深拷贝后使用 setData 方法进行更新。
如果你需要更新时不记录在 redo 或 undo 上留下记录,那么请操作队列删除其中保存内容即可。
对于改变数据后想即使看见视图更新那么使用forceUpdate即可。

View File

@@ -1,5 +1,11 @@
---
title: functionCenter
title: 事件
sTitle: dooringx-lib基础
order: 3
order: 5
---
dooringx-lib 的事件是在eventCenter上它上面会集成functionCenter与一个事件链。
在eventCenter中可以获取组件注册的时机。时机类似于组件生命周期一样可以注册后在对应的时机进行调用。
而functionCenter中的函数则会与时机结合再由事件链对用户设定的队列进行统一处理。

View File

@@ -0,0 +1,12 @@
---
title: 命令
sTitle: dooringx-lib基础
order: 6
---
dooringx-lib 的命令是由commander进行管理。
内部默认提供redo与undo的命令您可以通过插件方式增加commander。
commander内部集成了快捷键配置使用键盘事件的key进行注册如果ctrl、alt、meta键可加对应的加号进行组合键注册内部忽略大小写注意不是忽略注册的键名大小写而是A和a的key处理时等价

View File

@@ -0,0 +1,15 @@
---
title: 弹窗
sTitle: dooringx-lib基础
order: 7
---
dooringx-lib内置弹窗系统弹窗系统是通过storeChanger进行转换而成。
所以在某些情景制作时,可能需要考虑是否在弹窗编辑下的情况。
每个弹窗是只保存block中的数据而事件等数据只会存在主数据内。
在弹窗保存后,弹窗数据会被置换于主数据内存着,需要编辑时重新置换出来。
可以使用storeChanger上的方法进行判断或者直接获取数据源数据等具体见API。

View File

@@ -0,0 +1,13 @@
---
title: 数据源
sTitle: dooringx-lib基础
order: 8
---
dooringx-lib 的数据源和前面说的store中存储的不是一个东西。
它位于dataCenter中。设计数据源的初衷是为了让不懂代码的人更好理解。
事件的运行完全可以脱离数据源运行,只要使用者知道如何去填写参数。
所以在事件配置时,可以多个选项在数据源中去获得数据转变为参数。

View File

@@ -0,0 +1,27 @@
---
title: 插件导入
sTitle: dooringx-lib插件开发
order: 9
---
dooringx-lib的插件需要一个类型为`Partial<InitConfig>`的对象。
对于多个插件需要使用dooringx-lib导出的`userConfigMerge`来进行合并。
userConfigMerge不是所有属性都会合并部分属性会进行覆盖。
```
* 部分无法合并属性如果b传了会以b为准
* initstore不合并
* leftallregistmap合并
* leftRenderListCategory合并
* rightRenderListCategory合并
* rightGlobalCustom 不合并
* initComponentCache合并
* initFunctionMap合并
* initDataCenterMap合并
* initCommandModule合并
* initFormComponents合并
```
config支持部分配置异步导入比如左侧分类等这个是实验性功能所以不推荐这么做。

View File

@@ -0,0 +1,29 @@
---
title: 左侧面板
sTitle: dooringx-lib插件开发
order: 10
---
左侧面板传入leftRenderListCategory即可。
```js
leftRenderListCategory: [
{
type: 'basic',
icon: <HighlightOutlined />,
displayName: '基础组件',
},
{
type: 'xxc',
icon: <ContainerOutlined />,
custom: true,
customRender: <div>我是自定义渲染</div>,
},
],
```
type是分类左侧组件显示在哪个分类由该字段决定。
icon则是左侧分类小图标。
当custom为true时可以使用customRender自定义渲染。

View File

@@ -0,0 +1,157 @@
---
title: 左侧组件
sTitle: dooringx-lib插件开发
order: 11
---
## 插件导入
左侧组件要至于对象的LeftRegistMap中。
左侧组件支持同步导入或者异步导入。
```js
const LeftRegistMap: LeftRegistComponentMapItem[] = [
{
type: 'basic',
component: 'button',
img: 'icon-anniu',
displayName: '按钮',
urlFn: () => import('./registComponents/button'),
},
];
```
如果需要异步导入组件则需要填写urlFn需要一个返回promise的函数。也可以支持远程载入组件只要webpack配上就行了。
如果需要同步导入组件则需要将组件放入配置项的initComponentCache中这样在载入时便会注册进componentRegister里。
```js
initComponentCache: {
modalMask: { component: MmodalMask },
},
```
## 组件编写
组件需要导出一个由ComponentItemFactory生成的对象。
```js
const MButton = new ComponentItemFactory(
'button',
'按钮',
{
style: [
createPannelOptions<FormMap, 'input'>('input', {
receive: 'text',
label: '文字',
text: 'yehuozhili',
}),
],
animate: [createPannelOptions<FormMap, 'animateControl'>('animateControl', {})],
actions: [createPannelOptions<FormMap, 'actionButton'>('actionButton', {})],
},
{
props: {
...
},
},
(data, context, store, config) => {
return <ButtonTemp data={data} store={store} context={context} config={config}></ButtonTemp>;
},
true
);
export default MButton;
```
其中第一个参数为组件注册名,第二个参数用来展示使用。
第三个参数用来配置右侧面板的配置项组件。其中键为右侧面板的分类,值为配置项组件数组。
第四个参数会配置组件的初始值,特别注意的是,制作组件必须要有初始宽度(非由内容撑开),否则会在适配时产生问题。
第五个参数是个函数你将获得配置项中的receive属性暂且都默认该配置为receive传来的配置比如上例中receive的是text则该函数中data里会收到该字段。
context一般只有preview和edit用来进行环境判断。
config可以拿到所有数据用来制作事件时使用。
第六个参数resize 是为了判断是否能进行缩放当为false时无法进行缩放。
第七个参数needPosition某些组件移入画布后会默认采取拖拽的落点该配置项默认为true为false时将使用组件自生top和left定位来放置。
## 事件注册
### 时机注册
前面说了事件有时机和函数所以组件内可以使用hook注册时机
```js
useDynamicAddEventCenter(pr, `${pr.data.id}-init`, '初始渲染时机'); //注册名必须带id 约定!
useDynamicAddEventCenter(pr, `${pr.data.id}-click`, '点击执行时机');
```
useDynamicAddEventCenter第一个参数是render的四个参数组成的对象。第二个参数是注册的时机名必须跟id相关这是约定否则多个组件可能会导致名称冲突并且方便查找该时机。
注册完时机后你需要将时机放入对应的触发位置上比如这个button的点击执行时机就放到onclick中
```js
<Button
onClick={() => {
eventCenter.runEventQueue(`${pr.data.id}-click`, pr.config);
}}
>
yehuozhili
</Button>
```
其中第一个参数则为注册的时机名第二个为render函数中最后个参数config
### 函数注册
函数由组件抛出,可以加载到事件链上。比如,注册个改变文本函数,那么我可以在任意组件的时机中去调用该函数,从而触发该组件改变文本。
函数注册需要放入useEffect中在组件卸载时需要卸载函数否则会导致函数越来越多。
```js
useEffect(() => {
const functionCenter = eventCenter.getFunctionCenter();
const unregist = functionCenter.register(
`${pr.data.id}+改变文本函数`,
async (ctx, next, config, args, _eventList, iname) => {
const userSelect = iname.data;
const ctxVal = changeUserValue(
userSelect['改变文本数据源'],
args,
'_changeval',
config,
ctx
);
const text = ctxVal[0];
setText(text);
next();
},
[
{
name: '改变文本数据源',
data: ['ctx', 'input', 'dataSource'],
options: {
receive: '_changeval',
multi: false,
},
},
]
);
return () => {
unregist();
};
}, []);
```
函数中参数与配置见后面函数开发。

View File

@@ -0,0 +1,22 @@
---
title: 右侧面板
sTitle: dooringx-lib插件开发
order: 12
---
右侧面板的配置和左侧面板一样:
```js
export interface RightMapRenderListPropsItemCategory {
type: string;
icon: ReactNode;
custom?: boolean;
customRender?: (type: string, current: IBlockType) => ReactNode;
}
```
type会影响左侧组件在开发时第三个参数的键名。那个键名中即代表该右侧中展示的type。
icon则是可以放文字或者图标用来进行面板切换的。
如果custom为true该面板下的显示可以通过customRender自定义。

View File

@@ -0,0 +1,101 @@
---
title: 右侧组件
sTitle: dooringx-lib插件开发
order: 13
---
## 右侧组件导入
导入时,只要将开发的组件配成一个对象放入initFormComponents即可。
```js
initFormComponents: Formmodules,
```
## 右侧组件开发
首先为了良好的开发体验需要定义个formMap类型
```js
export interface FormBaseType {
receive?: string;
}
export interface FormInputType extends FormBaseType {
label: string;
text: string;
}
export interface FormActionButtonType {}
export interface FormAnimateControlType {}
export interface FormMap {
input: FormInputType;
actionButton: FormActionButtonType;
animateControl: FormAnimateControlType;
}
```
formMap的键名就是initFormComponents键名formMap的值对应组件需要收到的值。
以input组件为例FormInputType此时有3个属性label,text,receive。
那么在其开发该组件时props会收到
```js
interface MInputProps {
data: CreateOptionsRes<FormMap, 'input'>;
current: IBlockType;
config: UserConfig;
}
```
也就是其中data是formMap类型而current是当前点击的组件config就不用说了。
还记得在左侧组件开发中的第三个参数吗?这样就都关联起来了:
```js
style: [
createPannelOptions<FormMap, 'input'>('input', {
receive: 'text',
label: '文字',
text: 'yehuozhili',
}),
],
```
createPannelOptions 这个函数的泛型里填入对应的组件,将会给收到的配置项良好的提示。
在配置项组件里所要做的就是接收组件传来的配置项然后去修改current的属性
```js
function MInput(props: MInputProps) {
const option = useMemo(() => {
return props.data?.option || {};
}, [props.data]);
return (
<Row style={{ padding: '10px 20px' }}>
<Col span={6} style={{ lineHeight: '30px' }}>
{(option as any)?.label || '文字'}
</Col>
<Col span={18}>
<Input
value={props.current.props[(option as any).receive] || ''}
onChange={(e) => {
const receive = (option as any).receive;
const clonedata = deepCopy(store.getData());
const newblock = clonedata.block.map((v: IBlockType) => {
if (v.id === props.current.id) {
v.props[receive] = e.target.value;
}
return v;
});
store.setData({ ...clonedata, block: [...newblock] });
}}
></Input>
</Col>
</Row>
);
}
```
由于可以很轻松的拿到store所以可以在任意地方进行修改数据。
将组件的value关联current的属性onChange去修改store这样就完成了个双向绑定。

View File

@@ -0,0 +1,5 @@
---
title: 命令开发
sTitle: dooringx-lib插件开发
order: 14
---

View File

@@ -0,0 +1,5 @@
---
title: 右键菜单
sTitle: dooringx-lib插件开发
order: 15
---

View File

@@ -0,0 +1,5 @@
---
title: 函数开发
sTitle: dooringx-lib插件开发
order: 16
---

View File

@@ -118,3 +118,6 @@ pre[class*='language-'] {
.token.entity {
cursor: help;
}
p {
line-height: 2.5;
}

View File

@@ -36,14 +36,14 @@
<Button>Github</Button>
</div>
<Switch
<!-- <Switch
{checked}
onChange={() => {
lang.update((pre) => {
return pre === 'cn' ? 'en' : 'cn';
});
}}
/>
/> -->
</div>
</nav>
</header>

View File

@@ -36,4 +36,5 @@
flex-direction: column;
overflow: hidden;
}
</style>

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili
* @Date: 2021-07-07 14:35:38
* @LastEditors: yehuozhili
* @LastEditTime: 2021-07-10 19:04:05
* @LastEditTime: 2021-07-10 23:04:56
* @FilePath: \dooringx\packages\dooringx-example\src\plugin\registComponents\button.tsx
*/
@@ -35,7 +35,6 @@ function ButtonTemp(pr: ComponentRenderConfigProps) {
const [text, setText] = useState('');
useEffect(() => {
//模拟改变文本
const functionCenter = eventCenter.getFunctionCenter();
const unregist = functionCenter.register(
`${pr.data.id}+改变文本函数`,
@@ -86,8 +85,6 @@ function ButtonTemp(pr: ComponentRenderConfigProps) {
onClick={() => {
eventCenter.runEventQueue(`${pr.data.id}-click`, pr.config);
}}
// type={props.type}
// size={props.size}
>
{text ? text : props.text}
</Button>
@@ -100,7 +97,7 @@ const MButton = new ComponentItemFactory(
{
style: [
createPannelOptions<FormMap, 'input'>('input', {
receive: 'text', //用于发送回的props必传 ,跨组件传递需要指定额外字段
receive: 'text',
label: '文字',
text: 'yehuozhili',
}),

View File

@@ -2,7 +2,7 @@
* @Author: yehuozhili
* @Date: 2021-02-25 21:16:58
* @LastEditors: yehuozhili
* @LastEditTime: 2021-07-10 18:29:07
* @LastEditTime: 2021-07-10 21:22:42
* @FilePath: \dooringx\packages\dooringx-lib\src\config\index.tsx
*/
import { IBlockType, IStoreData } from '../core/store/storetype';
@@ -34,7 +34,6 @@ import MmodalMask from '../core/components/defaultFormComponents/modalMask';
* @interface CacheComponentValueType
*/
export interface CacheComponentValueType {
urlFn?: () => Promise<any>;
component?: ComponentItemFactory;
}
export type CacheComponentType = Record<string, CacheComponentValueType> | {};