From 6005028439f14f47ba55bc5a384270a6a814d846 Mon Sep 17 00:00:00 2001 From: Tyler Hallada Date: Sun, 6 Feb 2022 20:45:18 -0500 Subject: [PATCH] Use downshift-js for better search bar experience --- components/SearchBar.tsx | 157 +++++++++++++++++------------------- package-lock.json | 32 ++++++-- package.json | 1 + styles/SearchBar.module.css | 4 + 4 files changed, 106 insertions(+), 88 deletions(-) diff --git a/components/SearchBar.tsx b/components/SearchBar.tsx index f396e71..88783a7 100644 --- a/components/SearchBar.tsx +++ b/components/SearchBar.tsx @@ -1,3 +1,4 @@ +import { useCombobox } from "downshift"; import React, { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; import type mapboxgl from "mapbox-gl"; @@ -5,8 +6,6 @@ import MiniSearch, { SearchResult } from "minisearch"; import useSWRImmutable from "swr/immutable"; import styles from "../styles/SearchBar.module.css"; -import { join } from "path/posix"; -import { countReset } from "console"; type Props = { clearSelectedCell: () => void; @@ -59,93 +58,87 @@ const SearchBar: React.FC = ({ clearSelectedCell, counts, map }) => { }, [data]); const searchInput = useRef(null); - const [search, setSearch] = useState(""); const [searchFocused, setSearchFocused] = useState(false); - const [clickingResult, setClickingResult] = useState(false); const [results, setResults] = useState([]); - useEffect(() => { - if (searchInput.current) { - if ( - searchFocused && - global.document.activeElement !== searchInput.current - ) { - searchInput.current.focus(); - } else if ( - !searchFocused && - global.document.activeElement === searchInput.current - ) { - searchInput.current.blur(); + const { + isOpen, + getMenuProps, + getInputProps, + getComboboxProps, + highlightedIndex, + getItemProps, + reset, + } = useCombobox({ + items: results, + itemToString: (item) => (item ? item.name : ""), + onInputValueChange: ({ inputValue }) => { + if (searchEngine.current && inputValue) { + const results: SearchResult[] = searchEngine.current.search(inputValue); + setResults(results); } - } - }, [searchFocused]); - - const onChange = (e: React.ChangeEvent) => { - setSearch(e.target.value); - if (searchEngine.current) { - const results: SearchResult[] = searchEngine.current.search( - e.target.value - ); - setResults(results); - } - }; - - const onChooseResult = - (item: Mod) => - (e: React.MouseEvent | React.TouchEvent) => { - router.push({ query: { mod: item.id } }); - setSearch(""); - setResults([]); - setClickingResult(false); - setSearchFocused(false); - }; + }, + onSelectedItemChange: ({ selectedItem }) => { + if (selectedItem) { + setSearchFocused(false); + router.push({ query: { mod: selectedItem.id } }); + if (searchInput.current) searchInput.current.blur(); + reset(); + } + }, + }); return ( -
- setSearchFocused(true)} - onBlur={() => { - if (!clickingResult) { - setSearch(""); - setResults([]); - setSearchFocused(false); - } - }} - value={search} - ref={searchInput} - disabled={!data} - /> - {results.length > 0 && ( -
    - {results - .sort((resultA, resultB) => { - if (counts) { - const countA = counts[resultA.id]; - const countB = counts[resultB.id]; - if (countA && countB) return countB[2] - countA[2]; - } - return 0; - }) - .map((result) => ( -
  • setClickingResult(true)} - onMouseDown={() => setClickingResult(true)} - > - {result.name} -
  • - ))} + <> +
    + setSearchFocused(true), + onBlur: () => { + if (!isOpen) setSearchFocused(false); + }, + disabled: !data, + ref: searchInput, + })} + /> +
      + {isOpen && + results + .sort((resultA, resultB) => { + if (counts) { + const countA = counts[resultA.id]; + const countB = counts[resultB.id]; + if (countA && countB) return countB[2] - countA[2]; + } + return 0; + }) + .map((result, index) => ( +
    • + {result.name} +
    • + ))}
    - )} -
    +
+ ); }; diff --git a/package-lock.json b/package-lock.json index f32b0c0..7460768 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -555,6 +554,11 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" }, + "compute-scroll-into-view": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz", + "integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -657,6 +661,25 @@ "esutils": "^2.0.2" } }, + "downshift": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-6.1.7.tgz", + "integrity": "sha512-cVprZg/9Lvj/uhYRxELzlu1aezRcgPWBjTvspiGTVEU64gF5pRdSRKFVLcxqsZC637cLAGMbL40JavEfWnqgNg==", + "requires": { + "@babel/runtime": "^7.14.8", + "compute-scroll-into-view": "^1.0.17", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "tslib": "^2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + } + } + }, "earcut": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.3.tgz", @@ -1976,7 +1999,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -1986,8 +2008,7 @@ "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, @@ -2055,8 +2076,7 @@ "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regexp.prototype.flags": { "version": "1.3.2", diff --git a/package.json b/package.json index 4576f8a..2771ea4 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@types/javascript-color-gradient": "^1.3.0", "@types/mapbox-gl": "^2.6.0", "date-fns": "^2.28.0", + "downshift": "^6.1.7", "javascript-color-gradient": "^1.3.2", "mapbox-gl": "^2.6.1", "minisearch": "^3.2.0", diff --git a/styles/SearchBar.module.css b/styles/SearchBar.module.css index 5b15534..5367398 100644 --- a/styles/SearchBar.module.css +++ b/styles/SearchBar.module.css @@ -44,3 +44,7 @@ .search-results li:first-child { border-top: none; } + +.highlighted-result { + background-color: #bde4ff; +}