feat(client): x-designer

This commit is contained in:
chenos 2022-02-18 20:29:24 +08:00
parent 992befe89e
commit 2c38b63f18
18 changed files with 159 additions and 125 deletions

View File

@ -1,18 +1,12 @@
import React from 'react'; import React from 'react';
import { useDesignable } from '../..';
import { SortableItem } from '../../common'; import { SortableItem } from '../../common';
import { useDesigner } from '../../hooks';
export const BlockItem: React.FC<any> = (props) => { export const BlockItem: React.FC<any> = (props) => {
const { remove } = useDesignable(); const Designer = useDesigner();
return ( return (
<SortableItem className={'nb-block-item'} style={{ position: 'relative' }}> <SortableItem className={'nb-block-item'} style={{ position: 'relative' }}>
<a <Designer />
onClick={() => {
remove();
}}
>
</a>
{props.children} {props.children}
</SortableItem> </SortableItem>
); );

View File

@ -0,0 +1,19 @@
import React from 'react';
import { DragHandler } from '../../common';
import { useDesignable } from '../../hooks';
export const TestDesigner = () => {
const { remove } = useDesignable();
return (
<div>
<a
onClick={() => {
remove();
}}
>
</a>
<DragHandler/>
</div>
);
};

View File

@ -1,2 +1,3 @@
export * from './BlockItem'; export * from './BlockItem';
export * from './TestDesigner';

View File

@ -2,6 +2,7 @@ export * from './useAttach';
export * from './useCompile'; export * from './useCompile';
export * from './useComponent'; export * from './useComponent';
export * from './useDesignable'; export * from './useDesignable';
export * from './useDesigner';
export * from './useFieldProps'; export * from './useFieldProps';
export * from './useSchemaComponentContext'; export * from './useSchemaComponentContext';

View File

@ -93,6 +93,18 @@ export class Designable {
}); });
} }
}); });
this.on('patch', async ({ schema }) => {
refresh();
if (schema?.['x-uid']) {
await api.request({
url: `/uiSchemas:patch`,
method: 'post',
data: {
...schema,
},
});
}
});
this.on('remove', async ({ removed }) => { this.on('remove', async ({ removed }) => {
refresh(); refresh();
if (removed?.['x-uid']) { if (removed?.['x-uid']) {
@ -121,14 +133,14 @@ export class Designable {
generateUid(schema); generateUid(schema);
} }
on(name: 'insertAdjacent' | 'remove' | 'error', listener: any) { on(name: 'insertAdjacent' | 'remove' | 'error' | 'patch', listener: any) {
if (!this.events[name]) { if (!this.events[name]) {
this.events[name] = []; this.events[name] = [];
} }
this.events[name].push(listener); this.events[name].push(listener);
} }
emit(name: 'insertAdjacent' | 'remove' | 'error', ...args) { emit(name: 'insertAdjacent' | 'remove' | 'error' | 'patch', ...args) {
if (!this.events[name]) { if (!this.events[name]) {
return; return;
} }
@ -435,6 +447,7 @@ export function useDesignable() {
const dn = createDesignable({ api, refresh, current: fieldSchema }); const dn = createDesignable({ api, refresh, current: fieldSchema });
dn.loadAPIClientEvents(); dn.loadAPIClientEvents();
return { return {
dn,
designable, designable,
reset, reset,
refresh, refresh,

View File

@ -0,0 +1,9 @@
import { useFieldSchema } from '@formily/react';
import { useComponent } from '.';
const Def = () => null;
export const useDesigner = () => {
const fieldSchema = useFieldSchema();
return useComponent(fieldSchema['x-designer'], Def);
};

View File

@ -18,6 +18,7 @@ const createSchema = (collectionName) => {
params: {}, params: {},
}, },
}, },
'x-designer': 'TestDesigner',
'x-component': 'CardItem', 'x-component': 'CardItem',
properties: { properties: {
form: { form: {
@ -33,8 +34,14 @@ const createSchema = (collectionName) => {
}, },
actions: { actions: {
type: 'void', type: 'void',
'x-component': 'ActionBar',
'x-action-initializer': 'FormActionInitializer', 'x-action-initializer': 'FormActionInitializer',
'x-component': 'ActionBar',
'x-component-props': {
layout: 'one-column',
style: {
marginTop: 24,
},
},
properties: {}, properties: {},
}, },
}, },

View File

@ -0,0 +1,29 @@
import { FormOutlined } from '@ant-design/icons';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..';
const itemWrap = SchemaInitializer.itemWrap;
export const MarkdownBlock = itemWrap((props) => {
const { insert } = props;
const { t } = useTranslation();
return (
<SchemaInitializer.Item
{...props}
icon={<FormOutlined />}
onClick={() => {
insert({
type: 'void',
'x-designer': 'Markdown.Void.Designer',
'x-decorator': 'CardItem',
'x-component': 'Markdown.Void',
'x-editable': false,
'x-component-props': {
content: '# Markdown content',
},
});
}}
/>
);
});

View File

@ -16,7 +16,6 @@ const createSchema = (collectionName) => {
resource: collectionName, resource: collectionName,
action: 'list', action: 'list',
params: { params: {
perPage: 20,
pageSize: 20, pageSize: 20,
filter: {}, filter: {},
// sort: ['sort'], // sort: ['sort'],
@ -24,15 +23,21 @@ const createSchema = (collectionName) => {
}, },
}, },
}, },
'x-designer': 'TestDesigner',
'x-component': 'CardItem', 'x-component': 'CardItem',
properties: { properties: {
actions: { actions: {
type: 'void', type: 'void',
'x-component': 'ActionBar',
'x-action-initializer': 'TableActionInitializer', 'x-action-initializer': 'TableActionInitializer',
'x-component': 'ActionBar',
'x-component-props': {
style: {
marginBottom: 16,
},
},
properties: {}, properties: {},
}, },
table1: { table: {
type: 'void', type: 'void',
'x-component': 'VoidTable', 'x-component': 'VoidTable',
'x-component-props': { 'x-component-props': {
@ -47,9 +52,10 @@ const createSchema = (collectionName) => {
actions: { actions: {
type: 'void', type: 'void',
title: 'Actions', title: 'Actions',
'x-designer': 'TestDesigner',
'x-decorator': 'TableColumnActionBar', 'x-decorator': 'TableColumnActionBar',
'x-component': 'VoidTable.Column', 'x-component': 'VoidTable.Column',
'x-action-initializer': 'TableColumnActionInitializer', 'x-action-initializer': 'TableRecordActionInitializer',
properties: { properties: {
actions: { actions: {
type: 'void', type: 'void',

View File

@ -4,6 +4,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SchemaInitializer } from '../..'; import { SchemaInitializer } from '../..';
import { FormBlock } from './FormBlock'; import { FormBlock } from './FormBlock';
import { MarkdownBlock } from './MarkdownBlock';
import { TableBlock } from './TableBlock'; import { TableBlock } from './TableBlock';
const gridRowColWrap = (schema: ISchema) => { const gridRowColWrap = (schema: ISchema) => {
@ -45,6 +46,17 @@ export const BlockInitializer = observer((props: any) => {
}, },
], ],
}, },
{
type: 'itemGroup',
title: t('Media'),
children: [
{
type: 'item',
title: t('Markdown'),
component: MarkdownBlock,
},
],
},
]} ]}
> >
{t('Add block')} {t('Add block')}

View File

@ -33,6 +33,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
} }
insert({ insert({
type: 'void', type: 'void',
'x-designer': 'TestDesigner',
'x-component': 'Action', 'x-component': 'Action',
...item.schema, ...item.schema,
}); });

View File

@ -34,6 +34,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
} }
insert({ insert({
type: 'void', type: 'void',
'x-designer': 'TestDesigner',
'x-component': 'Action', 'x-component': 'Action',
...item.schema, ...item.schema,
}); });
@ -52,7 +53,6 @@ export const FormActionInitializer = observer((props: any) => {
return ( return (
<SchemaInitializer.Button <SchemaInitializer.Button
insertPosition={'beforeEnd'} insertPosition={'beforeEnd'}
style={{ marginLeft: 8 }}
items={[ items={[
{ {
type: 'itemGroup', type: 'itemGroup',

View File

@ -2,11 +2,12 @@ import { ISchema, observer, Schema, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { Switch } from 'antd'; import { Switch } from 'antd';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next';
import { SchemaInitializer, SchemaInitializerItemOptions } from '../..'; import { SchemaInitializer, SchemaInitializerItemOptions } from '../..';
import { useCollection } from '../../../collection-manager'; import { useCollection } from '../../../collection-manager';
import { useDesignable } from '../../../schema-component'; import { useDesignable } from '../../../schema-component';
const useFormItemInitializerFields = () => { const useFormItems = () => {
const { name, fields } = useCollection(); const { name, fields } = useCollection();
return fields?.map((field) => { return fields?.map((field) => {
return { return {
@ -15,6 +16,7 @@ const useFormItemInitializerFields = () => {
component: InitializeFormItem, component: InitializeFormItem,
schema: { schema: {
name: field.name, name: field.name,
'x-designer': 'TestDesigner',
'x-component': 'CollectionField', 'x-component': 'CollectionField',
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-collection-field': `${name}.${field.name}`, 'x-collection-field': `${name}.${field.name}`,
@ -100,9 +102,13 @@ const InitializeTextFormItem = itemWrap((props) => {
onClick={() => { onClick={() => {
insert({ insert({
type: 'void', type: 'void',
'x-component': 'Markdown.Void', 'x-editable': false,
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
// 'x-editable': false, 'x-designer': 'Markdown.Void.Designer',
'x-component': 'Markdown.Void',
'x-component-props': {
content: '# Markdown content',
},
}); });
}} }}
/> />
@ -110,6 +116,7 @@ const InitializeTextFormItem = itemWrap((props) => {
}); });
export const FormItemInitializer = observer((props: any) => { export const FormItemInitializer = observer((props: any) => {
const { t } = useTranslation();
return ( return (
<SchemaInitializer.Button <SchemaInitializer.Button
wrap={gridRowColWrap} wrap={gridRowColWrap}
@ -117,20 +124,20 @@ export const FormItemInitializer = observer((props: any) => {
items={[ items={[
{ {
type: 'itemGroup', type: 'itemGroup',
title: 'Display fields', title: t('Display fields'),
children: useFormItemInitializerFields(), children: useFormItems(),
}, },
{ {
type: 'divider', type: 'divider',
}, },
{ {
type: 'item', type: 'item',
title: 'Add text', title: t('Add text'),
component: InitializeTextFormItem, component: InitializeTextFormItem,
}, },
]} ]}
> >
Configure fields {t('Configure fields')}
</SchemaInitializer.Button> </SchemaInitializer.Button>
); );
}); });

View File

@ -22,93 +22,15 @@ const gridRowColWrap = (schema: ISchema) => {
const itemWrap = SchemaInitializer.itemWrap; const itemWrap = SchemaInitializer.itemWrap;
const TestInitializerItem = itemWrap((props) => { const FormBlock = itemWrap((props) => {
const { insert } = props; const { insert } = props;
return ( return (
<SchemaInitializer.Item <SchemaInitializer.Item
icon={<FormOutlined />} icon={<FormOutlined />}
onClick={() => { onClick={() => {
insert({ insert({});
type: 'void',
name: uid(),
'x-decorator': 'CardItem',
'x-component': 'Grid',
'x-uid': uid(),
properties: {
row1: {
type: 'void',
'x-component': 'Grid.Row',
'x-uid': uid(),
properties: {
col11: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
block1: {
type: 'void',
title: '1',
'x-decorator': 'BlockItem',
'x-component': 'Block',
},
block2: {
type: 'void',
title: '2',
'x-decorator': 'BlockItem',
'x-component': 'Block',
},
},
},
col12: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
block3: {
type: 'void',
title: '3',
'x-decorator': 'BlockItem',
'x-component': 'Block',
},
},
},
},
},
row2: {
type: 'void',
'x-component': 'Grid.Row',
'x-uid': uid(),
properties: {
col21: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
block4: {
type: 'void',
title: '4',
'x-decorator': 'BlockItem',
'x-component': 'Block',
},
},
},
col22: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
block5: {
type: 'void',
title: '5',
'x-decorator': 'BlockItem',
'x-component': 'Block',
},
},
},
},
},
},
});
}} }}
> />
Test
</SchemaInitializer.Item>
); );
}); });
@ -125,12 +47,12 @@ export const RecordBlockInitializer = observer((props: any) => {
{ {
type: 'item', type: 'item',
title: 'Form', title: 'Form',
component: TestInitializerItem, component: FormBlock,
}, },
{ {
type: 'item', type: 'item',
title: 'Details', title: 'Details',
component: TestInitializerItem, component: FormBlock,
}, },
], ],
}, },

View File

@ -34,6 +34,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
} }
insert({ insert({
type: 'void', type: 'void',
'x-designer': 'TestDesigner',
'x-component': 'Action', 'x-component': 'Action',
...item.schema, ...item.schema,
}); });
@ -79,12 +80,14 @@ export const TableActionInitializer = observer((props: any) => {
'x-component': 'Action', 'x-component': 'Action',
'x-component-props': { 'x-component-props': {
type: 'primary', type: 'primary',
openMode: 'drawer',
}, },
properties: { properties: {
drawer: { drawer: {
type: 'void', type: 'void',
title: '{{ t("Add new record") }}', title: '{{ t("Add new record") }}',
'x-component': 'Action.Drawer', 'x-component': 'Action.Container',
'x-component-props': {},
'x-decorator': 'Form', 'x-decorator': 'Form',
properties: { properties: {
grid: { grid: {
@ -95,7 +98,7 @@ export const TableActionInitializer = observer((props: any) => {
}, },
footer: { footer: {
type: 'void', type: 'void',
'x-component': 'Action.Drawer.Footer', 'x-component': 'Action.Container.Footer',
properties: { properties: {
action1: { action1: {
title: '{{ t("Cancel") }}', title: '{{ t("Cancel") }}',
@ -126,6 +129,9 @@ export const TableActionInitializer = observer((props: any) => {
schema: { schema: {
title: '{{ t("Delete") }}', title: '{{ t("Delete") }}',
'x-action': 'destroy', 'x-action': 'destroy',
'x-component-props': {
useAction: '{{ useBulkDestroyAction }}',
},
}, },
}, },
], ],

View File

@ -7,7 +7,8 @@ import { useCollection, useDesignable } from '../../..';
const useTableColumnInitializerFields = () => { const useTableColumnInitializerFields = () => {
const { name, fields } = useCollection(); const { name, fields } = useCollection();
return fields return (
fields
// .filter((field) => field?.uiSchema?.title) // .filter((field) => field?.uiSchema?.title)
.map((field) => { .map((field) => {
return { return {
@ -15,12 +16,14 @@ const useTableColumnInitializerFields = () => {
title: field?.uiSchema?.title || field.name, title: field?.uiSchema?.title || field.name,
schema: { schema: {
name: field.name, name: field.name,
'x-designer': 'TestDesigner',
'x-collection-field': `${name}.${field.name}`, 'x-collection-field': `${name}.${field.name}`,
'x-component': 'CollectionField', 'x-component': 'CollectionField',
}, },
component: ColumnInitializerItem, component: ColumnInitializerItem,
} as SchemaInitializerItemOptions; } as SchemaInitializerItemOptions;
}); })
);
}; };
const useCurrentColumnSchema = (path: string) => { const useCurrentColumnSchema = (path: string) => {

View File

@ -67,6 +67,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
} }
insert({ insert({
type: 'void', type: 'void',
'x-designer': 'TestDesigner',
'x-component': 'Action.Link', 'x-component': 'Action.Link',
...item.schema, ...item.schema,
}); });
@ -79,7 +80,7 @@ const InitializeAction = SchemaInitializer.itemWrap((props) => {
); );
}); });
export const TableColumnActionInitializer = observer((props: any) => { export const TableRecordActionInitializer = observer((props: any) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const api = useAPIClient(); const api = useAPIClient();
const { refresh } = useDesignable(); const { refresh } = useDesignable();
@ -181,6 +182,9 @@ export const TableColumnActionInitializer = observer((props: any) => {
schema: { schema: {
title: '{{ t("Delete") }}', title: '{{ t("Delete") }}',
'x-action': 'destroy', 'x-action': 'destroy',
'x-component-props': {
useAction: '{{ useDestroyAction }}',
},
}, },
}, },
], ],

View File

@ -4,6 +4,6 @@ export * from './FormActionInitializer';
export * from './FormItemInitializer'; export * from './FormItemInitializer';
export * from './RecordBlockInitializer'; export * from './RecordBlockInitializer';
export * from './TableActionInitializer'; export * from './TableActionInitializer';
export * from './TableColumnActionInitializer';
export * from './TableColumnInitializer'; export * from './TableColumnInitializer';
export * from './TableRecordActionInitializer';