2022-06-05 18:56:42 +00:00
|
|
|
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 }) => (
|
2022-06-26 19:13:52 +00:00
|
|
|
<div className={classNames('border m-2', { 'border-neutral-700': !invisible, 'border-transparent': invisible })} />
|
2022-06-05 18:56:42 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
type TypedChild = (React.ReactChild | React.ReactFragment | React.ReactPortal) & {
|
|
|
|
type?: JSX.Element;
|
2022-06-26 19:13:52 +00:00
|
|
|
};
|
2022-06-05 18:56:42 +00:00
|
|
|
|
|
|
|
const Dropdown = forwardRef<typeof Menu, Props>(({ as, children }, ref) => {
|
2022-06-26 19:13:52 +00:00
|
|
|
const [Button, items] = useMemo(() => {
|
2022-06-05 18:56:42 +00:00
|
|
|
const list = React.Children.toArray(children) as unknown as TypedChild[];
|
|
|
|
|
|
|
|
return [
|
2022-06-26 19:13:52 +00:00
|
|
|
list.filter((child) => child.type === DropdownButton),
|
|
|
|
list.filter((child) => child.type !== DropdownButton),
|
2022-06-05 18:56:42 +00:00
|
|
|
];
|
2022-06-26 19:13:52 +00:00
|
|
|
}, [children]);
|
2022-06-05 18:56:42 +00:00
|
|
|
|
|
|
|
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')}>
|
2022-06-26 19:13:52 +00:00
|
|
|
<div className={'px-1 py-1'}>{items}</div>
|
2022-06-05 18:56:42 +00:00
|
|
|
</Menu.Items>
|
|
|
|
</Transition>
|
|
|
|
</Menu>
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
const _Dropdown = Object.assign(Dropdown, {
|
|
|
|
Button: DropdownButton,
|
|
|
|
Item: DropdownItem,
|
|
|
|
Gap: DropdownGap,
|
|
|
|
});
|
|
|
|
|
|
|
|
export { _Dropdown as default };
|