diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx index 583fef54..76ca7871 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx @@ -208,6 +208,8 @@ export const VoidCustomSelectBox = ({ getOptionsEqual, className, arrowTouchesText = true, + matchInputWidth = false, + gap = 0, }: { options: T[]; selectedOption?: T; @@ -216,32 +218,54 @@ export const VoidCustomSelectBox = ({ getOptionsEqual: (a: T, b: T) => boolean; className?: string; arrowTouchesText?: boolean; + matchInputWidth?: boolean; + gap?: number; }) => { const [isOpen, setIsOpen] = useState(false); - const [position, setPosition] = useState({ top: 0, left: 0 }); + const [readyToShow, setReadyToShow] = useState(false); + const [position, setPosition] = useState({ top: 0, left: 0, width: 0 }); const containerRef = useRef(null); const buttonRef = useRef(null); + const measureRef = useRef(null); if (!selectedOption) { selectedOption = options[0]; } - const updatePosition = () => { - if (!buttonRef.current) return; + const updatePosition = useCallback(() => { + if (!buttonRef.current || !containerRef.current || !measureRef.current) return; const rect = buttonRef.current.getBoundingClientRect(); + const containerWidth = containerRef.current.offsetWidth; const viewportHeight = window.innerHeight; const spaceBelow = viewportHeight - rect.bottom; - const spaceNeeded = options.length * 28; // Approximate height per option + const spaceNeeded = options.length * 28; const showAbove = spaceBelow < spaceNeeded && rect.top > spaceBelow; + // Calculate the menu width + let menuWidth = matchInputWidth ? containerWidth : rect.width; + + // If not matchInputWidth, calculate content width from measurement div + if (!matchInputWidth) { + const contentWidth = measureRef.current.offsetWidth; + menuWidth = Math.max(rect.width, contentWidth); + } + + // Calculate exact positions without any additional offsets + const topPosition = showAbove + ? rect.top - spaceNeeded // Align exactly to top when showing above + : rect.bottom + gap; // Add gap only when showing below + setPosition({ - top: showAbove ? rect.top - 4 - spaceNeeded : rect.bottom + 4, + top: topPosition, left: rect.left, + width: menuWidth, }); - }; + setReadyToShow(true); + }, [gap, matchInputWidth, options.length]); useEffect(() => { if (isOpen) { + setReadyToShow(false); updatePosition(); window.addEventListener('scroll', updatePosition, true); window.addEventListener('resize', updatePosition); @@ -250,8 +274,10 @@ export const VoidCustomSelectBox = ({ window.removeEventListener('scroll', updatePosition, true); window.removeEventListener('resize', updatePosition); }; + } else { + setReadyToShow(false); } - }, [isOpen]); + }, [isOpen, updatePosition]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -271,15 +297,26 @@ export const VoidCustomSelectBox = ({ ref={containerRef} className={`inline-block ${className}`} > + {/* Hidden measurement div */} +