UNPKG

@newrelic/gatsby-theme-newrelic

Version:

[![Community Project header](https://github.com/newrelic/opensource-website/raw/master/src/images/categories/Community_Project.png)](https://opensource.newrelic.com/oss-category/#community-project)

232 lines (211 loc) 6.82 kB
import React, { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { navigate } from '@reach/router'; import { css } from '@emotion/react'; import { useThrottle } from 'react-use'; import useKeyPress from '../hooks/useKeyPress'; import useThemeTranslation from '../hooks/useThemeTranslation'; import useScrollFreeze from '../hooks/useScrollFreeze'; import { addPageAction } from '../utils/nrBrowserAgent'; import useSearch from './SearchModal/useSearch'; import SearchInput from './SearchInput'; import SearchDropdown, { DEFAULT_FILTER_TYPES } from './SearchDropdown'; const GlobalSearch = ({ onClose }) => { const [open, setOpen] = useState(false); const [query, setQuery] = useState(''); const throttledQuery = useThrottle(query, 300); const { results, status, fetchNextPage, hasMore } = useSearch({ searchTerm: throttledQuery, filters: DEFAULT_FILTER_TYPES, }); // a const assignment here is causing the dev server to fail to build let recentQueries = []; try { recentQueries = JSON.parse(localStorage.getItem(SAVED_SEARCH_KEY) ?? '[]'); // eslint-disable-next-line no-empty } catch (err) {} // `null` when we're just in the searchbar and nothing is selected // otherwise, `selected` is an integer. const [selected, setSelected] = useState(null); const possibleSelections = results.length + recentQueries.length; const moveUp = () => setSelected((s) => { if (s == null) return possibleSelections - 1; const next = s - 1; if (next < 0) { return possibleSelections - 1; } return next; }); const moveDown = () => setSelected((s) => { if (s == null) return 0; const next = s + 1; if (next > possibleSelections - 1) { return 0; } return next; }); useEffect(() => { setSelected(null); }, [query]); const searchRef = useRef(null); const { t } = useThemeTranslation(); useKeyPress('/', (e) => { // prevent quick search from opening in Firefox e.preventDefault(); // rAF prevents `/` from being typed in input after focusing requestAnimationFrame(() => searchRef.current?.focus()); }); const showSearchDropdown = query.length > 1 && open; useScrollFreeze(showSearchDropdown); return ( <> <SearchInput onClear={() => { setQuery(''); setOpen(false); onClose(); }} onFocus={() => setOpen(true)} onMove={(direction) => { if (direction === 'prev') { moveUp(); } else { moveDown(); } }} onSubmit={(value) => { if (value.length < 2) return; if (selected != null) { if (selected > recentQueries.length - 1) { const position = selected - recentQueries.length; const selectedResult = results[position]; saveSearch(value); addPageAction({ eventName: 'swiftypeSearchResult', category: 'GlobalSearch', path: location.pathname, resultCount: results.length, position, searchTerm: query, searchType: 'globalSearch', url: selectedResult.url, }); navigate(selectedResult.url); } else { const position = selected; const recentQuery = recentQueries[position]; addPageAction({ eventName: 'savedQuerySearch', category: 'GlobalSearch', path: location.pathname, searchTerm: recentQuery, position, searchType: 'globalSearch', }); navigate(`/search-results/?query=${recentQuery}&page=1`); } } else { saveSearch(value); addPageAction({ eventName: 'swiftypeSearchInput', category: 'GlobalSearch', path: location.pathname, resultCount: results.length, searchTerm: query, searchType: 'globalSearch', }); navigate(`/search-results/?query=${value}&page=1`); } }} placeholder={t('searchInput.placeholder')} ref={searchRef} setValue={setQuery} showShortcut size={SearchInput.SIZE.MEDIUM} value={query} css={css` --icon-size: 1.5rem; width: var(--search-width); svg { width: 1rem; height: 1rem; } input { border: none; height: 40px; } @media (max-width: 760px) { border: 0; border-radius: 0; position: absolute; left: 0; top: 0; width: 100vw; height: var(--global-header-height); z-index: 99; & input { border-radius: 0; height: var(--global-header-height); } } `} /> {showSearchDropdown && ( <SearchDropdown hasMore={hasMore} onViewMore={fetchNextPage} onClose={() => setOpen(false)} onRecentClick={(query, i) => { addPageAction({ eventName: 'savedQuerySearch', category: 'GlobalSearch', path: location.pathname, searchTerm: query, position: i, searchType: 'globalSearch', }); }} onResultClick={(result, i) => { addPageAction({ eventName: 'swiftypeSearchResult', category: 'GlobalSearch', path: location.pathname, resultCount: results.length, position: i, searchTerm: query, searchType: 'globalSearch', url: result.url, }); saveSearch(query); }} query={query} recentQueries={recentQueries} results={results} selected={selected} status={status} /> )} </> ); }; GlobalSearch.propTypes = { onClose: PropTypes.func.isRequired, }; const SAVED_SEARCH_KEY = 'gatsby-theme-newrelic:saved-searches'; const saveSearch = (value) => { value = value.trim(); const savedSearches = JSON.parse( localStorage.getItem(SAVED_SEARCH_KEY) ?? '[]' ); const set = new Set(savedSearches); if (set.has(value)) { return; } savedSearches.push(value); // only save the four most recent searches const updated = savedSearches.slice(-4); localStorage.setItem(SAVED_SEARCH_KEY, JSON.stringify(updated)); }; export default GlobalSearch;