Files
readest/apps/readest-app/src/components/Dropdown.tsx
T
2024-11-06 22:24:28 +01:00

64 lines
1.6 KiB
TypeScript

import clsx from 'clsx';
import React, { useState, isValidElement, ReactElement } from 'react';
import useOutsideClick from '@/hooks/useOutsideClick';
interface DropdownProps {
className?: string;
buttonClassName?: string;
toggleButton: React.ReactNode;
children: ReactElement<{ setIsDropdownOpen: (isOpen: boolean) => void }>;
onToggle?: (isOpen: boolean) => void;
}
const Dropdown: React.FC<DropdownProps> = ({
className,
buttonClassName,
toggleButton,
children,
onToggle,
}) => {
const [isOpen, setIsOpen] = useState(false);
const toggleDropdown = () => {
const newIsOpen = !isOpen;
setIsOpen(newIsOpen);
onToggle?.(newIsOpen);
};
const setIsDropdownOpen = (isOpen: boolean) => {
setIsOpen(isOpen);
onToggle?.(isOpen);
};
const handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {
if (!dropdownRef.current?.contains(event.relatedTarget as Node)) {
setIsDropdownOpen(false);
}
};
const dropdownRef = useOutsideClick<HTMLDivElement>(() => setIsDropdownOpen(false));
const childrenWithToggle = isValidElement(children)
? React.cloneElement(children, { setIsDropdownOpen })
: children;
return (
<div className={clsx('dropdown', className)} ref={dropdownRef}>
<div
tabIndex={-1}
onClick={toggleDropdown}
onBlur={handleBlur}
className={clsx(
'dropdown-toggle hover:bg-gray-300',
buttonClassName,
isOpen && 'bg-base-300',
)}
>
{toggleButton}
</div>
{isOpen && childrenWithToggle}
</div>
);
};
export default Dropdown;