hunk.code_window: export type Props = {
     /* Aria label (for assistive tech) */
     'aria-label'?: string,
     /* HTML ID of an element that should be used as the label (for assistive tech) */
     'aria-labelledby'?: string,
     ariaLiveMessages?: AriaLiveMessagesProps,
     /* 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,
  
hunk.labels: keep keep keep keep add keep keep keep keep keep
hunk.after_edit:   /* 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'?: AriaLiveProp,
     /* Customize the messages used by the aria-live component */
  
hunk.file_path: packages/react-select/src/Select.js
hunk.type: add
file: // @flow
  
  import React, { Component, type ElementRef, type Node } from 'react';
  import { MenuPlacer } from './components/Menu';
  import LiveRegion from './components/LiveRegion';
  
  import { createFilter } from './filters';
  import { DummyInput, ScrollManager } from './internal/index';
  import {
    type AriaLiveMessagesProps,
    type AriaSelectionType,
  } from './accessibility/index';
  
  import {
    classNames,
    cleanValue,
    isTouchCapable,
    isMobileDevice,
    noop,
    scrollIntoView,
    isDocumentElement,
  } from './utils';
  
  import {
    formatGroupLabel as formatGroupLabelBuiltin,
    getOptionLabel as getOptionLabelBuiltin,
    getOptionValue as getOptionValueBuiltin,
    isOptionDisabled as isOptionDisabledBuiltin,
  } from './builtins';
  
  import {
    defaultComponents,
    type PlaceholderOrValue,
    type SelectComponentsConfig,
  } from './components/index';
  
  import { defaultStyles, type StylesConfig } from './styles';
  import { defaultTheme, type ThemeConfig } from './theme';
  
  import type {
    ActionMeta,
    ActionTypes,
    FocusDirection,
    FocusEventHandler,
    GroupType,
    InputActionMeta,
    KeyboardEventHandler,
    MenuPlacement,
    MenuPosition,
    OptionsType,
    OptionType,
    ValueType,
  } from './types';
  
  type MouseOrTouchEvent =
    | SyntheticMouseEvent<HTMLElement>
    | SyntheticTouchEvent<HTMLElement>;
  type FormatOptionLabelContext = 'menu' | 'value';
  type FormatOptionLabelMeta = {
    context: FormatOptionLabelContext,
    inputValue: string,
    selectValue: ValueType,
  };
  
  export type Props = {
    /* Aria label (for assistive tech) */
    'aria-label'?: string,
    /* HTML ID of an element that should be used as the label (for assistive tech) */
    'aria-labelledby'?: string,
    ariaLiveMessages?: AriaLiveMessagesProps,
    /* 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 | EventListener,
    /*
      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,
    /* 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:
      | (({ label: string, value: string, data: OptionType }, 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: typeof formatGroupLabelBuiltin,
    /* Formats option labels in the menu and control as React components */
    formatOptionLabel?: (OptionType, FormatOptionLabelMeta) => Node,
    /* Resolves option data to a string to be displayed as the label by components */
    getOptionLabel: typeof getOptionLabelBuiltin,
    /* Resolves option data to a string to compare options and specify value attributes */
    getOptionValue: typeof getOptionValueBuiltin,
    /* 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: (OptionType, OptionsType) => boolean | false,
    /* Override the built-in logic to detect whether an option is selected */
    isOptionSelected?: (OptionType, OptionsType) => boolean,
    /* Support multiple selected options */
    isMulti: boolean,
    /* Is the select direction right-to-left */
    isRtl: boolean,
    /* Whether to enable search functionality */
    isSearchable: boolean,
    /* Async: Text to display when loading options */
    loadingMessage: ({ inputValue: string }) => string | null,
    /* 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,
    /* 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: ({ inputValue: string }) => Node | null,
    /* Handle blur events on the control */
    onBlur?: FocusEventHandler,
    /* Handle change events on the select */
    onChange: (ValueType, ActionMeta) => void,
    /* Handle focus events on the control */
    onFocus?: FocusEventHandler,
    /* Handle change events on the input */
    onInputChange: (string, InputActionMeta) => void,
    /* Handle key down events on the select */
    onKeyDown?: KeyboardEventHandler,
    /* Handle the menu opening */
    onMenuOpen: () => void,
    /* Handle the menu closing */
    onMenuClose: () => void,
    /* Fired when the user scrolls to the top of the menu */
    onMenuScrollToTop?: (SyntheticEvent<HTMLElement>) => void,
    /* Fired when the user scrolls to the bottom of the menu */
    onMenuScrollToBottom?: (SyntheticEvent<HTMLElement>) => 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: OptionsType,
    /* Number of options to jump in menu when page{up|down} keys are used */
    pageSize: number,
    /* Placeholder for the select value */
    placeholder: Node,
    /* Status to relay to screen readers */
    screenReaderStatus: ({ 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,
    /* Theme modifier method */
    theme?: ThemeConfig,
    /* Sets the tabIndex attribute on the input */
    tabIndex: string,
    /* Select the currently focused option when the user presses tab */
    tabSelectsValue: boolean,
    /* The value of the select; reflected by the selected option */
    value: ValueType,
    /* Sets the form attribute on the input */
    form?: string,
  };
  
  export const defaultProps = {
    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: number }) =>
      `${count} result${count !== 1 ? 's' : ''} available`,
    styles: {},
    tabIndex: '0',
    tabSelectsValue: true,
  };
  
  type State = {
    ariaSelection: AriaSelectionType | null,
    inputIsHidden: boolean,
    isFocused: boolean,
    focusedOption: OptionType | null,
    focusedValue: OptionType | null,
    selectValue: OptionsType,
    clearFocusValueOnUpdate: boolean,
    inputIsHiddenAfterUpdate: ?boolean,
    prevProps: Props | void,
  };
  
  type ElRef = ElementRef<*>;
  
  type CategorizedOption = {
    type: 'option',
    data: OptionType,
    isDisabled: boolean,
    isSelected: boolean,
    label: string,
    value: string,
    index: number,
  };
  
  type CategorizedGroup = {
    type: 'group',
    data: GroupType,
    options: OptionsType,
    index: number,
  };
  
  type CategorizedGroupOrOption = CategorizedGroup | CategorizedOption;
  
  function toCategorizedOption(
    props: Props,
    option: OptionType,
    selectValue: OptionsType,
    index: number
  ) {
    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(
    props: Props,
    selectValue: OptionsType
  ): CategorizedGroupOrOption[] {
    return (props.options
      .map((groupOrOption, groupOrOptionIndex) => {
        if (groupOrOption.options) {
          const categorizedOptions = groupOrOption.options
            .map((option, optionIndex) =>
              toCategorizedOption(props, option, selectValue, optionIndex)
            )
            .filter(categorizedOption => isFocusable(props, categorizedOption));
          return categorizedOptions.length > 0
            ? {
                type: 'group',
                data: groupOrOption,
                options: categorizedOptions,
                index: groupOrOptionIndex,
              }
            : undefined;
        }
        const categorizedOption = toCategorizedOption(
          props,
          groupOrOption,
          selectValue,
          groupOrOptionIndex
        );
        return isFocusable(props, categorizedOption)
          ? categorizedOption
          : undefined;
      })
      // Flow limitation (see https://github.com/facebook/flow/issues/1414)
      .filter(categorizedOption => !!categorizedOption): any[]);
  }
  
  function buildFocusableOptionsFromCategorizedOptions(
    categorizedOptions: CategorizedGroupOrOption[]
  ) {
    return categorizedOptions.reduce((optionsAccumulator, categorizedOption) => {
      if (categorizedOption.type === 'group') {
        optionsAccumulator.push(...categorizedOption.data.options);
      } else {
        optionsAccumulator.push(categorizedOption.data);
      }
      return optionsAccumulator;
    }, []);
  }
  
  function buildFocusableOptions(props: Props, selectValue: OptionsType) {
    return buildFocusableOptionsFromCategorizedOptions(
      buildCategorizedOptions(props, selectValue)
    );
  }
  
  function isFocusable(props: Props, categorizedOption: CategorizedOption) {
    const { inputValue = '' } = props;
    const { data, isSelected, label, value } = categorizedOption;
  
    return (
      (!shouldHideSelectedOptions(props) || !isSelected) &&
      filterOption(props, { label, value, data }, inputValue)
    );
  }
  
  function getNextFocusedValue(state: State, nextSelectValue: OptionsType) {
    const { focusedValue, selectValue: lastSelectValue } = state;
    const lastFocusedIndex = lastSelectValue.indexOf(focusedValue);
    if (lastFocusedIndex > -1) {
      const nextFocusedIndex = nextSelectValue.indexOf(focusedValue);
      if (nextFocusedIndex > -1) {
        // the focused value is still in the selectValue, return it
        return focusedValue;
      } else if (lastFocusedIndex < nextSelectValue.length) {
        // the focusedValue is not present in the next selectValue array by
        // reference, so return the new value at the same index
        return nextSelectValue[lastFocusedIndex];
      }
    }
    return null;
  }
  
  function getNextFocusedOption(state: State, options: OptionsType) {
    const { focusedOption: lastFocusedOption } = state;
    return lastFocusedOption && options.indexOf(lastFocusedOption) > -1
      ? lastFocusedOption
      : options[0];
  }
  const getOptionLabel = (props: Props, data: OptionType): string => {
    return props.getOptionLabel(data);
  };
  const getOptionValue = (props: Props, data: OptionType): string => {
    return props.getOptionValue(data);
  };
  
  function isOptionDisabled(
    props: Props,
    option: OptionType,
    selectValue: OptionsType
  ): boolean {
    return typeof props.isOptionDisabled === 'function'
      ? props.isOptionDisabled(option, selectValue)
      : false;
  }
  function isOptionSelected(
    props: Props,
    option: OptionType,
    selectValue: OptionsType
  ): boolean {
    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(
    props: Props,
    option: { label: string, value: string, data: OptionType },
    inputValue: string
  ) {
    return props.filterOption ? props.filterOption(option, inputValue) : true;
  }
  
  const shouldHideSelectedOptions = (props: Props) => {
    const { hideSelectedOptions, isMulti } = props;
    if (hideSelectedOptions === undefined) return isMulti;
    return hideSelectedOptions;
  };
  
  let instanceId = 1;
  
  export default class Select extends Component<Props, State> {
    static defaultProps = defaultProps;
    state = {
      ariaSelection: null,
      focusedOption: null,
      focusedValue: null,
      inputIsHidden: false,
      isFocused: false,
      selectValue: [],
      clearFocusValueOnUpdate: false,
      inputIsHiddenAfterUpdate: undefined,
      prevProps: undefined,
    };
  
    // Misc. Instance Properties
    // ------------------------------
  
    blockOptionHover: boolean = false;
    isComposing: boolean = false;
    commonProps: any; // TODO
    initialTouchX: number = 0;
    initialTouchY: number = 0;
    instancePrefix: string = '';
    openAfterFocus: boolean = false;
    scrollToFocusedOptionOnUpdate: boolean = false;
    userIsDragging: ?boolean;
  
    // Refs
    // ------------------------------
  
    controlRef: ElRef = null;
    getControlRef = (ref: ?HTMLElement) => {
      this.controlRef = ref;
    };
    focusedOptionRef: ElRef = null;
    getFocusedOptionRef = (ref: ?HTMLElement) => {
      this.focusedOptionRef = ref;
    };
    menuListRef: ElRef = null;
    getMenuListRef = (ref: ?HTMLElement) => {
      this.menuListRef = ref;
    };
    inputRef: ElRef = null;
    getInputRef = (ref: ?HTMLElement) => {
      this.inputRef = ref;
    };
  
    // Lifecycle
    // ------------------------------
  
    constructor(props: Props) {
      super(props);
      this.instancePrefix =
        'react-select-' + (this.props.instanceId || ++instanceId);
      this.state.selectValue = cleanValue(props.value);
    }
    static getDerivedStateFromProps(props: Props, state: State) {
      const {
        prevProps,
        clearFocusValueOnUpdate,
        inputIsHiddenAfterUpdate,
      } = state;
      const { options, value, menuIsOpen, inputValue } = props;
      let newMenuOptionsState = {};
      if (
        prevProps &&
        (value !== prevProps.value ||
          options !== prevProps.options ||
          menuIsOpen !== prevProps.menuIsOpen ||
          inputValue !== prevProps.inputValue)
      ) {
        const selectValue = cleanValue(value);
        const focusableOptions = menuIsOpen
          ? buildFocusableOptions(props, selectValue)
          : [];
        const focusedValue = clearFocusValueOnUpdate
          ? getNextFocusedValue(state, selectValue)
          : null;
        const focusedOption = getNextFocusedOption(state, focusableOptions);
        newMenuOptionsState = {
          selectValue,
          focusedOption,
          focusedValue,
          clearFocusValueOnUpdate: false,
        };
      }
      // some updates should toggle the state of the input visibility
      const newInputIsHiddenState =
        inputIsHiddenAfterUpdate != null && props !== prevProps
          ? {
              inputIsHidden: inputIsHiddenAfterUpdate,
              inputIsHiddenAfterUpdate: undefined,
            }
          : {};
      return {
        ...newMenuOptionsState,
        ...newInputIsHiddenState,
        prevProps: props,
      };
    }
    componentDidMount() {
      this.startListeningComposition();
      this.startListeningToTouch();
  
      if (this.props.closeMenuOnScroll && document && document.addEventListener) {
        // Listen to all scroll events, and filter them out inside of 'onScroll'
        document.addEventListener('scroll', this.onScroll, true);
      }
  
      if (this.props.autoFocus) {
        this.focusInput();
      }
    }
    componentDidUpdate(prevProps: Props) {
      const { isDisabled, menuIsOpen } = this.props;
      const { isFocused } = this.state;
  
      if (
        // ensure focus is restored correctly when the control becomes enabled
        (isFocused && !isDisabled && prevProps.isDisabled) ||
        // ensure focus is on the Input when the menu opens
        (isFocused && menuIsOpen && !prevProps.menuIsOpen)
      ) {
        this.focusInput();
      }
  
      if (isFocused && isDisabled && !prevProps.isDisabled) {
        // ensure select state gets blurred in case Select is programatically disabled while focused
        this.setState({ isFocused: false }, this.onMenuClose);
      }
  
      // scroll the focused option into view if necessary
      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);
    }
  
    // ==============================
    // Consumer Handlers
    // ==============================
  
    onMenuOpen() {
      this.props.onMenuOpen();
    }
    onMenuClose() {
      this.onInputChange('', { action: 'menu-close' });
      this.props.onMenuClose();
    }
    onInputChange(newValue: string, actionMeta: InputActionMeta) {
      this.props.onInputChange(newValue, actionMeta);
    }
  
    // ==============================
    // Methods
    // ==============================
  
    focusInput() {
      if (!this.inputRef) return;
      this.inputRef.focus();
    }
    blurInput() {
      if (!this.inputRef) return;
      this.inputRef.blur();
    }
  
    // aliased for consumers
    focus = this.focusInput;
    blur = this.blurInput;
  
    openMenu(focusOption: 'first' | 'last') {
      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;
        }
      }
  
      // only scroll if the menu isn't already open
      this.scrollToFocusedOptionOnUpdate = !(isFocused && this.menuListRef);
  
      this.setState(
        {
          inputIsHiddenAfterUpdate: false,
          focusedValue: null,
          focusedOption: focusableOptions[openAtIndex],
        },
        () => this.onMenuOpen()
      );
    }
  
    focusValue(direction: 'previous' | 'next') {
      const { selectValue, focusedValue } = this.state;
  
      // Only multiselects support value focusing
      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) {
            // don't cycle from the start to the end
            nextFocus = 0;
          } else if (focusedIndex === -1) {
            // if nothing is focused, focus the last value first
            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: FocusDirection = 'first') {
      const { pageSize } = this.props;
      const { focusedOption } = this.state;
      const options = this.getFocusableOptions();
  
      if (!options.length) return;
      let nextFocus = 0; // handles 'first'
      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: ValueType, actionMeta: ActionMeta) => {
      const { onChange, name } = this.props;
      const context = { ...actionMeta, name };
  
      this.ariaOnChange(newValue, context);
      onChange(newValue, context);
    };
    setValue = (
      newValue: ValueType,
      action: ActionTypes = 'set-value',
      option?: OptionType
    ) => {
      const { closeMenuOnSelect, isMulti } = this.props;
      this.onInputChange('', { action: 'set-value' });
      if (closeMenuOnSelect) {
        this.setState({ inputIsHiddenAfterUpdate: !isMulti });
        this.onMenuClose();
      }
      // when the select value should change, we should reset focusedValue
      this.setState({ clearFocusValueOnUpdate: true });
      this.onChange(newValue, { action, option });
    };
    selectOption = (newValue: OptionType) => {
      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(
          selectValue.filter(i => this.getOptionValue(i) !== candidate),
          'deselect-option',
          newValue
        );
      } else if (!isDisabled) {
        // Select option if option is not disabled
        if (isMulti) {
          this.setValue([...selectValue, newValue], 'select-option', newValue);
        } else {
          this.setValue(newValue, 'select-option');
        }
      } else {
        this.ariaOnChange(selectValue, {
          action: 'select-option',
          option: newValue,
          name,
          isDisabled,
        });
        return;
      }
  
      if (blurInputOnSelect) {
        this.blurInput();
      }
    };
    removeValue = (removedValue: OptionType) => {
      const { isMulti } = this.props;
      const { selectValue } = this.state;
      const candidate = this.getOptionValue(removedValue);
      const newValueArray = selectValue.filter(
        i => this.getOptionValue(i) !== candidate
      );
      const newValue = isMulti ? newValueArray : newValueArray[0] || null;
  
      this.onChange(newValue, { action: 'remove-value', removedValue });
      this.focusInput();
    };
    clearValue = () => {
      this.onChange(this.props.isMulti ? [] : null, { action: 'clear' });
    };
    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 = isMulti ? newValueArray : newValueArray[0] || null;
  
      this.onChange(newValue, {
        action: 'pop-value',
        removedValue: lastSelectedValue,
      });
    };
  
    // ==============================
    // Getters
    // ==============================
  
    getTheme() {
      // Use the default theme if there are no customizations.
      if (!this.props.theme) {
        return defaultTheme;
      }
      // If the theme prop is a function, assume the function
      // knows how to merge the passed-in default theme with
      // its own modifications.
      if (typeof this.props.theme === 'function') {
        return this.props.theme(defaultTheme);
      }
      // Otherwise, if a plain theme object was passed in,
      // overlay it with the default theme.
      return {
        ...defaultTheme,
        ...this.props.theme,
      };
    }
  
    getValue = () => this.state.selectValue;
  
    cx = (...args: any) => classNames(this.props.classNamePrefix, ...args);
  
    getCommonProps() {
      const {
        clearValue,
        cx,
        getStyles,
        getValue,
        setValue,
        selectOption,
        props,
      } = this;
      const { isMulti, isRtl, options } = props;
      const hasValue = this.hasValue();
  
      return {
        cx,
        clearValue,
        getStyles,
        getValue,
        hasValue,
        isMulti,
        isRtl,
        options,
        selectOption,
        setValue,
        selectProps: props,
        theme: this.getTheme(),
      };
    }
  
    getOptionLabel = (data: OptionType): string => {
      return getOptionLabel(this.props, data);
    };
    getOptionValue = (data: OptionType): string => {
      return getOptionValue(this.props, data);
    };
    getStyles = (key: string, props: {}): {} => {
      const base = defaultStyles[key](props);
      base.boxSizing = 'border-box';
      const custom = this.props.styles[key];
      return custom ? custom(base, props) : base;
    };
    getElementId = (element: 'group' | 'input' | 'listbox' | 'option') => {
      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() : [];
  
    // ==============================
    // Helpers
    // ==============================
  
    ariaOnChange = (value: ValueType, actionMeta: ActionMeta) => {
      const { option, removedValue } = actionMeta;
      const asOption = val => (!Array.isArray(val) ? val : null);
      const selected = removedValue || option || asOption(value);
  
      const label = selected ? this.getOptionLabel(selected) : '';
      const ariaSelection = { value, selected, label, ...actionMeta };
  
      this.setState({ ariaSelection });
    };
  
    hasValue() {
      const { selectValue } = this.state;
      return selectValue.length > 0;
    }
    hasOptions() {
      return !!this.getFocusableOptions().length;
    }
    isClearable(): boolean {
      const { isClearable, isMulti } = this.props;
  
      // single select, by default, IS NOT clearable
      // multi select, by default, IS clearable
      if (isClearable === undefined) return isMulti;
  
      return isClearable;
    }
    isOptionDisabled(option: OptionType, selectValue: OptionsType): boolean {
      return isOptionDisabled(this.props, option, selectValue);
    }
    isOptionSelected(option: OptionType, selectValue: OptionsType): boolean {
      return isOptionSelected(this.props, option, selectValue);
    }
    filterOption(
      option: { label: string, value: string, data: OptionType },
      inputValue: string
    ) {
      return filterOption(this.props, option, inputValue);
    }
    formatOptionLabel(data: OptionType, context: FormatOptionLabelContext): Node {
      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: GroupType) {
      return this.props.formatGroupLabel(data);
    }
  
    // ==============================
    // Mouse Handlers
    // ==============================
  
    onMenuMouseDown = (event: SyntheticMouseEvent<HTMLElement>) => {
      if (event.button !== 0) {
        return;
      }
      event.stopPropagation();
      event.preventDefault();
      this.focusInput();
    };
    onMenuMouseMove = (event: SyntheticMouseEvent<HTMLElement>) => {
      this.blockOptionHover = false;
    };
    onControlMouseDown = (event: MouseOrTouchEvent) => {
      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 (
          // $FlowFixMe
          event.target.tagName !== 'INPUT' &&
          event.target.tagName !== 'TEXTAREA'
        ) {
          this.onMenuClose();
        }
      }
      if (
        // $FlowFixMe
        event.target.tagName !== 'INPUT' &&
        event.target.tagName !== 'TEXTAREA'
      ) {
        event.preventDefault();
      }
    };
    onDropdownIndicatorMouseDown = (event: MouseOrTouchEvent) => {
      // ignore mouse events that weren't triggered by the primary button
      if (event && event.type === 'mousedown' && event.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: MouseOrTouchEvent) => {
      // ignore mouse events that weren't triggered by the primary button
      if (event && event.type === 'mousedown' && event.button !== 0) {
        return;
      }
      this.clearValue();
      event.stopPropagation();
      this.openAfterFocus = false;
      if (event.type === 'touchend') {
        this.focusInput();
      } else {
        setTimeout(() => this.focusInput());
      }
    };
    onScroll = (event: 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();
        }
      }
    };
  
    // ==============================
    // Composition Handlers
    // ==============================
  
    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;
    };
  
    // ==============================
    // Touch Handlers
    // ==============================
  
    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 }: TouchEvent) => {
      const touch = touches && touches.item(0);
      if (!touch) {
        return;
      }
  
      this.initialTouchX = touch.clientX;
      this.initialTouchY = touch.clientY;
      this.userIsDragging = false;
    };
    onTouchMove = ({ touches }: TouchEvent) => {
      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: TouchEvent) => {
      if (this.userIsDragging) return;
  
      // close the menu if the user taps outside
      // we're checking on event.target here instead of event.currentTarget, because we want to assert information
      // on events on child elements, not the document (which we've attached this handler to).
      if (
        this.controlRef &&
        !this.controlRef.contains(event.target) &&
        this.menuListRef &&
        !this.menuListRef.contains(event.target)
      ) {
        this.blurInput();
      }
  
      // reset move vars
      this.initialTouchX = 0;
      this.initialTouchY = 0;
    };
    onControlTouchEnd = (event: SyntheticTouchEvent<HTMLElement>) => {
      if (this.userIsDragging) return;
      this.onControlMouseDown(event);
    };
    onClearIndicatorTouchEnd = (event: SyntheticTouchEvent<HTMLElement>) => {
      if (this.userIsDragging) return;
  
      this.onClearIndicatorMouseDown(event);
    };
    onDropdownIndicatorTouchEnd = (event: SyntheticTouchEvent<HTMLElement>) => {
      if (this.userIsDragging) return;
  
      this.onDropdownIndicatorMouseDown(event);
    };
  
    // ==============================
    // Focus Handlers
    // ==============================
  
    handleInputChange = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
      const inputValue = event.currentTarget.value;
      this.setState({ inputIsHiddenAfterUpdate: false });
      this.onInputChange(inputValue, { action: 'input-change' });
      if (!this.props.menuIsOpen) {
        this.onMenuOpen();
      }
    };
    onInputFocus = (event: SyntheticFocusEvent<HTMLInputElement>) => {
      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 = (event: SyntheticFocusEvent<HTMLInputElement>) => {
      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' });
      this.onMenuClose();
      this.setState({
        focusedValue: null,
        isFocused: false,
      });
    };
    onOptionHover = (focusedOption: OptionType) => {
      if (this.blockOptionHover || this.state.focusedOption === focusedOption) {
        return;
      }
      this.setState({ focusedOption });
    };
    shouldHideSelectedOptions = () => {
      return shouldHideSelectedOptions(this.props);
    };
  
    // ==============================
    // Keyboard Handlers
    // ==============================
  
    onKeyDown = (event: SyntheticKeyboardEvent<HTMLElement>) => {
      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;
        }
      }
  
      // Block option hover events when the user has just pressed a key
      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 ||
            // don't capture the event if the menu opens on focus and the focused
            // option is already selected; it breaks the flow of navigation
            (openMenuOnFocus && this.isOptionSelected(focusedOption, selectValue))
          ) {
            return;
          }
          this.selectOption(focusedOption);
          break;
        case 'Enter':
          if (event.keyCode === 229) {
            // ignore the keydown event from an Input Method Editor(IME)
            // ref. https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
            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' });
            this.onMenuClose();
          } else if (isClearable && escapeClearsValue) {
            this.clearValue();
          }
          break;
        case ' ': // space
          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();
    };
  
    // ==============================
    // Renderers
    // ==============================
    renderInput() {
      const {
        isDisabled,
        isSearchable,
        inputId,
        inputValue,
        tabIndex,
        form,
      } = this.props;
      const { Input } = this.getComponents();
      const { inputIsHidden } = this.state;
  
      const id = inputId || this.getElementId('input');
  
      // aria attributes makes the JSX "noisy", separated for clarity
      const ariaAttributes = {
        'aria-autocomplete': 'list',
        'aria-label': this.props['aria-label'],
        'aria-labelledby': this.props['aria-labelledby'],
      };
  
      if (!isSearchable) {
        // use a dummy input to maintain focus/blur functionality
        return (
          <DummyInput
            id={id}
            innerRef={this.getInputRef}
            onBlur={this.onInputBlur}
            onChange={noop}
            onFocus={this.onInputFocus}
            readOnly
            disabled={isDisabled}
            tabIndex={tabIndex}
            form={form}
            value=""
            {...ariaAttributes}
          />
        );
      }
  
      const { cx, theme, selectProps } = this.commonProps;
  
      return (
        <Input
          autoCapitalize="none"
          autoComplete="off"
          autoCorrect="off"
          cx={cx}
          getStyles={this.getStyles}
          id={id}
          innerRef={this.getInputRef}
          isDisabled={isDisabled}
          isHidden={inputIsHidden}
          onBlur={this.onInputBlur}
          onChange={this.handleInputChange}
          onFocus={this.onInputFocus}
          selectProps={selectProps}
          spellCheck="false"
          tabIndex={tabIndex}
          form={form}
          theme={theme}
          type="text"
          value={inputValue}
          {...ariaAttributes}
        />
      );
    }
    renderPlaceholderOrValue(): ?PlaceholderOrValue {
      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}
          >
            {placeholder}
          </Placeholder>
        );
      }
  
      if (isMulti) {
        const selectValues: Array<any> = selectValue.map((opt, index) => {
          const isOptionFocused = opt === focusedValue;
  
          return (
            <MultiValue
              {...commonProps}
              components={{
                Container: MultiValueContainer,
                Label: MultiValueLabel,
                Remove: MultiValueRemove,
              }}
              isFocused={isOptionFocused}
              isDisabled={isDisabled}
              key={`${this.getOptionValue(opt)}${index}`}
              index={index}
              removeProps={{
                onClick: () => this.removeValue(opt),
                onTouchEnd: () => this.removeValue(opt),
                onMouseDown: e => {
                  e.preventDefault();
                  e.stopPropagation();
                },
              }}
              data={opt}
            >
              {this.formatOptionLabel(opt, 'value')}
            </MultiValue>
          );
        });
        return selectValues;
      }
  
      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();
  
      // separator doesn't make sense without the dropdown indicator
      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;
  
      // TODO: Internal Option Type here
      const render = (props: OptionType, id: string) => {
        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,
              }}
              isLoading={isLoading}
              placement={placement}
            >
              <ScrollManager
                captureEnabled={captureMenuScroll}
                onTopArrive={onMenuScrollToTop}
                onBottomArrive={onMenuScrollToBottom}
                lockEnabled={menuShouldBlockScroll}
              >
                {scrollTargetRef => (
                  <MenuList
                    {...commonProps}
                    innerRef={(instance: HTMLElement | null): void => {
                      this.getMenuListRef(instance);
                      scrollTargetRef(instance);
                    }}
                    isLoading={isLoading}
                    maxHeight={maxHeight}
                  >
                    {menuUI}
                  </MenuList>
                )}
              </ScrollManager>
            </Menu>
          )}
        </MenuPlacer>
      );
  
      // positioning behaviour is almost identical for portalled and fixed,
      // so we use the same component. the actual portalling logic is forked
      // within the component based on `menuPosition`
      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 { inputValue, menuIsOpen, options } = this.props;
  
      const focusableOptions = menuIsOpen ? this.getFocusableOptions() : [];
  
      return (
        <LiveRegion
          {...commonProps}
          ariaSelection={ariaSelection}
          focusedOption={focusedOption}
          focusedValue={focusedValue}
          isFocused={isFocused}
          selectValue={selectValue}
          focusableOptions={focusableOptions}
          inputValue={inputValue}
          menuIsOpen={menuIsOpen}
          options={options}
        />
      );
    }
  
    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>
      );
    }
  }
  