2.4 KB102 lines
Blame
1"use client";
2
3import { useState, useRef, useEffect } from "react";
4
5export interface DropdownProps {
6 trigger: React.ReactNode;
7 children: React.ReactNode;
8 align?: "left" | "right";
9 className?: string;
10}
11
12export function Dropdown({
13 trigger,
14 children,
15 align = "left",
16 className = "",
17}: DropdownProps) {
18 const [open, setOpen] = useState(false);
19 const ref = useRef<HTMLDivElement>(null);
20
21 useEffect(() => {
22 if (!open) return;
23
24 function handleClick(e: MouseEvent) {
25 if (ref.current && !ref.current.contains(e.target as Node)) {
26 setOpen(false);
27 }
28 }
29
30 function handleKey(e: KeyboardEvent) {
31 if (e.key === "Escape") setOpen(false);
32 }
33
34 document.addEventListener("mousedown", handleClick);
35 document.addEventListener("keydown", handleKey);
36 return () => {
37 document.removeEventListener("mousedown", handleClick);
38 document.removeEventListener("keydown", handleKey);
39 };
40 }, [open]);
41
42 return (
43 <div ref={ref} className={`relative ${className}`}>
44 <div onClick={() => setOpen((v) => !v)}>{trigger}</div>
45 {open && (
46 <div
47 className="absolute mt-1 py-1 min-w-[160px] z-50"
48 onClick={() => setOpen(false)}
49 style={{
50 backgroundColor: "var(--bg-card)",
51 border: "1px solid var(--border)",
52 boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
53 [align === "right" ? "right" : "left"]: 0,
54 }}
55 >
56 {children}
57 </div>
58 )}
59 </div>
60 );
61}
62
63export interface DropdownItemProps
64 extends React.ButtonHTMLAttributes<HTMLButtonElement> {
65 active?: boolean;
66}
67
68export function DropdownItem({
69 active = false,
70 className = "",
71 style,
72 children,
73 ...props
74}: DropdownItemProps) {
75 return (
76 <button
77 className={`w-full text-left text-sm px-3 py-1.5 ${className}`}
78 style={{
79 background: active ? "var(--bg-hover)" : "none",
80 border: "none",
81 color: "var(--text-primary)",
82 cursor: "pointer",
83 font: "inherit",
84 fontSize: "inherit",
85 ...style,
86 }}
87 onMouseEnter={(e) => {
88 (e.currentTarget as HTMLButtonElement).style.backgroundColor =
89 "var(--bg-hover)";
90 }}
91 onMouseLeave={(e) => {
92 (e.currentTarget as HTMLButtonElement).style.backgroundColor = active
93 ? "var(--bg-hover)"
94 : "transparent";
95 }}
96 {...props}
97 >
98 {children}
99 </button>
100 );
101}
102