feat(client): improve schema Initializer
This commit is contained in:
parent
3b5f43ea09
commit
ce52361ac4
@ -15,6 +15,7 @@ export * from './plugin-manager';
|
|||||||
export * from './record-provider';
|
export * from './record-provider';
|
||||||
export * from './route-switch';
|
export * from './route-switch';
|
||||||
export * from './schema-component';
|
export * from './schema-component';
|
||||||
|
export * from './schema-initializer';
|
||||||
export * from './system-settings';
|
export * from './system-settings';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { ArrayField } from '@formily/core';
|
|||||||
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { Table, TableColumnProps } from 'antd';
|
import { Table, TableColumnProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { TableColumnInitializeButton } from './Initializer';
|
||||||
|
|
||||||
const isColumnComponent = (schema: Schema) => {
|
const isColumnComponent = (schema: Schema) => {
|
||||||
return schema['x-component']?.endsWith('.Column') > -1;
|
return schema['x-component']?.endsWith('.Column') > -1;
|
||||||
@ -27,7 +28,14 @@ const useTableColumns = () => {
|
|||||||
} as TableColumnProps<any>;
|
} as TableColumnProps<any>;
|
||||||
});
|
});
|
||||||
console.log(columns);
|
console.log(columns);
|
||||||
return columns;
|
if (!schema['x-column-initializer']) {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
return columns.concat({
|
||||||
|
title: <TableColumnInitializeButton />,
|
||||||
|
dataIndex: 'TableColumnInitializeButton',
|
||||||
|
key: 'TableColumnInitializeButton',
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
type ArrayTableType = React.FC<any> & {
|
type ArrayTableType = React.FC<any> & {
|
||||||
@ -41,7 +49,7 @@ export const ArrayTable: ArrayTableType = observer((props) => {
|
|||||||
const { onChange, ...others } = props;
|
const { onChange, ...others } = props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Table {...others} columns={columns} dataSource={field.value?.slice()}/>
|
<Table {...others} columns={columns} dataSource={field.value?.slice()} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { SchemaInitializer } from '../../../schema-initializer';
|
||||||
|
|
||||||
|
const useTableColumnInitializerFields = () => {
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
key: 'field1',
|
||||||
|
title: 'Field1',
|
||||||
|
component: fieldSchema['x-column-initializer'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'field2',
|
||||||
|
title: 'Field2',
|
||||||
|
component: fieldSchema['x-column-initializer'],
|
||||||
|
},
|
||||||
|
].filter((field) => field.component);
|
||||||
|
return fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableColumnInitializeButton = observer((props: any) => {
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
wrap={(schema) => schema}
|
||||||
|
insertPosition={'beforeEnd'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Display fields',
|
||||||
|
children: useTableColumnInitializerFields(),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Configure columns
|
||||||
|
</SchemaInitializer.Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ArrayTableColumnInitializer = (props) => {
|
||||||
|
const { title, insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
onClick={(info) => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: 'Name',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import { FormItem } from '@formily/antd';
|
import { FormItem } from '@formily/antd';
|
||||||
import { ISchema } from '@formily/react';
|
import { ISchema } from '@formily/react';
|
||||||
import { ArrayTable, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
import { ArrayTable, ArrayTableColumnInitializer, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const schema: ISchema = {
|
const schema: ISchema = {
|
||||||
@ -19,6 +19,7 @@ const schema: ISchema = {
|
|||||||
],
|
],
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'ArrayTable',
|
'x-component': 'ArrayTable',
|
||||||
|
'x-column-initializer': 'ArrayTableColumnInitializer',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
rowKey: 'id',
|
rowKey: 'id',
|
||||||
},
|
},
|
||||||
@ -60,7 +61,7 @@ const schema: ISchema = {
|
|||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
<SchemaComponentProvider components={{ Input, ArrayTable, FormItem }}>
|
<SchemaComponentProvider components={{ Input, ArrayTable, FormItem, ArrayTableColumnInitializer }}>
|
||||||
<SchemaComponent schema={schema} />
|
<SchemaComponent schema={schema} />
|
||||||
</SchemaComponentProvider>
|
</SchemaComponentProvider>
|
||||||
);
|
);
|
||||||
|
@ -1 +1,3 @@
|
|||||||
export * from './ArrayTable';
|
export * from './ArrayTable';
|
||||||
|
export * from './Initializer';
|
||||||
|
|
||||||
|
146
packages/client/src/schema-initializer/SchemaInitializer.tsx
Normal file
146
packages/client/src/schema-initializer/SchemaInitializer.tsx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { observer, Schema, SchemaOptionsContext } from '@formily/react';
|
||||||
|
import { useDesignable } from '@nocobase/client';
|
||||||
|
import { Button, Dropdown, Menu } from 'antd';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
const defaultWrap = (s: Schema) => s;
|
||||||
|
|
||||||
|
export const SchemaInitializerItemContext = createContext(null);
|
||||||
|
|
||||||
|
export const SchemaInitializer = () => null;
|
||||||
|
|
||||||
|
SchemaInitializer.Button = observer((props: any) => {
|
||||||
|
const { wrap = defaultWrap, items = [], insertPosition, dropdown, ...others } = props;
|
||||||
|
const { insertAdjacent } = useDesignable();
|
||||||
|
const { components } = useContext(SchemaOptionsContext);
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu>
|
||||||
|
{items?.map((item, indexA) => {
|
||||||
|
if (item.type === 'divider') {
|
||||||
|
return <Menu.Divider key={`item-${indexA}`} />;
|
||||||
|
}
|
||||||
|
if (item.component) {
|
||||||
|
const Component = typeof item.component === 'object' ? item.component : get(components, item.component);
|
||||||
|
return (
|
||||||
|
Component && (
|
||||||
|
<SchemaInitializerItemContext.Provider
|
||||||
|
key={`item-${indexA}`}
|
||||||
|
value={{
|
||||||
|
index: indexA,
|
||||||
|
item,
|
||||||
|
info: item,
|
||||||
|
insert: (schema) => {
|
||||||
|
insertAdjacent(insertPosition, wrap(schema));
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Component
|
||||||
|
{...item}
|
||||||
|
insert={(schema) => {
|
||||||
|
insertAdjacent(insertPosition, wrap(schema));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaInitializerItemContext.Provider>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
item?.children?.length > 0 && (
|
||||||
|
<Menu.ItemGroup key={`item-group-${indexA}`} title={item.title}>
|
||||||
|
{item?.children?.map((child, indexB) => {
|
||||||
|
if (!child.component) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const Component =
|
||||||
|
typeof child.component === 'object' ? child.component : get(components, child.component);
|
||||||
|
return (
|
||||||
|
Component && (
|
||||||
|
<SchemaInitializerItemContext.Provider
|
||||||
|
key={`item-${indexB}`}
|
||||||
|
value={{
|
||||||
|
index: indexB,
|
||||||
|
item: child,
|
||||||
|
info: child,
|
||||||
|
insert: (schema) => {
|
||||||
|
insertAdjacent(insertPosition, wrap(schema));
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Component
|
||||||
|
{...child}
|
||||||
|
insert={(schema) => {
|
||||||
|
insertAdjacent(insertPosition, wrap(schema));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaInitializerItemContext.Provider>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dropdown
|
||||||
|
visible={visible}
|
||||||
|
onVisibleChange={(visible) => {
|
||||||
|
setVisible(visible);
|
||||||
|
}}
|
||||||
|
{...dropdown}
|
||||||
|
overlay={menu}
|
||||||
|
>
|
||||||
|
<Button {...others} />
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
SchemaInitializer.Item = (props) => {
|
||||||
|
const { items = [], children, icon, onClick, ...others } = props;
|
||||||
|
const { index, info } = useContext(SchemaInitializerItemContext);
|
||||||
|
if (items?.length > 0) {
|
||||||
|
const renderMenuItem = (items) => {
|
||||||
|
if (!items?.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return items.map((item, indexA) => {
|
||||||
|
if (item.type === 'itemGroup') {
|
||||||
|
return (
|
||||||
|
<Menu.ItemGroup eventKey={indexA} key={indexA} title={item.title}>
|
||||||
|
{renderMenuItem(item.children)}
|
||||||
|
</Menu.ItemGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (item.type === 'subMenu') {
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<Menu.SubMenu eventKey={indexA} key={indexA} title={item.title}>
|
||||||
|
{renderMenuItem(item.children)}
|
||||||
|
</Menu.SubMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Menu.Item eventKey={item.key} key={item.key} onClick={onClick}>
|
||||||
|
{item.title}
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<Menu.SubMenu eventKey={index} key={index} title={children} icon={icon}>
|
||||||
|
{renderMenuItem(items)}
|
||||||
|
</Menu.SubMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Menu.Item eventKey={info.key || index} icon={icon} onClick={onClick} {...others}>
|
||||||
|
{children}
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
};
|
123
packages/client/src/schema-initializer/demos/demo1.tsx
Normal file
123
packages/client/src/schema-initializer/demos/demo1.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { FormOutlined, TableOutlined } from '@ant-design/icons';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
|
||||||
|
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 InitializerButton = observer((props: any) => {
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
wrap={(schema) => schema}
|
||||||
|
insertPosition={'beforeBegin'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Data blocks',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Table',
|
||||||
|
component: 'TableBlockInitializer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Form',
|
||||||
|
component: 'FormBlockInitializer',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Create block
|
||||||
|
</SchemaInitializer.Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const TableBlockInitializer = (props) => {
|
||||||
|
const { insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
icon={<TableOutlined />}
|
||||||
|
onClick={(info) => {
|
||||||
|
console.log({ info });
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: info.key,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: 'select a data source',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'users',
|
||||||
|
title: 'Users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'posts',
|
||||||
|
title: 'Posts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Table
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormBlockInitializer = (props) => {
|
||||||
|
const { insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
icon={<FormOutlined />}
|
||||||
|
onClick={(info) => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: 'form',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Form
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Hello, InitializerButton, TableBlockInitializer, FormBlockInitializer }}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'div',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
initializer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'InitializerButton',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
}
|
123
packages/client/src/schema-initializer/demos/demo2.tsx
Normal file
123
packages/client/src/schema-initializer/demos/demo2.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { FormOutlined, TableOutlined } from '@ant-design/icons';
|
||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
|
||||||
|
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 InitializerDemo = observer((props: any) => {
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
wrap={(schema) => schema}
|
||||||
|
insertPosition={'beforeBegin'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Data blocks',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Table',
|
||||||
|
component: 'TableBlockInitializer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Form',
|
||||||
|
component: 'FormBlockInitializer',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Configure actions
|
||||||
|
</SchemaInitializer.Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const TableBlockInitializer = (props) => {
|
||||||
|
const { insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
icon={<TableOutlined />}
|
||||||
|
onClick={(info) => {
|
||||||
|
console.log({ info });
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: info.key,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: 'select a data source',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'users',
|
||||||
|
title: 'Users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'posts',
|
||||||
|
title: 'Posts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Table
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FormBlockInitializer = (props) => {
|
||||||
|
const { insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
icon={<FormOutlined />}
|
||||||
|
onClick={(info) => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: 'form',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Form
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Hello, InitializerDemo, TableBlockInitializer, FormBlockInitializer }}>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'div',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
initializer: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'InitializerDemo',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
}
|
123
packages/client/src/schema-initializer/demos/demo3.tsx
Normal file
123
packages/client/src/schema-initializer/demos/demo3.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { Field } from '@formily/core';
|
||||||
|
import { observer, useField } from '@formily/react';
|
||||||
|
import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
|
||||||
|
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 = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'field1',
|
||||||
|
title: 'Field1',
|
||||||
|
component: 'FormItemInitializer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'field2',
|
||||||
|
title: 'Field2',
|
||||||
|
component: 'FormItemInitializer',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const InitializerButton = observer((props: any) => {
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
wrap={(schema) => schema}
|
||||||
|
insertPosition={'beforeEnd'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Display fields',
|
||||||
|
children: useFormItemInitializerFields(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'divider',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Add text',
|
||||||
|
component: 'AddTextFormItemInitializer',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Configure fields
|
||||||
|
</SchemaInitializer.Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const FormItemInitializer = (props) => {
|
||||||
|
const { title, insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
onClick={(info) => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: info.key,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddTextFormItemInitializer = (props) => {
|
||||||
|
const { title, insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
onClick={(info) => {
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: 'Text',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Page = (props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.children}
|
||||||
|
<InitializerButton />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider
|
||||||
|
components={{ Page, Hello, InitializerButton, FormItemInitializer, AddTextFormItemInitializer }}
|
||||||
|
>
|
||||||
|
<SchemaComponent
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
name: 'page',
|
||||||
|
'x-component': 'Page',
|
||||||
|
properties: {
|
||||||
|
hello1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test1',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
hello2: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Test2',
|
||||||
|
'x-component': 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
}
|
49
packages/client/src/schema-initializer/demos/demo4.tsx
Normal file
49
packages/client/src/schema-initializer/demos/demo4.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
ArrayTable,
|
||||||
|
ArrayTableColumnInitializer,
|
||||||
|
Input,
|
||||||
|
SchemaComponent,
|
||||||
|
SchemaComponentProvider
|
||||||
|
} from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
input: {
|
||||||
|
type: 'array',
|
||||||
|
default: [
|
||||||
|
{ id: 1, name: 'Name1' },
|
||||||
|
{ id: 2, name: 'Name2' },
|
||||||
|
{ id: 3, name: 'Name3' },
|
||||||
|
],
|
||||||
|
'x-component': 'ArrayTable',
|
||||||
|
'x-column-initializer': 'ArrayTableColumnInitializer',
|
||||||
|
'x-component-props': {
|
||||||
|
rowKey: 'id',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
column1: {
|
||||||
|
type: 'void',
|
||||||
|
title: 'Name',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-read-pretty': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<SchemaComponentProvider components={{ Input, ArrayTable, ArrayTableColumnInitializer }}>
|
||||||
|
<SchemaComponent schema={schema} />
|
||||||
|
</SchemaComponentProvider>
|
||||||
|
);
|
||||||
|
}
|
@ -5,12 +5,38 @@ group:
|
|||||||
path: /client
|
path: /client
|
||||||
---
|
---
|
||||||
|
|
||||||
# SchemaInitializer <Badge>待定</Badge>
|
# SchemaInitializer
|
||||||
|
|
||||||
用于配置各种 schema 的初始化
|
用于配置各种 schema 的初始化
|
||||||
|
|
||||||
|
## SchemaInitializerProvider
|
||||||
|
|
||||||
```tsx | pure
|
```tsx | pure
|
||||||
const actions = {
|
<SchemaInitializerProvider
|
||||||
|
shortcuts={{
|
||||||
|
Block: [
|
||||||
|
{
|
||||||
|
title: 'Data blocks',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Table',
|
||||||
|
component: 'TableBlockInitializer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Form',
|
||||||
|
component: 'FormBlockInitializer',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
></SchemaInitializerProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
目前可能需要配置 shortcuts 的有:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const shortcuts = {
|
||||||
// 表单可选操作
|
// 表单可选操作
|
||||||
FormAction: [],
|
FormAction: [],
|
||||||
// 抽屉表单可选操作
|
// 抽屉表单可选操作
|
||||||
@ -25,9 +51,6 @@ const actions = {
|
|||||||
TableRowAction: [],
|
TableRowAction: [],
|
||||||
// 日历可选操作
|
// 日历可选操作
|
||||||
CalendarAction: [],
|
CalendarAction: [],
|
||||||
};
|
|
||||||
|
|
||||||
const blocks = {
|
|
||||||
// 普通页面可添加区块
|
// 普通页面可添加区块
|
||||||
Block: [],
|
Block: [],
|
||||||
// 当前数据页面可添加区块
|
// 当前数据页面可添加区块
|
||||||
@ -37,12 +60,122 @@ const blocks = {
|
|||||||
// 可添加的表格列
|
// 可添加的表格列
|
||||||
TableColumn: [],
|
TableColumn: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const initializer = new SchemaInitializer({ actions, blocks });
|
|
||||||
|
|
||||||
initializer.addActionSchema({});
|
|
||||||
initializer.addBlockSchema({});
|
|
||||||
|
|
||||||
<SchemaInitializerProvider initializer={initializer}>
|
|
||||||
</SchemaInitializerProvider>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## SchemaInitializer.Button
|
||||||
|
|
||||||
|
常规区块的初始化按钮
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
// 待插入的节点,wrap 处理
|
||||||
|
wrap={(schema) => schema}
|
||||||
|
// 插入位置
|
||||||
|
insertPosition={'beforeBegin'}
|
||||||
|
// 从上下文获取 shortcuts 里配置的 items
|
||||||
|
shortcut={'Block'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Data blocks',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Table',
|
||||||
|
component: 'TableBlockInitializer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Form',
|
||||||
|
component: 'FormBlockInitializer',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>Create block</SchemaInitializer.Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
动态字段的配置
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const useFormItemInitializerFields = () => {
|
||||||
|
const { fields } = useCollection();
|
||||||
|
return fields.map(field => {
|
||||||
|
return {
|
||||||
|
key: field.name,
|
||||||
|
component: 'FormItemInitializer'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<SchemaInitializer.Button
|
||||||
|
// 待插入的节点,wrap 处理
|
||||||
|
wrap={(schema) => schema}
|
||||||
|
// 插入位置
|
||||||
|
insertPosition={'beforeBegin'}
|
||||||
|
// 从上下文获取 shortcuts 里配置的 items
|
||||||
|
shortcut={'Block'}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
title: 'Display fields',
|
||||||
|
children: useFormItemInitializerFields(),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>Configure fields</SchemaInitializer.Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## SchemaInitializer.Item
|
||||||
|
|
||||||
|
扩展项
|
||||||
|
|
||||||
|
```tsx | pure
|
||||||
|
const TableBlockInitializer = (props) => {
|
||||||
|
const { insert } = props;
|
||||||
|
return (
|
||||||
|
<SchemaInitializer.Item
|
||||||
|
icon={<TableOutlined />}
|
||||||
|
onClick={(info) => {
|
||||||
|
console.log({ info });
|
||||||
|
insert({
|
||||||
|
type: 'void',
|
||||||
|
title: info.key,
|
||||||
|
'x-component': 'Hello',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
type: 'itemGroup',
|
||||||
|
title: 'select a data source',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'users',
|
||||||
|
title: 'Users',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'posts',
|
||||||
|
title: 'Posts',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
Table
|
||||||
|
</SchemaInitializer.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Block
|
||||||
|
|
||||||
|
<code src="./demos/demo1.tsx" />
|
||||||
|
|
||||||
|
### Action
|
||||||
|
|
||||||
|
<code src="./demos/demo2.tsx" />
|
||||||
|
|
||||||
|
### FormItem
|
||||||
|
|
||||||
|
<code src="./demos/demo3.tsx" />
|
||||||
|
|
||||||
|
### Table.Column
|
||||||
|
|
||||||
|
<code src="./demos/demo4.tsx" />
|
||||||
|
1
packages/client/src/schema-initializer/index.ts
Normal file
1
packages/client/src/schema-initializer/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './SchemaInitializer';
|
Loading…
Reference in New Issue
Block a user