@primer/react
Version:
An implementation of GitHub's Primer Design System using React
102 lines (96 loc) • 3.65 kB
JavaScript
import React__default, { useState, useCallback } from 'react';
import { ActionList } from '../../ActionList/index.js';
import { useCombobox } from '../hooks/useCombobox.js';
import { getSuggestionValue, getSuggestionKey } from './utils.js';
import Overlay from '../../Overlay/Overlay.js';
import Box from '../../Box/Box.js';
import Spinner from '../../Spinner/Spinner.js';
const LoadingIndicator = () => /*#__PURE__*/React__default.createElement(Box, {
sx: {
display: 'flex',
justifyContent: 'center',
py: 2
}
}, /*#__PURE__*/React__default.createElement(Spinner, {
size: "small"
}));
LoadingIndicator.displayName = "LoadingIndicator";
const SuggestionListItem = ({
suggestion
}) => {
const value = getSuggestionValue(suggestion);
const sharedProps = {
id: value,
children: value,
role: 'option',
sx: {
'&[aria-selected]': {
backgroundColor: 'actionListItem.default.activeBg'
},
'&[data-combobox-option-default]:not([aria-selected])': {
backgroundColor: 'actionListItem.default.selectedBg'
}
}
};
return typeof suggestion === 'string' ? /*#__PURE__*/React__default.createElement(ActionList.Item, sharedProps) : suggestion.render(sharedProps);
};
/**
* Renders an overlayed list at the given relative coordinates. Handles keyboard navigation
* and accessibility concerns.
*/
const AutocompleteSuggestions = ({
suggestions,
portalName,
top,
left,
onClose,
onCommit: externalOnCommit,
inputRef,
visible,
tabInsertsSuggestions
}) => {
// It seems wierd to use state instead of a ref here, but because the list is inside an
// AnchoredOverlay it is not always mounted - so we want to reinitialize the Combobox when it mounts
const [list, setList] = useState(null);
const onCommit = useCallback(({
option
}) => {
externalOnCommit(getSuggestionValue(option));
}, [externalOnCommit]);
// Setup keyboard navigation
useCombobox({
// Even though the list is visible when loading, we don't want to do keyboard binding in that case
isOpen: visible && suggestions !== 'loading',
listElement: list,
inputElement: inputRef.current,
onCommit,
options: Array.isArray(suggestions) ? suggestions : [],
tabInsertsSuggestions,
defaultFirstOption: true
});
// Conditional rendering appears wrong at first - it means that we are reconstructing the
// Combobox instance every time the suggestions appear. But this is what we want - otherwise
// the textarea would always have the `combobox` role, which is incorrect (a textarea should
// not technically ever be a combobox). We compromise by dynamically applying the combobox
// role only when suggestions are available.
return visible ? /*#__PURE__*/React__default.createElement(Overlay, {
onEscape: onClose,
onClickOutside: onClose,
returnFocusRef: inputRef,
preventFocusOnOpen: true,
portalContainerName: portalName,
sx: {
position: 'fixed'
},
top,
left
}, /*#__PURE__*/React__default.createElement(ActionList, {
ref: setList
}, suggestions === 'loading' ? /*#__PURE__*/React__default.createElement(LoadingIndicator, null) : suggestions === null || suggestions === void 0 ? void 0 : suggestions.map(suggestion => /*#__PURE__*/React__default.createElement(SuggestionListItem, {
suggestion: suggestion,
key: getSuggestionKey(suggestion)
})))) : /*#__PURE__*/React__default.createElement(React__default.Fragment, null);
};
AutocompleteSuggestions.displayName = 'SuggestionList';
var AutocompleteSuggestions$1 = AutocompleteSuggestions;
export { AutocompleteSuggestions$1 as default };