feat(client): add x-initializer parameter to schema

This commit is contained in:
chenos 2022-02-07 23:30:24 +08:00
parent 7cc821c06f
commit 39e80fad61
11 changed files with 163 additions and 96 deletions

View File

@ -0,0 +1,32 @@
import { RecursionField, useFieldSchema } from '@formily/react';
import { Space } from 'antd';
import React from 'react';
import { useComponent } from '../../hooks';
export const ActionBar = () => {
const fieldSchema = useFieldSchema();
const Initializer = useComponent(fieldSchema['x-initializer']);
return (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<Space>
{fieldSchema.mapProperties((schema, key) => {
if (schema['x-align'] !== 'left') {
return null;
}
return <RecursionField key={key} name={key} schema={schema} />;
})}
</Space>
<Space>
{fieldSchema.mapProperties((schema, key) => {
if (schema['x-align'] === 'left') {
return null;
}
return <RecursionField key={key} name={key} schema={schema} />;
})}
</Space>
</div>
{Initializer && <Initializer />}
</div>
);
};

View File

@ -1,3 +1,5 @@
export * from './Action'; export * from './Action';
export * from './ActionBar';
export * from './context'; export * from './context';
export * from './hooks'; export * from './hooks';

View File

@ -2,7 +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'; import { useComponent } from '../../hooks';
const isColumnComponent = (schema: Schema) => { const isColumnComponent = (schema: Schema) => {
return schema['x-component']?.endsWith('.Column') > -1; return schema['x-component']?.endsWith('.Column') > -1;
@ -11,6 +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 columns = schema const columns = schema
.reduceProperties((buf, s) => { .reduceProperties((buf, s) => {
if (isColumnComponent(s)) { if (isColumnComponent(s)) {
@ -28,13 +29,13 @@ const useTableColumns = () => {
} as TableColumnProps<any>; } as TableColumnProps<any>;
}); });
console.log(columns); console.log(columns);
if (!schema['x-column-initializer']) { if (!Initializer) {
return columns; return columns;
} }
return columns.concat({ return columns.concat({
title: <TableColumnInitializeButton />, title: <Initializer />,
dataIndex: 'TableColumnInitializeButton', dataIndex: 'TableColumnInitializer',
key: 'TableColumnInitializeButton', key: 'TableColumnInitializer',
}); });
}; };

View File

@ -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, ArrayTableColumnInitializer, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client'; import { ArrayTable, Input, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
import React from 'react'; import React from 'react';
const schema: ISchema = { const schema: ISchema = {
@ -19,7 +19,6 @@ 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',
}, },
@ -61,7 +60,7 @@ const schema: ISchema = {
export default () => { export default () => {
return ( return (
<SchemaComponentProvider components={{ Input, ArrayTable, FormItem, ArrayTableColumnInitializer }}> <SchemaComponentProvider components={{ Input, ArrayTable, FormItem }}>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</SchemaComponentProvider> </SchemaComponentProvider>
); );

View File

@ -29,3 +29,4 @@ export * from './time-picker';
export * from './tree-select'; export * from './tree-select';
export * from './upload'; export * from './upload';
export * from './void-table'; export * from './void-table';

View File

@ -1,5 +1,6 @@
export * from './useAttach'; export * from './useAttach';
export * from './useCompile'; export * from './useCompile';
export * from './useComponent';
export * from './useDesignable'; export * from './useDesignable';
export * from './useFieldProps'; export * from './useFieldProps';
export * from './useSchemaComponentContext'; export * from './useSchemaComponentContext';

View File

@ -0,0 +1,14 @@
import { SchemaOptionsContext } from '@formily/react';
import { get } from 'lodash';
import { useContext } from 'react';
export const useComponent = (component: any) => {
if (!component) {
return null;
}
if (typeof component !== 'string') {
return component;
}
const { components } = useContext(SchemaOptionsContext);
return get(components, component);
};

View File

@ -1,5 +1,6 @@
import { ISchema, Schema, useField, useFieldSchema } from '@formily/react'; import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import get from 'lodash/get';
import set from 'lodash/set'; import set from 'lodash/set';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { SchemaComponentContext } from '../context'; import { SchemaComponentContext } from '../context';
@ -250,7 +251,7 @@ export class Designable {
const { wrap = defaultWrap, removeEmptyParents } = options; const { wrap = defaultWrap, removeEmptyParents } = options;
if (Schema.isSchemaInstance(schema)) { if (Schema.isSchemaInstance(schema)) {
schema.parent.removeProperty(schema.name); schema.parent.removeProperty(schema.name);
console.log('insertAfterEnd', removeEmptyParents) console.log('insertAfterEnd', removeEmptyParents);
if (removeEmptyParents) { if (removeEmptyParents) {
this.removeIfChildrenEmpty(schema.parent); this.removeIfChildrenEmpty(schema.parent);
} }
@ -281,6 +282,7 @@ export class Designable {
// TODO // TODO
export function useDesignable() { export function useDesignable() {
const { designable, setDesignable, refresh, reset } = useContext(SchemaComponentContext); const { designable, setDesignable, refresh, reset } = useContext(SchemaComponentContext);
const { components } = useContext(SchemaOptionsContext);
const DesignableBar = () => { const DesignableBar = () => {
return <></>; return <></>;
}; };
@ -295,6 +297,15 @@ export function useDesignable() {
refresh, refresh,
setDesignable, setDesignable,
DesignableBar, DesignableBar,
findComponent(component: any) {
if (!component) {
return null;
}
if (typeof component !== 'string') {
return component;
}
return get(components, component);
},
on: dn.on.bind(dn), on: dn.on.bind(dn),
// TODO // TODO
patch: (key: ISchema | string, value?: any) => { patch: (key: ISchema | string, value?: any) => {

View File

@ -1,7 +1,6 @@
import { observer, Schema, SchemaOptionsContext } from '@formily/react'; import { observer, Schema } from '@formily/react';
import { useDesignable } from '@nocobase/client'; import { useDesignable } from '@nocobase/client';
import { Button, Dropdown, Menu } from 'antd'; import { Button, Dropdown, Menu } from 'antd';
import { get } from 'lodash';
import React, { createContext, useContext, useState } from 'react'; import React, { createContext, useContext, useState } from 'react';
const defaultWrap = (s: Schema) => s; const defaultWrap = (s: Schema) => s;
@ -12,8 +11,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 } = useDesignable(); const { insertAdjacent, findComponent } = useDesignable();
const { components } = useContext(SchemaOptionsContext);
const menu = ( const menu = (
<Menu> <Menu>
@ -22,7 +20,7 @@ SchemaInitializer.Button = observer((props: any) => {
return <Menu.Divider key={`item-${indexA}`} />; return <Menu.Divider key={`item-${indexA}`} />;
} }
if (item.component) { if (item.component) {
const Component = typeof item.component === 'object' ? item.component : get(components, item.component); const Component = findComponent(item.component);
return ( return (
Component && ( Component && (
<SchemaInitializerItemContext.Provider <SchemaInitializerItemContext.Provider
@ -53,8 +51,7 @@ SchemaInitializer.Button = observer((props: any) => {
if (!child.component) { if (!child.component) {
return null; return null;
} }
const Component = const Component = findComponent(child.component);
typeof child.component === 'object' ? child.component : get(components, child.component);
return ( return (
Component && ( Component && (
<SchemaInitializerItemContext.Provider <SchemaInitializerItemContext.Provider

View File

@ -1,34 +1,29 @@
import { FormOutlined, TableOutlined } from '@ant-design/icons'; import { observer } from '@formily/react';
import { Field } from '@formily/core'; import { Action, ActionBar, SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
import { observer, useField } from '@formily/react';
import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
import React from 'react'; import React from 'react';
const Hello = observer((props) => { const InitializerButton = observer((props: any) => {
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 ( return (
<SchemaInitializer.Button <SchemaInitializer.Button
wrap={(schema) => schema} wrap={(schema) => schema}
insertPosition={'beforeBegin'} insertPosition={'beforeEnd'}
style={{ marginLeft: 8 }}
items={[ items={[
{ {
title: 'Data blocks', title: 'Enable actions',
children: [ children: [
{ {
title: 'Table', title: 'Create',
component: 'TableBlockInitializer', key: 'create',
component: 'ActionInitializer',
schema: {
'x-align': 'left',
},
}, },
{ {
title: 'Form', title: 'Update',
component: 'FormBlockInitializer', key: 'update',
component: 'ActionInitializer',
}, },
], ],
}, },
@ -39,81 +34,43 @@ const InitializerDemo = observer((props: any) => {
); );
}); });
const TableBlockInitializer = (props) => { const ActionInitializer = (props) => {
const { insert } = props; const { title, schema, insert } = props;
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
icon={<TableOutlined />}
onClick={(info) => { onClick={(info) => {
console.log({ info });
insert({ insert({
type: 'void', type: 'void',
title: info.key, title: info.key,
'x-component': 'Hello', 'x-component': 'Action',
}); ...schema,
}}
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 {title}
</SchemaInitializer.Item> </SchemaInitializer.Item>
); );
}; };
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ Hello, InitializerDemo, TableBlockInitializer, FormBlockInitializer }}> <SchemaComponentProvider components={{ ActionBar, Action, ActionInitializer, InitializerButton }}>
<SchemaComponent <SchemaComponent
schema={{ schema={{
type: 'void', type: 'void',
name: 'page', name: 'page',
'x-component': 'div', 'x-component': 'ActionBar',
'x-initializer': 'InitializerButton',
properties: { properties: {
hello1: { action1: {
type: 'void', type: 'void',
title: 'Test1', title: 'Test1',
'x-component': 'Hello', 'x-component': 'Action',
}, },
hello2: { action2: {
type: 'void', type: 'void',
title: 'Test2', title: 'Test2',
'x-component': 'Hello', 'x-component': 'Action',
},
initializer: {
type: 'void',
'x-component': 'InitializerDemo',
}, },
}, },
}} }}

View File

@ -1,12 +1,64 @@
import { import { observer } from '@formily/react';
ArrayTable, import { ArrayTable, Input, SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
ArrayTableColumnInitializer,
Input,
SchemaComponent,
SchemaComponentProvider
} from '@nocobase/client';
import React from 'react'; import React from 'react';
const useTableColumnInitializerFields = () => {
const fields = [
{
key: 'field1',
title: 'Field1',
component: ColumnInitializer,
},
{
key: 'field2',
title: 'Field2',
component: 'ColumnInitializer',
},
];
return fields;
};
export const InitializerButton = observer((props: any) => {
return (
<SchemaInitializer.Button
wrap={(schema) => schema}
insertPosition={'beforeEnd'}
items={[
{
title: 'Display fields',
children: useTableColumnInitializerFields(),
},
]}
>
Configure columns
</SchemaInitializer.Button>
);
});
export const ColumnInitializer = (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>
);
};
const schema = { const schema = {
type: 'object', type: 'object',
properties: { properties: {
@ -18,7 +70,7 @@ const schema = {
{ id: 3, name: 'Name3' }, { id: 3, name: 'Name3' },
], ],
'x-component': 'ArrayTable', 'x-component': 'ArrayTable',
'x-column-initializer': 'ArrayTableColumnInitializer', 'x-initializer': 'InitializerButton',
'x-component-props': { 'x-component-props': {
rowKey: 'id', rowKey: 'id',
}, },
@ -42,7 +94,7 @@ const schema = {
export default function App() { export default function App() {
return ( return (
<SchemaComponentProvider components={{ Input, ArrayTable, ArrayTableColumnInitializer }}> <SchemaComponentProvider components={{ ColumnInitializer, Input, ArrayTable, InitializerButton }}>
<SchemaComponent schema={schema} /> <SchemaComponent schema={schema} />
</SchemaComponentProvider> </SchemaComponentProvider>
); );