UNPKG

@navinc/base-react-components

Version:
159 lines 6.6 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { forwardRef, useRef, useEffect, useState, } from 'react'; import { BaseStringInput } from '../base-string-input/base-string-input.js'; import { useDebouncedCallback } from 'use-debounce'; import { createProxyWithOverrides } from '@navinc/utils'; import { combineRefs } from '../../combine-refs.js'; import { triggerChangeEvent } from '../../trigger-change.js'; const caseInsensitiveEqual = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }) === 0; const BaseSearchInputComponent = (_a, ref) => { var { onFocus, onChange, onBlur, value, results, resultToQuery, search, isLoading, children } = _a, props = __rest(_a, ["onFocus", "onChange", "onBlur", "value", "results", "resultToQuery", "search", "isLoading", "children"]); const [focusedResultIndex, setFocusedResultIndex] = useState(-1); const [canBeOpen, setCanBeOpen] = useState(false); const [query, setQuery] = useState(''); const inputRef = useRef(null); const combinedInputRef = combineRefs(ref, inputRef); const lastSearchRef = useRef(''); const shouldShowDropDown = !!(query.length && canBeOpen && query === lastSearchRef.current && !isLoading); const shouldShowResults = !!(shouldShowDropDown && results.length); const shouldShowNoResults = !!(shouldShowDropDown && !results.length); // sync value and query useEffect(() => { if (!value) { return; } const newQuery = resultToQuery(value); if (!newQuery) { return; } setQuery(newQuery); }, [resultToQuery, value]); // update the value once the results are loaded if the current query is one of the results useEffect(() => { if (!value && !!query) { const result = results.find((result) => caseInsensitiveEqual(resultToQuery(result), query)); if (result) { if (inputRef.current) { inputRef.current.value = resultToQuery(result); triggerChangeEvent(inputRef.current); } } } }, [query, resultToQuery, results, value]); const searchDebounced = useDebouncedCallback((query) => { search(query); setFocusedResultIndex(-1); }, 500); const handleChange = (e) => { var _a; const updatedEvent = e; const newQuery = e.target.value; setQuery(newQuery); const result = (_a = results.find((result) => caseInsensitiveEqual(resultToQuery(result), newQuery))) !== null && _a !== void 0 ? _a : undefined; updatedEvent.target = createProxyWithOverrides(updatedEvent.target, { value: result }); onChange === null || onChange === void 0 ? void 0 : onChange(updatedEvent); if (newQuery.length > 0 && !caseInsensitiveEqual(newQuery, lastSearchRef.current)) { lastSearchRef.current = newQuery; searchDebounced(newQuery); setCanBeOpen(true); } }; const handleInputFocus = (event) => { setCanBeOpen(true); onFocus === null || onFocus === void 0 ? void 0 : onFocus(event); }; const goToNextResult = () => { const newIndex = focusedResultIndex + 1; if (newIndex < results.length) { setFocusedResultIndex(newIndex); } }; const goToPreviousResult = () => { const newIndex = focusedResultIndex - 1; if (newIndex > -2) { setFocusedResultIndex(newIndex); } }; const closeResults = () => { setCanBeOpen(false); setFocusedResultIndex(-1); }; const onSelectResult = (selectedResult) => { closeResults(); if (inputRef.current) { const query = resultToQuery(selectedResult); inputRef.current.value = query; lastSearchRef.current = query; triggerChangeEvent(inputRef.current); } }; const handleBlurOnInput = (event) => { closeResults(); onBlur === null || onBlur === void 0 ? void 0 : onBlur(event); }; const handleKeyDownOnInput = (event) => { switch (event.key) { case 'ArrowDown': if (shouldShowResults) { event.preventDefault(); goToNextResult(); } else { setCanBeOpen(true); } break; case 'Tab': if (shouldShowResults) { event.preventDefault(); if (event.shiftKey) { goToPreviousResult(); } else { goToNextResult(); } } break; case 'ArrowUp': if (shouldShowResults) { event.preventDefault(); goToPreviousResult(); } break; case 'Escape': if (focusedResultIndex > -1) { setFocusedResultIndex(-1); } else { setQuery(''); } break; case 'Enter': if (focusedResultIndex > -1) { event.preventDefault(); onSelectResult(results[focusedResultIndex]); } break; } }; return (_jsxs(_Fragment, { children: [_jsx(BaseStringInput, Object.assign({ ref: combinedInputRef, autoComplete: "off", onBlur: handleBlurOnInput, onChange: handleChange, onFocus: handleInputFocus, onKeyDown: handleKeyDownOnInput, value: query }, props)), children({ shouldShowResults, shouldShowNoResults, query, results, focusedResultIndex, handleResultSelect: onSelectResult, })] })); }; export const BaseSearchInput = forwardRef(BaseSearchInputComponent); //# sourceMappingURL=base-search-input.js.map