Files
minimax/webui/src/components/ui/Dropdown.tsx
2025-10-29 20:36:09 -07:00

104 lines
2.1 KiB
TypeScript

"use client";
import { useState, useRef, useEffect, ReactNode } from "react";
import clsx from "clsx";
import styles from "@/styles/Dropdown.module.css";
interface DropdownItem {
text: string;
onClick: () => void;
}
interface DropdownProps {
trigger?: string;
triggerIcon?: string;
disabled?: boolean;
items?: DropdownItem[];
customContent?: ReactNode;
align?: "left" | "right";
}
export function Dropdown({
trigger,
triggerIcon,
disabled = false,
items = [],
customContent,
align = "left",
}: DropdownProps) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
}
if (isOpen) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [isOpen]);
return (
<div
className={clsx(styles.dropdown, isOpen && styles.isActive)}
ref={dropdownRef}
>
<button
className={styles.dropdownTrigger}
type="button"
onClick={() => !disabled && setIsOpen(!isOpen)}
disabled={disabled}
>
{triggerIcon && <i className={`mdi mdi-${triggerIcon}`} />}
{trigger && <span>{trigger}</span>}
{!triggerIcon && !trigger && (
<i className="mdi mdi-dots-horizontal" />
)}
<i className="mdi mdi-menu-down" />
</button>
{isOpen && (
<div
className={clsx(
styles.dropdownMenu,
align === "right" && styles.alignRight,
)}
>
<div className={styles.dropdownContent}>
{customContent ? (
<div className={styles.dropdownItem}>
{customContent}
</div>
) : (
items.map((item, index) => (
<a
key={index}
href="#"
className={styles.dropdownItem}
onClick={(e) => {
e.preventDefault();
item.onClick();
setIsOpen(false);
}}
>
{item.text}
</a>
))
)}
</div>
</div>
)}
</div>
);
}