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