1import { useCallback, useRef, useState, useEffect } from "react";
2import { glossary } from "../lib/glossary";
3import Link from "next/link";
4import { useRouter } from "next/router";
6const glossarySearch = (query) => {
7 return glossary.filter((entry) => {
8 return query.toLowerCase() === entry.name || entry.symbol === query;
12export default function Search(props) {
13 const searchRef = useRef(null);
14 const [query, setQuery] = useState("");
15 const [active, setActive] = useState(false);
16 const [results, setResults] = useState([]);
17 const searchEndpoint = (query) => `/api/search?q=${query}`;
19 const router = useRouter();
22 router.events.on("routeChangeComplete", handleRouteChange);
24 router.events.off("routeChangeComplete", handleRouteChange);
28 const handleRouteChange = () => {
35 const onChange = useCallback((event) => {
36 const query = event.target.value;
40 fetch(searchEndpoint(query))
41 .then((res) => res.json())
43 setResults(res.results);
50 const onFocus = useCallback(() => {
52 window.addEventListener("click", onClick);
55 const onClick = useCallback((event) => {
56 if (searchRef.current && !searchRef.current.contains(event.target)) {
58 window.removeEventListener("click", onClick);
63 let glossaryResults = [];
65 glossaryResults = glossarySearch(query);
68 const clear = useCallback((e) => {
73 // Locks document scrolling when search is open
74 if (typeof document !== "undefined") {
75 if (props.showSearch) {
76 document.body.style.overflow = "hidden";
78 document.body.style.overflow = "visible";
82 if (props.showSearch) {
85 onClick={props.closeSearch}
86 className="fixed w-screen h-screen bg-washedWall z-50 flex flex-col items-center p-4"
89 className="relative flex flex-col max-w-screen-lg md:my-32 w-full md:w-10/12 lg:w-8/12 xl:w-6/12 rounded-xl bg-white min-h-0"
92 <div className="relative">
94 className="text-lg md:text-xl lg:text-2xl font-medium text-green bg-transparent py-2 px-4 outline-none relative w-full"
97 placeholder="Search..."
100 onClick={(e) => e.stopPropagation()}
105 onClick={props.toggleSearch}
106 className="absolute cursor-pointer right-1.5 top-1.5 md:right-2 md:top-2 lg:right-2.5 lg:top-2.5"
108 <p className="px-2 h-8 bg-wall flex items-center justify-center text-sm rounded">
115 className={"overflow-y-scroll" + (query.length > 0 ? "" : "hidden")}
119 results.length === 0 &&
120 glossaryResults.length === 0 &&
122 <div className="flex font-semibold text-sm p-3 py-8 w-full">
123 <p className="mr-2 text-gray">No Results found for</p>
124 <p className="font-semibold">"{query}"</p>
127 {active && glossaryResults.length > 0 ? (
129 <div className="sticky top-0 flex font-semibold text-sm p-3 py-1 w-full bg-ultraDeepWall text-white">
130 <p className="font-semibold text-sm">Glossary</p>
133 {glossaryResults.map(({ name, link, desc, symbol }) => {
135 <Link href={link} as={link} key={link}>
136 <div className="cursor-pointer hover:bg-wall">
137 <div className="font-semibold p-3">
138 {symbol.length > 0 && (
139 <code className="mr-1 bg-wall rounded px-1 py-0.5">
144 <p className="font-normal text-base mt-1">{desc}</p>
152 {active && results.length > 0 && (
154 <div className="sticky top-0 flex font-semibold text-sm p-3 py-1 w-full bg-ultraDeepWall text-white">
155 <p className="font-semibold text-sm">
156 {results.length} results for "{query}"
161 .filter((e) => e.parent !== "Glossary")
162 .map(({ title, slug, content, parent }) => {
166 className="cursor-pointer hover:bg-wall p-3"
168 <Link href={slug} as={slug}>
170 <p className="font-medium text-base">
173 <p className="text-base font-regular text-small text-gray">
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list