AIEXTRACT1 / frontend /src /components /ui /dropdown-menu.jsx
Seth0330's picture
Create frontend/src/components/ui/dropdown-menu.jsx
9a66b73 verified
import React, {
createContext,
useContext,
useState,
useRef,
useEffect,
} from "react";
import { cn } from "@/lib/utils";
const DropdownContext = createContext(null);
export function DropdownMenu({ children }) {
const [open, setOpen] = useState(false);
const triggerRef = useRef(null);
// Close on outside click
useEffect(() => {
if (!open) return;
function handleClick(e) {
if (!triggerRef.current) return;
if (!triggerRef.current.parentElement.contains(e.target)) {
setOpen(false);
}
}
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, [open]);
return (
<DropdownContext.Provider value={{ open, setOpen, triggerRef }}>
<div className="relative inline-block">{children}</div>
</DropdownContext.Provider>
);
}
export function DropdownMenuTrigger({ asChild, children }) {
const { setOpen, triggerRef } = useContext(DropdownContext);
const handleClick = (e) => {
e.stopPropagation();
setOpen((o) => !o);
};
if (asChild && React.isValidElement(children)) {
return React.cloneElement(children, {
ref: triggerRef,
onClick: (e) => {
children.props.onClick?.(e);
handleClick(e);
},
});
}
return (
<button
ref={triggerRef}
type="button"
onClick={handleClick}
className="inline-flex"
>
{children}
</button>
);
}
export function DropdownMenuContent({ className, align = "end", ...props }) {
const { open } = useContext(DropdownContext);
if (!open) return null;
const alignment =
align === "end"
? "right-0 origin-top-right"
: align === "start"
? "left-0 origin-top-left"
: "left-1/2 -translate-x-1/2 origin-top";
return (
<div
className={cn(
"absolute z-50 mt-2 min-w-[8rem] rounded-md border border-slate-200 bg-white shadow-lg focus:outline-none",
alignment,
className
)}
{...props}
/>
);
}
export function DropdownMenuItem({ className, onClick, ...props }) {
const { setOpen } = useContext(DropdownContext);
const handleClick = (e) => {
onClick?.(e);
setOpen(false);
};
return (
<div
className={cn(
"flex cursor-pointer select-none items-center px-2 py-1.5 text-sm text-slate-700 hover:bg-slate-100 rounded-md",
className
)}
onClick={handleClick}
{...props}
/>
);
}
export function DropdownMenuSeparator({ className }) {
return (
<div
className={cn("my-1 h-px bg-slate-200 w-full", className)}
/>
);
}