A
This commit is contained in:
103
webui/src/components/ui/Dropdown.tsx
Normal file
103
webui/src/components/ui/Dropdown.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user