From 7caa1bb48253468407e912d171402291ceca6ed1 Mon Sep 17 00:00:00 2001 From: pratapalakshmi <137189067+pratapalakshmi@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:57:15 +0530 Subject: [PATCH] [PAI-963] feat: enhance CustomSelect component with context for dropdown management (#8202) * feat: enhance CustomSelect component with context for dropdown management * refactor: streamline CustomSelect component structure and improve dropdown options rendering --- packages/ui/src/dropdowns/custom-select.tsx | 176 +++++++++++--------- 1 file changed, 96 insertions(+), 80 deletions(-) diff --git a/packages/ui/src/dropdowns/custom-select.tsx b/packages/ui/src/dropdowns/custom-select.tsx index 3876ee9fd..4423bd49c 100644 --- a/packages/ui/src/dropdowns/custom-select.tsx +++ b/packages/ui/src/dropdowns/custom-select.tsx @@ -1,6 +1,6 @@ import { Combobox } from "@headlessui/react"; import { Check } from "lucide-react"; -import React, { useRef, useState } from "react"; +import React, { createContext, useCallback, useContext, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { usePopper } from "react-popper"; import { useOutsideClickDetector } from "@plane/hooks"; @@ -13,6 +13,9 @@ import { cn } from "../utils"; // types import type { ICustomSelectItemProps, ICustomSelectProps } from "./helper"; +// Context to share the close handler with option components +const DropdownContext = createContext<() => void>(() => {}); + function CustomSelect(props: ICustomSelectProps) { const { customButtonClassName = "", @@ -42,99 +45,112 @@ function CustomSelect(props: ICustomSelectProps) { placement: placement ?? "bottom-start", }); - const openDropdown = () => { + const openDropdown = useCallback(() => { setIsOpen(true); if (referenceElement) referenceElement.focus(); - }; - const closeDropdown = () => setIsOpen(false); + }, [referenceElement]); + + const closeDropdown = useCallback(() => setIsOpen(false), []); const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); useOutsideClickDetector(dropdownRef, closeDropdown); - const toggleDropdown = () => { + const toggleDropdown = useCallback(() => { if (isOpen) closeDropdown(); else openDropdown(); - }; + }, [closeDropdown, isOpen, openDropdown]); return ( - - <> - {customButton ? ( - - - - ) : ( - - - - )} - - {isOpen && - createPortal( - -
-
+ { + onChange?.(val); + closeDropdown(); + }} + className={cn("relative flex-shrink-0 text-left", className)} + onKeyDown={handleKeyDown} + disabled={disabled} + > + <> + {customButton ? ( + + + + ) : ( + + + + )} + + {isOpen && + createPortal( + +
+
+ {children} +
-
- , - document.body - )} - + , + document.body + )} + + ); } function Option(props: ICustomSelectItemProps) { const { children, value, className } = props; + const closeDropdown = useContext(DropdownContext); + + const handleMouseDown = useCallback(() => { + // Close dropdown for both new and already-selected options. + requestAnimationFrame(() => closeDropdown()); + }, [closeDropdown]); + return ( {({ selected }) => ( - <> +
{children} {selected && } - +
)}
);