/[sudobot]/trunk/docs/components/Searching/SearchModal.tsx
ViewVC logotype

Contents of /trunk/docs/components/Searching/SearchModal.tsx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 575 - (show annotations)
Mon Jul 29 17:59:26 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 5385 byte(s)
chore: add trunk
1 import useDebouncedState from "@/hooks/useDebouncedState";
2 import { Button, CircularProgress, TextField } from "@mui/material";
3 import { useEffect, useState } from "react";
4 import { MdClose } from "react-icons/md";
5 import SearchResult from "./SearchResult";
6
7 type SearchModalProps = {
8 onClose: () => void;
9 };
10
11 export type SearchResultItem = {
12 title?: string;
13 description?: string;
14 data: string;
15 match: "title" | "description" | "data";
16 url: string;
17 };
18
19 export default function SearchModal({ onClose }: SearchModalProps) {
20 const [query, isQueued, setQuery] = useDebouncedState<string | null>(
21 null,
22 500,
23 );
24 const [results, setResults] = useState<SearchResultItem[] | null>(null);
25 const [isLoading, setIsLoading] = useState(false);
26 const [isNotFound, setIsNotFound] = useState(false);
27
28 useEffect(() => {
29 if (!query?.trim()) {
30 return;
31 }
32
33 const controller = new AbortController();
34
35 if (!isLoading) {
36 setIsLoading(true);
37 }
38
39 fetch(`/search?q=${encodeURIComponent(query)}`, {
40 signal: controller.signal,
41 })
42 .then(response => response.json())
43 .then(data => {
44 if (isNotFound) {
45 setIsNotFound(false);
46 }
47
48 setIsLoading(false);
49 setResults(data.results);
50
51 if (data.results.length === 0) {
52 setIsNotFound(true);
53 }
54 })
55 .catch(console.error);
56
57 return () => controller.abort();
58 }, [query]);
59
60 return (
61 <>
62 <div
63 className="h-[100vh] w-[100vw] fixed top-0 left-0 bg-[rgba(0,0,0,0.3)] z-[10001]"
64 onClick={onClose}
65 >
66 <div
67 onClick={event => event.stopPropagation()}
68 className="max-h-[95vh] block z-[10002] shadow-[0_0_1px_1px_rgba(255,255,255,0.2)] fixed bottom-[10px] lg:top-[50vh] left-[50%] translate-x-[-50%] lg:translate-y-[-50%] bg-[#222] min-h-[50vh] overflow-y-scroll w-[calc(100%-20px)] lg:w-[auto] md:min-w-[50vw] rounded-md p-4"
69 >
70 <div className="text-xl lg:text-2xl text-center mb-5 grid grid-cols-[1fr_5fr_1fr]">
71 <span></span>
72 <span>Search Docs</span>
73 <div className="flex justify-end">
74 <Button
75 style={{ minWidth: 0, color: "white" }}
76 onClick={onClose}
77 >
78 <MdClose />
79 </Button>
80 </div>
81 </div>
82
83 <TextField
84 fullWidth
85 autoFocus
86 type="text"
87 variant="outlined"
88 placeholder="Type here to search"
89 onChange={event => setQuery(event.target.value.trim())}
90 onKeyUp={event => {
91 if (!(event.target as HTMLInputElement).value) {
92 setQuery(null);
93 setResults(null);
94 }
95
96 if (isNotFound) {
97 setIsNotFound(false);
98 }
99 }}
100 />
101 <br />
102 <div className="mt-4">
103 {isLoading ? (
104 <div className="flex justify-center items-center">
105 <CircularProgress />
106 </div>
107 ) : results && results.length > 0 && !isNotFound ? (
108 <>
109 {results?.length && (
110 <>
111 <p className="text-[#aaa] text-sm">
112 Found {results.length} results.
113 </p>
114 <br />
115 </>
116 )}
117
118 {results?.map((result, index) => (
119 <SearchResult
120 result={result}
121 query={query ?? ""}
122 key={index}
123 onClick={onClose}
124 />
125 ))}
126 </>
127 ) : isNotFound ? (
128 <h3 className="text-lg md:text-xl text-center">
129 No results found.{" "}
130 <span className="text-[#999]">
131 Maybe search again with a different
132 keyboard?
133 </span>
134 </h3>
135 ) : (
136 ""
137 )}
138 </div>
139 </div>
140 </div>
141 </>
142 );
143 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26