mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-08 15:09:27 +08:00
251 lines
7.4 KiB
TypeScript
251 lines
7.4 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import { ISchema, observer } from '@formily/react';
|
|
import { Button, Dropdown, Menu, Switch } from 'antd';
|
|
import classNames from 'classnames';
|
|
import React, { createContext, useContext, useState } from 'react';
|
|
import { Icon } from '../icon';
|
|
import { useCompile, useDesignable } from '../schema-component/hooks';
|
|
import './style.less';
|
|
import {
|
|
SchemaInitializerButtonProps,
|
|
SchemaInitializerItemComponent,
|
|
SchemaInitializerItemOptions,
|
|
SchemaInitializerItemProps,
|
|
} from './types';
|
|
|
|
const defaultWrap = (s: ISchema) => s;
|
|
|
|
export const SchemaInitializerItemContext = createContext(null);
|
|
export const SchemaInitializerButtonContext = createContext<any>({});
|
|
|
|
export const SchemaInitializer = () => null;
|
|
|
|
const menuItemGroupCss = 'nb-menu-item-group';
|
|
|
|
SchemaInitializer.Button = observer((props: SchemaInitializerButtonProps) => {
|
|
const {
|
|
title,
|
|
insert,
|
|
wrap = defaultWrap,
|
|
items = [],
|
|
insertPosition = 'beforeEnd',
|
|
dropdown,
|
|
component,
|
|
style,
|
|
icon,
|
|
onSuccess,
|
|
...others
|
|
} = props;
|
|
const compile = useCompile();
|
|
let { insertAdjacent, findComponent, designable } = useDesignable();
|
|
const [visible, setVisible] = useState(false);
|
|
const insertSchema = (schema) => {
|
|
if (props.insert) {
|
|
props.insert(wrap(schema));
|
|
} else {
|
|
insertAdjacent(insertPosition, wrap(schema), { onSuccess });
|
|
}
|
|
};
|
|
const renderItems = (items: any) => {
|
|
return items
|
|
.filter((v) => {
|
|
return v && (v?.visible ? v.visible() : true);
|
|
})
|
|
?.map((item, indexA) => {
|
|
if (item.type === 'divider') {
|
|
return <Menu.Divider key={item.key || `item-${indexA}`} />;
|
|
}
|
|
if (item.type === 'item' && item.component) {
|
|
const Component = findComponent(item.component);
|
|
item.key = `${item.key || item.title}-${indexA}`;
|
|
return (
|
|
Component && (
|
|
<SchemaInitializerItemContext.Provider
|
|
key={item.key}
|
|
value={{
|
|
index: indexA,
|
|
item,
|
|
info: item,
|
|
insert: insertSchema,
|
|
}}
|
|
>
|
|
<Component
|
|
{...item}
|
|
item={{
|
|
...item,
|
|
title: compile(item.title),
|
|
}}
|
|
insert={insertSchema}
|
|
/>
|
|
</SchemaInitializerItemContext.Provider>
|
|
)
|
|
);
|
|
}
|
|
if (item.type === 'itemGroup') {
|
|
return (
|
|
!!item.children?.length && (
|
|
<Menu.ItemGroup key={item.key || `item-group-${indexA}`} title={compile(item.title)}>
|
|
{renderItems(item.children)}
|
|
</Menu.ItemGroup>
|
|
)
|
|
);
|
|
}
|
|
if (item.type === 'subMenu') {
|
|
return (
|
|
!!item.children?.length && (
|
|
<Menu.SubMenu
|
|
key={item.key || `item-group-${indexA}`}
|
|
title={compile(item.title)}
|
|
popupClassName={menuItemGroupCss}
|
|
>
|
|
{renderItems(item.children)}
|
|
</Menu.SubMenu>
|
|
)
|
|
);
|
|
}
|
|
});
|
|
};
|
|
const menu = <Menu style={{ maxHeight: '60vh', overflowY: 'auto' }}>{renderItems(items)}</Menu>;
|
|
if (!designable && props.designable !== true) {
|
|
return null;
|
|
}
|
|
return (
|
|
<SchemaInitializerButtonContext.Provider value={{ visible, setVisible }}>
|
|
<Dropdown
|
|
className={classNames('nb-schema-initializer-button')}
|
|
openClassName={`nb-schema-initializer-button-open`}
|
|
overlayClassName={classNames(
|
|
'nb-schema-initializer-button-overlay',
|
|
css`
|
|
.ant-dropdown-menu-item-group-list {
|
|
max-height: 40vh;
|
|
overflow: auto;
|
|
}
|
|
`,
|
|
)}
|
|
open={visible}
|
|
onOpenChange={(visible) => {
|
|
setVisible(visible);
|
|
}}
|
|
{...dropdown}
|
|
overlay={menu}
|
|
>
|
|
{component ? (
|
|
component
|
|
) : (
|
|
<Button
|
|
type={'dashed'}
|
|
style={{
|
|
borderColor: '#f18b62',
|
|
color: '#f18b62',
|
|
...style,
|
|
}}
|
|
{...others}
|
|
icon={<Icon type={icon as string} />}
|
|
>
|
|
{compile(props.children || props.title)}
|
|
</Button>
|
|
)}
|
|
</Dropdown>
|
|
</SchemaInitializerButtonContext.Provider>
|
|
);
|
|
});
|
|
|
|
SchemaInitializer.Item = (props: SchemaInitializerItemProps) => {
|
|
const { index, info } = useContext(SchemaInitializerItemContext);
|
|
const compile = useCompile();
|
|
const { eventKey, items = [], children = info?.title, icon, onClick, ...others } = props;
|
|
if (items?.length > 0) {
|
|
const renderMenuItem = (items: SchemaInitializerItemOptions[]) => {
|
|
if (!items?.length) {
|
|
return null;
|
|
}
|
|
return items.map((item, indexA) => {
|
|
if (item.type === 'divider') {
|
|
return <Menu.Divider key={`divider-${indexA}`} />;
|
|
}
|
|
if (item.type === 'itemGroup') {
|
|
return (
|
|
<Menu.ItemGroup
|
|
// @ts-ignore
|
|
eventKey={item.key || `item-group-${indexA}`}
|
|
key={item.key || `item-group-${indexA}`}
|
|
title={compile(item.title)}
|
|
className={menuItemGroupCss}
|
|
>
|
|
{renderMenuItem(item.children)}
|
|
</Menu.ItemGroup>
|
|
);
|
|
}
|
|
if (item.type === 'subMenu') {
|
|
return (
|
|
<Menu.SubMenu
|
|
// @ts-ignore
|
|
eventKey={item.key || `sub-menu-${indexA}`}
|
|
key={item.key || `sub-menu-${indexA}`}
|
|
title={compile(item.title)}
|
|
>
|
|
{renderMenuItem(item.children)}
|
|
</Menu.SubMenu>
|
|
);
|
|
}
|
|
return (
|
|
<Menu.Item
|
|
eventKey={item.key}
|
|
key={item.key}
|
|
onClick={(info) => {
|
|
item?.clearKeywords?.();
|
|
if (item.onClick) {
|
|
item.onClick({ ...info, item });
|
|
} else {
|
|
onClick({ ...info, item });
|
|
}
|
|
}}
|
|
>
|
|
{compile(item.title)}
|
|
</Menu.Item>
|
|
);
|
|
});
|
|
};
|
|
return (
|
|
<Menu.SubMenu
|
|
// @ts-ignore
|
|
eventKey={eventKey ? `${eventKey}-${index}` : info.key}
|
|
key={info.key}
|
|
title={compile(children)}
|
|
icon={typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
|
|
>
|
|
{renderMenuItem(items)}
|
|
</Menu.SubMenu>
|
|
);
|
|
}
|
|
return (
|
|
<Menu.Item
|
|
// {...others}
|
|
key={info.key}
|
|
eventKey={eventKey ? `${eventKey}-${index}` : info.key}
|
|
icon={typeof icon === 'string' ? <Icon type={icon as string} /> : icon}
|
|
onClick={(opts) => {
|
|
info?.clearKeywords?.();
|
|
onClick({ ...opts, item: info });
|
|
}}
|
|
>
|
|
{compile(children)}
|
|
</Menu.Item>
|
|
);
|
|
};
|
|
|
|
SchemaInitializer.itemWrap = (component?: SchemaInitializerItemComponent) => {
|
|
return component;
|
|
};
|
|
|
|
SchemaInitializer.SwitchItem = (props) => {
|
|
return (
|
|
<SchemaInitializer.Item onClick={props.onClick}>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
{props.title} <Switch style={{ marginLeft: 20 }} size={'small'} checked={props.checked} />
|
|
</div>
|
|
</SchemaInitializer.Item>
|
|
);
|
|
};
|