import * as D from '@radix-ui/react-dropdown-menu'; import { CheckIcon } from '@radix-ui/react-icons'; import classnames from 'classnames'; import { motion } from 'framer-motion'; import type { ForwardedRef, ReactElement, ReactNode } from 'react'; import { forwardRef, useImperativeHandle, useLayoutEffect, useState } from 'react'; export interface DropdownMenuRadioItem { label: string; value: string; } export interface DropdownMenuRadioProps { children: ReactElement; onValueChange: ((v: DropdownMenuRadioItem) => void) | null; value: string; label?: string; items: DropdownMenuRadioItem[]; } export function DropdownMenuRadio({ children, items, onValueChange, label, value, }: DropdownMenuRadioProps) { const handleChange = (value: string) => { const item = items.find((item) => item.value === value); if (item && onValueChange) { onValueChange(item); } }; return ( {children} {label && {label}} {items.map((item) => ( {item.label} ))} ); } export interface DropdownProps { children: ReactElement; items: ( | { label: string; onSelect?: () => void; disabled?: boolean; leftSlot?: ReactNode; } | '-----' )[]; } export function Dropdown({ children, items }: DropdownProps) { return ( {children} {items.map((item, i) => { if (item === '-----') { return ; } else { return ( item.onSelect?.()} disabled={item.disabled} leftSlot={item.leftSlot} > {item.label} ); } })} ); } interface DropdownMenuPortalProps { children: ReactNode; } function DropdownMenuPortal({ children }: DropdownMenuPortalProps) { const container = document.querySelector('#radix-portal'); if (container === null) return null; return ( {children} ); } const DropdownMenuContent = forwardRef( function DropdownMenuContent( { className, children, ...props }: D.DropdownMenuContentProps, ref: ForwardedRef, ) { const [styles, setStyles] = useState<{ maxHeight: number }>(); const [divRef, setDivRef] = useState(null); useImperativeHandle(ref, () => divRef); const initDivRef = (ref: HTMLDivElement | null) => { setDivRef(ref); }; // Calculate the max height so we can scroll useLayoutEffect(() => { if (divRef === null) return; // Needs to be in a setTimeout because the ref is not positioned yet // TODO: Make this better? setTimeout(() => { const windowBox = document.documentElement.getBoundingClientRect(); const menuBox = divRef.getBoundingClientRect(); const styles = { maxHeight: windowBox.height - menuBox.top - 5 - 45 }; setStyles(styles); }); }, [divRef]); return ( {children} ); }, ); type DropdownMenuItemProps = D.DropdownMenuItemProps & ItemInnerProps; function DropdownMenuItem({ leftSlot, rightSlot, className, children, disabled, ...props }: DropdownMenuItemProps) { return ( {children} ); } // type DropdownMenuCheckboxItemProps = DropdownMenu.DropdownMenuCheckboxItemProps & ItemInnerProps; // // function DropdownMenuCheckboxItem({ // leftSlot, // rightSlot, // children, // ...props // }: DropdownMenuCheckboxItemProps) { // return ( // // // {children} // // // ); // } // type DropdownMenuSubTriggerProps = DropdownMenu.DropdownMenuSubTriggerProps & ItemInnerProps; // // function DropdownMenuSubTrigger({ // leftSlot, // rightSlot, // children, // ...props // }: DropdownMenuSubTriggerProps) { // return ( // // // {children} // // // ); // } type DropdownMenuRadioItemProps = Omit; function DropdownMenuRadioItem({ rightSlot, children, ...props }: DropdownMenuRadioItemProps) { return ( } rightSlot={rightSlot} > {children} ); } // const DropdownMenuSubContent = forwardRef( // function DropdownMenuSubContent( // { className, ...props }: DropdownMenu.DropdownMenuSubContentProps, // ref, // ) { // return ( // // ); // }, // ); function DropdownMenuLabel({ className, children, ...props }: D.DropdownMenuLabelProps) { return ( {children} ); } function DropdownMenuSeparator({ className, ...props }: D.DropdownMenuSeparatorProps) { return ( ); } type DropdownMenuTriggerProps = D.DropdownMenuTriggerProps & { children: ReactNode; className?: string; }; export function DropdownMenuTrigger({ children, className, ...props }: DropdownMenuTriggerProps) { return ( {children} ); } interface ItemInnerProps { leftSlot?: ReactNode; rightSlot?: ReactNode; children: ReactNode; noHover?: boolean; className?: string; } const ItemInner = forwardRef(function ItemInner( { leftSlot, rightSlot, children, className, noHover, ...props }: ItemInnerProps, ref, ) { return (
{leftSlot &&
{leftSlot}
}
{children}
{rightSlot &&
{rightSlot}
}
); });