on df16dfb help sign in
211 lines 7.9 kB history raw
1import { Component, createRef } from "react";
2import { glossary } from "../lib/glossary";
3import { withRouter } from "next/router";
4import Downshift from "downshift";
6class Search extends Component {
7 constructor(props) {
8 super(props);
9 this.state = {
10 results: [],
11 };
12 this.searchEndpoint = this.searchEndpoint.bind(this);
13 this.onInputValueChange = this.onInputValueChange.bind(this);
14 this.onSelect = this.onSelect.bind(this);
15 this.glossarySearch = this.glossarySearch.bind(this);
16 }
18 searchEndpoint(query) {
19 return `/api/search?q=${query}`;
20 }
22 glossarySearch(query) {
23 return glossary.filter((entry) => {
24 return query.toLowerCase() === entry.name || entry.symbol === query;
25 });
26 }
28 onSelect(item) {
29 console.log(item);
30 if (item.slug) {
31 this.props.router.push(item.slug);
32 }
34 this.setState({
35 query: "",
36 results: [],
37 });
39 this.props.closeSearch();
40 }
42 onInputValueChange(query) {
43 if (query.length) {
44 fetch(this.searchEndpoint(query))
45 .then((res) => res.json())
46 .then((res) => {
47 // Wrap results in an object which will tell React what component to use to render results.
48 const results = res.results.map((item) => ({
49 type: "RESULT",
50 content: item,
51 }));
53 const glossaryResults = this.glossarySearch(query).map((item) => ({
54 type: "GLOSSARY_RESULT",
55 content: item,
56 }));
58 const list = [...glossaryResults, ...results];
60 this.setState({ results: list });
61 });
62 } else {
63 this.setState({ results: [] });
64 }
65 }
67 render() {
68 const { state, props } = this;
70 if (props.showSearch) {
71 return (
72 <Downshift
73 onSelect={(selection) => this.onSelect(selection)}
74 onInputValueChange={(event) => this.onInputValueChange(event)}
75 itemToString={(item) => (item ? item.slug : "")}
76 defaultHighlightedIndex={0}
77 >
78 {({
79 getInputProps,
80 getItemProps,
81 getLabelProps,
82 getMenuProps,
83 isOpen,
84 inputValue,
85 highlightedIndex,
86 selectedItem,
87 getRootProps,
88 }) => (
89 <div className="fixed w-screen h-screen z-50 flex flex-col items-center p-4">
90 <div
91 onClick={(event) => props.closeSearch(event)}
92 className="top-0 left-0 fixed w-screen h-screen bg-washedWall"
93 />
94 <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">
95 <div
96 style={{ display: "inline-block" }}
97 {...getRootProps({}, { suppressRefError: true })}
98 >
99 <input
100 autoFocus
101 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 placeholder="Search..."
103 type="text"
104 onClick={(e) => e.stopPropagation()}
105 {...getInputProps({
106 onKeyDown: (event) => {
107 if (event.key === "Escape") {
108 // Prevent Downshift's default 'Escape' behavior.
109 event.nativeEvent.preventDownshiftDefault = true;
110 this.props.closeSearch(event);
111 }
112 },
113 })}
114 />
115 </div>
116 <ul {...getMenuProps()} className="overflow-y-scroll">
117 {isOpen
118 ? state.results.map((item, index) => {
119 const selected = highlightedIndex === index;
120 if (item.type === "GLOSSARY_RESULT") {
121 return (
122 <li
123 className={`cursor-pointer flex text-left w-full ${
124 selected ? "bg-green" : ""
125 }`}
126 {...getItemProps({
127 key: item.content.slug + "-" + index,
128 index,
129 item: item.content,
130 selected: highlightedIndex === index,
131 })}
132 >
133 <div className="font-semibold p-3">
134 <p
135 className={`text-base ${
136 selected ? "text-white" : "text-black"
137 }`}
138 >
139 {item.content.symbol.length > 0 && (
140 <code
141 className={`mr-1 rounded px-1 py-0.5 ${
142 selected ? "bg-washedWhite" : "bg-wall"
143 }`}
144 >
145 {item.content.symbol}
146 </code>
147 )}
148 {item.content.name}
149 </p>
150 <p
151 className={`font-normal text-base mt-1 ${
152 selected ? "text-white" : "text-black"
153 }`}
154 >
155 {item.content.desc}
156 </p>
157 </div>
158 </li>
159 );
160 }
161 if (item.type === "RESULT") {
162 return (
163 <li
164 className={`cursor-pointer flex text-left w-full ${
165 selected ? "bg-green" : ""
166 }`}
167 {...getItemProps({
168 key: item.content.link + "-" + index,
169 index,
170 item: item.content,
171 selected,
172 })}
173 >
174 <div className="p-3">
175 <p
176 className={`font-medium text-base ${
177 selected ? "text-white" : "text-black"
178 }`}
179 >
180 {item.content.parent !== "Content"
181 ? `${item.content.parent} /`
182 : ""}{" "}
183 {item.content.title}
184 </p>
185 <p
186 className={`text-base font-regular text-small ${
187 selected ? "text-midWhite" : "text-gray"
188 }`}
189 >
190 {item.content.content}
191 </p>
192 </div>
193 </li>
194 );
195 }
196 return null;
197 })
198 : null}
199 </ul>
200 </div>
201 </div>
202 )}
203 </Downshift>
204 );
205 } else {
206 return null;
207 }
208 }
211export default withRouter(Search);