UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

117 lines (116 loc) 4.66 kB
import { useMergedRefs } from "../hooks/useMergedRefs.js"; import useSafeTimeout from "../hooks/useSafeTimeout.js"; import { AutocompleteContext, AutocompleteInputContext } from "./AutocompleteContext.js"; import TextInput_default from "../TextInput/TextInput.js"; import { jsx } from "react/jsx-runtime"; import React, { useCallback, useContext, useEffect, useState } from "react"; //#region src/Autocomplete/AutocompleteInput.tsx const ARROW_KEYS_NAV = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown"]); const AutocompleteInput = /*#__PURE__*/ React.forwardRef(({ as: Component = TextInput_default, onFocus, onBlur, onChange, onKeyDown, onKeyUp, onKeyPress, value, openOnFocus = false, ...props }, forwardedRef) => { const autocompleteContext = useContext(AutocompleteContext); const inputContext = useContext(AutocompleteInputContext); if (autocompleteContext === null || inputContext === null) throw new Error("AutocompleteContext returned null values"); const { activeDescendantRef, id, inputRef, setInputValue, setShowMenu, showMenu } = autocompleteContext; const { autocompleteSuggestion = "", inputValue = "", isMenuDirectlyActivated } = inputContext; const mergedRef = useMergedRefs(forwardedRef, inputRef); const [highlightRemainingText, setHighlightRemainingText] = useState(true); const { safeSetTimeout } = useSafeTimeout(); const handleInputFocus = (event) => { onFocus === null || onFocus === void 0 || onFocus(event); if (openOnFocus) setShowMenu(true); }; const handleInputBlur = useCallback((event) => { onBlur && onBlur(event); safeSetTimeout(() => { if (document.activeElement !== inputRef.current) { setShowMenu(false); if (inputRef.current && autocompleteSuggestion && inputRef.current.value !== inputValue) inputRef.current.value = inputValue; } }, 0); }, [ onBlur, setShowMenu, inputRef, safeSetTimeout, autocompleteSuggestion, inputValue ]); const handleInputChange = (event) => { onChange && onChange(event); setInputValue(event.currentTarget.value); if (!showMenu) setShowMenu(true); }; const handleInputKeyDown = useCallback((event) => { var _inputRef$current; onKeyDown && onKeyDown(event); if (event.key === "Backspace") setHighlightRemainingText(false); if (event.key === "Escape" && (_inputRef$current = inputRef.current) !== null && _inputRef$current !== void 0 && _inputRef$current.value) { setInputValue(""); inputRef.current.value = ""; } if (!showMenu && ARROW_KEYS_NAV.has(event.key) && !event.altKey) setShowMenu(true); }, [ inputRef, setInputValue, setHighlightRemainingText, onKeyDown, showMenu, setShowMenu ]); const handleInputKeyUp = useCallback((event) => { onKeyUp && onKeyUp(event); if (event.key === "Backspace") setHighlightRemainingText(true); }, [setHighlightRemainingText, onKeyUp]); const onInputKeyPress = useCallback((event) => { onKeyPress && onKeyPress(event); if (showMenu && event.key === "Enter" && activeDescendantRef.current) { event.preventDefault(); event.nativeEvent.stopImmediatePropagation(); const activeDescendantEvent = new KeyboardEvent(event.type, event.nativeEvent); activeDescendantRef.current.dispatchEvent(activeDescendantEvent); } }, [ activeDescendantRef, showMenu, onKeyPress ]); useEffect(() => { if (!inputRef.current) return; if (!autocompleteSuggestion) inputRef.current.value = inputValue; if (document.activeElement === inputRef.current && highlightRemainingText && autocompleteSuggestion && (inputValue || isMenuDirectlyActivated)) { inputRef.current.value = autocompleteSuggestion; if (autocompleteSuggestion.toLowerCase().indexOf(inputValue.toLowerCase()) === 0) inputRef.current.setSelectionRange(inputValue.length, autocompleteSuggestion.length); } }, [ autocompleteSuggestion, inputValue, inputRef, isMenuDirectlyActivated ]); useEffect(() => { setInputValue(typeof value !== "undefined" ? value.toString() : ""); }, [value, setInputValue]); return /*#__PURE__*/ jsx(Component, { onFocus: handleInputFocus, onBlur: handleInputBlur, onChange: handleInputChange, onKeyDown: handleInputKeyDown, onKeyPress: onInputKeyPress, onKeyUp: handleInputKeyUp, ref: mergedRef, "aria-controls": `${id}-listbox`, "aria-autocomplete": "both", role: "combobox", "aria-expanded": showMenu, "aria-haspopup": "listbox", "aria-owns": `${id}-listbox`, autoComplete: "off", id, ...props, "data-component": "Autocomplete.Input" }); }); AutocompleteInput.displayName = "AutocompleteInput"; AutocompleteInput.__SLOT__ = Symbol("Autocomplete.Input"); //#endregion export { AutocompleteInput as default };