Skip to content

Commit 83090f3

Browse files
fix(c): dropdown tweaks
1 parent 47d1353 commit 83090f3

File tree

2 files changed

+91
-24
lines changed

2 files changed

+91
-24
lines changed

src/components/dropdown/Dropdown.tsx

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,83 @@
11
import * as React from "react";
2-
import { Menu } from "@headlessui/react";
2+
import { Menu, Transition, Portal } from "@headlessui/react";
33
import { cn } from "../../utils/cn";
44
import { ZINDEX } from "../../utils/z-index";
55

66
export interface DropdownProps {
77
trigger: React.ReactNode;
88
children: React.ReactNode;
99
align?: "left" | "right";
10+
direction?: "up" | "down";
1011
className?: string;
1112
}
1213

1314
export const Dropdown: React.FC<DropdownProps> = ({
1415
trigger,
1516
children,
1617
align = "left",
18+
direction = "down",
1719
className,
1820
}) => {
21+
const buttonRef = React.useRef<HTMLDivElement>(null);
22+
23+
const updatePosition = React.useCallback((open: boolean) => {
24+
if (!buttonRef.current || !open) return;
25+
const rect = buttonRef.current.getBoundingClientRect();
26+
27+
document.documentElement.style.setProperty('--x', `${rect.left}px`);
28+
document.documentElement.style.setProperty('--rx', `${window.innerWidth - rect.right}px`);
29+
document.documentElement.style.setProperty('--y', `${rect.bottom + 8}px`);
30+
document.documentElement.style.setProperty('--by', `${window.innerHeight - rect.top + 8}px`);
31+
}, []);
32+
1933
return (
20-
<Menu as="div" className="relative inline-block text-left">
21-
<Menu.Button className="inline-flex cursor-pointer">
22-
{trigger}
23-
</Menu.Button>
34+
<Menu>
35+
{({ open }) => (
36+
<div className="relative inline-block text-left">
37+
<div ref={buttonRef}>
38+
<Menu.Button
39+
className="inline-flex cursor-pointer"
40+
onClick={() => updatePosition(true)}
41+
>
42+
{trigger}
43+
</Menu.Button>
44+
</div>
2445

25-
<Menu.Items
26-
className={cn(
27-
"absolute mt-2 rounded-lg shadow-lg",
28-
"bg-white dark:bg-gray-800",
29-
"border border-gray-200 dark:border-gray-700",
30-
"focus:outline-none",
31-
"min-w-[8rem] py-1",
32-
{
33-
"left-0": align === "left",
34-
"right-0": align === "right",
35-
},
36-
className
37-
)}
38-
style={{ zIndex: ZINDEX.dropdown }}
39-
>
40-
{children}
41-
</Menu.Items>
46+
<Portal>
47+
<Transition
48+
show={open}
49+
enter="transition duration-100 ease-out"
50+
enterFrom="transform scale-95 opacity-0"
51+
enterTo="transform scale-100 opacity-100"
52+
leave="transition duration-75 ease-out"
53+
leaveFrom="transform scale-100 opacity-100"
54+
leaveTo="transform scale-95 opacity-0"
55+
beforeEnter={() => updatePosition(true)}
56+
afterLeave={() => updatePosition(false)}
57+
>
58+
<Menu.Items
59+
className={cn(
60+
"fixed rounded-lg shadow-lg",
61+
"bg-white dark:bg-gray-800",
62+
"border border-gray-200 dark:border-gray-700",
63+
"focus:outline-none",
64+
"min-w-[8rem] py-1",
65+
"z-[100]",
66+
className
67+
)}
68+
style={{
69+
left: align === "left" ? "var(--x)" : "unset",
70+
right: align === "right" ? "var(--rx)" : "unset",
71+
top: direction === "down" ? "var(--y)" : "unset",
72+
bottom: direction === "up" ? "var(--by)" : "unset",
73+
}}
74+
>
75+
{children}
76+
</Menu.Items>
77+
</Transition>
78+
</Portal>
79+
</div>
80+
)}
4281
</Menu>
4382
);
4483
};

src/docs/pages/components/DropdownPage.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ export const DropdownPage = () => {
2424
type: "string",
2525
description: "Additional CSS classes for the dropdown menu",
2626
},
27+
{
28+
name: "direction",
29+
type: "'up' | 'down'",
30+
defaultValue: "down",
31+
description: "The direction the dropdown opens",
32+
},
2733
];
2834

2935
const dropdownItemProps = [
@@ -80,7 +86,7 @@ const MyComponent = () => (
8086
title="Right Aligned"
8187
description="Dropdown menu aligned to the right"
8288
code={`<Dropdown
83-
trigger={<Button>Long Buttn with a Right Menu</Button>}
89+
trigger={<Button>Long Button with a Right Menu</Button>}
8490
align="right"
8591
>
8692
<DropdownItem>Option 1</DropdownItem>
@@ -89,7 +95,7 @@ const MyComponent = () => (
8995
>
9096
<div className="text-right">
9197
<Dropdown
92-
trigger={<Button>Long Buttn with a Right Menu</Button>}
98+
trigger={<Button>Long Button with a Right Menu</Button>}
9399
align="right"
94100
>
95101
<DropdownItem>Option 1</DropdownItem>
@@ -175,6 +181,28 @@ const MyComponent = () => (
175181
</DropdownItem>
176182
</Dropdown>
177183
</ComponentDemo>
184+
185+
<ComponentDemo
186+
title="Upward Direction"
187+
description="Dropdown that opens upward"
188+
code={`<Dropdown
189+
trigger={<Button>Upward Menu</Button>}
190+
direction="up"
191+
>
192+
<DropdownItem>Option 1</DropdownItem>
193+
<DropdownItem>Option 2</DropdownItem>
194+
</Dropdown>`}
195+
>
196+
<div className="mt-32">
197+
<Dropdown
198+
trigger={<Button>Upward Menu</Button>}
199+
direction="up"
200+
>
201+
<DropdownItem>Option 1</DropdownItem>
202+
<DropdownItem>Option 2</DropdownItem>
203+
</Dropdown>
204+
</div>
205+
</ComponentDemo>
178206
</div>
179207
</section>
180208

0 commit comments

Comments
 (0)