Use downshift-js for better search bar experience
This commit is contained in:
parent
86a7516c3b
commit
6005028439
@ -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,72 +58,63 @@ const SearchBar: React.FC<Props> = ({ clearSelectedCell, counts, map }) => {
|
||||
}, [data]);
|
||||
|
||||
const searchInput = useRef<HTMLInputElement | null>(null);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
const [searchFocused, setSearchFocused] = useState<boolean>(false);
|
||||
const [clickingResult, setClickingResult] = useState<boolean>(false);
|
||||
const [results, setResults] = useState<SearchResult[]>([]);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}, [searchFocused]);
|
||||
|
||||
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value);
|
||||
if (searchEngine.current) {
|
||||
const results: SearchResult[] = searchEngine.current.search(
|
||||
e.target.value
|
||||
);
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
const onChooseResult =
|
||||
(item: Mod) =>
|
||||
(e: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>) => {
|
||||
router.push({ query: { mod: item.id } });
|
||||
setSearch("");
|
||||
setResults([]);
|
||||
setClickingResult(false);
|
||||
},
|
||||
onSelectedItemChange: ({ selectedItem }) => {
|
||||
if (selectedItem) {
|
||||
setSearchFocused(false);
|
||||
};
|
||||
router.push({ query: { mod: selectedItem.id } });
|
||||
if (searchInput.current) searchInput.current.blur();
|
||||
reset();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${styles["search-bar"]} ${
|
||||
searchFocused ? styles["search-bar-focused"] : ""
|
||||
}`}
|
||||
{...getComboboxProps()}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search mods or cells…"
|
||||
onChange={onChange}
|
||||
onFocus={() => setSearchFocused(true)}
|
||||
onBlur={() => {
|
||||
if (!clickingResult) {
|
||||
setSearch("");
|
||||
setResults([]);
|
||||
setSearchFocused(false);
|
||||
}
|
||||
}}
|
||||
value={search}
|
||||
ref={searchInput}
|
||||
disabled={!data}
|
||||
{...getInputProps({
|
||||
type: "text",
|
||||
placeholder: "Search mods or cells…",
|
||||
onFocus: () => setSearchFocused(true),
|
||||
onBlur: () => {
|
||||
if (!isOpen) setSearchFocused(false);
|
||||
},
|
||||
disabled: !data,
|
||||
ref: searchInput,
|
||||
})}
|
||||
/>
|
||||
{results.length > 0 && (
|
||||
<ul className={styles["search-results"]}>
|
||||
{results
|
||||
<ul
|
||||
className={styles["search-results"]}
|
||||
style={!isOpen ? { display: "none" } : {}}
|
||||
{...getMenuProps()}
|
||||
>
|
||||
{isOpen &&
|
||||
results
|
||||
.sort((resultA, resultB) => {
|
||||
if (counts) {
|
||||
const countA = counts[resultA.id];
|
||||
@ -133,19 +123,22 @@ const SearchBar: React.FC<Props> = ({ clearSelectedCell, counts, map }) => {
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
.map((result) => (
|
||||
.map((result, index) => (
|
||||
<li
|
||||
key={result.id}
|
||||
onClick={onChooseResult({ id: result.id, name: result.name })}
|
||||
onTouchStart={() => setClickingResult(true)}
|
||||
onMouseDown={() => setClickingResult(true)}
|
||||
{...getItemProps({ item: result, index })}
|
||||
className={`${
|
||||
highlightedIndex === index
|
||||
? styles["highlighted-result"]
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{result.name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
32
package-lock.json
generated
32
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -44,3 +44,7 @@
|
||||
.search-results li:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.highlighted-result {
|
||||
background-color: #bde4ff;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user