oranguru/src/MantineBetterMenu/MenuRenderer.tsx
2025-10-20 16:00:21 +02:00

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