@@ -1,142 +1,54 @@
1−import { useCallback, useRef, useState, useEffect, createContext } from "react";
1+import { Component, createRef } from "react";
2import { glossary } from "../lib/glossary";
2import { glossary } from "../lib/glossary";
3import Link from "next/link";
3import Link from "next/link";
4−import { useRouter } from "next/router";
4+import { withRouter } from "next/router";
5import { configure, HotKeys } from "react-hotkeys";
5import { configure, HotKeys } from "react-hotkeys";
6import classnames from "classnames";
6import classnames from "classnames";
7+import Downshift from "downshift";
8−const SearchContext = createContext();
9+class Search extends Component {
9−
10+ constructor(props) {
10−const glossarySearch = (query) => {
11+ super(props);
11− return glossary.filter((entry) => {
12+ this.state = {
12− return query.toLowerCase() === entry.name || entry.symbol === query;
13+ results: [],
16−function GlossaryResult(props) {
17− const selectedClassnames = classnames({
18− "bg-green": props.selected,
20−
15+ this.searchEndpoint = this.searchEndpoint.bind(this);
21− const codeBg = classnames({
16+ this.onInputValueChange = this.onInputValueChange.bind(this);
22− "bg-wall": !props.selected,
17+ this.onChange = this.onChange.bind(this);
23− "bg-washedWhite": props.selected,
18+ this.glossarySearch = this.glossarySearch.bind(this);
26− const text = classnames({
27− "text-black": !props.selected,
28− "text-white": props.selected,
33− className={`cursor-pointer ${selectedClassnames}`}
34− onMouseEnter={() => props.setCursor(props.index)}
36− <div className="font-semibold p-3">
37− <p className={`text-base ${text}`}>
38− {props.symbol.length > 0 && (
39− <code className={`mr-1 rounded px-1 py-0.5 ${codeBg}`}>
45− <p className={`font-normal text-base mt-1 ${text}`}>{props.desc}</p>
51−function Result(props) {
52− const selectedClassnames = classnames({
53− "bg-green": props.selected,
55− const text = classnames({
56− "text-black": !props.selected,
57− "text-white": props.selected,
59− const description = classnames({
60− "text-gray": !props.selected,
61− "text-midWhite": props.selected,
65− className={`cursor-pointer ${selectedClassnames}`}
66− onMouseEnter={() => props.setCursor(props.index)}
68− <div className="p-3">
69− <p className={`font-medium text-base ${text}`}>
70− {props.parent !== "Content" ? `${props.parent} /` : ""} {props.title}
73− className={`text-base font-regular text-small text-gray ${description}`}
82−function Subheading(props) {
84− <div className="sticky top-0 flex font-semibold text-sm p-3 py-1 w-full bg-ultraDeepWall text-white">
85− <p className="font-semibold text-sm">{props.title}</p>
90−export default function Search(props) {
91− const searchRef = useRef(null);
92− const [query, setQuery] = useState("");
93− const [active, setActive] = useState(false);
94− const [results, setResults] = useState([]);
95− const [cursor, setCursor] = useState(0);
96− const searchEndpoint = (query) => `/api/search?q=${query}`;
21+ searchEndpoint(query) {
97− const router = useRouter();
22+ return `/api/search?q=${query}`;
99− const keyMap = {
25+ glossarySearch(query) {
100− onNavUp: ["up"],
26+ console.log(query);
101− onNavDown: ["down"],
27+ return glossary.filter((entry) => {
102− onSelect: ["enter"],
28+ return query.toLowerCase() === entry.name || entry.symbol === query;
105− const handlers = {
32+ onChange(item) {
106− onNavUp: (event) => onNavUp(event),
33+ if (item.link) {
107− onNavDown: (event) => onNavDown(event),
34+ window.location.href = item.link;
108− onSelect: (event) => onSelect(event, results[cursor]),
111− configure({
37+ if (item.slug) {
112− // ignoreTags: [],
38+ this.props.router.push(item.slug);
113− ignoreTags: ["input", "select", "textarea"],
39+ }
114− ignoreEventsCondition: function () {},
117− {
41+ this.setState({
118− /* useEffect(() => {
42+ query: "",
119− router.events.on("routeChangeComplete", handleRouteChange);
43+ results: [],
120− return () => {
44+ });
121− router.events.off("routeChangeComplete", handleRouteChange);
123− }, [router.events]);
125− const handleRouteChange = () => {
129− props.closeSearch();
46+ this.props.closeSearch();
133− const onChange = useCallback((event) => {
49+ onInputValueChange(query) {
134− const query = event.target.value;
138 if (query.length) {
50 if (query.length) {
139− fetch(searchEndpoint(query))
51+ fetch(this.searchEndpoint(query))
140 .then((res) => res.json())
52 .then((res) => res.json())
141 .then((res) => {
53 .then((res) => {
142 // Wrap results in an object which will tell React what component to use to render results.
54 // Wrap results in an object which will tell React what component to use to render results.
@@ -145,154 +57,149 @@ export default function Search(props) {
145 content: item,
57 content: item,
148− const glossaryResults = glossarySearch(query).map((item) => ({
60+ const glossaryResults = this.glossarySearch(query).map((item) => ({
149 type: "GLOSSARY_RESULT",
61 type: "GLOSSARY_RESULT",
150 content: item,
62 content: item,
153 const list = [...glossaryResults, ...results];
65 const list = [...glossaryResults, ...results];
155− setResults(list);
67+ this.setState({ results: list });
158− setResults([]);
70+ this.setState({ results: [] });
162− const onFocus = useCallback(() => {
164− // window.addEventListener("click", onClick);
167− const onClick = useCallback((event) => {
168− if (searchRef.current && !searchRef.current.contains(event.target)) {
170− window.removeEventListener("click", onClick);
171− props.toggleSearch();
175− const onSelect = () => {
176− console.log(results, cursor);
179− /* const cursorItem = results[cursor].content
183− if (cursorItem.link) {
184− window.location.href = cursorItem.link
187− if (cursorItem.slug) {
188− router.push(cursorItem.slug)
191− props.toggleSearch();
195− props.closeSearch(); */
199− const onNavUp = useCallback((event) => {
200− // prevents cursor from moving in search input
201− event.preventDefault();
202− setCursor((state) => state - 1);
205− const onNavDown = useCallback((event) => {
206− // prevents cursor from moving in search input
207− event.preventDefault();
208− setCursor((state) => state + 1);
211− const clear = (e) => {
212− e.stopPropagation();
216− // Locks document scrolling when search is open
217− if (typeof document !== "undefined") {
218− if (props.showSearch) {
219− document.body.style.overflow = "hidden";
221− document.body.style.overflow = "visible";
75+ const { state, props } = this;
225− if (props.showSearch) {
77+ if (props.isOpen) {
226− return (
78+ return (
227− <SearchContext.Provider value={42}>
79+ <Downshift
228− <HotKeys handlers={handlers} keyMap={keyMap}>
80+ onChange={(selection) => this.onChange(selection)}
229− <div
81+ onInputValueChange={(event) => this.onInputValueChange(event)}
230− onClick={props.closeSearch}
82+ itemToString={(item) =>
83+ item ? item.content.slug || item.content.link : ""
85+ defaultHighlightedIndex={0}
231− className="fixed w-screen h-screen bg-washedWall z-50 flex flex-col items-center p-4"
98+ <div className="fixed w-screen h-screen bg-washedWall z-50 flex flex-col items-center p-4">
233− <div 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">
99+ <div 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 overflow-hidden">
234− <div className="relative">
100+ <div
235− <input
101+ style={{ display: "inline-block" }}
236− className="text-lg md:text-xl lg:text-2xl font-medium text-green bg-transparent py-2 px-4 outline-none relative w-full"
102+ {...getRootProps({}, { suppressRefError: true })}
239− placeholder="Search..."
242− onClick={(e) => e.stopPropagation()}
247− onClick={props.toggleSearch}
248− className="absolute cursor-pointer right-1.5 top-1.5 md:right-2 md:top-2 lg:right-2.5 lg:top-2.5"
250− <p className="px-2 h-8 bg-wall flex items-center justify-center text-sm rounded">
104+ <input
252− </p>
106+ className="text-lg md:text-xl lg:text-2xl font-medium text-green bg-transparent py-2 px-4 outline-none relative w-full"
253− </span>
107+ {...getInputProps()}
258− "overflow-y-scroll border-t-2 border-wall" +
110+ <ul {...getMenuProps()} className="overflow-y-scroll">
259− (query.length > 0 ? "" : "hidden border-transparent")
111+ {isOpen
262− {active && query !== ""
263− ? results.map((item, index) => {
112+ ? state.results.map((item, index) => {
113+ const selected = highlightedIndex === index;
264− if (item.type === "GLOSSARY_RESULT") {
114+ if (item.type === "GLOSSARY_RESULT") {
265− return (
115+ return (
266− <GlossaryResult
116+ <li
117+ className={`cursor-pointer flex text-left w-full ${
118+ selected ? "bg-green" : ""
267− key={item.link + "-" + index}
121+ key: item.content.link + "-" + index,
124+ selected: highlightedIndex === index,
127+ <div className="font-semibold p-3">
129+ className={`text-base ${
130+ selected ? "text-white" : "text-black"
133+ {item.content.symbol.length > 0 && (
135+ className={`mr-1 rounded px-1 py-0.5 ${
136+ selected ? "bg-washedWhite" : "bg-wall"
268− {...item.content}
139+ {item.content.symbol}
269− index={index}
140+ </code>
270− setCursor={setCursor}
141+ )}
271− selected={index === cursor}
142+ {item.content.name}
145+ className={`font-normal text-base mt-1 ${
146+ selected ? "text-white" : "text-black"
276− if (item.type === "RESULT") {
155+ if (item.type === "RESULT") {
277− return (
156+ return (
158+ className={`cursor-pointer flex text-left w-full ${
159+ selected ? "bg-green" : ""
279− key={item.link + "-" + index}
162+ key: item.content.link + "-" + index,
168+ <div className="p-3">
170+ className={`font-medium text-base ${
171+ selected ? "text-white" : "text-black"
174+ {item.content.parent !== "Content"
280− {...item.content}
175+ ? `${item.content.parent} /`
281− index={index}
176+ : ""}{" "}
282− setCursor={setCursor}
177+ {item.content.title}
283− selected={index === cursor}
178+ </p>
180+ className={`text-base font-regular text-small ${
181+ selected ? "text-midWhite" : "text-gray"
184+ {item.content.content}
287− return null;
190+ return null;
293− </HotKeys>
197+ </Downshift>
294− </SearchContext.Provider>
198+ );
205+export default withRouter(Search);