fix(searchable-select): reposition portal on scroll/resize

Adds scroll (capture) and resize listeners so the fixed-position dropdown
tracks its trigger as the page scrolls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-05-31 06:49:49 +05:30
parent 2057fc2d8d
commit 2c912caedb

View file

@ -34,13 +34,12 @@ export function SearchableSelect({
useEffect(() => { setMounted(true); }, []); useEffect(() => { setMounted(true); }, []);
// Recalculate portal position whenever the dropdown opens // Recalculate portal position on open, scroll, and resize so the panel tracks
useLayoutEffect(() => { // the trigger even when the page (or any ancestor) is scrolled.
if (!open || !containerRef.current) return; const updatePortalPos = useCallback(() => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect(); const rect = containerRef.current.getBoundingClientRect();
const PANEL_WIDTH = 420; const PANEL_WIDTH = 420;
// Prefer right-aligning with the trigger; clamp so it doesn't go off-screen left
const right = window.innerWidth - rect.right;
const left = Math.max(8, rect.right - PANEL_WIDTH); const left = Math.max(8, rect.right - PANEL_WIDTH);
setPortalStyle({ setPortalStyle({
position: "fixed", position: "fixed",
@ -49,7 +48,23 @@ export function SearchableSelect({
width: PANEL_WIDTH, width: PANEL_WIDTH,
zIndex: 9999, zIndex: 9999,
}); });
}, [open]); }, []);
useLayoutEffect(() => {
if (!open) return;
updatePortalPos();
}, [open, updatePortalPos]);
useEffect(() => {
if (!open) return;
// capture:true catches scroll on any ancestor, not just window
window.addEventListener("scroll", updatePortalPos, true);
window.addEventListener("resize", updatePortalPos);
return () => {
window.removeEventListener("scroll", updatePortalPos, true);
window.removeEventListener("resize", updatePortalPos);
};
}, [open, updatePortalPos]);
// Close on outside click / Escape // Close on outside click / Escape
useEffect(() => { useEffect(() => {