refactor(client): schema initializer
This commit is contained in:
parent
d486768eda
commit
f45b08af38
@ -10,6 +10,7 @@ import {
|
||||
DesignableSwitch,
|
||||
DocumentTitleProvider,
|
||||
i18n,
|
||||
MenuItemInitializers,
|
||||
PluginManagerProvider,
|
||||
RemoteCollectionManagerProvider,
|
||||
RouteSchemaComponent,
|
||||
@ -50,7 +51,7 @@ const providers = [
|
||||
],
|
||||
[SchemaComponentProvider, { components: { Link, NavLink } }],
|
||||
RemoteCollectionManagerProvider,
|
||||
SchemaInitializerProvider,
|
||||
[SchemaInitializerProvider, { initializers: { MenuItemInitializers } }],
|
||||
AntdSchemaComponentProvider,
|
||||
[DocumentTitleProvider, { addonAfter: 'NocoBase' }],
|
||||
[
|
||||
|
@ -125,7 +125,7 @@ export default (apiClient: APIClient) => {
|
||||
type: 'void',
|
||||
name: 'grid1',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'Grid.AddBlockItem',
|
||||
'x-initializer': 'Grid.AddBlockItem',
|
||||
'x-uid': uid(),
|
||||
properties: {},
|
||||
},
|
||||
@ -152,7 +152,7 @@ export default (apiClient: APIClient) => {
|
||||
type: 'void',
|
||||
name: 'grid1',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'Grid.AddBlockItem',
|
||||
'x-initializer': 'Grid.AddBlockItem',
|
||||
'x-uid': uid(),
|
||||
properties: {},
|
||||
},
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { observer, RecursionField, useFieldSchema } from '@formily/react';
|
||||
import { Space } from 'antd';
|
||||
import React from 'react';
|
||||
import { useSchemaInitializer } from '../../../schema-initializer';
|
||||
import { DndContext } from '../../common';
|
||||
import { useComponent } from '../../hooks';
|
||||
|
||||
export const ActionBar = observer((props: any) => {
|
||||
const { layout = 'tow-columns', style, ...others } = props;
|
||||
const fieldSchema = useFieldSchema();
|
||||
const ActionInitializer = useComponent(fieldSchema['x-action-initializer']);
|
||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
if (layout === 'one-column') {
|
||||
return (
|
||||
<div style={{ display: 'flex', ...style }} {...others}>
|
||||
{props.children && <div style={{ marginRight: 8 }}>{props.children}</div>}
|
||||
{ActionInitializer && <ActionInitializer />}
|
||||
{render()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -38,7 +38,7 @@ export const ActionBar = observer((props: any) => {
|
||||
</Space>
|
||||
</DndContext>
|
||||
</div>
|
||||
{ActionInitializer && <ActionInitializer />}
|
||||
{render()}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -5,8 +5,7 @@ import { Table, TableColumnProps } from 'antd';
|
||||
import cls from 'classnames';
|
||||
import React from 'react';
|
||||
import { DndContext } from '../..';
|
||||
import { RecordProvider } from '../../../';
|
||||
import { useComponent } from '../../hooks';
|
||||
import { RecordProvider, useSchemaInitializer } from '../../../';
|
||||
|
||||
const isColumnComponent = (schema: Schema) => {
|
||||
return schema['x-component']?.endsWith('.Column') > -1;
|
||||
@ -15,7 +14,7 @@ const isColumnComponent = (schema: Schema) => {
|
||||
const useTableColumns = () => {
|
||||
const field = useField<ArrayField>();
|
||||
const schema = useFieldSchema();
|
||||
const Initializer = useComponent(schema['x-column-initializer']);
|
||||
const { exists, render } = useSchemaInitializer(schema['x-initializer']);
|
||||
const columns = schema
|
||||
.reduceProperties((buf, s) => {
|
||||
if (isColumnComponent(s)) {
|
||||
@ -38,11 +37,11 @@ const useTableColumns = () => {
|
||||
} as TableColumnProps<any>;
|
||||
});
|
||||
console.log(columns);
|
||||
if (!Initializer) {
|
||||
if (!exists) {
|
||||
return columns;
|
||||
}
|
||||
return columns.concat({
|
||||
title: <Initializer />,
|
||||
title: render(),
|
||||
dataIndex: 'TABLE_COLUMN_INITIALIZER',
|
||||
key: 'TABLE_COLUMN_INITIALIZER',
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ export const useColumnSchema = () => {
|
||||
return buf;
|
||||
}, null);
|
||||
if (!fieldSchema) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
const collectionField = getField(fieldSchema.name);
|
||||
return { columnSchema, fieldSchema, collectionField };
|
||||
@ -21,7 +21,7 @@ export const useColumnSchema = () => {
|
||||
export const TableColumnDecorator = (props) => {
|
||||
const Designer = useDesigner();
|
||||
const field = useField();
|
||||
const { columnSchema, fieldSchema, collectionField } = useColumnSchema();
|
||||
const { fieldSchema, collectionField } = useColumnSchema();
|
||||
const { refresh } = useDesignable();
|
||||
useLayoutEffect(() => {
|
||||
if (field.title) {
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { DragOutlined } from '@ant-design/icons';
|
||||
import { useFieldSchema } from '@formily/react';
|
||||
import { useSchemaInitializer } from '@nocobase/client';
|
||||
import { Space } from 'antd';
|
||||
import React from 'react';
|
||||
import { DragHandler, useComponent } from '../../../schema-component';
|
||||
import { DragHandler } from '../../../schema-component';
|
||||
|
||||
export const TableRecordActionDesigner = (props: any) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const ActionInitializer = useComponent(fieldSchema['x-action-initializer']);
|
||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
console.log("fieldSchema['x-initializer']", fieldSchema['x-initializer'])
|
||||
return (
|
||||
<div className={'general-schema-designer'}>
|
||||
<div className={'general-schema-designer-icons'}>
|
||||
@ -14,7 +16,7 @@ export const TableRecordActionDesigner = (props: any) => {
|
||||
<DragHandler>
|
||||
<DragOutlined />
|
||||
</DragHandler>
|
||||
<ActionInitializer />
|
||||
{render()}
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@ import { observer, RecursionField, Schema, useField, useFieldSchema } from '@for
|
||||
import { uid } from '@formily/shared';
|
||||
import cls from 'classnames';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { useComponent } from '../..';
|
||||
import { useSchemaInitializer } from '../../../';
|
||||
import { DndContext } from '../../common/dnd-context';
|
||||
|
||||
const GridRowContext = createContext(null);
|
||||
@ -123,7 +123,7 @@ const useColProperties = () => {
|
||||
export const Grid: any = observer((props) => {
|
||||
const field = useField();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const ItemInitializer = useComponent(fieldSchema['x-item-initializer']);
|
||||
const { render } = useSchemaInitializer(fieldSchema['x-initializer']);
|
||||
const addr = field.address.toString();
|
||||
const rows = useRowProperties();
|
||||
return (
|
||||
@ -145,7 +145,7 @@ export const Grid: any = observer((props) => {
|
||||
);
|
||||
})}
|
||||
</DndContext>
|
||||
<div>{ItemInitializer && <ItemInitializer />}</div>
|
||||
<div>{render()}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -10,8 +10,8 @@ import {
|
||||
import { Menu as AntdMenu } from 'antd';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { createDesignable, DndContext, SortableItem, useComponent, useDesignable, useDesigner } from '../..';
|
||||
import { Icon, useAPIClient } from '../../../';
|
||||
import { createDesignable, DndContext, SortableItem, useDesignable, useDesigner } from '../..';
|
||||
import { Icon, useAPIClient, useSchemaInitializer } from '../../../';
|
||||
import { MenuDesigner } from './Menu.Designer';
|
||||
import { findKeysByUid, findMenuItem } from './util';
|
||||
|
||||
@ -146,7 +146,7 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
const schema = useFieldSchema();
|
||||
const { refresh } = useDesignable();
|
||||
const api = useAPIClient();
|
||||
const Initializer = useComponent(schema['x-initializer']);
|
||||
const { render } = useSchemaInitializer(schema['x-initializer']);
|
||||
const sideMenuRef = useSideMenuRef();
|
||||
const [defaultSelectedKeys, setDefaultSelectedKeys] = useState(() => {
|
||||
if (dSelectedKeys) {
|
||||
@ -241,7 +241,7 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
defaultSelectedKeys={defaultSelectedKeys}
|
||||
>
|
||||
<RecursionField schema={schema} onlyRenderProperties />
|
||||
{Initializer && <Initializer style={{ background: 'none', marginTop: 7, marginLeft: 8 }} />}
|
||||
{render({ style: { background: 'none', marginTop: 7, marginLeft: 8 } })}
|
||||
</AntdMenu>
|
||||
{loading
|
||||
? null
|
||||
@ -285,21 +285,19 @@ export const Menu: ComposedMenu = observer((props) => {
|
||||
`}
|
||||
>
|
||||
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
|
||||
{Initializer && (
|
||||
<Initializer
|
||||
style={{ margin: 8 }}
|
||||
insert={(s) => {
|
||||
console.log('createDesignable', s);
|
||||
const dn = createDesignable({
|
||||
api,
|
||||
refresh,
|
||||
current: sideMenuSchema,
|
||||
});
|
||||
dn.loadAPIClientEvents();
|
||||
dn.insertAdjacent('beforeEnd', s);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{render({
|
||||
style: { margin: 8 },
|
||||
insert: (s) => {
|
||||
console.log('createDesignable', s);
|
||||
const dn = createDesignable({
|
||||
api,
|
||||
refresh,
|
||||
current: sideMenuSchema,
|
||||
});
|
||||
dn.loadAPIClientEvents();
|
||||
dn.insertAdjacent('beforeEnd', s);
|
||||
},
|
||||
})}
|
||||
</AntdMenu>
|
||||
</MenuModeContext.Provider>,
|
||||
sideMenuRef.current.firstChild,
|
||||
@ -331,9 +329,15 @@ Menu.URL = observer((props) => {
|
||||
const field = useField();
|
||||
const Designer = useContext(MenuItemDesignerContext);
|
||||
return (
|
||||
<AntdMenu.Item {...others} key={schema.name} eventKey={schema.name} schema={schema} onClick={() => {
|
||||
window.open(props.href, '_blank');
|
||||
}}>
|
||||
<AntdMenu.Item
|
||||
{...others}
|
||||
key={schema.name}
|
||||
eventKey={schema.name}
|
||||
schema={schema}
|
||||
onClick={() => {
|
||||
window.open(props.href, '_blank');
|
||||
}}
|
||||
>
|
||||
<SortableItem className={designerCss}>
|
||||
<Icon style={{ marginRight: 5 }} type={icon} />
|
||||
{schema.title}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { FormDialog, FormLayout } from '@formily/antd';
|
||||
import { observer, SchemaOptionsContext } from '@formily/react';
|
||||
import { SchemaOptionsContext } from '@formily/react';
|
||||
import React, { useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaComponent, SchemaComponentOptions } from '../../..';
|
||||
import { SchemaInitializer } from '../../../../schema-initializer';
|
||||
|
||||
export const MenuItemInitializer = observer((props: any) => {
|
||||
export const MenuItemInitializers = (props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
@ -34,7 +34,7 @@ export const MenuItemInitializer = observer((props: any) => {
|
||||
{t('Add menu item')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
@ -137,7 +137,7 @@ export const PageMenuItem = itemWrap((props) => {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'BlockInitializer',
|
||||
'x-initializer': 'BlockInitializers',
|
||||
properties: {},
|
||||
},
|
||||
},
|
@ -1,4 +1,4 @@
|
||||
export * from './Menu';
|
||||
export * from './MenuItemInitializer';
|
||||
export * from './MenuItemInitializers';
|
||||
export * from './util';
|
||||
|
||||
|
@ -1,71 +0,0 @@
|
||||
import { ISchema, observer } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { CalendarBlock } from './CalendarBlock';
|
||||
import { FormBlock } from './FormBlock';
|
||||
import { MarkdownBlock } from './MarkdownBlock';
|
||||
import { TableBlock } from './TableBlock';
|
||||
|
||||
const gridRowColWrap = (schema: ISchema) => {
|
||||
return {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[schema.name || uid()]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const BlockInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Data blocks'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Table'),
|
||||
component: TableBlock,
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Form'),
|
||||
component: FormBlock,
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Calendar'),
|
||||
component: CalendarBlock,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Media'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Markdown'),
|
||||
component: MarkdownBlock,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Add block')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import { gridRowColWrap } from './utils';
|
||||
|
||||
// 页面里添加区块
|
||||
export const BlockInitializers = {
|
||||
title: '{{t("Add block")}}',
|
||||
wrap: gridRowColWrap,
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Data blocks',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Table',
|
||||
component: 'TableBlockInitializer',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Form',
|
||||
component: 'FormBlockInitializer',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Calendar',
|
||||
component: 'CalendarBlockInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Media',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Markdown',
|
||||
component: 'MarkdownBlockInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,187 +0,0 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
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 InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title}
|
||||
<Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const CalendarActionInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
style={{ marginLeft: 8 }}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Enable actions'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Today'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: t('Today'),
|
||||
'x-component': 'Calendar.Today',
|
||||
'x-action': `calendar:today`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Turn page'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: t('Turn page'),
|
||||
'x-component': 'Calendar.Nav',
|
||||
'x-action': `calendar:nav`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Title'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: t('Title'),
|
||||
'x-component': 'Calendar.Title',
|
||||
'x-action': `calendar:title`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Select view'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: t('Select view'),
|
||||
'x-component': 'Calendar.ViewSelect',
|
||||
'x-action': `calendar:viewSelect`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Filter'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Filter") }}',
|
||||
'x-action': 'filter',
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Add new'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
type: 'void',
|
||||
title: '{{ t("Add new") }}',
|
||||
'x-action': 'create',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
openMode: 'drawer',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
title: '{{ t("Add new record") }}',
|
||||
'x-component': 'Action.Container',
|
||||
'x-component-props': {},
|
||||
'x-decorator': 'Form',
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'FormItemInitializer',
|
||||
properties: {},
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Container.Footer',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-action-initializer': 'PopupFormActionInitializer',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Delete'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-action': 'destroy',
|
||||
'x-component-props': {
|
||||
confirm: {
|
||||
title: "{{t('Delete record')}}",
|
||||
content: "{{t('Are you sure you want to delete it?')}}",
|
||||
},
|
||||
useAction: '{{ cm.useBulkDestroyAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure actions')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,75 @@
|
||||
// 日历的操作配置
|
||||
export const CalendarActionInitializers = {
|
||||
title: 'Configure actions',
|
||||
style: { marginLeft: 8 },
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Today',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Today',
|
||||
'x-component': 'Calendar.Today',
|
||||
'x-action': `calendar:today`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Turn pages',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Turn page',
|
||||
'x-component': 'Calendar.Nav',
|
||||
'x-action': `calendar:nav`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Title',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Title',
|
||||
'x-component': 'Calendar.Title',
|
||||
'x-action': `calendar:title`,
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Select view',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Select view',
|
||||
'x-component': 'Calendar.ViewSelect',
|
||||
'x-action': `calendar:viewSelect`,
|
||||
'x-align': 'right',
|
||||
'x-designer': 'Action.Designer',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Filter',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Filter") }}',
|
||||
'x-action': 'filter',
|
||||
'x-align': 'right',
|
||||
'x-component': 'Action',
|
||||
'x-designer': 'Action.Designer',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: '{{ t("Add new") }}',
|
||||
component: 'AddNewActionInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,84 +0,0 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
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 InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'TestDesigner',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const DetailsActionInitializer = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
style={{ marginLeft: 8 }}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Edit',
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: 'Edit',
|
||||
'x-action': 'update',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Delete',
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: 'Delete',
|
||||
'x-action': 'delete',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure actions
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,30 @@
|
||||
// 详情的操作配置
|
||||
export const DetailsActionInitializers = {
|
||||
title: 'Configure actions',
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Edit',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Edit',
|
||||
'x-action': 'update',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Delete',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Delete',
|
||||
'x-action': 'delete',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,83 +0,0 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
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 InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title}
|
||||
<Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const FormActionInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Enable actions'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Submit'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Submit") }}',
|
||||
'x-action': 'submit',
|
||||
'x-align': 'left',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ cm.useCreateActionWithoutRefresh }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure actions')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,27 @@
|
||||
// 普通表单的操作配置
|
||||
export const FormActionInitializers = {
|
||||
title: 'Configure actions',
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: '{{ t("Submit") }}',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Submit") }}',
|
||||
'x-action': 'submit',
|
||||
'x-align': 'left',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ cm.useCreateActionWithoutRefresh }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,145 +0,0 @@
|
||||
import { ISchema, observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer, SchemaInitializerItemOptions } from '../..';
|
||||
import { useCollection } from '../../../collection-manager';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
const useFormItemInitializerFields = () => {
|
||||
const { name, fields } = useCollection();
|
||||
return fields?.map((field) => {
|
||||
return {
|
||||
type: 'item',
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
component: FormItem,
|
||||
schema: {
|
||||
name: field.name,
|
||||
'x-designer': 'FormItem.Designer',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
},
|
||||
} as SchemaInitializerItemOptions;
|
||||
});
|
||||
};
|
||||
|
||||
const gridRowColWrap = (schema: ISchema) => {
|
||||
return {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[schema.name || uid()]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
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, {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: (s) => {
|
||||
return s['x-component'] === 'Grid';
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
const FormItem = itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { schema, exists, remove } = useCurrentFieldSchema(item.schema['x-collection-field']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const TextItem = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-editable': false,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designer': 'Markdown.Void.Designer',
|
||||
'x-component': 'Markdown.Void',
|
||||
'x-component-props': {
|
||||
content: t('This is a demo text, **supports Markdown syntax**.'),
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const FormItemInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Display fields'),
|
||||
children: useFormItemInitializerFields(),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Add text'),
|
||||
component: TextItem,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure fields')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../SchemaInitializer';
|
||||
import { gridRowColWrap, useFormItemInitializerFields } from './utils';
|
||||
|
||||
// Grid 组件里配置字段
|
||||
export const GridFormItemInitializers = (props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Display fields',
|
||||
children: useFormItemInitializerFields(),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Add text',
|
||||
component: 'GeneralInitializer',
|
||||
schema: {
|
||||
type: 'void',
|
||||
'x-editable': false,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-designer': 'Markdown.Void.Designer',
|
||||
'x-component': 'Markdown.Void',
|
||||
'x-component-props': {
|
||||
content: t('This is a demo text, **supports Markdown syntax**.'),
|
||||
},
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure fields')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../SchemaInitializer';
|
||||
import { useCurrentSchema } from '../utils';
|
||||
|
||||
export const ActionInitializer = (props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentSchema(item.schema['x-action'], 'x-action', item.find);
|
||||
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
};
|
@ -0,0 +1,67 @@
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../SchemaInitializer';
|
||||
import { useCurrentSchema } from '../utils';
|
||||
|
||||
export const AddNewActionInitializer = (props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentSchema(item?.schema?.['x-action'] || 'create', 'x-action', item.find);
|
||||
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
title: '{{ t("Add new") }}',
|
||||
'x-action': 'create',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
openMode: 'drawer',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
title: '{{ t("Add new record") }}',
|
||||
'x-component': 'Action.Container',
|
||||
'x-component-props': {},
|
||||
'x-decorator': 'Form',
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'GridFormItemInitializers',
|
||||
properties: {},
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Container.Footer',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-initializer': 'PopupFormActionInitializers',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
};
|
@ -3,9 +3,9 @@ import { FormDialog, FormLayout } from '@formily/antd';
|
||||
import { ISchema, SchemaOptionsContext } from '@formily/react';
|
||||
import React, { useContext } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useCollectionManager } from '../../../collection-manager';
|
||||
import { SchemaComponent, SchemaComponentOptions } from '../../../schema-component';
|
||||
import { SchemaInitializer } from '../../SchemaInitializer';
|
||||
|
||||
const createSchema = (collectionName, { title, start, end }) => {
|
||||
const schema: ISchema = {
|
||||
@ -38,7 +38,12 @@ const createSchema = (collectionName, { title, start, end }) => {
|
||||
toolBar: {
|
||||
type: 'void',
|
||||
'x-component': 'Calendar.ActionBar',
|
||||
'x-action-initializer': 'CalendarActionInitializer',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
},
|
||||
'x-initializer': 'CalendarActionInitializers',
|
||||
properties: {},
|
||||
},
|
||||
event: {
|
||||
@ -58,7 +63,7 @@ const createSchema = (collectionName, { title, start, end }) => {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'FormItemInitializer',
|
||||
'x-initializer': 'GridFormItemInitializers',
|
||||
properties: {},
|
||||
},
|
||||
footer: {
|
||||
@ -67,7 +72,7 @@ const createSchema = (collectionName, { title, start, end }) => {
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-action-initializer': 'PopupFormActionInitializer',
|
||||
'x-initializer': 'PopupFormActionInitializers',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
@ -88,9 +93,7 @@ const createSchema = (collectionName, { title, start, end }) => {
|
||||
return schema;
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
export const CalendarBlock = itemWrap((props) => {
|
||||
export const CalendarBlockInitializer = (props) => {
|
||||
const { insert } = props;
|
||||
const { collections, getCollection } = useCollectionManager();
|
||||
const { t } = useTranslation();
|
||||
@ -160,4 +163,4 @@ export const CalendarBlock = itemWrap((props) => {
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
@ -0,0 +1,31 @@
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../SchemaInitializer';
|
||||
import { useCurrentSchema } from '../utils';
|
||||
|
||||
export const CollectionFieldInitializer = (props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentSchema(
|
||||
item.schema['x-collection-field'],
|
||||
'x-collection-field',
|
||||
item.find,
|
||||
item.remove,
|
||||
);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
console.log(item, exists);
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
};
|
@ -29,12 +29,12 @@ const createSchema = (collectionName) => {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'FormItemInitializer',
|
||||
'x-initializer': 'GridFormItemInitializers',
|
||||
properties: {},
|
||||
},
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-action-initializer': 'FormActionInitializer',
|
||||
'x-initializer': 'FormActionInitializers',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
@ -51,9 +51,7 @@ const createSchema = (collectionName) => {
|
||||
return schema;
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
export const FormBlock = itemWrap((props) => {
|
||||
export const FormBlockInitializer = (props) => {
|
||||
const { insert } = props;
|
||||
const { collections } = useCollectionManager();
|
||||
const { t } = useTranslation();
|
||||
@ -79,4 +77,4 @@ export const FormBlock = itemWrap((props) => {
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../../SchemaInitializer';
|
||||
|
||||
export const GeneralInitializer = (props) => {
|
||||
const { item, insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
insert({
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -3,9 +3,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
export const MarkdownBlock = itemWrap((props) => {
|
||||
export const MarkdownBlockInitializer = (props) => {
|
||||
const { insert } = props;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@ -26,4 +24,4 @@ export const MarkdownBlock = itemWrap((props) => {
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
@ -28,7 +28,7 @@ const createSchema = (collectionName) => {
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-action-initializer': 'TableActionInitializer',
|
||||
'x-initializer': 'TableActionInitializers',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
@ -47,7 +47,7 @@ const createSchema = (collectionName) => {
|
||||
},
|
||||
useDataSource: '{{ cm.useDataSourceFromRAC }}',
|
||||
},
|
||||
'x-column-initializer': 'TableColumnInitializer',
|
||||
'x-initializer': 'TableColumnInitializers',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
@ -55,7 +55,7 @@ const createSchema = (collectionName) => {
|
||||
'x-decorator': 'TableColumnActionBar',
|
||||
'x-component': 'VoidTable.Column',
|
||||
'x-designer': 'TableRecordActionDesigner',
|
||||
'x-action-initializer': 'TableRecordActionInitializer',
|
||||
'x-initializer': 'TableRecordActionInitializers',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
@ -75,9 +75,7 @@ const createSchema = (collectionName) => {
|
||||
return schema;
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
export const TableBlock = itemWrap((props) => {
|
||||
export const TableBlockInitializer = (props) => {
|
||||
const { insert } = props;
|
||||
const { collections } = useCollectionManager();
|
||||
const { t } = useTranslation();
|
||||
@ -103,4 +101,4 @@ export const TableBlock = itemWrap((props) => {
|
||||
]}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
export * from './ActionInitializer';
|
||||
export * from './AddNewActionInitializer';
|
||||
export * from './CalendarBlockInitializer';
|
||||
export * from './CollectionFieldInitializer';
|
||||
export * from './FormBlockInitializer';
|
||||
export * from './GeneralInitializer';
|
||||
export * from './MarkdownBlockInitializer';
|
||||
export * from './TableBlockInitializer';
|
||||
|
@ -1,95 +0,0 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
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 InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title}
|
||||
<Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const PopupFormActionInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Enable actions'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Submit'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Submit") }}',
|
||||
'x-action': 'submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ cm.useCreateAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Cancel'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Cancel") }}',
|
||||
'x-action': 'cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ cm.useCancelAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure actions')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,39 @@
|
||||
// 弹窗表单的操作配置
|
||||
export const PopupFormActionInitializers = {
|
||||
title: "{{t('Configure actions')}}",
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: "{{t('Enable actions')}}",
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: "{{t('Submit')}}",
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Submit") }}',
|
||||
'x-action': 'submit',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
useAction: '{{ cm.useCreateAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: "{{t('Cancel')}}",
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Cancel") }}',
|
||||
'x-action': 'cancel',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
useAction: '{{ cm.useCancelAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,64 +0,0 @@
|
||||
import { FormOutlined } from '@ant-design/icons';
|
||||
import { ISchema, observer } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import React from 'react';
|
||||
import { SchemaInitializer } from '../..';
|
||||
|
||||
const gridRowColWrap = (schema: ISchema) => {
|
||||
return {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[schema.name || uid()]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const itemWrap = SchemaInitializer.itemWrap;
|
||||
|
||||
const FormBlock = itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<FormOutlined />}
|
||||
onClick={() => {
|
||||
insert({});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const RecordBlockInitializer = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={gridRowColWrap}
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Data blocks',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Form',
|
||||
component: FormBlock,
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Details',
|
||||
component: FormBlock,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Add block
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,8 @@
|
||||
import { gridRowColWrap } from './utils';
|
||||
|
||||
// 当前行记录所在面板的添加区块
|
||||
export const RecordBlockInitializers = {
|
||||
wrap: gridRowColWrap,
|
||||
title: "{{t('Add block')}}",
|
||||
items: [],
|
||||
};
|
@ -1,143 +0,0 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useDesignable } from '../../../schema-component';
|
||||
|
||||
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 InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title}
|
||||
<Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const TableActionInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
style={{ marginLeft: 8 }}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Enable actions'),
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Filter'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Filter") }}',
|
||||
'x-action': 'filter',
|
||||
'x-align': 'left',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Add new'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
type: 'void',
|
||||
title: '{{ t("Add new") }}',
|
||||
'x-action': 'create',
|
||||
'x-component': 'Action',
|
||||
'x-component-props': {
|
||||
type: 'primary',
|
||||
openMode: 'drawer',
|
||||
},
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
title: '{{ t("Add new record") }}',
|
||||
'x-component': 'Action.Container',
|
||||
'x-component-props': {},
|
||||
'x-decorator': 'Form',
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid',
|
||||
'x-item-initializer': 'FormItemInitializer',
|
||||
properties: {},
|
||||
},
|
||||
footer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Container.Footer',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-action-initializer': 'PopupFormActionInitializer',
|
||||
'x-decorator': 'DndContext',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
layout: 'one-column',
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Delete'),
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-action': 'destroy',
|
||||
'x-component-props': {
|
||||
confirm: {
|
||||
title: "{{t('Delete record')}}",
|
||||
content: "{{t('Are you sure you want to delete it?')}}",
|
||||
},
|
||||
useAction: '{{ cm.useBulkDestroyAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure actions')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,50 @@
|
||||
// 表格操作配置
|
||||
export const TableActionInitializers = {
|
||||
title: "{{t('Configure actions')}}",
|
||||
style: {
|
||||
marginLeft: 8,
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: "{{t('Enable actions')}}",
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: "{{t('Filter')}}",
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Filter") }}',
|
||||
'x-action': 'filter',
|
||||
'x-align': 'left',
|
||||
'x-component': 'Action',
|
||||
'x-designer': 'Action.Designer',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: "{{t('Add new')}}",
|
||||
component: 'AddNewActionInitializer',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: "{{t('Delete')}}",
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-action': 'destroy',
|
||||
'x-component': 'Action',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component-props': {
|
||||
confirm: {
|
||||
title: "{{t('Delete record')}}",
|
||||
content: "{{t('Are you sure you want to delete it?')}}",
|
||||
},
|
||||
useAction: '{{ cm.useBulkDestroyAction }}',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,102 +0,0 @@
|
||||
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer, SchemaInitializerItemOptions } from '../..';
|
||||
import { useCollection, useDesignable } from '../../..';
|
||||
|
||||
const useTableColumnInitializerFields = () => {
|
||||
const { name, fields } = useCollection();
|
||||
return (
|
||||
fields
|
||||
// .filter((field) => field?.uiSchema?.title)
|
||||
.map((field) => {
|
||||
return {
|
||||
type: 'item',
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
schema: {
|
||||
name: field.name,
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
'x-component': 'CollectionField',
|
||||
},
|
||||
component: ColumnInitializerItem,
|
||||
} as SchemaInitializerItemOptions;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
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);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ColumnInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentColumnSchema(item.schema['x-collection-field']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-decorator': 'TableColumnDecorator',
|
||||
'x-designer': 'TableColumnDeigner',
|
||||
'x-component': 'VoidTable.Column',
|
||||
properties: {
|
||||
[item.schema.name]: {
|
||||
'x-read-pretty': true,
|
||||
...item.schema,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* 表格列配置器
|
||||
*/
|
||||
export const TableColumnInitializer = observer((props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Display fields'),
|
||||
children: useTableColumnInitializerFields(),
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure columns')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../SchemaInitializer';
|
||||
import { useTableColumnInitializerFields } from './utils';
|
||||
|
||||
// 表格列配置
|
||||
export const TableColumnInitializers = (props: any) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
wrap={(s) => {
|
||||
return {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableColumnDecorator',
|
||||
'x-designer': 'TableColumnDeigner',
|
||||
'x-component': 'VoidTable.Column',
|
||||
properties: {
|
||||
[s.name]: {
|
||||
'x-read-pretty': true,
|
||||
...s,
|
||||
},
|
||||
},
|
||||
};
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: t('Display fields'),
|
||||
children: useTableColumnInitializerFields(),
|
||||
},
|
||||
]}
|
||||
>
|
||||
{t('Configure columns')}
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
};
|
@ -1,92 +1,18 @@
|
||||
import { MenuOutlined } from '@ant-design/icons';
|
||||
import { css } from '@emotion/css';
|
||||
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { Switch } from 'antd';
|
||||
import { useFieldSchema } from '@formily/react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SchemaInitializer } from '../..';
|
||||
import { useAPIClient } from '../../../api-client';
|
||||
import { createDesignable, useDesignable } from '../../../schema-component';
|
||||
import { useAPIClient } from '../../api-client';
|
||||
import { createDesignable, useDesignable } from '../../schema-component';
|
||||
|
||||
const useCurrentActionSchema = (action: string) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { remove } = useDesignable();
|
||||
const findActionSchema = (schema: Schema) => {
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s['x-action'] === action) {
|
||||
return s;
|
||||
}
|
||||
const c = findActionSchema(s);
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
};
|
||||
const schema = findActionSchema(fieldSchema);
|
||||
return {
|
||||
schema,
|
||||
exists: !!schema,
|
||||
remove() {
|
||||
schema && remove(schema);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const InitializeViewAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
console.log('InitializeAction', insert);
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'Action.Link',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
console.log('InitializeAction', insert);
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action.Link',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export const TableRecordActionInitializer = observer((props: any) => {
|
||||
export const TableRecordActionInitializers = (props: any) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const api = useAPIClient();
|
||||
const { refresh } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
console.log('TableRecordActionInitializers');
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
className={css`
|
||||
@ -126,7 +52,7 @@ export const TableRecordActionInitializer = observer((props: any) => {
|
||||
{
|
||||
type: 'item',
|
||||
title: t('View'),
|
||||
component: InitializeViewAction,
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("View") }}',
|
||||
type: 'void',
|
||||
@ -171,11 +97,12 @@ export const TableRecordActionInitializer = observer((props: any) => {
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Edit'),
|
||||
component: InitializeAction,
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Edit") }}',
|
||||
type: 'void',
|
||||
'x-action': 'update',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {},
|
||||
properties: {
|
||||
@ -218,10 +145,12 @@ export const TableRecordActionInitializer = observer((props: any) => {
|
||||
{
|
||||
type: 'item',
|
||||
title: t('Delete'),
|
||||
component: InitializeAction,
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: '{{ t("Delete") }}',
|
||||
'x-action': 'destroy',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
confirm: {
|
||||
title: "{{t('Delete record')}}",
|
||||
@ -238,4 +167,4 @@ export const TableRecordActionInitializer = observer((props: any) => {
|
||||
<MenuOutlined />
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
export * from './BlockInitializer';
|
||||
export * from './CalendarActionInitializer';
|
||||
export * from './DetailsActionInitializer';
|
||||
export * from './FormActionInitializer';
|
||||
export * from './FormItemInitializer';
|
||||
export * from './PopupFormActionInitializer';
|
||||
export * from './RecordBlockInitializer';
|
||||
export * from './TableActionInitializer';
|
||||
export * from './TableColumnInitializer';
|
||||
export * from './TableRecordActionInitializer';
|
||||
|
@ -0,0 +1,36 @@
|
||||
import { BlockInitializers } from './BlockInitializers';
|
||||
import { CalendarActionInitializers } from './CalendarActionInitializers';
|
||||
import { DetailsActionInitializers } from './DetailsActionInitializers';
|
||||
import { FormActionInitializers } from './FormActionInitializers';
|
||||
import { GridFormItemInitializers } from './GridFormItemInitializers';
|
||||
import * as Items from './Items';
|
||||
import { PopupFormActionInitializers } from './PopupFormActionInitializers';
|
||||
import { RecordBlockInitializers } from './RecordBlockInitializers';
|
||||
import { TableActionInitializers } from './TableActionInitializers';
|
||||
import { TableColumnInitializers } from './TableColumnInitializers';
|
||||
import { TableRecordActionInitializers } from './TableRecordActionInitializers';
|
||||
|
||||
export const items = { ...Items };
|
||||
|
||||
export const initializes = {
|
||||
// 页面里的「添加区块」
|
||||
BlockInitializers,
|
||||
// 日历的「操作配置」
|
||||
CalendarActionInitializers,
|
||||
// 详情的「操作配置」
|
||||
DetailsActionInitializers,
|
||||
// 普通表单的「操作配置」
|
||||
FormActionInitializers,
|
||||
// Grid 组件里「配置字段」
|
||||
GridFormItemInitializers,
|
||||
// 弹窗表单的「操作配置」
|
||||
PopupFormActionInitializers,
|
||||
// 当前行记录所在面板的「添加区块」
|
||||
RecordBlockInitializers,
|
||||
// 表格「操作配置」
|
||||
TableActionInitializers,
|
||||
// 表格「列配置」
|
||||
TableColumnInitializers,
|
||||
// 表格当前行记录的「操作配置」
|
||||
TableRecordActionInitializers,
|
||||
};
|
109
packages/client/src/schema-initializer/Initializers/utils.ts
Normal file
109
packages/client/src/schema-initializer/Initializers/utils.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { ISchema, Schema, useFieldSchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { SchemaInitializerItemOptions } from '../';
|
||||
import { useCollection } from '../../collection-manager';
|
||||
import { useDesignable } from '../../schema-component';
|
||||
|
||||
export const gridRowColWrap = (schema: ISchema) => {
|
||||
return {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[uid()]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
properties: {
|
||||
[schema.name || uid()]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const removeTableColumn = (schema, cb) => {
|
||||
cb(schema, {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: {
|
||||
'x-component': 'ArrayTable',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const removeGridFormItem = (schema, cb) => {
|
||||
cb(schema, {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: {
|
||||
'x-component': 'Grid',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useTableColumnInitializerFields = () => {
|
||||
const { name, fields } = useCollection();
|
||||
return (
|
||||
fields
|
||||
// .filter((field) => field?.uiSchema?.title)
|
||||
.map((field) => {
|
||||
return {
|
||||
type: 'item',
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
schema: {
|
||||
name: field.name,
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
'x-component': 'CollectionField',
|
||||
},
|
||||
component: 'CollectionFieldInitializer',
|
||||
remove: removeTableColumn,
|
||||
} as SchemaInitializerItemOptions;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const useFormItemInitializerFields = () => {
|
||||
const { name, fields } = useCollection();
|
||||
return fields?.map((field) => {
|
||||
return {
|
||||
type: 'item',
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
component: 'CollectionFieldInitializer',
|
||||
remove: removeGridFormItem,
|
||||
schema: {
|
||||
name: field.name,
|
||||
'x-designer': 'FormItem.Designer',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
},
|
||||
} as SchemaInitializerItemOptions;
|
||||
});
|
||||
};
|
||||
|
||||
const findSchema = (schema: Schema, key: string, action: string) => {
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s[key] === action) {
|
||||
return s;
|
||||
}
|
||||
const c = findSchema(s, key, action);
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
};
|
||||
|
||||
const removeSchema = (schema, cb) => {
|
||||
return cb(schema);
|
||||
};
|
||||
|
||||
export const useCurrentSchema = (action: string, key: string, find = findSchema, rm = removeSchema) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { remove } = useDesignable();
|
||||
const schema = find(fieldSchema, key, action);
|
||||
return {
|
||||
schema,
|
||||
exists: !!schema,
|
||||
remove() {
|
||||
schema && rm(schema, remove);
|
||||
},
|
||||
};
|
||||
};
|
@ -2,6 +2,7 @@ import { ISchema, observer } from '@formily/react';
|
||||
import { Button, Dropdown, Menu } from 'antd';
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { useDesignable } from '../schema-component/hooks';
|
||||
import { initializes, items } from './Initializers';
|
||||
import {
|
||||
SchemaInitializerButtonProps,
|
||||
SchemaInitializerItemComponent,
|
||||
@ -15,8 +16,21 @@ export const SchemaInitializerItemContext = createContext(null);
|
||||
|
||||
export const SchemaInitializer = () => null;
|
||||
|
||||
SchemaInitializer.items = items;
|
||||
|
||||
SchemaInitializer.initializes = initializes;
|
||||
|
||||
SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
||||
const { insert, wrap = defaultWrap, items = [], insertPosition, dropdown, style, ...others } = props;
|
||||
const {
|
||||
title,
|
||||
insert,
|
||||
wrap = defaultWrap,
|
||||
items = [],
|
||||
insertPosition = 'beforeEnd',
|
||||
dropdown,
|
||||
style,
|
||||
...others
|
||||
} = props;
|
||||
let { insertAdjacent, findComponent, designable } = useDesignable();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const insertSchema = (schema) => {
|
||||
@ -102,7 +116,9 @@ SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
||||
borderColor: '#f18b62',
|
||||
color: '#f18b62',
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{props.children || props.title}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
});
|
||||
|
@ -1,7 +1,54 @@
|
||||
import React from 'react';
|
||||
import { isPlainObj } from '@formily/shared';
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { SchemaComponentOptions } from '../schema-component';
|
||||
import * as initializers from './Initializers';
|
||||
import { initializes as globals, items } from './Initializers';
|
||||
import { SchemaInitializer } from './SchemaInitializer';
|
||||
|
||||
export const SchemaInitializerProvider: React.FC = (props) => {
|
||||
return <SchemaComponentOptions components={initializers}>{props.children}</SchemaComponentOptions>;
|
||||
const SchemaInitializerContext = createContext(null);
|
||||
|
||||
export interface SchemaInitializerProviderProps {
|
||||
initializers?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const useSchemaInitializer = (name: string) => {
|
||||
const initializers = useContext(SchemaInitializerContext);
|
||||
|
||||
const render = (component?: any, props?: any) => {
|
||||
return component && React.createElement(component, props);
|
||||
};
|
||||
|
||||
if (!name) {
|
||||
return { exists: false, render };
|
||||
}
|
||||
|
||||
const initializer = initializers?.[name];
|
||||
|
||||
if (!initializer) {
|
||||
return { exists: false, render };
|
||||
}
|
||||
|
||||
if (isPlainObj(initializer)) {
|
||||
return {
|
||||
exists: true,
|
||||
render: (props?: any) => {
|
||||
const component = (initializer as any).component || SchemaInitializer.Button;
|
||||
return render(component, { ...initializer, ...props });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
exists: true,
|
||||
render: (props?: any) => render(initializer, props),
|
||||
};
|
||||
};
|
||||
|
||||
export const SchemaInitializerProvider: React.FC<SchemaInitializerProviderProps> = (props) => {
|
||||
const { initializers, children } = props;
|
||||
|
||||
return (
|
||||
<SchemaInitializerContext.Provider value={{ ...globals, ...initializers }}>
|
||||
<SchemaComponentOptions components={{ ...items }}>{children}</SchemaComponentOptions>
|
||||
</SchemaInitializerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { FormOutlined, TableOutlined } from '@ant-design/icons';
|
||||
import { TableOutlined } from '@ant-design/icons';
|
||||
import { Field } from '@formily/core';
|
||||
import { observer, useField } from '@formily/react';
|
||||
import { SchemaComponent, SchemaComponentProvider, SchemaInitializer } from '@nocobase/client';
|
||||
import {
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializer,
|
||||
SchemaInitializerProvider,
|
||||
useSchemaInitializer
|
||||
} from '@nocobase/client';
|
||||
import React from 'react';
|
||||
|
||||
const Hello = observer((props) => {
|
||||
@ -13,36 +19,7 @@ const Hello = observer((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
const AddBlockButton = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={(schema) => schema}
|
||||
insertPosition={'beforeBegin'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Data blocks',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Table',
|
||||
component: TableBlockInitializerItem,
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Form',
|
||||
component: FormBlockInitializerItem,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Add block
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
|
||||
const TableBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
const TableBlockInitializer = SchemaInitializer.itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
const items: any = [
|
||||
{
|
||||
@ -77,48 +54,69 @@ const TableBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
const FormBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
icon={<FormOutlined />}
|
||||
onClick={() => {
|
||||
insert({
|
||||
type: 'void',
|
||||
title: 'Form',
|
||||
'x-component': 'Hello',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const initializers = {
|
||||
AddBlock: {
|
||||
title: 'Add block',
|
||||
insertPosition: 'beforeBegin',
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Data blocks',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Table',
|
||||
component: 'TableBlockInitializer',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Form',
|
||||
component: 'GeneralInitializer',
|
||||
schema: {
|
||||
type: 'void',
|
||||
title: 'Form',
|
||||
'x-component': 'Hello',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const AddBlockButton = observer((props: any) => {
|
||||
const { render } = useSchemaInitializer('AddBlock');
|
||||
return <>{render()}</>;
|
||||
});
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<SchemaComponentProvider components={{ Hello, AddBlockButton }}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'void',
|
||||
name: 'page',
|
||||
'x-component': 'div',
|
||||
properties: {
|
||||
hello1: {
|
||||
type: 'void',
|
||||
title: 'Test1',
|
||||
'x-component': 'Hello',
|
||||
<SchemaComponentProvider components={{ TableBlockInitializer, Hello, AddBlockButton }}>
|
||||
<SchemaInitializerProvider initializers={initializers}>
|
||||
<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': 'AddBlockButton',
|
||||
},
|
||||
},
|
||||
hello2: {
|
||||
type: 'void',
|
||||
title: 'Test2',
|
||||
'x-component': 'Hello',
|
||||
},
|
||||
initializer: {
|
||||
type: 'void',
|
||||
'x-component': 'AddBlockButton',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
</SchemaInitializerProvider>
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,117 +1,77 @@
|
||||
import { observer, useFieldSchema } from '@formily/react';
|
||||
import {
|
||||
Action,
|
||||
ActionBar,
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializer,
|
||||
useDesignable
|
||||
SchemaInitializerProvider
|
||||
} from '@nocobase/client';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const AddActionButton = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
insertPosition={'beforeEnd'}
|
||||
style={{ marginLeft: 8 }}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
const initializers = {
|
||||
AddAction: {
|
||||
title: 'Configure actions',
|
||||
insertPosition: 'beforeEnd',
|
||||
style: { marginLeft: 8 },
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Enable actions',
|
||||
children: [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Create',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Create',
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: 'Create',
|
||||
'x-action': 'posts:create',
|
||||
'x-align': 'left',
|
||||
},
|
||||
'x-action': 'posts:create',
|
||||
'x-component': 'Action',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-align': 'left',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Update',
|
||||
component: 'ActionInitializer',
|
||||
schema: {
|
||||
title: 'Update',
|
||||
component: InitializeAction,
|
||||
schema: {
|
||||
title: 'Update',
|
||||
'x-action': 'posts:update',
|
||||
},
|
||||
'x-action': 'posts:update',
|
||||
'x-component': 'Action',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-align': 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure actions
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
|
||||
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 InitializeAction = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentActionSchema(item.schema['x-action']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
'x-component': 'Action',
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<SchemaComponentProvider components={{ ActionBar, Action, AddActionButton }}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'void',
|
||||
name: 'page',
|
||||
'x-component': 'ActionBar',
|
||||
// 指定初始化的按钮组件,
|
||||
// Table、Form、Details、Calendar、Kanban 等等不同区块
|
||||
// 可以根据情况组装自己的 initializer
|
||||
'x-action-initializer': 'AddActionButton',
|
||||
properties: {
|
||||
action1: {
|
||||
type: 'void',
|
||||
title: 'Update',
|
||||
// 使用 x-action 来标记 action schema
|
||||
'x-action': 'posts:update',
|
||||
'x-component': 'Action',
|
||||
<SchemaComponentProvider components={{ ActionBar, Action }}>
|
||||
<SchemaInitializerProvider initializers={initializers}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'void',
|
||||
name: 'page',
|
||||
'x-component': 'ActionBar',
|
||||
// 指定初始化的按钮组件,
|
||||
// Table、Form、Details、Calendar、Kanban 等等不同区块
|
||||
// 可以根据情况组装自己的 initializer
|
||||
'x-initializer': 'AddAction',
|
||||
properties: {
|
||||
action1: {
|
||||
type: 'void',
|
||||
title: 'Update',
|
||||
// 使用 x-action 来标记 action schema
|
||||
'x-action': 'posts:update',
|
||||
'x-component': 'Action',
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
</SchemaInitializerProvider>
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import {
|
||||
Form,
|
||||
FormItem,
|
||||
@ -6,18 +5,18 @@ import {
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializer,
|
||||
SchemaInitializerItemOptions,
|
||||
useDesignable
|
||||
SchemaInitializerItemOptions
|
||||
} from '@nocobase/client';
|
||||
import { Input, Switch } from 'antd';
|
||||
import { Input } from 'antd';
|
||||
import React from 'react';
|
||||
import { SchemaInitializerProvider, useSchemaInitializer } from '..';
|
||||
|
||||
const useFormItemInitializerFields = () => {
|
||||
return [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Name',
|
||||
component: InitializeFormItem,
|
||||
component: 'CollectionFieldInitializer',
|
||||
schema: {
|
||||
type: 'string',
|
||||
title: 'Name',
|
||||
@ -30,7 +29,7 @@ const useFormItemInitializerFields = () => {
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Title',
|
||||
component: InitializeFormItem,
|
||||
component: 'CollectionFieldInitializer',
|
||||
schema: {
|
||||
type: 'string',
|
||||
title: 'Title',
|
||||
@ -43,79 +42,7 @@ const useFormItemInitializerFields = () => {
|
||||
] as SchemaInitializerItemOptions[];
|
||||
};
|
||||
|
||||
const AddFormItemButton = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={(schema) => schema}
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Display fields',
|
||||
children: useFormItemInitializerFields(),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Add text',
|
||||
component: InitializeTextFormItem,
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure fields
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
|
||||
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 InitializeFormItem = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentFieldSchema(item.schema['x-collection-field']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
...item.schema,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const InitializeTextFormItem = SchemaInitializer.itemWrap((props) => {
|
||||
const TextInitializer = SchemaInitializer.itemWrap((props) => {
|
||||
const { insert } = props;
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
@ -132,41 +59,66 @@ const InitializeTextFormItem = SchemaInitializer.itemWrap((props) => {
|
||||
});
|
||||
|
||||
const Page = (props) => {
|
||||
const { render } = useSchemaInitializer('AddFormItem');
|
||||
return (
|
||||
<div>
|
||||
{props.children}
|
||||
<AddFormItemButton />
|
||||
{render()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
const initializers = {
|
||||
AddFormItem: {
|
||||
title: 'Configure fields',
|
||||
insertPosition: 'beforeEnd',
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Display fields',
|
||||
// 从 hook 动态加载字段
|
||||
children: useFormItemInitializerFields(),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Add text',
|
||||
component: 'TextInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
return (
|
||||
<SchemaComponentProvider components={{ Page, Form, Input, FormItem, Markdown }}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'void',
|
||||
name: 'page',
|
||||
'x-decorator': 'Form',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
title: 'Title',
|
||||
'x-component': 'Input',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': 'posts.title',
|
||||
<SchemaComponentProvider components={{ TextInitializer, Page, Form, Input, FormItem, Markdown }}>
|
||||
<SchemaInitializerProvider initializers={initializers}>
|
||||
<SchemaComponent
|
||||
schema={{
|
||||
type: 'void',
|
||||
name: 'page',
|
||||
'x-decorator': 'Form',
|
||||
'x-component': 'Page',
|
||||
properties: {
|
||||
title: {
|
||||
type: 'string',
|
||||
title: 'Title',
|
||||
'x-component': 'Input',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': 'posts.title',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
title: 'Name',
|
||||
'x-component': 'Input',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': 'posts.name',
|
||||
},
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
title: 'Name',
|
||||
'x-component': 'Input',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': 'posts.name',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
</SchemaInitializerProvider>
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,116 +1,90 @@
|
||||
import { observer, Schema, useFieldSchema } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import {
|
||||
ArrayTable,
|
||||
Input,
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
SchemaInitializer,
|
||||
SchemaInitializerItemOptions,
|
||||
useDesignable
|
||||
SchemaInitializerProvider
|
||||
} from '@nocobase/client';
|
||||
import { Switch } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const removeColumn = (schema, cb) => {
|
||||
cb(schema, {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: {
|
||||
'x-component': 'ArrayTable',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const useTableColumnInitializerFields = () => {
|
||||
const fields: SchemaInitializerItemOptions[] = [
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Name',
|
||||
remove: removeColumn,
|
||||
schema: {
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
title: 'Name',
|
||||
'x-collection-field': 'posts.name',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
component: ColumnInitializerItem,
|
||||
component: 'CollectionFieldInitializer',
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: 'Title',
|
||||
remove: removeColumn,
|
||||
schema: {
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
title: 'Title',
|
||||
'x-collection-field': 'posts.title',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
component: ColumnInitializerItem,
|
||||
component: 'CollectionFieldInitializer',
|
||||
},
|
||||
];
|
||||
return fields;
|
||||
};
|
||||
|
||||
export const AddTableColumn = observer((props: any) => {
|
||||
return (
|
||||
<SchemaInitializer.Button
|
||||
wrap={(schema) => schema}
|
||||
insertPosition={'beforeEnd'}
|
||||
items={[
|
||||
const columnWrap = (s) => {
|
||||
return {
|
||||
name: [uid()],
|
||||
type: 'void',
|
||||
title: s.title,
|
||||
'x-component': 'ArrayTable.Column',
|
||||
properties: {
|
||||
[s.name]: {
|
||||
...s,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// 因为有个动态获取字段的 hook,所以这里将 SchemaInitializerProvider 自定义的处理了一下
|
||||
const CustomSchemaInitializerProvider: React.FC = (props) => {
|
||||
const initializers = {
|
||||
AddColumn: {
|
||||
insertPosition: 'beforeEnd',
|
||||
title: 'Configure columns',
|
||||
wrap: columnWrap,
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
title: 'Display fields',
|
||||
children: useTableColumnInitializerFields(),
|
||||
},
|
||||
]}
|
||||
>
|
||||
Configure columns
|
||||
</SchemaInitializer.Button>
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
],
|
||||
},
|
||||
};
|
||||
return <SchemaInitializerProvider initializers={initializers}>{props.children}</SchemaInitializerProvider>;
|
||||
};
|
||||
|
||||
export const ColumnInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
const { item, insert } = props;
|
||||
const { exists, remove } = useCurrentColumnSchema(item.schema['x-collection-field']);
|
||||
return (
|
||||
<SchemaInitializer.Item
|
||||
onClick={() => {
|
||||
if (exists) {
|
||||
return remove();
|
||||
}
|
||||
insert({
|
||||
type: 'void',
|
||||
title: item.schema.name,
|
||||
'x-component': 'ArrayTable.Column',
|
||||
properties: {
|
||||
[item.schema.name]: {
|
||||
'x-read-pretty': true,
|
||||
...item.schema,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{item.title} <Switch size={'small'} checked={exists} />
|
||||
</div>
|
||||
</SchemaInitializer.Item>
|
||||
);
|
||||
});
|
||||
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@ -122,7 +96,7 @@ const schema = {
|
||||
{ id: 3, name: 'Name3' },
|
||||
],
|
||||
'x-component': 'ArrayTable',
|
||||
'x-column-initializer': 'AddTableColumn',
|
||||
'x-initializer': 'AddColumn',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
},
|
||||
@ -147,8 +121,10 @@ const schema = {
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<SchemaComponentProvider components={{ AddTableColumn, Input, ArrayTable }}>
|
||||
<SchemaComponent schema={schema} />
|
||||
<SchemaComponentProvider components={{ Input, ArrayTable }}>
|
||||
<CustomSchemaInitializerProvider>
|
||||
<SchemaComponent schema={schema} />
|
||||
</CustomSchemaInitializerProvider>
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -150,3 +150,70 @@ const TableBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
|
||||
### Table.Column
|
||||
|
||||
<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` 用于配置数据表字段,有插入和移除的逻辑
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './SchemaInitializer';
|
||||
export * from './SchemaInitializerProvider';
|
||||
export * from './types';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ISchema } from '@formily/react';
|
||||
import { ISchema, Schema } from '@formily/react';
|
||||
import { ButtonProps, DropDownProps, MenuItemProps } from 'antd';
|
||||
|
||||
export interface SchemaInitializerButtonProps extends ButtonProps {
|
||||
@ -25,10 +25,21 @@ interface SubMenuOptions extends ItemCommonOptions {
|
||||
children?: SchemaInitializerItemOptions[];
|
||||
}
|
||||
|
||||
type BreakFn = (s: ISchema) => boolean;
|
||||
|
||||
interface RemoveOptions {
|
||||
removeParentsIfNoChildren?: boolean;
|
||||
breakRemoveOn?: ISchema | BreakFn;
|
||||
}
|
||||
|
||||
type RemoveCallback = (s: Schema, options?: RemoveOptions) => void;
|
||||
|
||||
interface ItemOptions extends ItemCommonOptions {
|
||||
type: 'item';
|
||||
component?: any;
|
||||
schema?: ISchema;
|
||||
remove?: (schema: Schema, cb: RemoveCallback) => void;
|
||||
find?: (schema: Schema, current: string) => Schema | null | undefined;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ export class UiRoutesStoragePlugin extends Plugin {
|
||||
type: 'void',
|
||||
'x-component': 'Menu',
|
||||
'x-designer': 'Menu.Designer',
|
||||
'x-initializer': 'MenuItemInitializer',
|
||||
'x-initializer': 'MenuItemInitializers',
|
||||
'x-component-props': {
|
||||
mode: 'mix',
|
||||
theme: 'dark',
|
||||
|
Loading…
Reference in New Issue
Block a user