ates if the select should be without the outer box-shadow */ isPlain?: boolean; /** @hide Forwarded ref */ innerRef?: React.Ref; /** z-index of the select menu */ zIndex?: number; /** Determines the accessible role of the select. For a checkbox select pass in "menu". */ role?: string; /** Additional properties to pass to the popper */ popperProps?: SelectPopperProps; /** Height of the select menu */ menuHeight?: string; /** Maximum height of select menu */ maxMenuHeight?: string; /** Indicates if the select menu should be scrollable */ isScrollable?: boolean; /** Flag indicating if scroll on focus of the first menu item should occur. */ shouldPreventScrollOnItemFocus?: boolean; /** Time in ms to wait before firing the toggles' focus event. Defaults to 0 */ focusTimeoutDelay?: number; } const SelectBase: React.FunctionComponent = ({ children, className, onSelect, isOpen, selected, toggle, shouldFocusToggleOnSelect = false, shouldFocusFirstItemOnOpen = false, onOpenChange, onOpenChangeKeys = ['Escape', 'Tab'], onToggleKeydown, variant, isPlain, innerRef, zIndex = 9999, role = 'listbox', popperProps, menuHeight, maxMenuHeight, isScrollable, shouldPreventScrollOnItemFocus = true, focusTimeoutDelay = 0, ...props }: SelectProps & OUIAProps) => { const localMenuRef = React.useRef(); const localToggleRef = React.useRef(); const menuRef = (innerRef as React.RefObject) || localMenuRef; const toggleRef = typeof toggle === 'function' || (typeof toggle !== 'function' && !toggle.toggleRef) ? localToggleRef : (toggle?.toggleRef as React.RefObject); const prevIsOpen = React.useRef(isOpen); React.useEffect(() => { // menu was opened, focus on first menu item if (prevIsOpen.current === false && isOpen === true && shouldFocusFirstItemOnOpen) { setTimeout(() => { const firstElement = menuRef?.current?.querySelector('li button:not(:disabled),li input:not(:disabled)'); firstElement && (firstElement as HTMLElement).focus({ preventScroll: shouldPreventScrollOnItemFocus }); }, focusTimeoutDelay); } prevIsOpen.current = isOpen; // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOpen]); React.useEffect(() => { const handleMenuKeys = (event: KeyboardEvent) => { // Close the menu on tab or escape if onOpenChange is provided if ( isOpen && onOpenChange && (menuRef.current?.contains(event.target as Node) || toggleRef.current?.contains(event.target as Node)) ) { if (onOpenChangeKeys.includes(event.key)) { event.preventDefault(); onOpenChange(false); toggleRef.current?.focus(); } } if (toggleRef.current?.contains(event.target as Node)) { if (onToggleKeydown) { onToggleKeydown(event); } else if (isOpen && variant !== 'typeahead') { onToggleArrowKeydownDefault(event, menuRef); } } }; const handleClick = (event: MouseEvent) => { // If the event is not on the toggle and onOpenChange callback is provided, close the menu if (isOpen && onOpenChange && !toggleRef?.current?.contains(event.target as Node)) { if (isOpen && !menuRef.current?.contains(event.target as Node)) { onOpenChange(false); } } }; window.addEventListener('keydown', handleMenuKeys); window.addEventListener('click', handleClick); return () => { window.removeEventListener('keydown', handleMenuKeys); window.removeEventListener('click', handleClick); }; }, [ isOpen, menuRef, toggleRef, onOpenChange, onOpenChangeKeys, onToggleKeydown, shouldPreventScrollOnItemFocus, shouldFocusFirstItemOnOpen, focusTimeoutDelay ]); const menu = ( { onSelect && onSelect(event, value); shouldFocusToggleOnSelect && toggleRef.current.focus(); }} isPlain={isPlain} selected={selected} isScrollable={isScrollable ?? (menuHeight !== undefined || maxMenuHeight !== undefined)} {...getOUIAProps( Select.displayName, props.ouiaId !== undefined ? props.ouiaId : getDefaultOUIAId(Select.displayName), props.ouiaSafe !== undefined ? props.ouiaSafe : true )} {...props} > {children} ); return ( ); }; export const Select = React.forwardRef((props: SelectProps, ref: React.Ref) => ( )); Select.displayName = 'Select';