1import { Component, createRef } from "react";
2import { withRouter } from "next/router";
3import debounce from "lodash.debounce";
4import Downshift from "downshift";
5import ob from "urbit-ob";
6import Sigil from "../components/Sigil";
7import levenSort from "leven-sort";
9class Search extends Component {
15 this.searchEndpoint = this.searchEndpoint.bind(this);
16 this.onInputValueChange = this.onInputValueChange.bind(this);
17 this.onSelect = this.onSelect.bind(this);
18 this.glossarySearch = this.glossarySearch.bind(this);
19 this.patpSearch = this.patpSearch.bind(this);
20 this.devSearch = this.devSearch.bind(this);
21 this.opsSearch = this.opsSearch.bind(this);
24 searchEndpoint(query) {
25 return `/api/search?q=${query}`;
29 return `/api/dev-search?q=${query}`;
32 glossarySearch(query) {
33 return `/api/glossary?q=${encodeURIComponent(query)}`;
37 return `/api/ops-search?q=${query}`;
42 (ob.isValidPatp(`~${deSig(query.toLowerCase())}`) &&
43 `~${deSig(query.toLowerCase())}`.length < 15) ||
44 (!isNaN(query) && query <= 4294967295)
50 this.props.router.push(item.slug);
58 this.props.closeSearch();
61 onInputValueChange = debounce(async (query) => {
63 const search = fetch(this.searchEndpoint(query))
64 .then((res) => res.json())
65 .then(async (res) => {
66 return res.results.map((item) => ({
72 const patp = this.patpSearch(query)
75 : ob.patp(ob.patp2dec(`~${deSig(query)}`))
78 const patpResult = this.patpSearch(query)
90 const glossarySearch = fetch(this.glossarySearch(query))
91 .then((res) => res.json())
93 return res.results.map((item) => ({
94 type: "GLOSSARY_RESULT",
99 const devSearch = fetch(this.devSearch(query))
100 .then((res) => res.json())
102 return res.results.map((item) => ({
108 const opsSearch = fetch(this.opsSearch(query))
109 .then((res) => res.json())
111 return res.results.map((item) => ({
117 const [results, glossaryResults, devResults, opsResults] =
118 await Promise.all([search, glossarySearch, devSearch, opsSearch]);
128 this.setState({ results: list });
130 this.setState({ results: [] });
135 const { state, props } = this;
137 if (props.showSearch) {
140 onSelect={(selection) => this.onSelect(selection)}
141 onInputValueChange={(event) => this.onInputValueChange(event)}
142 itemToString={(item) => (item ? item.slug : "")}
143 defaultHighlightedIndex={0}
156 <div className="fixed w-screen h-screen z-50 flex flex-col items-center p-4">
158 onClick={(event) => props.closeSearch(event)}
159 className="top-0 left-0 fixed w-screen h-screen bg-washedWall"
161 <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">
163 style={{ display: "inline-block" }}
164 {...getRootProps({}, { suppressRefError: true })}
168 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"
169 placeholder="Search..."
171 onClick={(e) => e.stopPropagation()}
173 onKeyDown: (event) => {
174 if (event.key === "Escape") {
175 // Prevent Downshift's default 'Escape' behavior.
176 event.nativeEvent.preventDownshiftDefault = true;
177 this.props.closeSearch(event);
183 <ul {...getMenuProps()} className="overflow-y-scroll">
185 ? state.results.map((item, index) => {
186 const selected = highlightedIndex === index;
187 if (item.type === "PATP") {
190 className={`cursor-pointer p-2 flex space-x-2 items-center text-left w-full ${
191 selected ? "bg-green-400" : ""
194 key: item.content.slug + "-" + index,
197 selected: highlightedIndex === index,
200 <div className="rounded-md overflow-hidden">
202 patp={item.content.patp}
207 <p className="font-mono">{item.content.patp}</p>
211 if (item.type === "GLOSSARY_RESULT") {
214 className={`cursor-pointer flex text-left w-full ${
215 selected ? "bg-green-400" : ""
218 key: item.content.slug + "-" + index,
221 selected: highlightedIndex === index,
224 <div className="font-semibold p-3">
226 className={`text-base ${
227 selected ? "text-white" : "text-wall-600"
230 {item.content.symbol.length > 0 && (
232 className={`mr-1 rounded px-1 py-0.5 ${
238 {item.content.symbol}
244 className={`font-normal text-base mt-1 ${
245 selected ? "text-white" : "text-wall-600"
247 dangerouslySetInnerHTML={{
248 __html: item.content.desc,
255 if (item.type === "RESULT") {
258 className={`cursor-pointer flex text-left w-full ${
259 selected ? "bg-green-400" : ""
262 key: item.content.link + "-" + index,
268 <div className="p-3">
270 className={`font-medium text-base ${
271 selected ? "text-white" : "text-wall-600"
274 {item.content.parent !== "Content"
275 ? `${item.content.parent} /`
280 className={`text-base font-regular text-small ${
281 selected ? "text-midWhite" : "text-wall-500"
284 {item.content.content}
290 if (item.type === "DEV_RESULT") {
291 const devItem = Object.assign({}, item.content);
294 ] = `https://developers.urbit.org${item.content.slug}`;
297 className={`cursor-pointer flex text-left w-full ${
298 selected ? "bg-green-400" : ""
301 key: item.content.link + "-" + index,
307 <div className="p-3">
309 className={`font-medium text-base ${
310 selected ? "text-white" : "text-wall-600"
313 <span className="text-wall-400">
314 {item.content.parent !== "Content"
315 ? `developers.urbit.org / ${item.content.parent} /`
316 : "developers.urbit.org /"}{" "}
321 className={`text-base font-regular text-small ${
322 selected ? "text-midWhite" : "text-wall-500"
325 {item.content.content}
331 if (item.type === "OPS_RESULT") {
332 const opsItem = Object.assign({}, item.content);
335 ] = `https://operators.urbit.org${item.content.slug}`;
338 className={`cursor-pointer flex text-left w-full ${
339 selected ? "bg-green-400" : ""
342 key: item.content.link + "-" + index,
348 <div className="p-3">
350 className={`font-medium text-base ${
351 selected ? "text-white" : "text-wall-600"
354 <span className="text-wall-400">
355 {item.content.parent !== "Content"
356 ? `operators.urbit.org / ${item.content.parent} /`
357 : "operators.urbit.org /"}{" "}
362 className={`text-base font-regular text-small ${
363 selected ? "text-midWhite" : "text-wall-500"
366 {item.content.content}
387function deSig(string) {
388 return string.startsWith("~") ? string.substring(1) : string;
391export default withRouter(Search);
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