refactor(client): schema initializer

This commit is contained in:
chenos 2022-02-22 11:17:24 +08:00
parent d486768eda
commit f45b08af38
51 changed files with 1108 additions and 1492 deletions

View File

@ -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' }],
[

View File

@ -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: {},
},

View File

@ -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>
);
});

View File

@ -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',
});

View File

@ -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) {

View File

@ -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>

View File

@ -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>
);
});

View File

@ -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,10 +285,9 @@ export const Menu: ComposedMenu = observer((props) => {
`}
>
<RecursionField schema={sideMenuSchema} onlyRenderProperties />
{Initializer && (
<Initializer
style={{ margin: 8 }}
insert={(s) => {
{render({
style: { margin: 8 },
insert: (s) => {
console.log('createDesignable', s);
const dn = createDesignable({
api,
@ -297,9 +296,8 @@ export const Menu: ComposedMenu = observer((props) => {
});
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={() => {
<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}

View File

@ -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: {},
},
},

View File

@ -1,4 +1,4 @@
export * from './Menu';
export * from './MenuItemInitializer';
export * from './MenuItemInitializers';
export * from './util';

View File

@ -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>
);
});

View File

@ -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',
},
],
},
],
};

View File

@ -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>
);
});

View File

@ -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',
},
],
},
],
};

View File

@ -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>
);
});

View File

@ -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',
},
},
],
},
],
};

View File

@ -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>
);
});

View File

@ -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 }}',
},
},
},
],
},
],
};

View File

@ -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>
);
});

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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) => {
]}
/>
);
});
};

View File

@ -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>
);
};

View File

@ -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) => {
]}
/>
);
});
};

View File

@ -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,
});
}}
/>
);
};

View File

@ -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) => {
}}
/>
);
});
};

View File

@ -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) => {
]}
/>
);
});
};

View File

@ -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';

View File

@ -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>
);
});

View File

@ -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 }}',
},
},
},
],
},
],
};

View File

@ -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>
);
});

View File

@ -0,0 +1,8 @@
import { gridRowColWrap } from './utils';
// 当前行记录所在面板的添加区块
export const RecordBlockInitializers = {
wrap: gridRowColWrap,
title: "{{t('Add block')}}",
items: [],
};

View File

@ -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>
);
});

View File

@ -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 }}',
},
},
},
],
},
],
};

View File

@ -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>
);
});

View File

@ -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>
);
};

View File

@ -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>
);
});
};

View File

@ -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';

View File

@ -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,
};

View 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);
},
};
};

View File

@ -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>
);
});

View File

@ -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>
);
};

View File

@ -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,25 +54,45 @@ const TableBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
);
});
const FormBlockInitializerItem = SchemaInitializer.itemWrap((props) => {
const { insert } = props;
return (
<SchemaInitializer.Item
icon={<FormOutlined />}
onClick={() => {
insert({
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 }}>
<SchemaComponentProvider components={{ TableBlockInitializer, Hello, AddBlockButton }}>
<SchemaInitializerProvider initializers={initializers}>
<SchemaComponent
schema={{
type: 'void',
@ -119,6 +116,7 @@ export default function App() {
},
}}
/>
</SchemaInitializerProvider>
</SchemaComponentProvider>
);
}

View File

@ -1,21 +1,18 @@
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={[
const initializers = {
AddAction: {
title: 'Configure actions',
insertPosition: 'beforeEnd',
style: { marginLeft: 8 },
items: [
{
type: 'itemGroup',
title: 'Enable actions',
@ -23,75 +20,37 @@ const AddActionButton = observer((props: any) => {
{
type: 'item',
title: 'Create',
component: InitializeAction,
component: 'ActionInitializer',
schema: {
title: 'Create',
'x-action': 'posts:create',
'x-component': 'Action',
'x-designer': 'Action.Designer',
'x-align': 'left',
},
},
{
type: 'item',
title: 'Update',
component: InitializeAction,
component: 'ActionInitializer',
schema: {
title: '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 }}>
<SchemaComponentProvider components={{ ActionBar, Action }}>
<SchemaInitializerProvider initializers={initializers}>
<SchemaComponent
schema={{
type: 'void',
@ -100,7 +59,7 @@ export default function App() {
// 指定初始化的按钮组件,
// Table、Form、Details、Calendar、Kanban 等等不同区块
// 可以根据情况组装自己的 initializer
'x-action-initializer': 'AddActionButton',
'x-initializer': 'AddAction',
properties: {
action1: {
type: 'void',
@ -112,6 +71,7 @@ export default function App() {
},
}}
/>
</SchemaInitializerProvider>
</SchemaComponentProvider>
);
}

View File

@ -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,17 +59,41 @@ 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 }}>
<SchemaComponentProvider components={{ TextInitializer, Page, Form, Input, FormItem, Markdown }}>
<SchemaInitializerProvider initializers={initializers}>
<SchemaComponent
schema={{
type: 'void',
@ -167,6 +118,7 @@ export default function App() {
},
}}
/>
</SchemaInitializerProvider>
</SchemaComponentProvider>
);
}

View File

@ -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 }}>
<SchemaComponentProvider components={{ Input, ArrayTable }}>
<CustomSchemaInitializerProvider>
<SchemaComponent schema={schema} />
</CustomSchemaInitializerProvider>
</SchemaComponentProvider>
);
}

View File

@ -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` 用于配置数据表字段,有插入和移除的逻辑

View File

@ -1,3 +1,4 @@
export * from './SchemaInitializer';
export * from './SchemaInitializerProvider';
export * from './types';

View File

@ -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;
}

View File

@ -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',