From a9fc89e9468cff98d5e3bc86907cd68197ab7f7a Mon Sep 17 00:00:00 2001 From: pablodanswer Date: Wed, 27 Nov 2024 07:46:36 -0800 Subject: [PATCH] move to utils --- web/src/components/Dropdown.tsx | 35 ++--------------------- web/src/lib/dropdown.ts | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 web/src/lib/dropdown.ts diff --git a/web/src/components/Dropdown.tsx b/web/src/components/Dropdown.tsx index c17c39cf581..2c5c4719efb 100644 --- a/web/src/components/Dropdown.tsx +++ b/web/src/components/Dropdown.tsx @@ -11,6 +11,7 @@ import { ChevronDownIcon } from "./icons/icons"; import { FiCheck, FiChevronDown } from "react-icons/fi"; import { Popover } from "./popover/Popover"; import { createPortal } from "react-dom"; +import { useDropdownPosition } from "@/lib/dropdown"; export interface Option { name: string; @@ -91,39 +92,7 @@ export function SearchMultiSelectDropdown({ }; }, []); - // Improved positioning logic - const updateMenuPosition = useCallback(() => { - if (isOpen && dropdownRef.current && dropdownMenuRef.current) { - const rect = dropdownRef.current.getBoundingClientRect(); - const menuRect = dropdownMenuRef.current.getBoundingClientRect(); - const viewportHeight = window.innerHeight; - - let top = rect.bottom + window.scrollY; - - // Check if the menu would go off the bottom of the viewport - if (top + menuRect.height > viewportHeight) { - // Position the menu above the input if it would go off the bottom - top = rect.top + window.scrollY - menuRect.height; - } - - dropdownMenuRef.current.style.position = "absolute"; - dropdownMenuRef.current.style.top = `${top}px`; - dropdownMenuRef.current.style.left = `${rect.left + window.scrollX}px`; - dropdownMenuRef.current.style.width = `${rect.width}px`; - dropdownMenuRef.current.style.zIndex = "10000"; - } - }, [isOpen]); - - useEffect(() => { - updateMenuPosition(); - window.addEventListener("resize", updateMenuPosition); - window.addEventListener("scroll", updateMenuPosition); - - return () => { - window.removeEventListener("resize", updateMenuPosition); - window.removeEventListener("scroll", updateMenuPosition); - }; - }, [isOpen, updateMenuPosition]); + useDropdownPosition({ isOpen, dropdownRef, dropdownMenuRef }); return (
diff --git a/web/src/lib/dropdown.ts b/web/src/lib/dropdown.ts new file mode 100644 index 00000000000..b4fcf42d68e --- /dev/null +++ b/web/src/lib/dropdown.ts @@ -0,0 +1,49 @@ +import { RefObject, useCallback, useEffect } from "react"; + +interface DropdownPositionProps { + isOpen: boolean; + dropdownRef: RefObject; + dropdownMenuRef: RefObject; +} + +// This hook manages the positioning of a dropdown menu relative to its trigger element. +// It ensures the menu is positioned correctly, adjusting for viewport boundaries and scroll position. +// Also adds event listeners for window resize and scroll to update the position dynamically. +export const useDropdownPosition = ({ + isOpen, + dropdownRef, + dropdownMenuRef, +}: DropdownPositionProps) => { + const updateMenuPosition = useCallback(() => { + if (isOpen && dropdownRef.current && dropdownMenuRef.current) { + const rect = dropdownRef.current.getBoundingClientRect(); + const menuRect = dropdownMenuRef.current.getBoundingClientRect(); + const viewportHeight = window.innerHeight; + + let top = rect.bottom + window.scrollY; + + if (top + menuRect.height > viewportHeight) { + top = rect.top + window.scrollY - menuRect.height; + } + + dropdownMenuRef.current.style.position = "absolute"; + dropdownMenuRef.current.style.top = `${top}px`; + dropdownMenuRef.current.style.left = `${rect.left + window.scrollX}px`; + dropdownMenuRef.current.style.width = `${rect.width}px`; + dropdownMenuRef.current.style.zIndex = "10000"; + } + }, [isOpen, dropdownRef, dropdownMenuRef]); + + useEffect(() => { + updateMenuPosition(); + window.addEventListener("resize", updateMenuPosition); + window.addEventListener("scroll", updateMenuPosition); + + return () => { + window.removeEventListener("resize", updateMenuPosition); + window.removeEventListener("scroll", updateMenuPosition); + }; + }, [isOpen, updateMenuPosition]); + + return updateMenuPosition; +};