feat(client): improve schema Initializer demos

This commit is contained in:
chenos 2022-02-08 12:17:06 +08:00
parent 39e80fad61
commit 01d172e5c4
9 changed files with 264 additions and 172 deletions

View File

@ -5,7 +5,7 @@ import { useComponent } from '../../hooks';
export const ActionBar = () => { export const ActionBar = () => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const Initializer = useComponent(fieldSchema['x-initializer']); const ActionInitializer = useComponent(fieldSchema['x-action-initializer']);
return ( return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}> <div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}> <div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
@ -26,7 +26,7 @@ export const ActionBar = () => {
})} })}
</Space> </Space>
</div> </div>
{Initializer && <Initializer />} {ActionInitializer && <ActionInitializer />}
</div> </div>
); );
}; };

View File

@ -11,7 +11,7 @@ const isColumnComponent = (schema: Schema) => {
const useTableColumns = () => { const useTableColumns = () => {
const field = useField<ArrayField>(); const field = useField<ArrayField>();
const schema = useFieldSchema(); const schema = useFieldSchema();
const Initializer = useComponent(schema['x-initializer']); const Initializer = useComponent(schema['x-column-initializer']);
const columns = schema const columns = schema
.reduceProperties((buf, s) => { .reduceProperties((buf, s) => {
if (isColumnComponent(s)) { if (isColumnComponent(s)) {

View File

@ -332,8 +332,8 @@ export function useDesignable() {
update(key); update(key);
refresh(); refresh();
}, },
remove() { remove(schema: any, options?: any) {
dn.remove(); dn.remove(schema, options);
}, },
insertAdjacent(position: Position, schema: ISchema, options: InsertAdjacentOptions = {}) { insertAdjacent(position: Position, schema: ISchema, options: InsertAdjacentOptions = {}) {
dn.insertAdjacent(position, schema, options); dn.insertAdjacent(position, schema, options);

View File

@ -12,6 +12,7 @@ export const SchemaInitializer = () => null;
SchemaInitializer.Button = observer((props: any) => { SchemaInitializer.Button = observer((props: any) => {
const { wrap = defaultWrap, items = [], insertPosition, dropdown, ...others } = props; const { wrap = defaultWrap, items = [], insertPosition, dropdown, ...others } = props;
const { insertAdjacent, findComponent } = useDesignable(); const { insertAdjacent, findComponent } = useDesignable();
const [visible, setVisible] = useState(false);
const menu = ( const menu = (
<Menu> <Menu>
@ -81,7 +82,6 @@ SchemaInitializer.Button = observer((props: any) => {
})} })}
</Menu> </Menu>
); );
const [visible, setVisible] = useState(false);
return ( return (
<Dropdown <Dropdown

View File

@ -24,11 +24,11 @@ const InitializerButton = observer((props: any) => {
children: [ children: [
{ {
title: 'Table', title: 'Table',
component: 'TableBlockInitializer', component: TableBlockInitializerItem,
}, },
{ {
title: 'Form', title: 'Form',
component: 'FormBlockInitializer', component: FormBlockInitializerItem,
}, },
], ],
}, },
@ -39,7 +39,7 @@ const InitializerButton = observer((props: any) => {
); );
}); });
const TableBlockInitializer = (props) => { const TableBlockInitializerItem = (props) => {
const { insert } = props; const { insert } = props;
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
@ -74,7 +74,7 @@ const TableBlockInitializer = (props) => {
); );
}; };
const FormBlockInitializer = (props) => { const FormBlockInitializerItem = (props) => {
const { insert } = props; const { insert } = props;
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
@ -94,7 +94,7 @@ const FormBlockInitializer = (props) => {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ Hello, InitializerButton, TableBlockInitializer, FormBlockInitializer }}> <SchemaComponentProvider components={{ Hello, InitializerButton }}>
<SchemaComponent <SchemaComponent
schema={{ schema={{
type: 'void', type: 'void',

View File

@ -1,11 +1,18 @@
import { observer } from '@formily/react'; import { observer, useFieldSchema } from '@formily/react';
import { Action, ActionBar, SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client'; import {
Action,
ActionBar,
SchemaComponent,
SchemaComponentProvider,
SchemaInitializer,
useDesignable
} from '@nocobase/client';
import { Switch } from 'antd';
import React from 'react'; import React from 'react';
const InitializerButton = observer((props: any) => { const ActionInitializerButton = observer((props: any) => {
return ( return (
<SchemaInitializer.Button <SchemaInitializer.Button
wrap={(schema) => schema}
insertPosition={'beforeEnd'} insertPosition={'beforeEnd'}
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
items={[ items={[
@ -15,15 +22,21 @@ const InitializerButton = observer((props: any) => {
{ {
title: 'Create', title: 'Create',
key: 'create', key: 'create',
component: 'ActionInitializer', component: 'ActionInitializerItem',
schema: { schema: {
title: 'Create',
'x-action': 'posts:create',
'x-align': 'left', 'x-align': 'left',
}, },
}, },
{ {
title: 'Update', title: 'Update',
key: 'update', key: 'update',
component: 'ActionInitializer', schema: {
title: 'Update',
'x-action': 'posts:update',
},
component: 'ActionInitializerItem',
}, },
], ],
}, },
@ -34,44 +47,70 @@ const InitializerButton = observer((props: any) => {
); );
}); });
const ActionInitializer = (props) => { const useCurrentActionSchema = (action: string) => {
const fieldSchema = useFieldSchema();
const { remove } = useDesignable();
const schema = fieldSchema.reduceProperties((buf, s) => {
if (s['x-action'] === action) {
return s;
}
return buf;
});
return {
schema,
exists: !!schema,
remove() {
schema && remove(schema);
},
};
};
const ActionInitializerItem = (props) => {
const { title, schema, insert } = props; const { title, schema, insert } = props;
const { exists, remove } = useCurrentActionSchema(schema['x-action']);
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
onClick={(info) => { onClick={(info) => {
if (exists) {
return remove();
}
insert({ insert({
type: 'void', type: 'void',
title: info.key,
'x-component': 'Action', 'x-component': 'Action',
...schema, ...schema,
}); });
}} }}
> >
{title} <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{title} <Switch size={'small'} checked={exists} />
</div>
</SchemaInitializer.Item> </SchemaInitializer.Item>
); );
}; };
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ ActionBar, Action, ActionInitializer, InitializerButton }}> <SchemaComponentProvider components={{ ActionBar, Action, ActionInitializerItem, ActionInitializerButton }}>
<SchemaComponent <SchemaComponent
schema={{ schema={{
type: 'void', type: 'void',
name: 'page', name: 'page',
'x-component': 'ActionBar', 'x-component': 'ActionBar',
'x-initializer': 'InitializerButton', 'x-action-initializer': 'ActionInitializerButton',
properties: { properties: {
action1: { action1: {
type: 'void', type: 'void',
title: 'Test1', title: 'Update',
'x-component': 'Action', 'x-action': 'posts:update',
},
action2: {
type: 'void',
title: 'Test2',
'x-component': 'Action', 'x-component': 'Action',
}, },
// action2: {
// type: 'void',
// title: 'Create',
// 'x-action': 'posts:create',
// 'x-align': 'left',
// 'x-component': 'Action',
// },
}, },
}} }}
/> />

View File

@ -1,33 +1,46 @@
import { Field } from '@formily/core'; import { observer, Schema, useFieldSchema } from '@formily/react';
import { observer, useField } from '@formily/react'; import {
import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client'; Form,
FormItem,
Markdown,
SchemaComponent,
SchemaComponentProvider,
SchemaInitializer,
useDesignable
} from '@nocobase/client';
import { Input, Switch } from 'antd';
import React from 'react'; import React from 'react';
const Hello = observer((props) => {
const field = useField<Field>();
return (
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
{field.title}
</div>
);
});
const useFormItemInitializerFields = () => { const useFormItemInitializerFields = () => {
return [ return [
{ {
key: 'field1', key: 'name',
title: 'Field1', title: 'Name',
component: 'FormItemInitializer', component: 'FormItemInitializer',
schema: {
type: 'string',
title: 'Name',
'x-component': 'Input',
'x-decorator': 'FormItem',
'x-collection-field': 'posts.name',
},
}, },
{ {
key: 'field2', key: 'title',
title: 'Field2', title: 'Title',
component: 'FormItemInitializer', component: 'FormItemInitializer',
schema: {
type: 'string',
title: 'Title',
'x-component': 'Input',
'x-decorator': 'FormItem',
'x-collection-field': 'posts.title',
},
}, },
]; ];
}; };
const InitializerButton = observer((props: any) => { const FormItemInitializerButton = observer((props: any) => {
return ( return (
<SchemaInitializer.Button <SchemaInitializer.Button
wrap={(schema) => schema} wrap={(schema) => schema}
@ -51,19 +64,48 @@ const InitializerButton = observer((props: any) => {
); );
}); });
const useCurrentFieldSchema = (path: string) => {
const fieldSchema = useFieldSchema();
const { remove } = useDesignable();
const findFieldSchema = (schema: Schema) => {
return schema.reduceProperties((buf, s) => {
if (s['x-collection-field'] === path) {
return s;
}
const child = findFieldSchema(s);
if (child) {
return child;
}
return buf;
});
};
const schema = findFieldSchema(fieldSchema);
return {
schema,
exists: !!schema,
remove() {
schema && remove(schema);
},
};
};
const FormItemInitializer = (props) => { const FormItemInitializer = (props) => {
const { title, insert } = props; const { title, schema, insert } = props;
const { exists, remove } = useCurrentFieldSchema(schema['x-collection-field']);
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
onClick={(info) => { onClick={() => {
if (exists) {
return remove();
}
insert({ insert({
type: 'void', ...schema,
title: info.key,
'x-component': 'Hello',
}); });
}} }}
> >
{title} <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{title} <Switch size={'small'} checked={exists} />
</div>
</SchemaInitializer.Item> </SchemaInitializer.Item>
); );
}; };
@ -75,8 +117,9 @@ const AddTextFormItemInitializer = (props) => {
onClick={(info) => { onClick={(info) => {
insert({ insert({
type: 'void', type: 'void',
title: 'Text', 'x-component': 'Markdown.Void',
'x-component': 'Hello', 'x-decorator': 'FormItem',
// 'x-editable': false,
}); });
}} }}
> >
@ -89,7 +132,7 @@ const Page = (props) => {
return ( return (
<div> <div>
{props.children} {props.children}
<InitializerButton /> <FormItemInitializerButton />
</div> </div>
); );
}; };
@ -97,23 +140,28 @@ const Page = (props) => {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider <SchemaComponentProvider
components={{ Page, Hello, InitializerButton, FormItemInitializer, AddTextFormItemInitializer }} components={{ Page, Form, Input, FormItem, Markdown, FormItemInitializer, AddTextFormItemInitializer }}
> >
<SchemaComponent <SchemaComponent
schema={{ schema={{
type: 'void', type: 'void',
name: 'page', name: 'page',
'x-component': 'Page', 'x-decorator': 'Page',
'x-component': 'Form',
properties: { properties: {
hello1: { title: {
type: 'void', type: 'string',
title: 'Test1', title: 'Title',
'x-component': 'Hello', 'x-component': 'Input',
'x-decorator': 'FormItem',
'x-collection-field': 'posts.title',
}, },
hello2: { name: {
type: 'void', type: 'string',
title: 'Test2', title: 'Name',
'x-component': 'Hello', 'x-component': 'Input',
'x-decorator': 'FormItem',
'x-collection-field': 'posts.name',
}, },
}, },
}} }}

View File

@ -1,18 +1,38 @@
import { observer } from '@formily/react'; import { observer, Schema, useFieldSchema } from '@formily/react';
import { ArrayTable, Input, SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client'; import {
ArrayTable,
Input,
SchemaComponent,
SchemaComponentProvider,
SchemaInitializer,
useDesignable
} from '@nocobase/client';
import { Switch } from 'antd';
import React from 'react'; import React from 'react';
const useTableColumnInitializerFields = () => { const useTableColumnInitializerFields = () => {
const fields = [ const fields = [
{ {
key: 'field1', key: 'name',
title: 'Field1', title: 'Name',
schema: {
type: 'string',
name: 'name',
'x-collection-field': 'posts.name',
'x-component': 'Input',
},
component: ColumnInitializer, component: ColumnInitializer,
}, },
{ {
key: 'field2', key: 'title',
title: 'Field2', title: 'Title',
component: 'ColumnInitializer', schema: {
type: 'string',
name: 'title',
'x-collection-field': 'posts.title',
'x-component': 'Input',
},
component: ColumnInitializer,
}, },
]; ];
return fields; return fields;
@ -35,26 +55,56 @@ export const InitializerButton = observer((props: any) => {
); );
}); });
const useCurrentColumnSchema = (path: string) => {
const fieldSchema = useFieldSchema();
const { remove } = useDesignable();
const findFieldSchema = (schema: Schema) => {
return schema.reduceProperties((buf, s) => {
if (s['x-collection-field'] === path) {
return s;
}
const child = findFieldSchema(s);
if (child) {
return child;
}
return buf;
});
};
const schema = findFieldSchema(fieldSchema);
return {
schema,
exists: !!schema,
remove() {
schema && remove(schema.parent);
},
};
};
export const ColumnInitializer = (props) => { export const ColumnInitializer = (props) => {
const { title, insert } = props; const { title, schema, insert } = props;
const { exists, remove } = useCurrentColumnSchema(schema['x-collection-field']);
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
onClick={(info) => { onClick={() => {
if (exists) {
return remove();
}
insert({ insert({
type: 'void', type: 'void',
title: 'Name', title: schema.name,
'x-component': 'ArrayTable.Column', 'x-component': 'ArrayTable.Column',
properties: { properties: {
name: { [schema.name]: {
type: 'string',
'x-component': 'Input',
'x-read-pretty': true, 'x-read-pretty': true,
...schema,
}, },
}, },
}); });
}} }}
> >
{title} <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{title} <Switch size={'small'} checked={exists} />
</div>
</SchemaInitializer.Item> </SchemaInitializer.Item>
); );
}; };
@ -70,7 +120,7 @@ const schema = {
{ id: 3, name: 'Name3' }, { id: 3, name: 'Name3' },
], ],
'x-component': 'ArrayTable', 'x-component': 'ArrayTable',
'x-initializer': 'InitializerButton', 'x-column-initializer': 'InitializerButton',
'x-component-props': { 'x-component-props': {
rowKey: 'id', rowKey: 'id',
}, },
@ -83,6 +133,7 @@ const schema = {
name: { name: {
type: 'string', type: 'string',
'x-component': 'Input', 'x-component': 'Input',
'x-collection-field': 'posts.name',
'x-read-pretty': true, 'x-read-pretty': true,
}, },
}, },
@ -94,7 +145,7 @@ const schema = {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ ColumnInitializer, Input, ArrayTable, InitializerButton }}> <SchemaComponentProvider components={{ InitializerButton, Input, ArrayTable }}>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</SchemaComponentProvider> </SchemaComponentProvider>
); );

View File

@ -9,87 +9,39 @@ group:
用于配置各种 schema 的初始化 用于配置各种 schema 的初始化
## SchemaInitializerProvider
```tsx | pure
<SchemaInitializerProvider
shortcuts={{
Block: [
{
title: 'Data blocks',
children: [
{
title: 'Table',
component: 'TableBlockInitializer',
},
{
title: 'Form',
component: 'FormBlockInitializer',
},
],
},
]
}}
></SchemaInitializerProvider>
```
目前可能需要配置 shortcuts 的有:
```ts
const shortcuts = {
// 表单可选操作
FormAction: [],
// 抽屉表单可选操作
DrawerFormAction: [],
// 对话框表单可选操作
ModalFormAction: [],
// 详情区块可选操作
DetailsAction: [],
// 表格区块可选操作
TableAction: [],
// 表格行可选操作
TableRowAction: [],
// 日历可选操作
CalendarAction: [],
// 普通页面可添加区块
Block: [],
// 当前数据页面可添加区块
RecordBlock: [],
// 可添加的表单项
FormItem: [],
// 可添加的表格列
TableColumn: [],
};
```
## SchemaInitializer.Button ## SchemaInitializer.Button
常规区块的初始化按钮 常规区块的初始化按钮
```tsx | pure ```tsx | pure
<SchemaInitializer.Button const items = [
// 待插入的节点wrap 处理
wrap={(schema) => schema}
// 插入位置
insertPosition={'beforeBegin'}
// 从上下文获取 shortcuts 里配置的 items
shortcut={'Block'}
items={[
{ {
title: 'Data blocks', title: 'Data blocks',
children: [ children: [
{ {
title: 'Table', title: 'Table',
component: 'TableBlockInitializer', component: 'TableBlockInitializerItem',
}, },
{ {
title: 'Form', title: 'Form',
component: 'FormBlockInitializer', component: 'FormBlockInitializerItem',
}, },
], ],
}, },
]} ];
>Create block</SchemaInitializer.Button>
export const AddBlockButton = () => {
return (
<SchemaInitializer.Button
// 待插入的节点wrap 处理
wrap={(schema) => schema}
// 插入位置
insertPosition={'beforeBegin'}
// 菜单项
items={items}
>Create block</SchemaInitializer.Button>
);
}
``` ```
动态字段的配置 动态字段的配置
@ -100,25 +52,27 @@ const useFormItemInitializerFields = () => {
return fields.map(field => { return fields.map(field => {
return { return {
key: field.name, key: field.name,
component: 'FormItemInitializer' component: 'FormItemInitializerItem'
} }
}); });
} }
<SchemaInitializer.Button export const AddFieldButton = () => {
return (
<SchemaInitializer.Button
// 待插入的节点wrap 处理 // 待插入的节点wrap 处理
wrap={(schema) => schema} wrap={(schema) => schema}
// 插入位置 // 插入位置
insertPosition={'beforeBegin'} insertPosition={'beforeBegin'}
// 从上下文获取 shortcuts 里配置的 items
shortcut={'Block'}
items={[ items={[
{ {
title: 'Display fields', title: 'Display fields',
children: useFormItemInitializerFields(), children: useFormItemInitializerFields(),
}, },
]} ]}
>Configure fields</SchemaInitializer.Button> >Configure fields</SchemaInitializer.Button>
)
}
``` ```
## SchemaInitializer.Item ## SchemaInitializer.Item
@ -126,7 +80,7 @@ const useFormItemInitializerFields = () => {
扩展项 扩展项
```tsx | pure ```tsx | pure
const TableBlockInitializer = (props) => { const TableBlockInitializerItem = (props) => {
const { insert } = props; const { insert } = props;
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item