138 lines
3.8 KiB
TypeScript
138 lines
3.8 KiB
TypeScript
import { Menu, Portal } from '@mantine/core';
|
|
import React, { useState } from 'react';
|
|
|
|
import { type MantineBetterMenuInstanceItem, useMantineBetterMenus } from './Store';
|
|
|
|
export function MenuRenderer() {
|
|
const { menus, providerID, setInstanceState, width } = useMantineBetterMenus((s) => ({
|
|
menus: s.menus,
|
|
providerID: s.providerID,
|
|
setInstanceState: s.setInstanceState,
|
|
setState: s.setState,
|
|
width: s.width,
|
|
}));
|
|
|
|
return (
|
|
<Portal id={`bmm_portal_${providerID}`} key={`bmm_portal_${providerID}`}>
|
|
{React.Children.toArray(
|
|
menus?.map((m, menuIndex) => {
|
|
return (
|
|
<Menu
|
|
shadow="md"
|
|
width={width ?? '300'}
|
|
{...m.menuProps}
|
|
key={`bmm_menu_${providerID}_${menuIndex}`}
|
|
onClose={() => {
|
|
setInstanceState(m.id, 'visible', false);
|
|
m.menuProps?.onClose?.();
|
|
}}
|
|
opened={m.visible}
|
|
>
|
|
<Menu.Target>
|
|
<div
|
|
id={`bmm_${providerID}_${menuIndex}_target`}
|
|
style={{ left: m.x, position: 'fixed', top: m.y, visibility: 'hidden' }}
|
|
/>
|
|
</Menu.Target>
|
|
|
|
<Menu.Dropdown>
|
|
{m.renderer
|
|
? m.renderer
|
|
: React.Children.toArray(
|
|
m.items?.map((item, itemIndex) => (
|
|
<MenuItemRenderer
|
|
key={`bmm_${providerID}_${menuIndex}_item${itemIndex}`}
|
|
{...item}
|
|
/>
|
|
))
|
|
)}
|
|
</Menu.Dropdown>
|
|
</Menu>
|
|
);
|
|
})
|
|
)}
|
|
</Portal>
|
|
);
|
|
}
|
|
|
|
const MenuItemRenderer = ({ children, label, ...props }: MantineBetterMenuInstanceItem) => {
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
|
|
if (typeof props.renderer === 'function') {
|
|
return props.renderer({ ...props, loading, renderer: undefined, setLoading });
|
|
}
|
|
if (typeof props.renderer === 'object') {
|
|
return props.renderer;
|
|
}
|
|
|
|
if (props.isDivider) {
|
|
return <Menu.Divider />;
|
|
}
|
|
|
|
if (props.items && props.items.length > 0) {
|
|
return (
|
|
<Menu.Sub>
|
|
<Menu.Sub.Target>
|
|
<Menu.Sub.Item
|
|
{...props}
|
|
disabled={loading}
|
|
onClick={(e) => {
|
|
props.onClick?.(e);
|
|
if (props.onClickAsync) {
|
|
setLoading(true);
|
|
props.onClickAsync().finally(() => setLoading(false));
|
|
}
|
|
}}
|
|
styles={{
|
|
itemLabel: {
|
|
overflow: 'auto',
|
|
wordWrap: 'break-word',
|
|
},
|
|
...props.styles,
|
|
}}
|
|
>
|
|
{children ?? label}
|
|
</Menu.Sub.Item>
|
|
</Menu.Sub.Target>
|
|
<Menu.Sub.Dropdown>
|
|
{React.Children.toArray(
|
|
props.items.map((subitem, subitemIndex) => (
|
|
<MenuItemRenderer
|
|
key={`bmm_subitem_${subitem?.id ?? ''}${subitemIndex}`}
|
|
{...subitem}
|
|
/>
|
|
))
|
|
)}
|
|
</Menu.Sub.Dropdown>
|
|
</Menu.Sub>
|
|
);
|
|
}
|
|
|
|
if (!props.onClick && !props.onClickAsync) {
|
|
return <Menu.Label {...(props as Record<string, unknown>)}> {children ?? label}</Menu.Label>;
|
|
}
|
|
|
|
return (
|
|
<Menu.Item
|
|
{...props}
|
|
disabled={loading}
|
|
onClick={(e) => {
|
|
props.onClick?.(e);
|
|
if (props.onClickAsync) {
|
|
setLoading(true);
|
|
props.onClickAsync().finally(() => setLoading(false));
|
|
}
|
|
}}
|
|
styles={{
|
|
itemLabel: {
|
|
overflow: 'auto',
|
|
wordWrap: 'break-word',
|
|
},
|
|
...props.styles,
|
|
}}
|
|
>
|
|
{children ?? label}
|
|
</Menu.Item>
|
|
);
|
|
};
|