Skip to content

Commit 5b6f640

Browse files
fix(c): dropdown fixes
1 parent 041f819 commit 5b6f640

File tree

5 files changed

+115
-63372
lines changed

5 files changed

+115
-63372
lines changed

src/components/dropdown/Dropdown.tsx

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
2121
className,
2222
}) => {
2323
const [isOpen, setIsOpen] = React.useState(false);
24+
const [position, setPosition] = React.useState<{ top?: number; bottom?: number; left: number; right: number }>({ left: 0, right: 0 });
2425
const dropdownRef = React.useRef<HTMLDivElement>(null);
2526
const triggerRef = React.useRef<HTMLDivElement>(null);
2627

@@ -50,6 +51,34 @@ export const Dropdown: React.FC<DropdownProps> = ({
5051
};
5152
}, [variant]);
5253

54+
// Update position when scrolling or resizing
55+
React.useEffect(() => {
56+
if (!isOpen) return;
57+
58+
const updatePosition = () => {
59+
if (!triggerRef.current) return;
60+
const rect = triggerRef.current.getBoundingClientRect();
61+
62+
setPosition({
63+
left: rect.left,
64+
right: window.innerWidth - rect.right,
65+
...(direction === 'down'
66+
? { top: rect.bottom + 8 }
67+
: { bottom: window.innerHeight - rect.top + 8 }
68+
),
69+
});
70+
};
71+
72+
updatePosition();
73+
window.addEventListener('scroll', updatePosition, true);
74+
window.addEventListener('resize', updatePosition);
75+
76+
return () => {
77+
window.removeEventListener('scroll', updatePosition, true);
78+
window.removeEventListener('resize', updatePosition);
79+
};
80+
}, [isOpen, direction]);
81+
5382
const triggerClasses = cn({
5483
"cursor-pointer": true,
5584
"flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200 transition-colors":
@@ -76,22 +105,6 @@ export const Dropdown: React.FC<DropdownProps> = ({
76105
className
77106
);
78107

79-
// Get position for the dropdown menu
80-
const getDropdownPosition = () => {
81-
if (!triggerRef.current) return {};
82-
const rect = triggerRef.current.getBoundingClientRect();
83-
84-
return {
85-
position: 'fixed' as const,
86-
left: rect.left,
87-
right: window.innerWidth - rect.right,
88-
...(direction === 'down'
89-
? { top: rect.bottom + 8 }
90-
: { bottom: window.innerHeight - rect.top + 8 }
91-
),
92-
};
93-
};
94-
95108
return (
96109
<div className="relative inline-block" ref={dropdownRef}>
97110
<div
@@ -105,7 +118,8 @@ export const Dropdown: React.FC<DropdownProps> = ({
105118
<div
106119
className={menuClasses}
107120
style={{
108-
...getDropdownPosition(),
121+
position: 'fixed',
122+
...position,
109123
zIndex: ZINDEX.dropdown,
110124
}}
111125
>
@@ -117,22 +131,26 @@ export const Dropdown: React.FC<DropdownProps> = ({
117131
);
118132
};
119133

120-
export const DropdownItem = React.forwardRef<
121-
HTMLButtonElement,
122-
React.ButtonHTMLAttributes<HTMLButtonElement>
123-
>(({ className, children, ...props }, ref) => (
124-
<button
125-
ref={ref}
126-
className={cn(
127-
"flex w-full items-center px-3 py-2 text-sm",
128-
"text-gray-700 dark:text-gray-200",
129-
"hover:bg-gray-100 dark:hover:bg-gray-800",
130-
"focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800",
131-
className
132-
)}
133-
{...props}
134-
>
135-
{children}
136-
</button>
137-
));
134+
export interface DropdownItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
135+
icon?: React.ReactNode;
136+
}
137+
138+
export const DropdownItem = React.forwardRef<HTMLButtonElement, DropdownItemProps>(
139+
({ className, children, icon, ...props }, ref) => (
140+
<button
141+
ref={ref}
142+
className={cn(
143+
"flex w-full items-center px-3 py-2 text-sm gap-2",
144+
"text-gray-700 dark:text-gray-200",
145+
"hover:bg-gray-100 dark:hover:bg-gray-800",
146+
"focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800",
147+
className
148+
)}
149+
{...props}
150+
>
151+
{icon}
152+
{children}
153+
</button>
154+
)
155+
);
138156
DropdownItem.displayName = "DropdownItem";

src/docs/pages/components/DropdownPage.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,68 @@ const MyComponent = () => (
138138
</ComponentDemo>
139139
</div>
140140
</section>
141+
142+
<ComponentDemo
143+
title="With Images"
144+
description="Dropdown items with images"
145+
code={`<Dropdown trigger={<Button>User Menu</Button>}>
146+
<DropdownItem>
147+
<img
148+
src="/avatars/user1.jpg"
149+
alt="User 1 avatar"
150+
className="h-5 w-5 rounded-full object-cover"
151+
/>
152+
John Doe
153+
</DropdownItem>
154+
<DropdownItem>
155+
<img
156+
src="/avatars/user2.jpg"
157+
alt="User 2 avatar"
158+
className="h-5 w-5 rounded-full object-cover"
159+
/>
160+
Jane Smith
161+
</DropdownItem>
162+
</Dropdown>`}
163+
>
164+
<Dropdown trigger={<Button>User Menu</Button>}>
165+
<DropdownItem>
166+
<img
167+
src="https://github.com/github.png"
168+
alt="GitHub avatar"
169+
className="h-5 w-5 rounded-full object-cover"
170+
/>
171+
GitHub
172+
</DropdownItem>
173+
<DropdownItem>
174+
<img
175+
src="https://avatars.githubusercontent.com/u/6412038?s=200&v=4"
176+
alt="React avatar"
177+
className="h-5 w-5 rounded-full object-cover"
178+
/>
179+
React
180+
</DropdownItem>
181+
</Dropdown>
182+
</ComponentDemo>
183+
184+
<section>
185+
<Title level={3}>DropdownItem Properties</Title>
186+
<div className="mt-4">
187+
<PropsTable
188+
props={[
189+
{
190+
name: "icon",
191+
type: "ReactNode",
192+
description: "Icon element to display",
193+
},
194+
{
195+
name: "className",
196+
type: "string",
197+
description: "Additional CSS classes",
198+
},
199+
]}
200+
/>
201+
</div>
202+
</section>
141203
</div>
142204
);
143205
};

tmp/index-7k4560vb.css

Lines changed: 0 additions & 17 deletions
This file was deleted.

tmp/index-xqc55zjs.css

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)