UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

457 lines (452 loc) • 15.5 kB
import { c } from 'react-compiler-runtime'; import React, { useContext, useRef, useState, useEffect } from 'react'; import { debounce } from '@github/mini-throttle'; import { announce } from '@primer/live-region-element'; import { scrollIntoView } from '@primer/behaviors'; import { ActionList } from '../ActionList/index.js'; import { useFocusZone } from '../hooks/useFocusZone.js'; import { useId } from '../hooks/useId.js'; import { AutocompleteContext } from './AutocompleteContext.js'; import { PlusIcon } from '@primer/octicons-react'; import VisuallyHidden from '../_VisuallyHidden.js'; import { isElement } from 'react-is'; import classes from './AutocompleteMenu.module.css.js'; import { jsx, jsxs } from 'react/jsx-runtime'; import Spinner from '../Spinner/Spinner.js'; const getDefaultSortFn = isItemSelectedFn => (itemIdA, itemIdB) => isItemSelectedFn(itemIdA) === isItemSelectedFn(itemIdB) ? 0 : isItemSelectedFn(itemIdA) ? -1 : 1; const menuScrollMargins = { startMargin: 0, endMargin: 8 }; 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 getdefaultCheckedSelectionChange(setInputValueFn) { return function (itemOrItems) { const { text = '' } = Array.isArray(itemOrItems) ? itemOrItems.slice(-1)[0] : itemOrItems; setInputValueFn(text); }; } const isItemSelected = (itemId, selectedItemIds) => selectedItemIds.includes(itemId); function getItemById(itemId, items) { return items.find(item => item.id === itemId); } // eslint-disable-next-line @typescript-eslint/no-explicit-any /** * Announces a message to screen readers at a slowed-down rate. This is useful when you want to announce don't want to * overwhelm the user with too many announcements in rapid succession. */ const debounceAnnouncement = debounce(announcement => { announce(announcement); }, 250); function AutocompleteMenu(props) { const $ = c(78); const autocompleteContext = useContext(AutocompleteContext); if (autocompleteContext === null) { throw new Error("AutocompleteContext returned null values"); } const { activeDescendantRef, id, inputRef, inputValue: t0, scrollContainerRef, setAutocompleteSuggestion, setShowMenu, setInputValue, setIsMenuDirectlyActivated, setSelectedItemLength, showMenu } = autocompleteContext; const inputValue = t0 === undefined ? "" : t0; const { items, selectedItemIds, sortOnCloseFn, emptyStateText: t1, addNewItem, loading, selectionVariant: t2, filterFn, "aria-labelledby": ariaLabelledBy, onOpenChange, onSelectedChange, customScrollContainerRef } = props; const emptyStateText = t1 === undefined ? "No selectable options" : t1; const selectionVariant = t2 === undefined ? "single" : t2; const listContainerRef = useRef(null); let t3; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t3 = []; $[0] = t3; } else { t3 = $[0]; } const allItemsToRenderRef = useRef(t3); const [highlightedItem, setHighlightedItem] = useState(); let t4; if ($[1] !== items) { t4 = items.map(_temp); $[1] = items; $[2] = t4; } else { t4 = $[2]; } const [sortedItemIds, setSortedItemIds] = useState(t4); const generatedUniqueId = useId(id); let t5; if ($[3] !== (highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.id) || $[4] !== inputRef || $[5] !== items || $[6] !== onSelectedChange || $[7] !== selectedItemIds || $[8] !== selectionVariant || $[9] !== setAutocompleteSuggestion || $[10] !== setInputValue || $[11] !== setShowMenu) { t5 = items.map(selectableItem => ({ ...selectableItem, role: "option", id: selectableItem.id, active: (highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.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]; const onSelectedChangeFn = onSelectedChange ? onSelectedChange : getdefaultCheckedSelectionChange(setInputValue); onSelectedChangeFn(newSelectedItemIds.map(newSelectedItemId => getItemById(newSelectedItemId, items))); if (selectionVariant === "multiple") { setInputValue(""); setAutocompleteSuggestion(""); } else { var _inputRef$current; setShowMenu(false); (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length); } } })); $[3] = highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.id; $[4] = inputRef; $[5] = items; $[6] = onSelectedChange; $[7] = selectedItemIds; $[8] = selectionVariant; $[9] = setAutocompleteSuggestion; $[10] = setInputValue; $[11] = setShowMenu; $[12] = t5; } else { t5 = $[12]; } const selectableItems = t5; let t6; if ($[13] !== sortedItemIds) { t6 = sortedItemIds.reduce(_temp2, {}); $[13] = sortedItemIds; $[14] = t6; } else { t6 = $[14]; } const itemSortOrderData = t6; let t7; if ($[15] !== filterFn || $[16] !== inputValue || $[17] !== itemSortOrderData || $[18] !== selectableItems) { let t8; if ($[20] !== itemSortOrderData) { t8 = (a, b) => itemSortOrderData[a.id] - itemSortOrderData[b.id]; $[20] = itemSortOrderData; $[21] = t8; } else { t8 = $[21]; } t7 = selectableItems.filter(filterFn ? filterFn : getDefaultItemFilter(inputValue)).sort(t8); $[15] = filterFn; $[16] = inputValue; $[17] = itemSortOrderData; $[18] = selectableItems; $[19] = t7; } else { t7 = $[19]; } const sortedAndFilteredItemsToRender = t7; let t8; if ($[22] !== addNewItem || $[23] !== generatedUniqueId || $[24] !== (highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.id) || $[25] !== selectedItemIds || $[26] !== selectionVariant || $[27] !== setAutocompleteSuggestion || $[28] !== setInputValue) { t8 = addNewItem ? [{ ...addNewItem, role: "option", key: addNewItem.id, active: (highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.id) === addNewItem.id, selected: selectionVariant === "multiple" ? selectedItemIds.includes(addNewItem.id) : undefined, leadingVisual: _temp3, onAction: item_0 => { addNewItem.handleAddItem({ ...item_0, id: item_0.id || generatedUniqueId, leadingVisual: undefined }); if (selectionVariant === "multiple") { setInputValue(""); setAutocompleteSuggestion(""); } } }] : []; $[22] = addNewItem; $[23] = generatedUniqueId; $[24] = highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.id; $[25] = selectedItemIds; $[26] = selectionVariant; $[27] = setAutocompleteSuggestion; $[28] = setInputValue; $[29] = t8; } else { t8 = $[29]; } let t9; if ($[30] !== sortedAndFilteredItemsToRender || $[31] !== t8) { t9 = [...sortedAndFilteredItemsToRender, ...t8]; $[30] = sortedAndFilteredItemsToRender; $[31] = t8; $[32] = t9; } else { t9 = $[32]; } const allItemsToRender = t9; let t10; if ($[33] !== allItemsToRender) { t10 = () => { allItemsToRenderRef.current = allItemsToRender; }; $[33] = allItemsToRender; $[34] = t10; } else { t10 = $[34]; } React.useEffect(t10); let t11; let t12; if ($[35] !== allItemsToRender || $[36] !== emptyStateText) { t11 = () => { if (allItemsToRender.length === 0) { debounceAnnouncement(emptyStateText); } }; t12 = [allItemsToRender, emptyStateText]; $[35] = allItemsToRender; $[36] = emptyStateText; $[37] = t11; $[38] = t12; } else { t11 = $[37]; t12 = $[38]; } React.useEffect(t11, t12); let t13; if ($[39] !== activeDescendantRef || $[40] !== customScrollContainerRef || $[41] !== scrollContainerRef || $[42] !== setIsMenuDirectlyActivated) { t13 = (current, _previous, directlyActivated) => { activeDescendantRef.current = current || null; if (current) { const selectedItem = allItemsToRenderRef.current.find(item_1 => { var _current$closest; return item_1.id === ((_current$closest = current.closest("li")) === null || _current$closest === void 0 ? void 0 : _current$closest.getAttribute("data-id")); }); setHighlightedItem(selectedItem); setIsMenuDirectlyActivated(directlyActivated); } if (current && customScrollContainerRef && customScrollContainerRef.current && directlyActivated) { scrollIntoView(current, customScrollContainerRef.current, menuScrollMargins); } else { if (current && scrollContainerRef.current && directlyActivated) { scrollIntoView(current, scrollContainerRef.current, menuScrollMargins); } } }; $[39] = activeDescendantRef; $[40] = customScrollContainerRef; $[41] = scrollContainerRef; $[42] = setIsMenuDirectlyActivated; $[43] = t13; } else { t13 = $[43]; } let t14; if ($[44] !== inputRef || $[45] !== t13) { t14 = { containerRef: listContainerRef, focusOutBehavior: "wrap", focusableElementFilter: _temp4, activeDescendantFocus: inputRef, onActiveDescendantChanged: t13 }; $[44] = inputRef; $[45] = t13; $[46] = t14; } else { t14 = $[46]; } let t15; if ($[47] !== loading) { t15 = [loading]; $[47] = loading; $[48] = t15; } else { t15 = $[48]; } useFocusZone(t14, t15); let t16; let t17; if ($[49] !== highlightedItem || $[50] !== inputValue || $[51] !== selectedItemIds || $[52] !== setAutocompleteSuggestion) { t16 = () => { var _highlightedItem$text; 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(""); } }; t17 = [highlightedItem, inputValue, selectedItemIds, setAutocompleteSuggestion]; $[49] = highlightedItem; $[50] = inputValue; $[51] = selectedItemIds; $[52] = setAutocompleteSuggestion; $[53] = t16; $[54] = t17; } else { t16 = $[53]; t17 = $[54]; } useEffect(t16, t17); let t18; let t19; if ($[55] !== onOpenChange || $[56] !== selectedItemIds || $[57] !== showMenu || $[58] !== sortOnCloseFn || $[59] !== sortedItemIds) { t18 = () => { const itemIdSortResult = [...sortedItemIds].sort(sortOnCloseFn ? sortOnCloseFn : getDefaultSortFn(itemId_0 => isItemSelected(itemId_0, selectedItemIds))); const sortResultMatchesState = itemIdSortResult.length === sortedItemIds.length && itemIdSortResult.every((element_0, index) => element_0 === sortedItemIds[index]); if (showMenu === false && !sortResultMatchesState) { setSortedItemIds(itemIdSortResult); } onOpenChange && onOpenChange(Boolean(showMenu)); }; t19 = [showMenu, onOpenChange, selectedItemIds, sortOnCloseFn, sortedItemIds]; $[55] = onOpenChange; $[56] = selectedItemIds; $[57] = showMenu; $[58] = sortOnCloseFn; $[59] = sortedItemIds; $[60] = t18; $[61] = t19; } else { t18 = $[60]; t19 = $[61]; } useEffect(t18, t19); let t20; if ($[62] !== selectedItemIds.length || $[63] !== setSelectedItemLength) { t20 = () => { if (selectedItemIds.length) { setSelectedItemLength(selectedItemIds.length); } }; $[62] = selectedItemIds.length; $[63] = setSelectedItemLength; $[64] = t20; } else { t20 = $[64]; } let t21; if ($[65] !== selectedItemIds || $[66] !== setSelectedItemLength) { t21 = [selectedItemIds, setSelectedItemLength]; $[65] = selectedItemIds; $[66] = setSelectedItemLength; $[67] = t21; } else { t21 = $[67]; } useEffect(t20, t21); if (selectionVariant === "single" && selectedItemIds.length > 1) { throw new Error("Autocomplete: selectionVariant \"single\" cannot be used with multiple selected items"); } let t22; if ($[68] !== allItemsToRender || $[69] !== ariaLabelledBy || $[70] !== emptyStateText || $[71] !== id || $[72] !== loading || $[73] !== selectionVariant) { t22 = loading ? /*#__PURE__*/jsx("div", { className: classes.SpinnerWrapper, children: /*#__PURE__*/jsx(Spinner, {}) }) : /*#__PURE__*/jsx("div", { ref: listContainerRef, children: allItemsToRender.length ? /*#__PURE__*/jsx(ActionList, { selectionVariant: selectionVariant, role: "listbox", id: `${id}-listbox`, "aria-labelledby": ariaLabelledBy, children: allItemsToRender.map(_temp5) }) : emptyStateText !== false && emptyStateText !== null ? /*#__PURE__*/jsx("div", { className: classes.EmptyStateWrapper, children: emptyStateText }) : null }); $[68] = allItemsToRender; $[69] = ariaLabelledBy; $[70] = emptyStateText; $[71] = id; $[72] = loading; $[73] = selectionVariant; $[74] = t22; } else { t22 = $[74]; } let t23; if ($[75] !== showMenu || $[76] !== t22) { t23 = /*#__PURE__*/jsx(VisuallyHidden, { isVisible: showMenu, children: t22 }); $[75] = showMenu; $[76] = t22; $[77] = t23; } else { t23 = $[77]; } return t23; } function _temp5(item_2) { const { id: id_0, onAction, children, text, leadingVisual: LeadingVisual, trailingVisual: TrailingVisual, key, role, ...itemProps } = item_2; return /*#__PURE__*/jsxs(ActionList.Item, { onSelect: () => onAction(item_2), ...itemProps, id: id_0, "data-id": id_0, role: role, children: [LeadingVisual && /*#__PURE__*/jsx(ActionList.LeadingVisual, { children: isElement(LeadingVisual) ? LeadingVisual : /*#__PURE__*/jsx(LeadingVisual, {}) }), children !== null && children !== void 0 ? children : text, TrailingVisual && /*#__PURE__*/jsx(ActionList.TrailingVisual, { children: isElement(TrailingVisual) ? TrailingVisual : /*#__PURE__*/jsx(TrailingVisual, {}) })] }, key !== null && key !== void 0 ? key : id_0); } _temp5.displayName = "_temp5"; function _temp4(element) { return !(element instanceof HTMLInputElement); } function _temp3() { return /*#__PURE__*/jsx(PlusIcon, {}); } _temp3.displayName = "_temp3"; function _temp2(acc, curr, i) { acc[curr] = i; return acc; } function _temp(t0) { const { id: itemId } = t0; return itemId; } AutocompleteMenu.displayName = 'AutocompleteMenu'; AutocompleteMenu.__SLOT__ = Symbol('Autocomplete.Menu'); export { AutocompleteMenu as default };