import * as React from 'react' ; import { AriaAttributes , Component , FocusEventHandler , FormEventHandler , KeyboardEventHandler , MouseEventHandler , ReactNode , RefCallback , TouchEventHandler , } from 'react' ; import { MenuPlacer } from './components/Menu' ; import LiveRegion from './components/LiveRegion' ; import { createFilter , FilterOptionOption } from './filters' ; import { DummyInput , ScrollManager } from './internal/index' ; import { AriaLiveMessages , AriaSelection } from './accessibility/index' ; import { classNames , cleanValue , isTouchCapable , isMobileDevice , noop , scrollIntoView , isDocumentElement , notNullish , valueTernary , multiValueAsValue , singleValueAsValue , } from './utils' ; import { formatGroupLabel as formatGroupLabelBuiltin , getOptionLabel as getOptionLabelBuiltin , getOptionValue as getOptionValueBuiltin , isOptionDisabled as isOptionDisabledBuiltin , } from './builtins' ; import { defaultComponents , SelectComponentsConfig } from './components/index' ; import { defaultStyles , StylesConfig , StylesProps } from './styles' ; import { defaultTheme , ThemeConfig } from './theme' ; import { ActionMeta , FocusDirection , GetOptionLabel , GetOptionValue , GroupBase , InputActionMeta , MenuPlacement , MenuPosition , OnChangeValue , Options , OptionsOrGroups , PropsValue , SetValueAction , } from './types' ; export type FormatOptionLabelContext = 'menu' | 'value' ; export interface FormatOptionLabelMeta < Option > { context : FormatOptionLabelContext ; inputValue : string ; selectValue : Options < Option > ; } export interface Props < Option , IsMulti extends boolean , Group extends GroupBase < Option > > { /** HTML ID of an element containing an error message related to the input**/ 'aria-errormessage' ? : AriaAttributes [ 'aria-errormessage' ] ; /** Indicate if the value entered in the field is invalid **/ 'aria-invalid' ? : AriaAttributes [ 'aria-invalid' ] ; /** Aria label (for assistive tech) */ 'aria-label' ? : AriaAttributes [ 'aria-label' ] ; /** HTML ID of an element that should be used as the label (for assistive tech) */ 'aria-labelledby' ? : AriaAttributes [ 'aria-labelledby' ] ; /** Used to set the priority with which screen reader should treat updates to live regions. The possible settings are: off, polite (default) or assertive */ 'aria-live' ? : AriaAttributes [ 'aria-live' ] ; /** Customize the messages used by the aria-live component */ ariaLiveMessages ? : AriaLiveMessages < Option , IsMulti , Group > ; /** Focus the control when it is mounted */ autoFocus ? : boolean ; /** Remove the currently focused option when the user presses backspace when Select isClearable or isMulti */ backspaceRemovesValue : boolean ; /** Remove focus from the input when the user selects an option (handy for dismissing the keyboard on touch devices) */ blurInputOnSelect : boolean ; /** When the user reaches the top/bottom of the menu, prevent scroll on the scroll-parent  */ captureMenuScroll : boolean ; /** Sets a className attribute on the outer component */ className ? : string ; /**
     * If provided, all inner components will be given a prefixed className attribute.
     *
     * This is useful when styling via CSS classes instead of the Styles API approach.
     */ classNamePrefix ? : string | null ; /** Close the select menu when the user selects an option */ closeMenuOnSelect : boolean ; /**
     * If `true`, close the select menu when the user scrolls the document/body.
     *
     * If a function, takes a standard javascript `ScrollEvent` you return a boolean:
     *
     * `true` => The menu closes
     *
     * `false` => The menu stays open
     *
     * This is useful when you have a scrollable modal and want to portal the menu out,
     * but want to avoid graphical issues.
     */ closeMenuOnScroll : boolean | ( ( event : Event ) => boolean ) ; /**
     * This complex object includes all the compositional components that are used
     * in `react-select`. If you wish to overwrite a component, pass in an object
     * with the appropriate namespace.
     *
     * If you only wish to restyle a component, we recommend using the `styles` prop
     * instead. For a list of the components that can be passed in, and the shape
     * that will be passed to them, see [the components docs](/components)
     */ components : SelectComponentsConfig < Option , IsMulti , Group > ; /** Whether the value of the select, e.g. SingleValue, should be displayed in the control. */ controlShouldRenderValue : boolean ; /** Delimiter used to join multiple values into a single HTML Input value */ delimiter ? : string ; /** Clear all values when the user presses escape AND the menu is closed */ escapeClearsValue : boolean ; /** Custom method to filter whether an option should be displayed in the menu */ filterOption : ( ( option : FilterOptionOption < Option > , inputValue : string ) => boolean ) | null ; /**
     * Formats group labels in the menu as React components
     *
     * An example can be found in the [Replacing builtins](/advanced#replacing-builtins) documentation.
     */ formatGroupLabel : ( group : Group ) => ReactNode ; /** Formats option labels in the menu and control as React components */ formatOptionLabel ? : ( data : Option , formatOptionLabelMeta : FormatOptionLabelMeta < Option > ) => ReactNode ; /**
     * Resolves option data to a string to be displayed as the label by components
     *
     * Note: Failure to resolve to a string type can interfere with filtering and
     * screen reader support.
     */ getOptionLabel : GetOptionLabel < Option > ; /** Resolves option data to a string to compare options and specify value attributes */ getOptionValue : GetOptionValue < Option > ; /** Hide the selected option from the menu */ hideSelectedOptions ? : boolean ; /** The id to set on the SelectContainer component. */ id ? : string ; /** The value of the search input */ inputValue : string ; /** The id of the search input */ inputId ? : string ; /** Define an id prefix for the select components e.g. {your-id}-value */ instanceId ? : number | string ; /** Is the select value clearable */ isClearable ? : boolean ; /** Is the select disabled */ isDisabled : boolean ; /** Is the select in a state of loading (async) */ isLoading : boolean ; /**
     * Override the built-in logic to detect whether an option is disabled
     *
     * An example can be found in the [Replacing builtins](/advanced#replacing-builtins) documentation.
     */ isOptionDisabled : ( option : Option , selectValue : Options < Option > ) => boolean ; /** Override the built-in logic to detect whether an option is selected */ isOptionSelected ? : ( option : Option , selectValue : Options < Option > ) => boolean ; /** Support multiple selected options */ isMulti : IsMulti ; /** Is the select direction right-to-left */ isRtl : boolean ; /** Whether to enable search functionality */ isSearchable : boolean ; /** Async: Text to display when loading options */ loadingMessage : ( obj : { inputValue : string ; } ) => ReactNode ; /** Minimum height of the menu before flipping */ minMenuHeight : number ; /** Maximum height of the menu before scrolling */ maxMenuHeight : number ; /** Whether the menu is open */ menuIsOpen : boolean ; /**
     * Default placement of the menu in relation to the control. 'auto' will flip
     * when there isn't enough space below the control.
     */ menuPlacement : MenuPlacement ; /** The CSS position value of the menu, when "fixed" extra layout management is required */ menuPosition : MenuPosition ; /**
     * Whether the menu should use a portal, and where it should attach
     *
     * An example can be found in the [Portaling](/advanced#portaling) documentation
     */ menuPortalTarget ? : HTMLElement | null ; /** Whether to block scroll events when the menu is open */ menuShouldBlockScroll : boolean ; /** Whether the menu should be scrolled into view when it opens */ menuShouldScrollIntoView : boolean ; /** Name of the HTML Input (optional - without this, no input will be rendered) */ name ? : string ; /** Text to display when there are no options */ noOptionsMessage : ( obj : { inputValue : string ; } ) => ReactNode ; /** Handle blur events on the control */ onBlur ? : FocusEventHandler < HTMLInputElement > ; /** Handle change events on the select */ onChange : ( newValue : OnChangeValue < Option , IsMulti > , actionMeta : ActionMeta < Option > ) => void ; /** Handle focus events on the control */ onFocus ? : FocusEventHandler < HTMLInputElement > ; /** Handle change events on the input */ onInputChange : ( newValue : string , actionMeta : InputActionMeta ) => void ; /** Handle key down events on the select */ onKeyDown ? : KeyboardEventHandler < HTMLDivElement > ; /** Handle the menu opening */ onMenuOpen : ( ) => void ; /** Handle the menu closing */ onMenuClose : ( ) => void ; /** Fired when the user scrolls to the top of the menu */ onMenuScrollToTop ? : ( event : WheelEvent | TouchEvent ) => void ; /** Fired when the user scrolls to the bottom of the menu */ onMenuScrollToBottom ? : ( event : WheelEvent | TouchEvent ) => void ; /** Allows control of whether the menu is opened when the Select is focused */ openMenuOnFocus : boolean ; /** Allows control of whether the menu is opened when the Select is clicked */ openMenuOnClick : boolean ; /** Array of options that populate the select menu */ options : OptionsOrGroups < Option , Group > ; /** Number of options to jump in menu when page{up|down} keys are used */ pageSize : number ; /** Placeholder for the select value */ placeholder : ReactNode ; /** Status to relay to screen readers */ screenReaderStatus : ( obj : { count : number ; } ) => string ; /**
     * Style modifier methods
     *
     * A basic example can be found at the bottom of the [Replacing builtins](/advanced#replacing-builtins) documentation.
     */ styles : StylesConfig < Option , IsMulti , Group > ; /** Theme modifier method */ theme ? : ThemeConfig ; /** Sets the tabIndex attribute on the input */ tabIndex : number ; /** Select the currently focused option when the user presses tab */ tabSelectsValue : boolean ; /** The value of the select; reflected by the selected option */ value : PropsValue < Option > ; /** Sets the form attribute on the input */ form ? : string ; } export const defaultProps = { 'aria-live' : 'polite' , backspaceRemovesValue : true , blurInputOnSelect : isTouchCapable ( ) , captureMenuScroll : ! isTouchCapable ( ) , closeMenuOnSelect : true , closeMenuOnScroll : false , components : { } , controlShouldRenderValue : true , escapeClearsValue : false , filterOption : createFilter ( ) , formatGroupLabel : formatGroupLabelBuiltin , getOptionLabel : getOptionLabelBuiltin , getOptionValue : getOptionValueBuiltin , isDisabled : false , isLoading : false , isMulti : false , isRtl : false , isSearchable : true , isOptionDisabled : isOptionDisabledBuiltin , loadingMessage : ( ) => 'Loading...' , maxMenuHeight : 300 , minMenuHeight : 140 , menuIsOpen : false , menuPlacement : 'bottom' , menuPosition : 'absolute' , menuShouldBlockScroll : false , menuShouldScrollIntoView : ! isMobileDevice ( ) , noOptionsMessage : ( ) => 'No options' , openMenuOnFocus : false , openMenuOnClick : true , options : [ ] , pageSize : 5 , placeholder : 'Select...' , screenReaderStatus : ( { count } ) => `${ count } result${ count !== 1 ? 's' : '' } available` , styles : { } , tabIndex : 0 , tabSelectsValue : true , } ; interface State < Option , IsMulti extends boolean , Group extends GroupBase < Option > > { ariaSelection : AriaSelection < Option , IsMulti > | null ; inputIsHidden : boolean ; isFocused : boolean ; focusedOption : Option | null ; focusedValue : Option | null ; selectValue : Options < Option > ; clearFocusValueOnUpdate : boolean ; prevWasFocused : boolean ; inputIsHiddenAfterUpdate : boolean | null | undefined ; prevProps : Props < Option , IsMulti , Group > | void ; } interface CategorizedOption < Option > { type : 'option' ; data : Option ; isDisabled : boolean ; isSelected : boolean ; label : string ; value : string ; index : number ; } interface CategorizedGroup < Option , Group extends GroupBase < Option > > { type : 'group' ; data : Group ; options : readonly CategorizedOption < Option > [ ] ; index : number ; } type CategorizedGroupOrOption < Option , Group extends GroupBase < Option > > = CategorizedGroup < Option , Group > | CategorizedOption < Option > ; function toCategorizedOption < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , option , selectValue , index ) { const isDisabled = isOptionDisabled ( props , option , selectValue ) ; const isSelected = isOptionSelected ( props , option , selectValue ) ; const label = getOptionLabel ( props , option ) ; const value = getOptionValue ( props , option ) ; return { type : 'option' , data : option , isDisabled , isSelected , label , value , index , } ; } function buildCategorizedOptions < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , selectValue ) { return props . options . map ( ( groupOrOption , groupOrOptionIndex ) => { if ( 'options' in groupOrOption ) { const categorizedOptions = groupOrOption . options . map ( ( option , optionIndex ) => toCategorizedOption ( props , option , selectValue , optionIndex ) ) . filter ( ( categorizedOption ) => isFocusable ( props , categorizedOption ) ) ; return categorizedOptions . length > 0 ? { type : 'group' as const , data : groupOrOption , options : categorizedOptions , index : groupOrOptionIndex , } : undefined ; } const categorizedOption = toCategorizedOption ( props , groupOrOption , selectValue , groupOrOptionIndex ) ; return isFocusable ( props , categorizedOption ) ? categorizedOption : undefined ; } ) . filter ( notNullish ) ; } function buildFocusableOptionsFromCategorizedOptions < Option , Group extends GroupBase < Option > > ( categorizedOptions ) { return categorizedOptions . reduce < Option [ ] > ( ( optionsAccumulator , categorizedOption ) => { if ( categorizedOption . type === 'group' ) { optionsAccumulator . push ( ... categorizedOption . options . map ( ( option ) => option . data ) ) ; } else { optionsAccumulator . push ( categorizedOption . data ) ; } return optionsAccumulator ; } , [ ] ) ; } function buildFocusableOptions < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , selectValue ) { return buildFocusableOptionsFromCategorizedOptions ( buildCategorizedOptions ( props , selectValue ) ) ; } function isFocusable < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , categorizedOption ) { const { inputValue = '' } = props ; const { data , isSelected , label , value } = categorizedOption ; return ( ( ! shouldHideSelectedOptions ( props ) || ! isSelected ) && filterOption ( props , { label , value , data } , inputValue ) ) ; } function getNextFocusedValue < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( state , nextSelectValue ) { const { focusedValue , selectValue : lastSelectValue } = state ; const lastFocusedIndex = lastSelectValue . indexOf ( focusedValue ! ) ; if ( lastFocusedIndex > - 1 ) { const nextFocusedIndex = nextSelectValue . indexOf ( focusedValue ! ) ; if ( nextFocusedIndex > - 1 ) { return focusedValue ; } else if ( lastFocusedIndex < nextSelectValue . length ) { return nextSelectValue [ lastFocusedIndex ] ; } } return null ; } function getNextFocusedOption < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( state , options ) { const { focusedOption : lastFocusedOption } = state ; return lastFocusedOption && options . indexOf ( lastFocusedOption ) > - 1 ? lastFocusedOption : options [ 0 ] ; } const getOptionLabel = < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , data ) : string => { return props . getOptionLabel ( data ) ; } ; const getOptionValue = < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , data ) : string => { return props . getOptionValue ( data ) ; } ; function isOptionDisabled < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , option , selectValue ) { return typeof props . isOptionDisabled === 'function' ? props . isOptionDisabled ( option , selectValue ) : false ; } function isOptionSelected < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , option , selectValue ) { if ( selectValue . indexOf ( option ) > - 1 ) return true ; if ( typeof props . isOptionSelected === 'function' ) { return props . isOptionSelected ( option , selectValue ) ; } const candidate = getOptionValue ( props , option ) ; return selectValue . some ( ( i ) => getOptionValue ( props , i ) === candidate ) ; } function filterOption < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props , option , inputValue ) { return props . filterOption ? props . filterOption ( option , inputValue ) : true ; } const shouldHideSelectedOptions = < Option , IsMulti extends boolean , Group extends GroupBase < Option > > ( props ) => { const { hideSelectedOptions , isMulti } = props ; if ( hideSelectedOptions === undefined ) return isMulti ; return hideSelectedOptions ; } ; let instanceId = 1 ; export default class Select < Option = unknown , IsMulti extends boolean = false , Group extends GroupBase < Option > = GroupBase < Option > > extends Component < Props < Option , IsMulti , Group > , State < Option , IsMulti , Group > > { static defaultProps = defaultProps ; state : State < Option , IsMulti , Group > = { ariaSelection : null , focusedOption : null , focusedValue : null , inputIsHidden : false , isFocused : false , selectValue : [ ] , clearFocusValueOnUpdate : false , prevWasFocused : false , inputIsHiddenAfterUpdate : undefined , prevProps : undefined , } ; blockOptionHover = false ; isComposing = false ; commonProps : any ; initialTouchX = 0 ; initialTouchY = 0 ; instancePrefix = '' ; openAfterFocus = false ; scrollToFocusedOptionOnUpdate = false ; userIsDragging ? : boolean ; controlRef : HTMLDivElement | null = null ; getControlRef : RefCallback < HTMLDivElement > = ( ref ) => { this . controlRef = ref ; } ; focusedOptionRef : HTMLDivElement | null = null ; getFocusedOptionRef : RefCallback < HTMLDivElement > = ( ref ) => { this . focusedOptionRef = ref ; } ; menuListRef : HTMLDivElement | null = null ; getMenuListRef : RefCallback < HTMLDivElement > = ( ref ) => { this . menuListRef = ref ; } ; inputRef : HTMLInputElement | null = null ; getInputRef : RefCallback < HTMLInputElement > = ( ref ) => { this . inputRef = ref ; } ; constructor ( props ) { super ( props ) ; this . instancePrefix = 'react-select-' + ( this . props . instanceId || ++ instanceId ) ; this . state . selectValue = cleanValue ( props . value ) ; } static getDerivedStateFromProps ( props , state ) { const { prevProps , clearFocusValueOnUpdate , inputIsHiddenAfterUpdate , ariaSelection , isFocused , prevWasFocused , } = state ; const { options , value , menuIsOpen , inputValue , isMulti } = props ; const selectValue = cleanValue ( value ) ; let newMenuOptionsState = { } ; if ( prevProps && ( value !== prevProps . value || options !== prevProps . options || menuIsOpen !== prevProps . menuIsOpen || inputValue !== prevProps . inputValue ) ) { const focusableOptions = menuIsOpen ? buildFocusableOptions ( props , selectValue ) : [ ] ; const focusedValue = clearFocusValueOnUpdate ? getNextFocusedValue ( state , selectValue ) : null ; const focusedOption = getNextFocusedOption ( state , focusableOptions ) ; newMenuOptionsState = { selectValue , focusedOption , focusedValue , clearFocusValueOnUpdate : false , } ; } const newInputIsHiddenState = inputIsHiddenAfterUpdate != null && props !== prevProps ? { inputIsHidden : inputIsHiddenAfterUpdate , inputIsHiddenAfterUpdate : undefined , } : { } ; let newAriaSelection = ariaSelection ; let hasKeptFocus = isFocused && prevWasFocused ; if ( isFocused && ! hasKeptFocus ) { newAriaSelection = { value : valueTernary ( isMulti , selectValue , selectValue [ 0 ] || null ) , options : selectValue , action : 'initial-input-focus' , } ; hasKeptFocus = ! prevWasFocused ; } if ( ariaSelection ?. action === 'initial-input-focus' ) { newAriaSelection = null ; } return { ... newMenuOptionsState , ... newInputIsHiddenState , prevProps : props , ariaSelection : newAriaSelection , prevWasFocused : hasKeptFocus , } ; } componentDidMount ( ) { this . startListeningComposition ( ) ; this . startListeningToTouch ( ) ; if ( this . props . closeMenuOnScroll && document && document . addEventListener ) { document . addEventListener ( 'scroll' , this . onScroll , true ) ; } if ( this . props . autoFocus ) { this . focusInput ( ) ; } } componentDidUpdate ( prevProps ) { const { isDisabled , menuIsOpen } = this . props ; const { isFocused } = this . state ; if ( ( isFocused && ! isDisabled && prevProps . isDisabled ) || ( isFocused && menuIsOpen && ! prevProps . menuIsOpen ) ) { this . focusInput ( ) ; } if ( isFocused && isDisabled && ! prevProps . isDisabled ) { this . setState ( { isFocused : false } , this . onMenuClose ) ; } if ( this . menuListRef && this . focusedOptionRef && this . scrollToFocusedOptionOnUpdate ) { scrollIntoView ( this . menuListRef , this . focusedOptionRef ) ; this . scrollToFocusedOptionOnUpdate = false ; } } componentWillUnmount ( ) { this . stopListeningComposition ( ) ; this . stopListeningToTouch ( ) ; document . removeEventListener ( 'scroll' , this . onScroll , true ) ; } onMenuOpen ( ) { this . props . onMenuOpen ( ) ; } onMenuClose ( ) { this . onInputChange ( '' , { action : 'menu-close' , prevInputValue : this . props . inputValue , } ) ; this . props . onMenuClose ( ) ; } onInputChange ( newValue , actionMeta ) { this . props . onInputChange ( newValue , actionMeta ) ; } focusInput ( ) { if ( ! this . inputRef ) return ; this . inputRef . focus ( ) ; } blurInput ( ) { if ( ! this . inputRef ) return ; this . inputRef . blur ( ) ; } focus = this . focusInput ; blur = this . blurInput ; openMenu ( focusOption ) { const { selectValue , isFocused } = this . state ; const focusableOptions = this . buildFocusableOptions ( ) ; let openAtIndex = focusOption === 'first' ? 0 : focusableOptions . length - 1 ; if ( ! this . props . isMulti ) { const selectedIndex = focusableOptions . indexOf ( selectValue [ 0 ] ) ; if ( selectedIndex > - 1 ) { openAtIndex = selectedIndex ; } } this . scrollToFocusedOptionOnUpdate = ! ( isFocused && this . menuListRef ) ; this . setState ( { inputIsHiddenAfterUpdate : false , focusedValue : null , focusedOption : focusableOptions [ openAtIndex ] , } , ( ) => this . onMenuOpen ( ) ) ; } focusValue ( direction ) { const { selectValue , focusedValue } = this . state ; if ( ! this . props . isMulti ) return ; this . setState ( { focusedOption : null , } ) ; let focusedIndex = selectValue . indexOf ( focusedValue ! ) ; if ( ! focusedValue ) { focusedIndex = - 1 ; } const lastIndex = selectValue . length - 1 ; let nextFocus = - 1 ; if ( ! selectValue . length ) return ; switch ( direction ) { case 'previous' : if ( focusedIndex === 0 ) { nextFocus = 0 ; } else if ( focusedIndex === - 1 ) { nextFocus = lastIndex ; } else { nextFocus = focusedIndex - 1 ; } break ; case 'next' : if ( focusedIndex > - 1 && focusedIndex < lastIndex ) { nextFocus = focusedIndex + 1 ; } break ; } this . setState ( { inputIsHidden : nextFocus !== - 1 , focusedValue : selectValue [ nextFocus ] , } ) ; } focusOption ( direction = 'first' ) { const { pageSize } = this . props ; const { focusedOption } = this . state ; const options = this . getFocusableOptions ( ) ; if ( ! options . length ) return ; let nextFocus = 0 ; let focusedIndex = options . indexOf ( focusedOption ! ) ; if ( ! focusedOption ) { focusedIndex = - 1 ; } if ( direction === 'up' ) { nextFocus = focusedIndex > 0 ? focusedIndex - 1 : options . length - 1 ; } else if ( direction === 'down' ) { nextFocus = ( focusedIndex + 1 ) % options . length ; } else if ( direction === 'pageup' ) { nextFocus = focusedIndex - pageSize ; if ( nextFocus < 0 ) nextFocus = 0 ; } else if ( direction === 'pagedown' ) { nextFocus = focusedIndex + pageSize ; if ( nextFocus > options . length - 1 ) nextFocus = options . length - 1 ; } else if ( direction === 'last' ) { nextFocus = options . length - 1 ; } this . scrollToFocusedOptionOnUpdate = true ; this . setState ( { focusedOption : options [ nextFocus ] , focusedValue : null , } ) ; } onChange = ( newValue , actionMeta ) => { const { onChange , name } = this . props ; actionMeta . name = name ; this . ariaOnChange ( newValue , actionMeta ) ; onChange ( newValue , actionMeta ) ; } ; setValue = ( newValue , action , option ? ) => { const { closeMenuOnSelect , isMulti , inputValue } = this . props ; this . onInputChange ( '' , { action : 'set-value' , prevInputValue : inputValue } ) ; if ( closeMenuOnSelect ) { this . setState ( { inputIsHiddenAfterUpdate : ! isMulti } ) ; this . onMenuClose ( ) ; } this . setState ( { clearFocusValueOnUpdate : true } ) ; this . onChange ( newValue , { action , option } ) ; } ; selectOption = ( newValue ) => { const { blurInputOnSelect , isMulti , name } = this . props ; const { selectValue } = this . state ; const deselected = isMulti && this . isOptionSelected ( newValue , selectValue ) ; const isDisabled = this . isOptionDisabled ( newValue , selectValue ) ; if ( deselected ) { const candidate = this . getOptionValue ( newValue ) ; this . setValue ( multiValueAsValue ( selectValue . filter ( ( i ) => this . getOptionValue ( i ) !== candidate ) ) , 'deselect-option' , newValue ) ; } else if ( ! isDisabled ) { if ( isMulti ) { this . setValue ( multiValueAsValue ( [ ... selectValue , newValue ] ) , 'select-option' , newValue ) ; } else { this . setValue ( singleValueAsValue ( newValue ) , 'select-option' ) ; } } else { this . ariaOnChange ( singleValueAsValue ( newValue ) , { action : 'select-option' , option : newValue , name , } ) ; return ; } if ( blurInputOnSelect ) { this . blurInput ( ) ; } } ; removeValue = ( removedValue ) => { const { isMulti } = this . props ; const { selectValue } = this . state ; const candidate = this . getOptionValue ( removedValue ) ; const newValueArray = selectValue . filter ( ( i ) => this . getOptionValue ( i ) !== candidate ) ; const newValue = valueTernary ( isMulti , newValueArray , newValueArray [ 0 ] || null ) ; this . onChange ( newValue , { action : 'remove-value' , removedValue } ) ; this . focusInput ( ) ; } ; clearValue = ( ) => { const { selectValue } = this . state ; this . onChange ( valueTernary ( this . props . isMulti , [ ] , null ) , { action : 'clear' , removedValues : selectValue , } ) ; } ; popValue = ( ) => { const { isMulti } = this . props ; const { selectValue } = this . state ; const lastSelectedValue = selectValue [ selectValue . length - 1 ] ; const newValueArray = selectValue . slice ( 0 , selectValue . length - 1 ) ; const newValue = valueTernary ( isMulti , newValueArray , newValueArray [ 0 ] || null ) ; this . onChange ( newValue , { action : 'pop-value' , removedValue : lastSelectedValue , } ) ; } ; getTheme ( ) { if ( ! this . props . theme ) { return defaultTheme ; } if ( typeof this . props . theme === 'function' ) { return this . props . theme ( defaultTheme ) ; } return { ... defaultTheme , ... this . props . theme , } ; } getValue = ( ) => this . state . selectValue ; cx = ( ... args ) => classNames ( this . props . classNamePrefix , ... args ) ; getCommonProps ( ) { const { clearValue , cx , getStyles , getValue , selectOption , setValue , props , } = this ; const { isMulti , isRtl , options } = props ; const hasValue = this . hasValue ( ) ; return { clearValue , cx , getStyles , getValue , hasValue , isMulti , isRtl , options , selectOption , selectProps : props , setValue , theme : this . getTheme ( ) , } ; } getOptionLabel = ( data ) : string => { return getOptionLabel ( this . props , data ) ; } ; getOptionValue = ( data ) : string => { return getOptionValue ( this . props , data ) ; } ; getStyles = < Key extends keyof StylesProps < Option , IsMulti , Group > > ( key , props ) => { const base = defaultStyles [ key ] ( props as any ) ; base . boxSizing = 'border-box' ; const custom = this . props . styles [ key ] ; return custom ? custom ( base , props as any ) : base ; } ; getElementId = ( element ) => { return `${ this . instancePrefix }-${ element }` ; } ; getComponents = ( ) => { return defaultComponents ( this . props ) ; } ; buildCategorizedOptions = ( ) => buildCategorizedOptions ( this . props , this . state . selectValue ) ; getCategorizedOptions = ( ) => this . props . menuIsOpen ? this . buildCategorizedOptions ( ) : [ ] ; buildFocusableOptions = ( ) => buildFocusableOptionsFromCategorizedOptions ( this . buildCategorizedOptions ( ) ) ; getFocusableOptions = ( ) => this . props . menuIsOpen ? this . buildFocusableOptions ( ) : [ ] ; ariaOnChange = ( value , actionMeta ) => { this . setState ( { ariaSelection : { value , ... actionMeta } } ) ; } ; hasValue ( ) { const { selectValue } = this . state ; return selectValue . length > 0 ; } hasOptions ( ) { return ! ! this . getFocusableOptions ( ) . length ; } isClearable ( ) { const { isClearable , isMulti } = this . props ; if ( isClearable === undefined ) return isMulti ; return isClearable ; } isOptionDisabled ( option , selectValue ) { return isOptionDisabled ( this . props , option , selectValue ) ; } isOptionSelected ( option , selectValue ) { return isOptionSelected ( this . props , option , selectValue ) ; } filterOption ( option , inputValue ) { return filterOption ( this . props , option , inputValue ) ; } formatOptionLabel ( data , context ) { if ( typeof this . props . formatOptionLabel === 'function' ) { const { inputValue } = this . props ; const { selectValue } = this . state ; return this . props . formatOptionLabel ( data , { context , inputValue , selectValue , } ) ; } else { return this . getOptionLabel ( data ) ; } } formatGroupLabel ( data ) { return this . props . formatGroupLabel ( data ) ; } onMenuMouseDown : MouseEventHandler < HTMLDivElement > = ( event ) => { if ( event . button !== 0 ) { return ; } event . stopPropagation ( ) ; event . preventDefault ( ) ; this . focusInput ( ) ; } ; onMenuMouseMove : MouseEventHandler < HTMLDivElement > = ( event ) => { this . blockOptionHover = false ; } ; onControlMouseDown = ( event ) => { const { openMenuOnClick } = this . props ; if ( ! this . state . isFocused ) { if ( openMenuOnClick ) { this . openAfterFocus = true ; } this . focusInput ( ) ; } else if ( ! this . props . menuIsOpen ) { if ( openMenuOnClick ) { this . openMenu ( 'first' ) ; } } else { if ( ( event . target as HTMLElement ) . tagName !== 'INPUT' && ( event . target as HTMLElement ) . tagName !== 'TEXTAREA' ) { this . onMenuClose ( ) ; } } if ( ( event . target as HTMLElement ) . tagName !== 'INPUT' && ( event . target as HTMLElement ) . tagName !== 'TEXTAREA' ) { event . preventDefault ( ) ; } } ; onDropdownIndicatorMouseDown = ( event ) => { if ( event && event . type === 'mousedown' && ( event as React . MouseEvent < HTMLDivElement > ) . button !== 0 ) { return ; } if ( this . props . isDisabled ) return ; const { isMulti , menuIsOpen } = this . props ; this . focusInput ( ) ; if ( menuIsOpen ) { this . setState ( { inputIsHiddenAfterUpdate : ! isMulti } ) ; this . onMenuClose ( ) ; } else { this . openMenu ( 'first' ) ; } event . preventDefault ( ) ; event . stopPropagation ( ) ; } ; onClearIndicatorMouseDown = ( event ) => { if ( event && event . type === 'mousedown' && ( event as React . MouseEvent < HTMLDivElement > ) . button !== 0 ) { return ; } this . clearValue ( ) ; event . preventDefault ( ) ; event . stopPropagation ( ) ; this . openAfterFocus = false ; if ( event . type === 'touchend' ) { this . focusInput ( ) ; } else { setTimeout ( ( ) => this . focusInput ( ) ) ; } } ; onScroll = ( event ) => { if ( typeof this . props . closeMenuOnScroll === 'boolean' ) { if ( event . target instanceof HTMLElement && isDocumentElement ( event . target ) ) { this . props . onMenuClose ( ) ; } } else if ( typeof this . props . closeMenuOnScroll === 'function' ) { if ( this . props . closeMenuOnScroll ( event ) ) { this . props . onMenuClose ( ) ; } } } ; startListeningComposition ( ) { if ( document && document . addEventListener ) { document . addEventListener ( 'compositionstart' , this . onCompositionStart , false ) ; document . addEventListener ( 'compositionend' , this . onCompositionEnd , false ) ; } } stopListeningComposition ( ) { if ( document && document . removeEventListener ) { document . removeEventListener ( 'compositionstart' , this . onCompositionStart ) ; document . removeEventListener ( 'compositionend' , this . onCompositionEnd ) ; } } onCompositionStart = ( ) => { this . isComposing = true ; } ; onCompositionEnd = ( ) => { this . isComposing = false ; } ; startListeningToTouch ( ) { if ( document && document . addEventListener ) { document . addEventListener ( 'touchstart' , this . onTouchStart , false ) ; document . addEventListener ( 'touchmove' , this . onTouchMove , false ) ; document . addEventListener ( 'touchend' , this . onTouchEnd , false ) ; } } stopListeningToTouch ( ) { if ( document && document . removeEventListener ) { document . removeEventListener ( 'touchstart' , this . onTouchStart ) ; document . removeEventListener ( 'touchmove' , this . onTouchMove ) ; document . removeEventListener ( 'touchend' , this . onTouchEnd ) ; } } onTouchStart = ( { touches } ) => { const touch = touches && touches . item ( 0 ) ; if ( ! touch ) { return ; } this . initialTouchX = touch . clientX ; this . initialTouchY = touch . clientY ; this . userIsDragging = false ; } ; onTouchMove = ( { touches } ) => { const touch = touches && touches . item ( 0 ) ; if ( ! touch ) { return ; } const deltaX = Math . abs ( touch . clientX - this . initialTouchX ) ; const deltaY = Math . abs ( touch . clientY - this . initialTouchY ) ; const moveThreshold = 5 ; this . userIsDragging = deltaX > moveThreshold || deltaY > moveThreshold ; } ; onTouchEnd = ( event ) => { if ( this . userIsDragging ) return ; if ( this . controlRef && ! this . controlRef . contains ( event . target as Node ) && this . menuListRef && ! this . menuListRef . contains ( event . target as Node ) ) { this . blurInput ( ) ; } this . initialTouchX = 0 ; this . initialTouchY = 0 ; } ; onControlTouchEnd : TouchEventHandler < HTMLDivElement > = ( event ) => { if ( this . userIsDragging ) return ; this . onControlMouseDown ( event ) ; } ; onClearIndicatorTouchEnd : TouchEventHandler < HTMLDivElement > = ( event ) => { if ( this . userIsDragging ) return ; this . onClearIndicatorMouseDown ( event ) ; } ; onDropdownIndicatorTouchEnd : TouchEventHandler < HTMLDivElement > = ( event ) => { if ( this . userIsDragging ) return ; this . onDropdownIndicatorMouseDown ( event ) ; } ; handleInputChange : FormEventHandler < HTMLInputElement > = ( event ) => { const { inputValue : prevInputValue } = this . props ; const inputValue = event . currentTarget . value ; this . setState ( { inputIsHiddenAfterUpdate : false } ) ; this . onInputChange ( inputValue , { action : 'input-change' , prevInputValue } ) ; if ( ! this . props . menuIsOpen ) { this . onMenuOpen ( ) ; } } ; onInputFocus : FocusEventHandler < HTMLInputElement > = ( event ) => { if ( this . props . onFocus ) { this . props . onFocus ( event ) ; } this . setState ( { inputIsHiddenAfterUpdate : false , isFocused : true , } ) ; if ( this . openAfterFocus || this . props . openMenuOnFocus ) { this . openMenu ( 'first' ) ; } this . openAfterFocus = false ; } ; onInputBlur : FocusEventHandler < HTMLInputElement > = ( event ) => { const { inputValue : prevInputValue } = this . props ; if ( this . menuListRef && this . menuListRef . contains ( document . activeElement ) ) { this . inputRef ! . focus ( ) ; return ; } if ( this . props . onBlur ) { this . props . onBlur ( event ) ; } this . onInputChange ( '' , { action : 'input-blur' , prevInputValue } ) ; this . onMenuClose ( ) ; this . setState ( { focusedValue : null , isFocused : false , } ) ; } ; onOptionHover = ( focusedOption ) => { if ( this . blockOptionHover || this . state . focusedOption === focusedOption ) { return ; } this . setState ( { focusedOption } ) ; } ; shouldHideSelectedOptions = ( ) => { return shouldHideSelectedOptions ( this . props ) ; } ; onKeyDown : KeyboardEventHandler < HTMLDivElement > = ( event ) => { const { isMulti , backspaceRemovesValue , escapeClearsValue , inputValue , isClearable , isDisabled , menuIsOpen , onKeyDown , tabSelectsValue , openMenuOnFocus , } = this . props ; const { focusedOption , focusedValue , selectValue } = this . state ; if ( isDisabled ) return ; if ( typeof onKeyDown === 'function' ) { onKeyDown ( event ) ; if ( event . defaultPrevented ) { return ; } } this . blockOptionHover = true ; switch ( event . key ) { case 'ArrowLeft' : if ( ! isMulti || inputValue ) return ; this . focusValue ( 'previous' ) ; break ; case 'ArrowRight' : if ( ! isMulti || inputValue ) return ; this . focusValue ( 'next' ) ; break ; case 'Delete' : case 'Backspace' : if ( inputValue ) return ; if ( focusedValue ) { this . removeValue ( focusedValue ) ; } else { if ( ! backspaceRemovesValue ) return ; if ( isMulti ) { this . popValue ( ) ; } else if ( isClearable ) { this . clearValue ( ) ; } } break ; case 'Tab' : if ( this . isComposing ) return ; if ( event . shiftKey || ! menuIsOpen || ! tabSelectsValue || ! focusedOption || ( openMenuOnFocus && this . isOptionSelected ( focusedOption , selectValue ) ) ) { return ; } this . selectOption ( focusedOption ) ; break ; case 'Enter' : if ( event . keyCode === 229 ) { break ; } if ( menuIsOpen ) { if ( ! focusedOption ) return ; if ( this . isComposing ) return ; this . selectOption ( focusedOption ) ; break ; } return ; case 'Escape' : if ( menuIsOpen ) { this . setState ( { inputIsHiddenAfterUpdate : false } ) ; this . onInputChange ( '' , { action : 'menu-close' , prevInputValue : inputValue , } ) ; this . onMenuClose ( ) ; } else if ( isClearable && escapeClearsValue ) { this . clearValue ( ) ; } break ; case ' ' : if ( inputValue ) { return ; } if ( ! menuIsOpen ) { this . openMenu ( 'first' ) ; break ; } if ( ! focusedOption ) return ; this . selectOption ( focusedOption ) ; break ; case 'ArrowUp' : if ( menuIsOpen ) { this . focusOption ( 'up' ) ; } else { this . openMenu ( 'last' ) ; } break ; case 'ArrowDown' : if ( menuIsOpen ) { this . focusOption ( 'down' ) ; } else { this . openMenu ( 'first' ) ; } break ; case 'PageUp' : if ( ! menuIsOpen ) return ; this . focusOption ( 'pageup' ) ; break ; case 'PageDown' : if ( ! menuIsOpen ) return ; this . focusOption ( 'pagedown' ) ; break ; case 'Home' : if ( ! menuIsOpen ) return ; this . focusOption ( 'first' ) ; break ; case 'End' : if ( ! menuIsOpen ) return ; this . focusOption ( 'last' ) ; break ; default : return ; } event . preventDefault ( ) ; } ; renderInput ( ) { const { isDisabled , isSearchable , inputId , inputValue , tabIndex , form , menuIsOpen , } = this . props ; const { Input } = this . getComponents ( ) ; const { inputIsHidden , ariaSelection } = this . state ; const { commonProps } = this ; const id = inputId || this . getElementId ( 'input' ) ; const ariaAttributes = { 'aria-autocomplete' : 'list' as const , 'aria-expanded' : menuIsOpen , 'aria-haspopup' : true , 'aria-controls' : this . getElementId ( 'listbox' ) , 'aria-owns' : this . getElementId ( 'listbox' ) , 'aria-errormessage' : this . props [ 'aria-errormessage' ] , 'aria-invalid' : this . props [ 'aria-invalid' ] , 'aria-label' : this . props [ 'aria-label' ] , 'aria-labelledby' : this . props [ 'aria-labelledby' ] , role : 'combobox' , ... ( ! isSearchable && { 'aria-readonly' : true , } ) , ... ( this . hasValue ( ) ? ariaSelection ?. action === 'initial-input-focus' && { 'aria-describedby' : this . getElementId ( 'live-region' ) , } : { 'aria-describedby' : this . getElementId ( 'placeholder' ) , } ) , } ; if ( ! isSearchable ) { return ( < DummyInput id = { id } innerRef = { this . getInputRef } onBlur = { this . onInputBlur } onChange = { noop } onFocus = { this . onInputFocus } disabled = { isDisabled } tabIndex = { tabIndex } inputMode = "none" form = { form } value = "" { ... ariaAttributes } />); } return ( < Input { ... commonProps } autoCapitalize = "none" autoComplete = "off" autoCorrect = "off" id = { id } innerRef = { this . getInputRef } isDisabled = { isDisabled } isHidden = { inputIsHidden } onBlur = { this . onInputBlur } onChange = { this . handleInputChange } onFocus = { this . onInputFocus } spellCheck = "false" tabIndex = { tabIndex } form = { form } type = "text" value = { inputValue } { ... ariaAttributes } />); } renderPlaceholderOrValue ( ) { const { MultiValue , MultiValueContainer , MultiValueLabel , MultiValueRemove , SingleValue , Placeholder , } = this . getComponents ( ) ; const { commonProps } = this ; const { controlShouldRenderValue , isDisabled , isMulti , inputValue , placeholder , } = this . props ; const { selectValue , focusedValue , isFocused } = this . state ; if ( ! this . hasValue ( ) || ! controlShouldRenderValue ) { return inputValue ? null : ( < Placeholder { ... commonProps } key = "placeholder" isDisabled = { isDisabled } isFocused = { isFocused } innerProps = { { id : this . getElementId ( 'placeholder' ) } } > { placeholder } < /Placeholder>); } if ( isMulti ) { return selectValue . map ( ( opt , index ) => { const isOptionFocused = opt === focusedValue ; const key = `${ this . getOptionLabel ( opt ) }-${ this . getOptionValue ( opt ) }` ; return ( < MultiValue { ... commonProps } components = { { Container : MultiValueContainer , Label : MultiValueLabel , Remove : MultiValueRemove , } } isFocused = { isOptionFocused } isDisabled = { isDisabled } key = { key } index = { index } removeProps = { { onClick : ( ) => this . removeValue ( opt ) , onTouchEnd : ( ) => this . removeValue ( opt ) , onMouseDown : ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; } , } } data = { opt } > { this . formatOptionLabel ( opt , 'value' ) } < /MultiValue>); } ) ; } if ( inputValue ) { return null ; } const singleValue = selectValue [ 0 ] ; return ( < SingleValue { ... commonProps } data = { singleValue } isDisabled = { isDisabled } > { this . formatOptionLabel ( singleValue , 'value' ) } < /SingleValue>); } renderClearIndicator ( ) { const { ClearIndicator } = this . getComponents ( ) ; const { commonProps } = this ; const { isDisabled , isLoading } = this . props ; const { isFocused } = this . state ; if ( ! this . isClearable ( ) || ! ClearIndicator || isDisabled || ! this . hasValue ( ) || isLoading ) { return null ; } const innerProps = { onMouseDown : this . onClearIndicatorMouseDown , onTouchEnd : this . onClearIndicatorTouchEnd , 'aria-hidden' : 'true' , } ; return ( < ClearIndicator { ... commonProps } innerProps = { innerProps } isFocused = { isFocused } / > ) ; } renderLoadingIndicator ( ) { const { LoadingIndicator } = this . getComponents ( ) ; const { commonProps } = this ; const { isDisabled , isLoading } = this . props ; const { isFocused } = this . state ; if ( ! LoadingIndicator || ! isLoading ) return null ; const innerProps = { 'aria-hidden' : 'true' } ; return ( < LoadingIndicator { ... commonProps } innerProps = { innerProps } isDisabled = { isDisabled } isFocused = { isFocused } / > ) ; } renderIndicatorSeparator ( ) { const { DropdownIndicator , IndicatorSeparator } = this . getComponents ( ) ; if ( ! DropdownIndicator || ! IndicatorSeparator ) return null ; const { commonProps } = this ; const { isDisabled } = this . props ; const { isFocused } = this . state ; return ( < IndicatorSeparator { ... commonProps } isDisabled = { isDisabled } isFocused = { isFocused } / > ) ; } renderDropdownIndicator ( ) { const { DropdownIndicator } = this . getComponents ( ) ; if ( ! DropdownIndicator ) return null ; const { commonProps } = this ; const { isDisabled } = this . props ; const { isFocused } = this . state ; const innerProps = { onMouseDown : this . onDropdownIndicatorMouseDown , onTouchEnd : this . onDropdownIndicatorTouchEnd , 'aria-hidden' : 'true' , } ; return ( < DropdownIndicator { ... commonProps } innerProps = { innerProps } isDisabled = { isDisabled } isFocused = { isFocused } / > ) ; } renderMenu ( ) { const { Group , GroupHeading , Menu , MenuList , MenuPortal , LoadingMessage , NoOptionsMessage , Option , } = this . getComponents ( ) ; const { commonProps } = this ; const { focusedOption } = this . state ; const { captureMenuScroll , inputValue , isLoading , loadingMessage , minMenuHeight , maxMenuHeight , menuIsOpen , menuPlacement , menuPosition , menuPortalTarget , menuShouldBlockScroll , menuShouldScrollIntoView , noOptionsMessage , onMenuScrollToTop , onMenuScrollToBottom , } = this . props ; if ( ! menuIsOpen ) return null ; const render = ( props , id ) => { const { type , data , isDisabled , isSelected , label , value } = props ; const isFocused = focusedOption === data ; const onHover = isDisabled ? undefined : ( ) => this . onOptionHover ( data ) ; const onSelect = isDisabled ? undefined : ( ) => this . selectOption ( data ) ; const optionId = `${ this . getElementId ( 'option' ) }-${ id }` ; const innerProps = { id : optionId , onClick : onSelect , onMouseMove : onHover , onMouseOver : onHover , tabIndex : - 1 , } ; return ( < Option { ... commonProps } innerProps = { innerProps } data = { data } isDisabled = { isDisabled } isSelected = { isSelected } key = { optionId } label = { label } type = { type } value = { value } isFocused = { isFocused } innerRef = { isFocused ? this . getFocusedOptionRef : undefined } > { this . formatOptionLabel ( props . data , 'menu' ) } < /Option>); } ; let menuUI ; if ( this . hasOptions ( ) ) { menuUI = this . getCategorizedOptions ( ) . map ( ( item ) => { if ( item . type === 'group' ) { const { data , options , index : groupIndex } = item ; const groupId = `${ this . getElementId ( 'group' ) }-${ groupIndex }` ; const headingId = `${ groupId }-heading` ; return ( < Group { ... commonProps } key = { groupId } data = { data } options = { options } Heading = { GroupHeading } headingProps = { { id : headingId , data : item . data , } } label = { this . formatGroupLabel ( item . data ) } > { item . options . map ( ( option ) => render ( option , `${ groupIndex }-${ option . index }` ) ) } < /Group>); } else if ( item . type === 'option' ) { return render ( item , `${ item . index }` ) ; } } ) ; } else if ( isLoading ) { const message = loadingMessage ( { inputValue } ) ; if ( message === null ) return null ; menuUI = < LoadingMessage { ... commonProps } > { message } < /LoadingMessage>; } else { const message = noOptionsMessage ( { inputValue } ) ; if ( message === null ) return null ; menuUI = < NoOptionsMessage { ... commonProps } > { message } < /NoOptionsMessage>; } const menuPlacementProps = { minMenuHeight , maxMenuHeight , menuPlacement , menuPosition , menuShouldScrollIntoView , } ; const menuElement = ( < MenuPlacer { ... commonProps } { ... menuPlacementProps } > { ( { ref , placerProps : { placement , maxHeight } } ) => ( < Menu { ... commonProps } { ... menuPlacementProps } innerRef = { ref } innerProps = { { onMouseDown : this . onMenuMouseDown , onMouseMove : this . onMenuMouseMove , id : this . getElementId ( 'listbox' ) , } } isLoading = { isLoading } placement = { placement } > < ScrollManager captureEnabled = { captureMenuScroll } onTopArrive = { onMenuScrollToTop } onBottomArrive = { onMenuScrollToBottom } lockEnabled = { menuShouldBlockScroll } > { ( scrollTargetRef ) => ( < MenuList { ... commonProps } innerRef = { ( instance ) => { this . getMenuListRef ( instance ) ; scrollTargetRef ( instance ) ; } } isLoading = { isLoading } maxHeight = { maxHeight } focusedOption = { focusedOption } > { menuUI } < /MenuList>)} < /ScrollManager> < /Menu>)} < /MenuPlacer>); return menuPortalTarget || menuPosition === 'fixed' ? ( < MenuPortal { ... commonProps } appendTo = { menuPortalTarget } controlElement = { this . controlRef } menuPlacement = { menuPlacement } menuPosition = { menuPosition } > { menuElement } < /MenuPortal>) : (menuElement); } renderFormField ( ) { const { delimiter , isDisabled , isMulti , name } = this . props ; const { selectValue } = this . state ; if ( ! name || isDisabled ) return ; if ( isMulti ) { if ( delimiter ) { const value = selectValue . map ( ( opt ) => this . getOptionValue ( opt ) ) . join ( delimiter ) ; return < input name = { name } type = "hidden" value = { value } / > ; } else { const input = selectValue . length > 0 ? ( selectValue . map ( ( opt , i ) => ( < input key = { `i-${ i }` } name = { name } type = "hidden" value = { this . getOptionValue ( opt ) } / > ) ) ) : ( < input name = { name } type = "hidden" / > ) ; return < div > { input } < /div>; } } else { const value = selectValue [ 0 ] ? this . getOptionValue ( selectValue [ 0 ] ) : '' ; return < input name = { name } type = "hidden" value = { value } / > ; } } renderLiveRegion ( ) { const { commonProps } = this ; const { ariaSelection , focusedOption , focusedValue , isFocused , selectValue , } = this . state ; const focusableOptions = this . getFocusableOptions ( ) ; return ( < LiveRegion { ... commonProps } id = { this . getElementId ( 'live-region' ) } ariaSelection = { ariaSelection } focusedOption = { focusedOption } focusedValue = { focusedValue } isFocused = { isFocused } selectValue = { selectValue } focusableOptions = { focusableOptions } / > ) ; } render ( ) { const { Control , IndicatorsContainer , SelectContainer , ValueContainer } = this . getComponents ( ) ; const { className , id , isDisabled , menuIsOpen } = this . props ; const { isFocused } = this . state ; const commonProps = ( this . commonProps = this . getCommonProps ( ) ) ; return ( < SelectContainer { ... commonProps } className = { className } innerProps = { { id : id , onKeyDown : this . onKeyDown , } } isDisabled = { isDisabled } isFocused = { isFocused } > { this . renderLiveRegion ( ) } < Control { ... commonProps } innerRef = { this . getControlRef } innerProps = { { onMouseDown : this . onControlMouseDown , onTouchEnd : this . onControlTouchEnd , } } isDisabled = { isDisabled } isFocused = { isFocused } menuIsOpen = { menuIsOpen } > < ValueContainer { ... commonProps } isDisabled = { isDisabled } > { this . renderPlaceholderOrValue ( ) } { this . renderInput ( ) } < /ValueContainer> < IndicatorsContainer { ... commonProps } isDisabled = { isDisabled } > { this . renderClearIndicator ( ) } { this . renderLoadingIndicator ( ) } { this . renderIndicatorSeparator ( ) } { this . renderDropdownIndicator ( ) } < /IndicatorsContainer> < /Control> { this . renderMenu ( ) } { this . renderFormField ( ) } < /SelectContainer>); } } export type PublicBaseSelectProps < Option , IsMulti extends boolean , Group extends GroupBase < Option > > = JSX . LibraryManagedAttributes < typeof Select , Props < Option , IsMulti , Group > > ;