mirror of
				https://github.com/pelican-dev/panel.git
				synced 2025-10-31 20:06:52 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			61 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			61 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { ElementType, forwardRef, useMemo } from 'react';
 | |
| import { Menu, Transition } from '@headlessui/react';
 | |
| import styles from './style.module.css';
 | |
| import classNames from 'classnames';
 | |
| import DropdownItem from '@/components/elements/dropdown/DropdownItem';
 | |
| import DropdownButton from '@/components/elements/dropdown/DropdownButton';
 | |
| 
 | |
| interface Props {
 | |
|     as?: ElementType;
 | |
|     children: React.ReactNode;
 | |
| }
 | |
| 
 | |
| const DropdownGap = ({ invisible }: { invisible?: boolean }) => (
 | |
|     <div className={classNames('border m-2', { 'border-neutral-700': !invisible, 'border-transparent': invisible })} />
 | |
| );
 | |
| 
 | |
| type TypedChild = (React.ReactChild | React.ReactFragment | React.ReactPortal) & {
 | |
|     type?: JSX.Element;
 | |
| };
 | |
| 
 | |
| const Dropdown = forwardRef<typeof Menu, Props>(({ as, children }, ref) => {
 | |
|     const [Button, items] = useMemo(() => {
 | |
|         const list = React.Children.toArray(children) as unknown as TypedChild[];
 | |
| 
 | |
|         return [
 | |
|             list.filter((child) => child.type === DropdownButton),
 | |
|             list.filter((child) => child.type !== DropdownButton),
 | |
|         ];
 | |
|     }, [children]);
 | |
| 
 | |
|     if (!Button) {
 | |
|         throw new Error('Cannot mount <Dropdown /> component without a child <Dropdown.Button />.');
 | |
|     }
 | |
| 
 | |
|     return (
 | |
|         <Menu as={as || 'div'} className={styles.menu} ref={ref}>
 | |
|             {Button}
 | |
|             <Transition
 | |
|                 enter={'transition duration-100 ease-out'}
 | |
|                 enterFrom={'transition scale-95 opacity-0'}
 | |
|                 enterTo={'transform scale-100 opacity-100'}
 | |
|                 leave={'transition duration-75 ease-out'}
 | |
|                 leaveFrom={'transform scale-100 opacity-100'}
 | |
|                 leaveTo={'transform scale-95 opacity-0'}
 | |
|             >
 | |
|                 <Menu.Items className={classNames(styles.items_container, 'w-56')}>
 | |
|                     <div className={'px-1 py-1'}>{items}</div>
 | |
|                 </Menu.Items>
 | |
|             </Transition>
 | |
|         </Menu>
 | |
|     );
 | |
| });
 | |
| 
 | |
| const _Dropdown = Object.assign(Dropdown, {
 | |
|     Button: DropdownButton,
 | |
|     Item: DropdownItem,
 | |
|     Gap: DropdownGap,
 | |
| });
 | |
| 
 | |
| export { _Dropdown as default };
 | 
