Seth0330 commited on
Commit
9a66b73
·
verified ·
1 Parent(s): 963509e

Create frontend/src/components/ui/dropdown-menu.jsx

Browse files
frontend/src/components/ui/dropdown-menu.jsx ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useState,
5
+ useRef,
6
+ useEffect,
7
+ } from "react";
8
+ import { cn } from "@/lib/utils";
9
+
10
+ const DropdownContext = createContext(null);
11
+
12
+ export function DropdownMenu({ children }) {
13
+ const [open, setOpen] = useState(false);
14
+ const triggerRef = useRef(null);
15
+
16
+ // Close on outside click
17
+ useEffect(() => {
18
+ if (!open) return;
19
+ function handleClick(e) {
20
+ if (!triggerRef.current) return;
21
+ if (!triggerRef.current.parentElement.contains(e.target)) {
22
+ setOpen(false);
23
+ }
24
+ }
25
+ document.addEventListener("mousedown", handleClick);
26
+ return () => document.removeEventListener("mousedown", handleClick);
27
+ }, [open]);
28
+
29
+ return (
30
+ <DropdownContext.Provider value={{ open, setOpen, triggerRef }}>
31
+ <div className="relative inline-block">{children}</div>
32
+ </DropdownContext.Provider>
33
+ );
34
+ }
35
+
36
+ export function DropdownMenuTrigger({ asChild, children }) {
37
+ const { setOpen, triggerRef } = useContext(DropdownContext);
38
+
39
+ const handleClick = (e) => {
40
+ e.stopPropagation();
41
+ setOpen((o) => !o);
42
+ };
43
+
44
+ if (asChild && React.isValidElement(children)) {
45
+ return React.cloneElement(children, {
46
+ ref: triggerRef,
47
+ onClick: (e) => {
48
+ children.props.onClick?.(e);
49
+ handleClick(e);
50
+ },
51
+ });
52
+ }
53
+
54
+ return (
55
+ <button
56
+ ref={triggerRef}
57
+ type="button"
58
+ onClick={handleClick}
59
+ className="inline-flex"
60
+ >
61
+ {children}
62
+ </button>
63
+ );
64
+ }
65
+
66
+ export function DropdownMenuContent({ className, align = "end", ...props }) {
67
+ const { open } = useContext(DropdownContext);
68
+ if (!open) return null;
69
+
70
+ const alignment =
71
+ align === "end"
72
+ ? "right-0 origin-top-right"
73
+ : align === "start"
74
+ ? "left-0 origin-top-left"
75
+ : "left-1/2 -translate-x-1/2 origin-top";
76
+
77
+ return (
78
+ <div
79
+ className={cn(
80
+ "absolute z-50 mt-2 min-w-[8rem] rounded-md border border-slate-200 bg-white shadow-lg focus:outline-none",
81
+ alignment,
82
+ className
83
+ )}
84
+ {...props}
85
+ />
86
+ );
87
+ }
88
+
89
+ export function DropdownMenuItem({ className, onClick, ...props }) {
90
+ const { setOpen } = useContext(DropdownContext);
91
+ const handleClick = (e) => {
92
+ onClick?.(e);
93
+ setOpen(false);
94
+ };
95
+ return (
96
+ <div
97
+ className={cn(
98
+ "flex cursor-pointer select-none items-center px-2 py-1.5 text-sm text-slate-700 hover:bg-slate-100 rounded-md",
99
+ className
100
+ )}
101
+ onClick={handleClick}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ export function DropdownMenuSeparator({ className }) {
108
+ return (
109
+ <div
110
+ className={cn("my-1 h-px bg-slate-200 w-full", className)}
111
+ />
112
+ );
113
+ }