UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

376 lines (375 loc) • 14.7 kB
import { useId as useId$1 } from "../hooks/useId.js"; import Spinner from "../Spinner/Spinner.js"; import VisuallyHidden from "../_VisuallyHidden.js"; import { useFocusZone } from "../hooks/useFocusZone.js"; import { ActionList } from "../ActionList/index.js"; import { AutocompleteContext, AutocompleteDeferredInputContext } from "./AutocompleteContext.js"; import AutocompleteMenu_module_css_default from "./AutocompleteMenu.module.css.js"; import { c } from "react-compiler-runtime"; import { jsx, jsxs } from "react/jsx-runtime"; import React, { useContext, useEffect, useRef, useState } from "react"; import { scrollIntoView } from "@primer/behaviors"; import { announce } from "@primer/live-region-element"; import { isElement } from "react-is"; import { PlusIcon } from "@primer/octicons-react"; import { debounce } from "@github/mini-throttle"; //#region src/Autocomplete/AutocompleteMenu.tsx 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); } /** * 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); const deferredInputContext = useContext(AutocompleteDeferredInputContext); if (autocompleteContext === null || deferredInputContext === null) throw new Error("AutocompleteContext returned null values"); const { activeDescendantRef, id, inputRef, scrollContainerRef, setAutocompleteSuggestion, setShowMenu, setInputValue, setIsMenuDirectlyActivated, setSelectedItemLength, showMenu } = autocompleteContext; const { deferredInputValue } = deferredInputContext; const { items, selectedItemIds, sortOnCloseFn, emptyStateText: t0, addNewItem, loading, selectionVariant: t1, filterFn, "aria-labelledby": ariaLabelledBy, onOpenChange, onSelectedChange, customScrollContainerRef } = props; const emptyStateText = t0 === void 0 ? "No selectable options" : t0; const selectionVariant = t1 === void 0 ? "single" : t1; const listContainerRef = useRef(null); let t2; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t2 = []; $[0] = t2; } else t2 = $[0]; const allItemsToRenderRef = useRef(t2); const [highlightedItem, setHighlightedItem] = useState(); let t3; if ($[1] !== items) { t3 = items.map(_temp); $[1] = items; $[2] = t3; } else t3 = $[2]; const [sortedItemIds, setSortedItemIds] = useState(t3); const generatedUniqueId = useId$1(id); let t4; 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) { t4 = 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) : void 0, onAction: (item) => { const otherSelectedItemIds = selectedItemIds.filter((selectedItemId) => selectedItemId !== item.id); const newSelectedItemIds = selectedItemIds.includes(item.id) ? otherSelectedItemIds : [...otherSelectedItemIds, item.id]; (onSelectedChange ? onSelectedChange : getdefaultCheckedSelectionChange(setInputValue))(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 || _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] = t4; } else t4 = $[12]; const selectableItems = t4; let t5; if ($[13] !== sortedItemIds) { t5 = sortedItemIds.reduce(_temp2, {}); $[13] = sortedItemIds; $[14] = t5; } else t5 = $[14]; const itemSortOrderData = t5; let t6; if ($[15] !== deferredInputValue || $[16] !== filterFn || $[17] !== itemSortOrderData || $[18] !== selectableItems) { let t7; if ($[20] !== itemSortOrderData) { t7 = (a, b) => itemSortOrderData[a.id] - itemSortOrderData[b.id]; $[20] = itemSortOrderData; $[21] = t7; } else t7 = $[21]; t6 = selectableItems.filter(filterFn ? filterFn : getDefaultItemFilter(deferredInputValue)).sort(t7); $[15] = deferredInputValue; $[16] = filterFn; $[17] = itemSortOrderData; $[18] = selectableItems; $[19] = t6; } else t6 = $[19]; const sortedAndFilteredItemsToRender = t6; let t7; if ($[22] !== addNewItem || $[23] !== generatedUniqueId || $[24] !== (highlightedItem === null || highlightedItem === void 0 ? void 0 : highlightedItem.id) || $[25] !== selectedItemIds || $[26] !== selectionVariant || $[27] !== setAutocompleteSuggestion || $[28] !== setInputValue) { t7 = 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) : void 0, leadingVisual: _temp3, onAction: (item_0) => { addNewItem.handleAddItem({ ...item_0, id: item_0.id || generatedUniqueId, leadingVisual: void 0 }); 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] = t7; } else t7 = $[29]; let t8; if ($[30] !== sortedAndFilteredItemsToRender || $[31] !== t7) { t8 = [...sortedAndFilteredItemsToRender, ...t7]; $[30] = sortedAndFilteredItemsToRender; $[31] = t7; $[32] = t8; } else t8 = $[32]; const allItemsToRender = t8; let t9; if ($[33] !== allItemsToRender) { t9 = () => { allItemsToRenderRef.current = allItemsToRender; }; $[33] = allItemsToRender; $[34] = t9; } else t9 = $[34]; React.useEffect(t9); let t10; let t11; if ($[35] !== allItemsToRender || $[36] !== emptyStateText) { t10 = () => { if (allItemsToRender.length === 0) debounceAnnouncement(emptyStateText); }; t11 = [allItemsToRender, emptyStateText]; $[35] = allItemsToRender; $[36] = emptyStateText; $[37] = t10; $[38] = t11; } else { t10 = $[37]; t11 = $[38]; } React.useEffect(t10, t11); let t12; if ($[39] !== activeDescendantRef || $[40] !== customScrollContainerRef || $[41] !== scrollContainerRef || $[42] !== setIsMenuDirectlyActivated) { t12 = (current, _previous, directlyActivated) => { activeDescendantRef.current = current || null; if (current) { setHighlightedItem(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")); })); 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] = t12; } else t12 = $[43]; let t13; if ($[44] !== inputRef || $[45] !== t12) { t13 = { containerRef: listContainerRef, focusOutBehavior: "wrap", focusableElementFilter: _temp4, activeDescendantFocus: inputRef, onActiveDescendantChanged: t12 }; $[44] = inputRef; $[45] = t12; $[46] = t13; } else t13 = $[46]; let t14; if ($[47] !== loading) { t14 = [loading]; $[47] = loading; $[48] = t14; } else t14 = $[48]; useFocusZone(t13, t14); let t15; let t16; if ($[49] !== deferredInputValue || $[50] !== highlightedItem || $[51] !== selectedItemIds || $[52] !== setAutocompleteSuggestion) { t15 = () => { var _highlightedItem$text; if (highlightedItem !== null && highlightedItem !== void 0 && (_highlightedItem$text = highlightedItem.text) !== null && _highlightedItem$text !== void 0 && _highlightedItem$text.startsWith(deferredInputValue) && !selectedItemIds.includes(highlightedItem.id)) setAutocompleteSuggestion(highlightedItem.text); else setAutocompleteSuggestion(""); }; t16 = [ highlightedItem, deferredInputValue, selectedItemIds, setAutocompleteSuggestion ]; $[49] = deferredInputValue; $[50] = highlightedItem; $[51] = selectedItemIds; $[52] = setAutocompleteSuggestion; $[53] = t15; $[54] = t16; } else { t15 = $[53]; t16 = $[54]; } useEffect(t15, t16); let t17; let t18; if ($[55] !== onOpenChange || $[56] !== selectedItemIds || $[57] !== showMenu || $[58] !== sortOnCloseFn || $[59] !== sortedItemIds) { t17 = () => { 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)); }; t18 = [ showMenu, onOpenChange, selectedItemIds, sortOnCloseFn, sortedItemIds ]; $[55] = onOpenChange; $[56] = selectedItemIds; $[57] = showMenu; $[58] = sortOnCloseFn; $[59] = sortedItemIds; $[60] = t17; $[61] = t18; } else { t17 = $[60]; t18 = $[61]; } useEffect(t17, t18); let t19; if ($[62] !== selectedItemIds.length || $[63] !== setSelectedItemLength) { t19 = () => { if (selectedItemIds.length) setSelectedItemLength(selectedItemIds.length); }; $[62] = selectedItemIds.length; $[63] = setSelectedItemLength; $[64] = t19; } else t19 = $[64]; let t20; if ($[65] !== selectedItemIds || $[66] !== setSelectedItemLength) { t20 = [selectedItemIds, setSelectedItemLength]; $[65] = selectedItemIds; $[66] = setSelectedItemLength; $[67] = t20; } else t20 = $[67]; useEffect(t19, t20); if (selectionVariant === "single" && selectedItemIds.length > 1) throw new Error("Autocomplete: selectionVariant \"single\" cannot be used with multiple selected items"); let t21; if ($[68] !== allItemsToRender || $[69] !== ariaLabelledBy || $[70] !== emptyStateText || $[71] !== id || $[72] !== loading || $[73] !== selectionVariant) { t21 = loading ? /*#__PURE__*/ jsx("div", { className: AutocompleteMenu_module_css_default.SpinnerWrapper, children: /*#__PURE__*/ jsx(Spinner, {}) }) : /*#__PURE__*/ jsx("div", { ref: listContainerRef, children: allItemsToRender.length ? /*#__PURE__*/ jsx(ActionList, { "data-component": "Autocomplete.Menu", selectionVariant, role: "listbox", id: `${id}-listbox`, "aria-labelledby": ariaLabelledBy, children: allItemsToRender.map(_temp5) }) : emptyStateText !== false && emptyStateText !== null ? /*#__PURE__*/ jsx("div", { className: AutocompleteMenu_module_css_default.EmptyStateWrapper, children: emptyStateText }) : null }); $[68] = allItemsToRender; $[69] = ariaLabelledBy; $[70] = emptyStateText; $[71] = id; $[72] = loading; $[73] = selectionVariant; $[74] = t21; } else t21 = $[74]; let t22; if ($[75] !== showMenu || $[76] !== t21) { t22 = /*#__PURE__*/ jsx(VisuallyHidden, { isVisible: showMenu, children: t21 }); $[75] = showMenu; $[76] = t21; $[77] = t22; } else t22 = $[77]; return t22; } 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, 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"); //#endregion export { AutocompleteMenu as default };