feat: improve schema initializer

This commit is contained in:
chenos 2022-02-22 23:40:08 +08:00
parent 9dd590c459
commit f121999568
8 changed files with 77 additions and 181 deletions

View File

@ -13,13 +13,15 @@ const randomString = (prefix: string = '') => {
}; };
export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (props) => { export const SchemaComponentProvider: React.FC<ISchemaComponentProvider> = (props) => {
const { components, children } = props; const { designable, components, children } = props;
const [, setUid] = useState(uid()); const [, setUid] = useState(uid());
const [formId, setFormId] = useState(uid()); const [formId, setFormId] = useState(uid());
const form = props.form || useMemo(() => createForm(), [formId]); const form = props.form || useMemo(() => createForm(), [formId]);
const { t } = useTranslation(); const { t } = useTranslation();
const scope = { ...props.scope, t, randomString }; const scope = { ...props.scope, t, randomString };
const [active, setActive] = useCookieState('useCookieDesignable'); const [active, setActive] = useCookieState('useCookieDesignable', {
defaultValue: designable ? 'true' : 'false',
});
return ( return (
<SchemaComponentContext.Provider <SchemaComponentContext.Provider
value={{ value={{

View File

@ -1,19 +1,9 @@
import React, { createContext, useContext, useMemo, useState } from 'react'; import { Form } from '@formily/core';
import { uid } from '@formily/shared';
import { createForm, Form } from '@formily/core';
import { useCookieState } from 'ahooks';
import { useTranslation } from 'react-i18next';
import { import {
Schema,
FormProvider,
RecursionField,
createSchemaField,
IRecursionFieldProps, IRecursionFieldProps,
ISchemaFieldProps, ISchemaFieldProps, SchemaReactComponents
SchemaOptionsContext,
SchemaReactComponents,
SchemaExpressionScopeContext,
} from '@formily/react'; } from '@formily/react';
import React from 'react';
export interface ISchemaComponentContext { export interface ISchemaComponentContext {
scope?: any; scope?: any;
@ -26,6 +16,7 @@ export interface ISchemaComponentContext {
} }
export interface ISchemaComponentProvider { export interface ISchemaComponentProvider {
designable?: boolean;
form?: Form; form?: Form;
scope?: any; scope?: any;
components?: SchemaReactComponents; components?: SchemaReactComponents;

View File

@ -7,6 +7,7 @@ import { SchemaInitializer } from './SchemaInitializer';
const SchemaInitializerContext = createContext(null); const SchemaInitializerContext = createContext(null);
export interface SchemaInitializerProviderProps { export interface SchemaInitializerProviderProps {
components?: any;
initializers?: Record<string, any>; initializers?: Record<string, any>;
} }
@ -44,11 +45,11 @@ export const useSchemaInitializer = (name: string) => {
}; };
export const SchemaInitializerProvider: React.FC<SchemaInitializerProviderProps> = (props) => { export const SchemaInitializerProvider: React.FC<SchemaInitializerProviderProps> = (props) => {
const { initializers, children } = props; const { initializers, components, children } = props;
return ( return (
<SchemaInitializerContext.Provider value={{ ...globals, ...initializers }}> <SchemaInitializerContext.Provider value={{ ...globals, ...initializers }}>
<SchemaComponentOptions components={{ ...items }}>{children}</SchemaComponentOptions> <SchemaComponentOptions components={{ ...items, ...components }}>{children}</SchemaComponentOptions>
</SchemaInitializerContext.Provider> </SchemaInitializerContext.Provider>
); );
}; };

View File

@ -91,7 +91,7 @@ const AddBlockButton = observer((props: any) => {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ TableBlockInitializer, Hello, AddBlockButton }}> <SchemaComponentProvider designable components={{ TableBlockInitializer, Hello, AddBlockButton }}>
<SchemaInitializerProvider initializers={initializers}> <SchemaInitializerProvider initializers={initializers}>
<SchemaComponent <SchemaComponent
schema={{ schema={{

View File

@ -49,7 +49,7 @@ const initializers = {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ ActionBar, Action }}> <SchemaComponentProvider designable components={{ ActionBar, Action }}>
<SchemaInitializerProvider initializers={initializers}> <SchemaInitializerProvider initializers={initializers}>
<SchemaComponent <SchemaComponent
schema={{ schema={{

View File

@ -92,7 +92,7 @@ export default function App() {
}, },
}; };
return ( return (
<SchemaComponentProvider components={{ TextInitializer, Page, Form, Input, FormItem, Markdown }}> <SchemaComponentProvider designable components={{ TextInitializer, Page, Form, Input, FormItem, Markdown }}>
<SchemaInitializerProvider initializers={initializers}> <SchemaInitializerProvider initializers={initializers}>
<SchemaComponent <SchemaComponent
schema={{ schema={{

View File

@ -121,7 +121,7 @@ const schema = {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ Input, ArrayTable }}> <SchemaComponentProvider designable components={{ Input, ArrayTable }}>
<CustomSchemaInitializerProvider> <CustomSchemaInitializerProvider>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</CustomSchemaInitializerProvider> </CustomSchemaInitializerProvider>

View File

@ -25,112 +25,81 @@ group:
} }
``` ```
SchemaInitializer 的核心包括 `<SchemaInitializer.Button/>``<SchemaInitializer.Item/>` 两个组件。`<SchemaInitializer.Button/>` 是用于创建 Schema 的 Dropdown 按钮,按钮有上下文,表示新增的 schema 要插入的位置,可以通过 `insertPosition` 属性来指定插入的具体位置。下拉菜单里的为 `<SchemaInitializer.Item/>`,用于自定义各种 schema 的初始化逻辑schema 可以是区块、字段、操作等 SchemaInitializer 的核心包括 `<SchemaInitializer.Button/>``<SchemaInitializer.Item/>` 两个组件。`<SchemaInitializer.Button/>` 用于创建 Schema 的下拉菜单按钮,下拉菜单的菜单项为 `<SchemaInitializer.Item/>`
## SchemaInitializer.Button SchemaInitializer.Item 用于实现各种初始化器Initializer负责各种 schema 的初始化逻辑,可以是区块、字段、操作等 schema 片段。目前内置的 Initializer 有:
常规区块的初始化按钮 - `ActionInitializer` 普通的 Action 操作按钮的初始化器
- `AddNewActionInitializer` 添加按钮的初始化器
- `CalendarBlockInitializer` 日历区块的初始化器
- `CollectionFieldInitializer` 字段的初始化器
- `FormBlockInitializer` 表单的初始化器
- `GeneralInitializer` 通用的初始化器
- `MarkdownBlockInitializer` Markdown 区块的初始化器
- `TableBlockInitializer` 表格区块的初始化器
SchemaInitializer.Button 用于将各种 Initializer 分组,以下拉菜单的方式呈现。内置的有:
- `BlockInitializers` 页面里的「添加区块」
- `CalendarActionInitializers` 日历的「操作配置」
- `DetailsActionInitializers` 详情的「操作配置」
- `FormActionInitializers` 普通表单的「操作配置」
- `GridFormItemInitializers` Grid 组件里「配置字段」
- `MenuItemInitializers` 菜单里「添加菜单项」
- `PopupFormActionInitializers` 弹窗表单的「操作配置」
- `RecordBlockInitializers` 当前行记录所在面板的「添加区块」
- `TableActionInitializers` 表格「操作配置」
- `TableColumnInitializers` 表格「列配置」
- `TableRecordActionInitializers` 表格当前行记录的「操作配置」
## 配置
```tsx | pure ```tsx | pure
const items = [ const initializers = {
{ // 可以是 SchemaInitializer.Button 的 props
type: 'itemGroup', BlockInitializers: {
title: 'Data blocks', title: 'Add new',
children: [ items: [
{ {
type: 'item', type: 'itemGroup',
title: 'Table', title: 'Data blocks',
component: 'TableBlockInitializerItem', children: [
{
type: 'item',
title: 'Table',
component: 'TableBlockInitializer',
},
{
type: 'item',
title: 'Form',
component: 'FormBlockInitializer',
},
],
}, },
{ {
type: 'item', type: 'itemGroup',
title: 'Form', title: 'Media',
component: 'FormBlockInitializerItem', children: [
{
type: 'item',
title: 'Markdown',
component: 'MarkdownBlockInitializer',
},
],
}, },
], ],
}, },
]; // 也可以是自定义的 SchemaInitializer.Button 组件
CustomSchemaInitializerButton,
};
export const AddBlockButton = () => { const CustomSchemaInitializerButton = () => {
return ( return <SchemaInitializer.Button title={'Add new'} items={[]}/>
<SchemaInitializer.Button
// 待插入的节点wrap 处理
wrap={(schema) => schema}
// 插入位置
insertPosition={'beforeBegin'}
// 菜单项
items={items}
>Create block</SchemaInitializer.Button>
);
}
```
动态字段的配置
```tsx | pure
const useFormItemInitializerFields = () => {
const { fields } = useCollection();
return fields.map(field => {
return {
type: 'item',
component: 'FormItemInitializerItem',
schema: {}, // TODO, 例如从 field.uiSchema 里获取
}
});
} }
export const AddFieldButton = () => { <SchemaInitializerProvider initializers={initializers}>
return ( {/* children */}
<SchemaInitializer.Button </SchemaInitializerProvider>
// 待插入的节点wrap 处理
wrap={(schema) => schema}
// 插入位置
insertPosition={'beforeBegin'}
items={[
{
title: 'Display fields',
children: useFormItemInitializerFields(),
},
]}
>Configure fields</SchemaInitializer.Button>
)
}
```
## SchemaInitializer.Item
用于自定义各种 schema 的初始化逻辑,配合 `SchemaInitializer.itemWrap()` 可获得更好的类型提示。
`<SchemaInitializer.Button/>` 的下拉菜单项items 属性里 type 为 item 的 component
```ts
{
type: 'item',
title: 'Table',
component: 'TableBlockInitializerItem',
}
```
例子如下:
```tsx | pure
const TableBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
const { insert } = props;
return (
<SchemaInitializer.Item
icon={<TableOutlined />}
onClick={() => {
// 插入的 schema在这里补充更完整的逻辑
insert({
type: 'void',
'x-component': 'Table',
});
}}
>
Table
</SchemaInitializer.Item>
);
});
``` ```
## Examples ## Examples
@ -150,70 +119,3 @@ const TableBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
### Table.Column ### Table.Column
<code src="./demos/demo4.tsx" /> <code src="./demos/demo4.tsx" />
## 配置
核心的参数
```tsx | pure
const initializers = {
xxx: {
title: '{{t("Add block")}}',
insertPosition: 'beforeEnd',
items: [], // 在这里配置
},
};
<SchemaInitializerProvider initializers={initializers}>
{/* children */}
</SchemaInitializerProvider>
```
items 例子:
- Initializer.Item 只有添加的逻辑
- Initializer.SwitchItem 可以添加或移除
```ts
{
BlockInitializer: {
insertPosition: 'beforeEnd',
items: [
{
type: 'itemGroup',
title: "{{t('Enable actions')}}",
children: [
{
type: 'item',
title: "{{t('Tody')}}",
component: 'Initializer.SwitchItem',
schema: {
title: "{{t('Tody')}}",
'x-component': 'Calendar.Today',
'x-action': `calendar:today`,
'x-align': 'left',
},
},
{
type: 'item',
title: "{{t('Tody')}}",
component: 'Initializer.Item',
schema: {
title: "{{t('Tody')}}",
'x-component': 'Calendar.Today',
'x-action': `calendar:today`,
'x-align': 'left',
},
},
],
},
],
},
}
```
内置的 Initializer 有:
- `GeneralInitializer` 通用的配置项,只有插入的逻辑
- `ActionInitializer` 用于配置操作按钮,有插入和移除的逻辑
- `CollectionFieldInitializer` 用于配置数据表字段,有插入和移除的逻辑