UNPKG

@navinc/base-react-components

Version:
265 lines (262 loc) 13.1 kB
"use strict"; 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SearchInput = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const styled_components_1 = __importDefault(require("styled-components")); const loading_dots_js_1 = __importDefault(require("./loading-dots.js")); const icon_js_1 = __importDefault(require("./icon.js")); const shared_js_1 = require("./form-elements/shared.js"); const copy_js_1 = __importDefault(require("./copy.js")); const utils_1 = require("@navinc/utils"); const use_debounce_1 = require("use-debounce"); const prop_types_1 = __importDefault(require("prop-types")); const is_rebrand_js_1 = __importDefault(require("./is-rebrand.js")); const id = (id) => id; const SearchInputContainer = styled_components_1.default.div.withConfig({ displayName: "brc-sc-SearchInputContainer", componentId: "brc-sc-1tkz17d" }) ` width: 100%; `; const ResultsContainer = styled_components_1.default.div.withConfig({ displayName: "brc-sc-ResultsContainer", componentId: "brc-sc-8e5pn8" }) ` position: relative; z-index: 2; & > ${shared_js_1.Helper} { margin-left: ${({ theme }) => theme.gu(2)}; } `; const getResultContainerBackgroundColor = (isActive, theme) => { if ((0, is_rebrand_js_1.default)(theme) && isActive) return `background-color: ${theme.navNeutral100}; border: 4px solid ${theme.navStatusPositive500}`; return isActive && `background-color: ${theme.neutral100};`; }; const ResultContainer = styled_components_1.default.div.withConfig({ displayName: "brc-sc-ResultContainer", componentId: "brc-sc-ed8c26" }) ` cursor: pointer; outline: none; padding: 16px; border-bottom: 1px solid ${({ theme }) => ((0, is_rebrand_js_1.default)(theme) ? theme.navNeutral300 : theme.neutral300)}; ${({ isActive, theme }) => getResultContainerBackgroundColor(isActive, theme)}; &:hover { background-color: ${({ theme }) => ((0, is_rebrand_js_1.default)(theme) ? theme.navNeutral100 : theme.neutral100)}; } &:focus { outline: ${({ theme }) => theme.focusOutline}; } `; const Results = styled_components_1.default.div.withConfig({ displayName: "brc-sc-Results", componentId: "brc-sc-1avlx1y" }) ` background-color: ${({ theme }) => ((0, is_rebrand_js_1.default)(theme) ? theme.navSecondary100 : theme.white)}; border: solid 1px ${({ theme }) => ((0, is_rebrand_js_1.default)(theme) ? theme.navNeutral300 : theme.neutral300)}; border-top: none; border-radius: ${({ theme }) => ((0, is_rebrand_js_1.default)(theme) ? '0 0 8px 8px' : '0 0 4px 4px')}; position: ${({ shouldPositionResultsRelative }) => (shouldPositionResultsRelative ? 'relative' : 'absolute')}; top: 0; overflow-y: auto; max-height: ${({ resultsMaxHeight }) => resultsMaxHeight || '300px'}; width: 100%; /* prettier-ignore */ ${ResultContainer}:last-child { border-bottom: none; } `; const StyledFieldWrapper = (0, styled_components_1.default)(shared_js_1.FieldWrapper).withConfig({ displayName: "brc-sc-StyledFieldWrapper", componentId: "brc-sc-13fj46g" }) ` & ${icon_js_1.default}, & ${loading_dots_js_1.default} { position: absolute; right: 16px; top: 50%; transform: translateY(-50%); } & ${icon_js_1.default} { width: 24px; } & ${loading_dots_js_1.default} { color: ${({ theme }) => theme.navPrimary}; width: 56px; } & ${shared_js_1.Input} { text-overflow: ellipsis; padding-right: 40px; ${({ shouldShowDropDown }) => shouldShowDropDown && 'border-radius: 4px 4px 0 0;'} } `; const StyledNoResults = styled_components_1.default.div.withConfig({ displayName: "brc-sc-StyledNoResults", componentId: "brc-sc-9i7w38" }) ` align-items: center; display: flex; flex-direction: column; margin-bottom: 24px; margin-top: 24px; `; const NoResultsIcon = (0, styled_components_1.default)(icon_js_1.default).withConfig({ displayName: "brc-sc-NoResultsIcon", componentId: "brc-sc-f379za" }) ` color: ${({ theme }) => ((0, is_rebrand_js_1.default)(theme) ? theme.navNeutral400 : theme.neutral400)}; margin-bottom: 8px; `; const DefaultNoResults = ({ query }) => ((0, jsx_runtime_1.jsxs)(StyledNoResults, { children: [(0, jsx_runtime_1.jsx)(NoResultsIcon, { name: "actions/circle-faq" }), (0, jsx_runtime_1.jsxs)(copy_js_1.default, { children: ["No results for \"", query, "\""] })] })); const SearchInput = (_a) => { var { autoFocus, className, errors, helperText, isLoading, isInvalid, label, lede, name, NoResults, onBlur, onChange, onFocus, Result, required, results, resultsMaxHeight, resultToQuery, search, shouldPositionResultsRelative, touched, type, value } = _a, inputProps = __rest(_a, ["autoFocus", "className", "errors", "helperText", "isLoading", "isInvalid", "label", "lede", "name", "NoResults", "onBlur", "onChange", "onFocus", "Result", "required", "results", "resultsMaxHeight", "resultToQuery", "search", "shouldPositionResultsRelative", "touched", "type", "value"]); const [focusedResultIndex, setFocusedResultIndex] = (0, react_1.useState)(-1); const [canBeOpen, setCanBeOpen] = (0, react_1.useState)(false); const [query, setQuery] = (0, react_1.useState)(''); const [lastSearchedQuery, setLastSearchedQuery] = (0, react_1.useState)(''); const inputRef = (0, react_1.useRef)(null); const resultsRef = (0, react_1.useRef)(null); const shouldShowDropDown = !!(query.length && canBeOpen && query === lastSearchedQuery && !isLoading); const shouldShowResults = !!(shouldShowDropDown && results.length); const shouldShowNoResults = !!(shouldShowDropDown && !results.length); const RenderNoResults = NoResults || DefaultNoResults; const isVisited = touched || value; (0, react_1.useEffect)(() => { resultToQuery(value) && setQuery(resultToQuery(value) || ''); }, [resultToQuery, value]); (0, react_1.useEffect)(() => { if (autoFocus) { (0, utils_1.focusWithoutScroll)(inputRef.current); } }, [autoFocus]); const searchDebounced = (0, use_debounce_1.useDebouncedCallback)((query) => { setLastSearchedQuery(query); search(query); setFocusedResultIndex(-1); }, 500); const handleChange = ({ target = {} } = {}) => { var _a; const newQuery = (_a = target.value) !== null && _a !== void 0 ? _a : ''; setQuery(newQuery); onChange({ target: { name, value: null, }, }); if (newQuery.length > 0) { searchDebounced(newQuery); setCanBeOpen(true); } }; const handleInputFocus = (event) => { setCanBeOpen(true); onFocus(event); }; const focusResult = (index) => { var _a, _b; const ref = (_b = (_a = resultsRef.current) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[index]; if (ref) { setFocusedResultIndex(index); ref.focus(); inputRef.current.focus(); } }; const goToNextResult = () => { const newIndex = focusedResultIndex + 1; if (newIndex < results.length) { focusResult(newIndex); } }; const goToPreviousResult = () => { const newIndex = focusedResultIndex - 1; if (newIndex > -2) { focusResult(newIndex); } }; const closeResults = () => { setCanBeOpen(false); setFocusedResultIndex(-1); }; const onSelectResult = (clickedResult) => { const result = clickedResult || results[focusedResultIndex]; closeResults(); onChange({ target: { name, value: result, }, }); }; const handleBlurOnInput = (event) => { if (!resultsRef.current || (resultsRef.current && !resultsRef.current.contains(event.relatedTarget))) { closeResults(); onBlur(event); } }; const handleKeyDownOnInput = (event) => { switch (event.keyCode) { case 40: // Down Arrow if (shouldShowResults) { event.preventDefault(); goToNextResult(); } else { setCanBeOpen(true); } break; case 9: // Tab if (shouldShowResults) { event.preventDefault(); if (event.shiftKey) { goToPreviousResult(); } else { goToNextResult(); } } break; case 38: // Up Arrow if (shouldShowResults) { event.preventDefault(); goToPreviousResult(); } break; case 27: // Escape if (focusedResultIndex > -1) { setFocusedResultIndex(-1); } else { setQuery(''); } break; case 13: // Enter if (focusedResultIndex > -1) { event.preventDefault(); onSelectResult(); } break; } }; return ((0, jsx_runtime_1.jsxs)(SearchInputContainer, Object.assign({ className: className }, { children: [(0, jsx_runtime_1.jsxs)(StyledFieldWrapper, Object.assign({ shouldShowDropDown: shouldShowDropDown }, { children: [lede && (0, jsx_runtime_1.jsx)(copy_js_1.default, Object.assign({ bold: true }, { children: lede })), (0, jsx_runtime_1.jsxs)(shared_js_1.Field, Object.assign({ isInvalid: isInvalid, isVisited: isVisited, required: required, type: type }, { children: [(0, jsx_runtime_1.jsx)(shared_js_1.Input, Object.assign({ autoComplete: "off", "data-testid": "search-input:input", name: name, onBlur: handleBlurOnInput, onChange: handleChange, onFocus: handleInputFocus, onKeyDown: handleKeyDownOnInput, type: type, ref: inputRef, required: required, value: query, isInvalid: isInvalid }, inputProps)), isLoading ? (0, jsx_runtime_1.jsx)(loading_dots_js_1.default, {}) : (0, jsx_runtime_1.jsx)(icon_js_1.default, { name: "system/search" }), (0, jsx_runtime_1.jsx)(shared_js_1.Label, Object.assign({ required: required, value: query }, { children: (0, utils_1.capitalize)(label) }))] }))] })), (0, jsx_runtime_1.jsxs)(ResultsContainer, { children: [shouldShowDropDown && ((0, jsx_runtime_1.jsxs)(Results, Object.assign({ resultsMaxHeight: resultsMaxHeight, ref: resultsRef, shouldPositionResultsRelative: shouldPositionResultsRelative }, { children: [shouldShowNoResults && (0, jsx_runtime_1.jsx)(RenderNoResults, { query: query }), shouldShowResults && results.map((result, index) => ((0, jsx_runtime_1.jsx)(ResultContainer, Object.assign({ isActive: focusedResultIndex === index, onMouseDown: () => onSelectResult(result), tabIndex: 0 }, { children: (0, jsx_runtime_1.jsx)(Result, { result: result }) }), JSON.stringify(result))))] }))), helperText && (0, jsx_runtime_1.jsx)(shared_js_1.Helper, { hasSpaceForHelper: true, helperText: helperText }), (0, jsx_runtime_1.jsx)(shared_js_1.Errors, Object.assign({ hasSpaceForErrors: true }, { children: !!errors.length && errors.map((err, i) => (0, jsx_runtime_1.jsx)(shared_js_1.Err, { children: err }, `err-${i}`)) }))] })] }))); }; exports.SearchInput = SearchInput; exports.SearchInput.defaultProps = { errors: [], onBlur: id, onChange: id, onFocus: id, results: [], resultToQuery: id, search: id, }; exports.SearchInput.propTypes = { errors: prop_types_1.default.array, isInvalid: prop_types_1.default.bool, NoResults: prop_types_1.default.oneOfType([prop_types_1.default.node, prop_types_1.default.func]), onBlur: prop_types_1.default.func, onChange: prop_types_1.default.func, onFocus: prop_types_1.default.func, results: prop_types_1.default.array, resultsMaxHeight: prop_types_1.default.string, resultToQuery: prop_types_1.default.func.isRequired, Result: prop_types_1.default.oneOfType([prop_types_1.default.node, prop_types_1.default.func]).isRequired, search: prop_types_1.default.func.isRequired, }; exports.default = (0, styled_components_1.default)(exports.SearchInput).withConfig({ componentId: "brc-sc-150ed28" }) ``; //# sourceMappingURL=search-input.js.map