UNPKG

@primer/components

Version:
222 lines (194 loc) • 9.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _ActionList = require("../ActionList"); var _useFocusZone = require("../hooks/useFocusZone"); var _ = require("../"); var _AutocompleteContext = require("./AutocompleteContext"); var _octiconsReact = require("@primer/octicons-react"); var _uniqueId = require("../utils/uniqueId"); var _scrollIntoViewingArea = require("../utils/scrollIntoViewingArea"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const getDefaultSortFn = isItemSelectedFn => (itemIdA, itemIdB) => isItemSelectedFn(itemIdA) === isItemSelectedFn(itemIdB) ? 0 : isItemSelectedFn(itemIdA) ? -1 : 1; function getDefaultItemFilter(filterValue) { return function (item, _i) { var _item$text; return Boolean((_item$text = item.text) === null || _item$text === void 0 ? void 0 : _item$text.toLowerCase().startsWith(filterValue.toLowerCase())); }; } function getDefaultOnSelectionChange(setInputValueFn) { return function (itemOrItems) { const { text = '' } = Array.isArray(itemOrItems) ? itemOrItems.slice(-1)[0] : itemOrItems; setInputValueFn && setInputValueFn(text); }; } const isItemSelected = (itemId, selectedItemIds) => selectedItemIds.includes(itemId); function getItemById(itemId, items) { return items.find(item => item.id === itemId); } ; // TODO: consider making 'aria-labelledby' required function AutocompleteMenu(props) { const { activeDescendantRef, id, inputRef, inputValue = '', scrollContainerRef, setAutocompleteSuggestion, setShowMenu, setInputValue, setIsMenuDirectlyActivated, setSelectedItemLength, showMenu } = (0, _react.useContext)(_AutocompleteContext.AutocompleteContext); const { items, selectedItemIds, sortOnCloseFn, emptyStateText, addNewItem, loading, selectionVariant, filterFn = getDefaultItemFilter(inputValue), "aria-labelledby": ariaLabelledBy, onOpenChange, onSelectedChange = getDefaultOnSelectionChange(setInputValue), customScrollContainerRef } = props; const listContainerRef = (0, _react.useRef)(null); const [highlightedItem, setHighlightedItem] = (0, _react.useState)(); const [sortedItemIds, setSortedItemIds] = (0, _react.useState)(items.map(({ id }) => id)); const selectableItems = (0, _react.useMemo)(() => items.map(selectableItem => { return { ...selectableItem, role: "option", id: selectableItem.id, selected: selectionVariant === 'multiple' ? selectedItemIds.includes(selectableItem.id) : undefined, onAction: item => { const otherSelectedItemIds = selectedItemIds.filter(selectedItemId => selectedItemId !== item.id); const newSelectedItemIds = selectedItemIds.includes(item.id) ? otherSelectedItemIds : [...otherSelectedItemIds, item.id]; onSelectedChange && onSelectedChange(newSelectedItemIds.map(newSelectedItemId => getItemById(newSelectedItemId, items))); if (selectionVariant === 'multiple') { setInputValue && setInputValue(''); setAutocompleteSuggestion && setAutocompleteSuggestion(''); } else { var _inputRef$current; setShowMenu && setShowMenu(false); inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length); } } }; }), [items, selectedItemIds]); const itemSortOrderData = (0, _react.useMemo)(() => sortedItemIds.reduce((acc, curr, i) => { acc[curr] = i; return acc; }, {}), [sortedItemIds]); const sortedAndFilteredItemsToRender = (0, _react.useMemo)(() => selectableItems.filter((item, i) => filterFn(item, i)).sort((a, b) => itemSortOrderData[a.id] - itemSortOrderData[b.id]), [selectableItems]); const allItemsToRender = (0, _react.useMemo)(() => [// sorted and filtered selectable items ...sortedAndFilteredItemsToRender, // menu item used for creating a token from whatever is in the text input ...(addNewItem ? [{ ...addNewItem, leadingVisual: () => /*#__PURE__*/_react.default.createElement(_octiconsReact.PlusIcon, null), onAction: (item, e) => { // TODO: make it possible to pass a leadingVisual when using `addNewItem` addNewItem.handleAddItem({ ...item, id: item.id || (0, _uniqueId.uniqueId)(), leadingVisual: undefined }); if (selectionVariant === 'multiple') { setInputValue && setInputValue(''); setAutocompleteSuggestion && setAutocompleteSuggestion(''); } } }] : [])], [sortedAndFilteredItemsToRender, addNewItem]); (0, _useFocusZone.useFocusZone)({ containerRef: listContainerRef, focusOutBehavior: 'wrap', focusableElementFilter: element => { return !(element instanceof HTMLInputElement); }, activeDescendantFocus: inputRef, onActiveDescendantChanged: (current, _previous, directlyActivated) => { if (activeDescendantRef) { activeDescendantRef.current = current || null; } if (current) { const selectedItem = selectableItems.find(item => item.id.toString() === (current === null || current === void 0 ? void 0 : current.dataset.id)); setHighlightedItem(selectedItem); setIsMenuDirectlyActivated && setIsMenuDirectlyActivated(directlyActivated); } if (current && customScrollContainerRef && customScrollContainerRef.current && directlyActivated) { (0, _scrollIntoViewingArea.scrollIntoViewingArea)(current, customScrollContainerRef.current); } else if (current && scrollContainerRef && scrollContainerRef.current && directlyActivated) { (0, _scrollIntoViewingArea.scrollIntoViewingArea)(current, scrollContainerRef.current); } } }, [loading]); (0, _react.useEffect)(() => { var _highlightedItem$text; if (!setAutocompleteSuggestion) { return; } if (highlightedItem !== null && highlightedItem !== void 0 && (_highlightedItem$text = highlightedItem.text) !== null && _highlightedItem$text !== void 0 && _highlightedItem$text.startsWith(inputValue) && !selectedItemIds.includes(highlightedItem.id)) { setAutocompleteSuggestion(highlightedItem.text); } else { setAutocompleteSuggestion(''); } }, [highlightedItem, inputValue]); (0, _react.useEffect)(() => { if (showMenu === false) { setSortedItemIds([...sortedItemIds].sort(sortOnCloseFn ? sortOnCloseFn : getDefaultSortFn(itemId => isItemSelected(itemId, selectedItemIds)))); } onOpenChange && onOpenChange(Boolean(showMenu)); }, [showMenu]); (0, _react.useEffect)(() => { if (selectedItemIds.length) { setSelectedItemLength && setSelectedItemLength(selectedItemIds.length); } }, [selectedItemIds]); return /*#__PURE__*/_react.default.createElement(_.Box, { sx: !showMenu ? { // visually hides this label for sighted users position: 'absolute', width: '1px', height: '1px', padding: '0', margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', whiteSpace: 'nowrap', borderWidth: '0' } : {} }, loading ? /*#__PURE__*/_react.default.createElement(_.Box, { p: 3, display: "flex", justifyContent: "center" }, /*#__PURE__*/_react.default.createElement(_.Spinner, null)) : /*#__PURE__*/_react.default.createElement("div", { ref: listContainerRef }, allItemsToRender.length ? /*#__PURE__*/_react.default.createElement(_ActionList.ActionList, { selectionVariant: "multiple" // have to typecast to `ItemProps` because we have an extra property // on `items` for Autocomplete: `metadata` , items: allItemsToRender, role: "listbox", id: `${id}-listbox`, "aria-labelledby": ariaLabelledBy }) : /*#__PURE__*/_react.default.createElement(_.Box, { p: 3 }, emptyStateText))); } AutocompleteMenu.displayName = "AutocompleteMenu"; AutocompleteMenu.defaultProps = { emptyStateText: 'No selectable options', selectionVariant: 'single' }; AutocompleteMenu.displayName = 'AutocompleteMenu'; var _default = AutocompleteMenu; exports.default = _default;