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(e).finally(() => setLoading(false));
 | |
|         }
 | |
|       }}
 | |
|       styles={{
 | |
|         itemLabel: {
 | |
|           overflow: 'auto',
 | |
|           wordWrap: 'break-word',
 | |
|         },
 | |
|         ...props.styles,
 | |
|       }}
 | |
|     >
 | |
|       {children ?? label}
 | |
|     </Menu.Item>
 | |
|   );
 | |
| };
 |