UNPKG

@zendeskgarden/container-combobox

Version:

Containers relating to Combobox in the Garden Design System

754 lines (747 loc) 28.4 kB
/** * Copyright Zendesk, Inc. * * Use of this source code is governed under the Apache License, Version 2.0 * found at http://www.apache.org/licenses/LICENSE-2.0. */ 'use strict'; var React = require('react'); var containerField = require('@zendeskgarden/container-field'); var containerUtilities = require('@zendeskgarden/container-utilities'); var downshift = require('downshift'); var PropTypes = require('prop-types'); const typeMap = { [downshift.useCombobox.stateChangeTypes.FunctionCloseMenu]: 'fn:setExpansion', [downshift.useCombobox.stateChangeTypes.FunctionOpenMenu]: 'fn:setExpansion', [downshift.useCombobox.stateChangeTypes.FunctionToggleMenu]: 'fn:setExpansion', [downshift.useCombobox.stateChangeTypes.FunctionReset]: 'fn:reset', [downshift.useCombobox.stateChangeTypes.FunctionSelectItem]: 'fn:setSelectionValue', [downshift.useCombobox.stateChangeTypes.FunctionSetHighlightedIndex]: 'fn:setActiveIndex', [downshift.useCombobox.stateChangeTypes.FunctionSetInputValue]: 'fn:setInputValue', [downshift.useCombobox.stateChangeTypes.InputBlur]: 'input:blur', [downshift.useCombobox.stateChangeTypes.InputChange]: 'input:change', [downshift.useCombobox.stateChangeTypes.InputClick]: 'input:click', [downshift.useCombobox.stateChangeTypes.InputKeyDownArrowDown]: `input:keyDown:${containerUtilities.KEYS.DOWN}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownArrowUp]: `input:keyDown:${containerUtilities.KEYS.UP}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownEnd]: `input:keyDown:${containerUtilities.KEYS.END}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownEnter]: `input:keyDown:${containerUtilities.KEYS.ENTER}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownEscape]: `input:keyDown:${containerUtilities.KEYS.ESCAPE}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownHome]: `input:keyDown:${containerUtilities.KEYS.HOME}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownPageDown]: `input:keyDown:${containerUtilities.KEYS.PAGE_DOWN}`, [downshift.useCombobox.stateChangeTypes.InputKeyDownPageUp]: `input:keyDown:${containerUtilities.KEYS.PAGE_UP}`, [downshift.useCombobox.stateChangeTypes.ItemClick]: 'option:click', [downshift.useCombobox.stateChangeTypes.ItemMouseMove]: 'option:mouseMove', [downshift.useCombobox.stateChangeTypes.MenuMouseLeave]: 'listbox:mouseLeave', [downshift.useCombobox.stateChangeTypes.ToggleButtonClick]: 'toggle:click' }; const toType = downshiftType => { return typeMap[downshiftType] || downshiftType; }; const toLabel = (labels, value) => { if (value === undefined) { return ''; } return labels[value]; }; const useCombobox = ({ idPrefix, triggerRef, inputRef, listboxRef, isAutocomplete, isMultiselectable, isEditable = true, disabled, hasHint, hasMessage, options = [], inputValue, selectionValue, isExpanded, defaultExpanded, initialExpanded, activeIndex, defaultActiveIndex, initialActiveIndex, onChange = () => undefined, environment }) => { const win = environment || window; const [triggerContainsInput, setTriggerContainsInput] = React.useState(); const [downshiftInputValue, setDownshiftInputValue] = React.useState(inputValue); const [matchValue, setMatchValue] = React.useState(''); const useInputValueRef = React.useRef(true); const matchTimeoutRef = React.useRef(); const previousStateRef = React.useRef(); const prefix = containerUtilities.useId(idPrefix); const idRef = React.useRef({ label: `${prefix}--label`, hint: `${prefix}--hint`, trigger: `${prefix}--trigger`, input: `${prefix}--input`, listbox: `${prefix}--listbox`, message: `${prefix}--message`, getOptionId: (index, isDisabled, isHidden) => `${prefix}--option${isDisabled ? '-disabled' : ''}${isHidden ? '-hidden' : ''}-${index}` }); const cache = React.useMemo(() => { const retVal = { values: [], labels: {}, selectedValues: [], disabledValues: [], hiddenValues: [] }; const setValues = option => { if (option.disabled || option.hidden) { if (option.disabled && !retVal.disabledValues.includes(option.value)) { retVal.disabledValues.push(option.value); } if (option.hidden && !retVal.hiddenValues.includes(option.value)) { retVal.hiddenValues.push(option.value); } } else { retVal.values.push(option.value); const disabledIndex = retVal.disabledValues.indexOf(option.value); if (disabledIndex !== -1) { retVal.disabledValues.splice(disabledIndex, 1); } const hiddenIndex = retVal.hiddenValues.indexOf(option.value); if (hiddenIndex !== -1) { retVal.hiddenValues.splice(hiddenIndex, 1); } } if (option.selected && !retVal.selectedValues.includes(option.value)) { retVal.selectedValues.push(option.value); } retVal.labels[option.value] = option.label || option.value; }; options.forEach(option => { if ('options' in option) { option.options.forEach(setValues); } else { setValues(option); } }); return retVal; }, [options]); const initialSelectionValue = isMultiselectable ? cache.selectedValues : cache.selectedValues[0]; const initialInputValue = isMultiselectable ? '' : toLabel(cache.labels, initialSelectionValue); const _defaultActiveIndex = React.useMemo(() => { if (defaultActiveIndex === undefined) { return isAutocomplete && isEditable ? 0 : undefined; } return defaultActiveIndex; }, [defaultActiveIndex, isAutocomplete, isEditable]); if (useInputValueRef.current && inputValue !== downshiftInputValue) { setDownshiftInputValue(inputValue); } else { useInputValueRef.current = true; } if (selectionValue === undefined || selectionValue === null) { if (!isMultiselectable && cache.selectedValues.length > 1) { throw new Error('Error: expected useCombobox `options` to have no more than one selected.'); } } if (selectionValue !== undefined && selectionValue !== null) { if (isMultiselectable && !Array.isArray(selectionValue)) { throw new Error('Error: expected multiselectable useCombobox `selectionValue` to be an array.'); } else if (!isMultiselectable && Array.isArray(selectionValue)) { throw new Error('Error: expected useCombobox `selectionValue` not to be an array.'); } } const handleDownshiftStateChange = React.useCallback(({ type, isOpen, selectedItem, inputValue: _inputValue, highlightedIndex }) => onChange({ type: toType(type), ...(isOpen !== undefined && { isExpanded: isOpen }), ...(selectedItem !== undefined && { selectionValue: selectedItem }), ...(_inputValue !== undefined && { inputValue: _inputValue }), ...(highlightedIndex !== undefined && { activeIndex: highlightedIndex }) }), [onChange]); const stateReducer = (state, { type, changes, altKey }) => { switch (type) { case downshift.useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem: return state; case downshift.useCombobox.stateChangeTypes.FunctionSetHighlightedIndex: if (previousStateRef.current?.altKey) { changes.highlightedIndex = -1; } break; case downshift.useCombobox.stateChangeTypes.FunctionCloseMenu: case downshift.useCombobox.stateChangeTypes.InputBlur: return { ...state, isOpen: type === downshift.useCombobox.stateChangeTypes.InputBlur && triggerContainsInput && isMultiselectable && state.isOpen || false }; case downshift.useCombobox.stateChangeTypes.InputClick: if (!isAutocomplete) { changes.isOpen = state.isOpen; } break; case downshift.useCombobox.stateChangeTypes.InputKeyDownArrowDown: case downshift.useCombobox.stateChangeTypes.FunctionOpenMenu: if (state.isOpen !== changes.isOpen && !altKey) { changes.highlightedIndex = 0; } break; case downshift.useCombobox.stateChangeTypes.InputKeyDownArrowUp: if (state.isOpen !== changes.isOpen) { changes.highlightedIndex = cache.values.length - 1; } break; case downshift.useCombobox.stateChangeTypes.InputKeyDownEnter: case downshift.useCombobox.stateChangeTypes.FunctionSelectItem: case downshift.useCombobox.stateChangeTypes.ItemClick: changes.highlightedIndex = state.highlightedIndex; if (isMultiselectable) { changes.isOpen = state.isOpen; changes.inputValue = ''; } break; case downshift.useCombobox.stateChangeTypes.InputKeyDownEscape: return { ...state, isOpen: false }; case downshift.useCombobox.stateChangeTypes.InputKeyDownPageDown: case downshift.useCombobox.stateChangeTypes.InputKeyDownPageUp: return state; } if (isMultiselectable && state.selectedItem !== changes.selectedItem) { if (state.selectedItem !== undefined && state.selectedItem !== null && changes.selectedItem !== undefined && changes.selectedItem !== null) { if (state.selectedItem.includes(changes.selectedItem)) { changes.selectedItem = state.selectedItem.filter(value => value !== changes.selectedItem); } else { changes.selectedItem = [...state.selectedItem, changes.selectedItem]; } } else if (changes.selectedItem !== undefined && changes.selectedItem !== null) { changes.selectedItem = [changes.selectedItem]; } else { changes.selectedItem = []; } } previousStateRef.current = { type, altKey, ...state }; return changes; }; const transformValue = value => value ? toLabel(cache.labels, value) : ''; const { selectedItem: _selectionValue, isOpen: _isExpanded, highlightedIndex: _activeIndex, inputValue: _inputValue, getToggleButtonProps: getDownshiftTriggerProps, getInputProps: getDownshiftInputProps, getMenuProps: getDownshiftListboxProps, getItemProps: getDownshiftOptionProps, closeMenu, openMenu, setHighlightedIndex, selectItem } = downshift.useCombobox({ toggleButtonId: idRef.current.trigger, menuId: idRef.current.listbox, getItemId: idRef.current.getOptionId, items: cache.values, inputValue: downshiftInputValue, initialInputValue, itemToString: transformValue , selectedItem: selectionValue, initialSelectedItem: initialSelectionValue, isOpen: isExpanded, defaultIsOpen: defaultExpanded, initialIsOpen: initialExpanded, highlightedIndex: activeIndex, defaultHighlightedIndex: _defaultActiveIndex, initialHighlightedIndex: initialActiveIndex, onStateChange: handleDownshiftStateChange, stateReducer, environment: win }); const closeListbox = React.useCallback(() => { closeMenu(); onChange({ type: toType(downshift.useCombobox.stateChangeTypes.FunctionCloseMenu), isExpanded: false }); }, [closeMenu, onChange]); const openListbox = React.useCallback(() => { openMenu(); onChange({ type: toType(downshift.useCombobox.stateChangeTypes.FunctionOpenMenu), isExpanded: true }); }, [openMenu, onChange]); const setActiveIndex = React.useCallback(index => { setHighlightedIndex(index); onChange({ type: toType(downshift.useCombobox.stateChangeTypes.FunctionSetHighlightedIndex), activeIndex: index }); }, [onChange, setHighlightedIndex]); const setDownshiftSelection = React.useCallback(value => { selectItem(value); onChange({ type: toType(downshift.useCombobox.stateChangeTypes.FunctionSelectItem), selectionValue: value }); }, [onChange, selectItem]); const { getLabelProps: getFieldLabelProps, getHintProps: getFieldHintProps, getInputProps: getFieldInputProps, getMessageProps: getFieldMessageProps } = containerField.useField({ hasHint, hasMessage }); React.useLayoutEffect(() => { if ((isAutocomplete || !isEditable) && _isExpanded && !previousStateRef.current?.isOpen && _selectionValue && !matchValue) { const value = Array.isArray(_selectionValue) ? _selectionValue[_selectionValue.length - 1 ] : _selectionValue; const index = cache.values.findIndex(current => current === value); if (index !== -1) { setActiveIndex(index); } else if (_defaultActiveIndex !== undefined) { setActiveIndex(_defaultActiveIndex); } } }, [ isAutocomplete, isEditable, _isExpanded, _selectionValue, _inputValue, cache.values, _defaultActiveIndex, setActiveIndex]); React.useEffect( () => setTriggerContainsInput(triggerRef.current?.contains(inputRef.current)), [triggerRef, inputRef]); React.useEffect(() => { clearTimeout(matchTimeoutRef.current); matchTimeoutRef.current = window.setTimeout(() => setMatchValue(''), 500); return () => clearTimeout(matchTimeoutRef.current); }, [matchValue]); React.useEffect(() => { if (previousStateRef.current?.type === downshift.useCombobox.stateChangeTypes.FunctionSelectItem) { if (isEditable) { inputRef.current?.focus(); } else { triggerRef.current?.focus(); } previousStateRef.current = { ...previousStateRef.current, type: downshift.useCombobox.stateChangeTypes.InputClick }; } }); React.useEffect(() => { if (isEditable && inputRef.current === win.document.activeElement) { inputRef.current?.scrollIntoView && inputRef.current?.scrollIntoView({ block: 'nearest' }); } }, [inputRef, isEditable, win.document.activeElement]); const getTriggerProps = React.useCallback(({ onBlur, onClick, onKeyDown, ...other } = {}) => { const triggerProps = getDownshiftTriggerProps({ 'data-garden-container-id': 'containers.combobox', 'data-garden-container-version': '2.0.8', onBlur, onClick, onKeyDown, ref: triggerRef, disabled, ...other }); const handleBlur = event => { if (event.relatedTarget === null || !event.currentTarget?.contains(event.relatedTarget)) { closeListbox(); } }; if (isEditable && triggerContainsInput) { const handleClick = event => { if (disabled) { event.preventDefault(); } else if (isAutocomplete) { triggerProps.onClick && triggerProps.onClick(event); } else { inputRef.current?.focus(); } }; return { ...triggerProps, onBlur: containerUtilities.composeEventHandlers(onBlur, handleBlur), onClick: containerUtilities.composeEventHandlers(onClick, handleClick), 'aria-controls': isAutocomplete ? triggerProps['aria-controls'] : undefined, 'aria-expanded': undefined, 'aria-disabled': disabled || undefined, disabled: undefined }; } else if (!isEditable) { const { 'aria-activedescendant': ariaActiveDescendant, onKeyDown: onDownshiftKeyDown } = getDownshiftInputProps({}, { suppressRefError: true }); const handleKeyDown = event => { event.stopPropagation(); if (!_isExpanded && (event.key === containerUtilities.KEYS.SPACE || event.key === containerUtilities.KEYS.ENTER)) { event.preventDefault(); openListbox(); } else if (_isExpanded && !matchValue && (event.key === containerUtilities.KEYS.SPACE || event.key === containerUtilities.KEYS.ENTER)) { event.preventDefault(); if (_activeIndex !== -1) { setDownshiftSelection(cache.values[_activeIndex]); } if (!isMultiselectable) { closeListbox(); } } else if (/^(?:\S| ){1}$/u.test(event.key)) { const _matchValue = `${matchValue}${event.key}`; setMatchValue(_matchValue); let offset = 0; if (_isExpanded) { if (_activeIndex !== -1) { offset = _matchValue.length === 1 ? _activeIndex + 1 : _activeIndex; } } else { openListbox(); const offsetValue = Array.isArray(_selectionValue) ? _selectionValue[_selectionValue.length - 1 ] : _selectionValue; if (offsetValue !== null) { offset = cache.values.findIndex(current => current === offsetValue); } } for (let index = 0; index < cache.values.length; index++) { const valueIndex = (index + offset) % cache.values.length; const value = cache.values[valueIndex]; if (toLabel(cache.labels, value).toLowerCase().startsWith(_matchValue.toLowerCase())) { setActiveIndex(valueIndex); break; } } } }; return { ...triggerProps, 'aria-activedescendant': ariaActiveDescendant, 'aria-haspopup': 'listbox', 'aria-labelledby': idRef.current.label, 'aria-disabled': disabled || undefined, disabled: undefined, role: 'combobox', onBlur: containerUtilities.composeEventHandlers(onBlur, handleBlur), onKeyDown: containerUtilities.composeEventHandlers(onKeyDown, onDownshiftKeyDown, handleKeyDown), tabIndex: disabled ? -1 : 0 }; } return triggerProps; }, [getDownshiftTriggerProps, getDownshiftInputProps, triggerRef, disabled, _selectionValue, _isExpanded, _activeIndex, closeListbox, openListbox, setActiveIndex, setDownshiftSelection, matchValue, cache.values, cache.labels, triggerContainsInput, isAutocomplete, isEditable, isMultiselectable, inputRef]); const getLabelProps = React.useCallback(({ onClick, ...other } = {}) => { const { htmlFor, ...labelProps } = getFieldLabelProps({ id: idRef.current.label, htmlFor: idRef.current.input, ...other }); const handleClick = () => !isEditable && triggerRef.current?.focus(); return { ...labelProps, onClick: containerUtilities.composeEventHandlers(onClick, handleClick), htmlFor: isEditable ? htmlFor : undefined }; }, [getFieldLabelProps, isEditable, triggerRef]); const getHintProps = React.useCallback(props => getFieldHintProps({ id: idRef.current.hint, ...props }), [getFieldHintProps]); const getInputProps = React.useCallback(({ role = isEditable ? 'combobox' : null, onChange: _onChange, onClick, onFocus, ...other } = {}) => { const inputProps = { 'data-garden-container-id': 'containers.combobox.input', 'data-garden-container-version': '2.0.8', ref: inputRef, role: role === null ? undefined : role, onChange: _onChange, onClick, onFocus }; if (isEditable) { const handleChange = event => { if (inputValue !== undefined) { setDownshiftInputValue(event.target.value); useInputValueRef.current = false; if (event.nativeEvent.isComposing) { handleDownshiftStateChange({ type: downshift.useCombobox.stateChangeTypes.InputChange, inputValue: event.target.value }); } } }; const handleClick = event => event.target instanceof Element && triggerRef.current?.contains(event.target) && event.stopPropagation(); const describedBy = []; if (hasHint) { describedBy.push(idRef.current.hint); } if (hasMessage) { describedBy.push(idRef.current.message); } return getDownshiftInputProps({ ...inputProps, disabled, role, 'aria-autocomplete': isAutocomplete ? 'list' : undefined, onChange: containerUtilities.composeEventHandlers(_onChange, handleChange), onClick: containerUtilities.composeEventHandlers(onClick, handleClick), ...getFieldInputProps({ id: idRef.current.input, 'aria-labelledby': idRef.current.label, 'aria-describedby': describedBy.length > 0 ? describedBy.join(' ') : undefined }), ...other }); } const downshiftInputProps = getDownshiftInputProps({ ...inputProps, disabled: true, 'aria-autocomplete': undefined, 'aria-activedescendant': undefined, 'aria-controls': undefined, 'aria-expanded': undefined, 'aria-hidden': true, 'aria-labelledby': undefined }); const handleFocus = () => { if (!isEditable) { triggerRef.current?.focus(); } }; return { ...downshiftInputProps, disabled, readOnly: true, tabIndex: -1, onFocus: containerUtilities.composeEventHandlers(onFocus, handleFocus), ...other }; }, [getDownshiftInputProps, getFieldInputProps, handleDownshiftStateChange, hasHint, hasMessage, inputValue, inputRef, triggerRef, disabled, isAutocomplete, isEditable]); const getTagProps = React.useCallback(({ option, onClick, onKeyDown, ...other }) => { const handleClick = event => event.target instanceof Element && triggerRef.current?.contains(event.target) && event.stopPropagation(); const handleKeyDown = event => { if (event.key === containerUtilities.KEYS.BACKSPACE || event.key === containerUtilities.KEYS.DELETE) { setDownshiftSelection(option.value); } else { const triggerContainsTag = event.target instanceof Element && triggerRef.current?.contains(event.target); if (triggerContainsTag && !isEditable) { event.stopPropagation(); } if (triggerContainsTag && (event.key === containerUtilities.KEYS.DOWN || event.key === containerUtilities.KEYS.UP || event.key === containerUtilities.KEYS.ESCAPE || !isEditable && (event.key === containerUtilities.KEYS.ENTER || event.key === containerUtilities.KEYS.SPACE))) { const inputProps = getDownshiftInputProps(); if (isEditable) { inputRef.current?.focus(); } else { event.preventDefault(); triggerRef.current?.focus(); } inputProps.onKeyDown && inputProps.onKeyDown(event); } } }; return { 'data-garden-container-id': 'containers.combobox.tag', 'data-garden-container-version': '2.0.8', onClick: containerUtilities.composeEventHandlers(onClick, handleClick), onKeyDown: containerUtilities.composeEventHandlers(onKeyDown, handleKeyDown), ...other }; }, [triggerRef, setDownshiftSelection, getDownshiftInputProps, isEditable, inputRef]); const getListboxProps = React.useCallback(({ role = 'listbox', ...other }) => getDownshiftListboxProps({ 'data-garden-container-id': 'containers.combobox.listbox', 'data-garden-container-version': '2.0.8', ref: listboxRef, role, 'aria-multiselectable': isMultiselectable ? true : undefined, ...other }), [getDownshiftListboxProps, listboxRef, isMultiselectable]); const getOptGroupProps = React.useCallback(({ role = 'group', ...other }) => ({ 'data-garden-container-id': 'containers.combobox.optgroup', 'data-garden-container-version': '2.0.8', role: role === null ? undefined : role, ...other }), []); const getOptionProps = React.useCallback(({ role = 'option', option, onMouseDown, ...other } = {}) => { const optionProps = { 'data-garden-container-id': 'containers.combobox.option', 'data-garden-container-version': '2.0.8', role, onMouseDown, ...other }; let ariaSelected = false; if (option?.value !== undefined) { ariaSelected = Array.isArray(_selectionValue) ? _selectionValue?.includes(option?.value) : _selectionValue === option?.value; } if (option?.hidden) { return { 'aria-disabled': option.disabled ? true : undefined, 'aria-hidden': true, 'aria-selected': ariaSelected, id: option ? idRef.current.getOptionId(cache.hiddenValues.indexOf(option.value), option.disabled, option.hidden) : undefined, ...optionProps }; } if (option === undefined || option.disabled) { const handleMouseDown = event => event.preventDefault(); return { 'aria-disabled': true, 'aria-selected': ariaSelected, id: option ? idRef.current.getOptionId(cache.disabledValues.indexOf(option.value), option.disabled, option.hidden) : undefined, ...optionProps, onMouseDown: containerUtilities.composeEventHandlers(onMouseDown, handleMouseDown) }; } return getDownshiftOptionProps({ item: option.value, index: cache.values.indexOf(option.value), 'aria-disabled': undefined, 'aria-hidden': undefined, 'aria-selected': ariaSelected, ...optionProps }); }, [getDownshiftOptionProps, cache.disabledValues, cache.hiddenValues, cache.values, _selectionValue]); const getMessageProps = React.useCallback(props => getFieldMessageProps({ id: idRef.current.message, ...props }), [getFieldMessageProps]); const removeSelection = React.useCallback(value => { if (value === undefined) { setDownshiftSelection(null); } else { const removeValue = typeof value === 'object' && 'value' in value ? value.value : value; if (Array.isArray(_selectionValue) && _selectionValue.includes(removeValue)) { setDownshiftSelection(removeValue); } else if (_selectionValue === removeValue) { setDownshiftSelection(null); } else if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') { console.warn(`Warning: useCombobox \`selection\` does not contain '${removeValue}' for removal.`); } } }, [_selectionValue, setDownshiftSelection]); const selection = React.useMemo(() => { if (Array.isArray(_selectionValue)) { return _selectionValue.map(value => ({ value, label: cache.labels[value], disabled: cache.disabledValues.includes(value), hidden: cache.hiddenValues.includes(value) })); } else if (_selectionValue) { return { value: _selectionValue, label: toLabel(cache.labels, _selectionValue), disabled: cache.disabledValues.includes(_selectionValue), hidden: cache.hiddenValues.includes(_selectionValue) }; } return null; }, [_selectionValue, cache.disabledValues, cache.hiddenValues, cache.labels]); return React.useMemo(() => ({ getLabelProps, getHintProps, getTriggerProps, getInputProps, getTagProps, getListboxProps, getOptGroupProps, getOptionProps, getMessageProps, selection, isExpanded: _isExpanded, activeValue: cache.values[_activeIndex], inputValue: _inputValue, removeSelection }), [cache.values, selection, _isExpanded, _activeIndex, _inputValue, getLabelProps, getHintProps, getTriggerProps, getInputProps, getTagProps, getListboxProps, getOptGroupProps, getOptionProps, getMessageProps, removeSelection]); }; const ComboboxContainer = props => { const { children, render = children, ...options } = props; return React.createElement(React.Fragment, null, render(useCombobox(options))); }; ComboboxContainer.propTypes = { children: PropTypes.func, render: PropTypes.func, idPrefix: PropTypes.string, triggerRef: PropTypes.any.isRequired, inputRef: PropTypes.any.isRequired, listboxRef: PropTypes.any.isRequired, isAutocomplete: PropTypes.bool, isMultiselectable: PropTypes.bool, isEditable: PropTypes.bool, disabled: PropTypes.bool, hasHint: PropTypes.bool, hasMessage: PropTypes.bool, options: PropTypes.arrayOf(PropTypes.any).isRequired, inputValue: PropTypes.string, selectionValue: PropTypes.any, isExpanded: PropTypes.bool, defaultExpanded: PropTypes.bool, initialExpanded: PropTypes.bool, activeIndex: PropTypes.number, defaultActiveIndex: PropTypes.number, initialActiveIndex: PropTypes.number, onChange: PropTypes.func, environment: PropTypes.any }; ComboboxContainer.defaultProps = { isEditable: true }; exports.ComboboxContainer = ComboboxContainer; exports.useCombobox = useCombobox;