DocsSuggestions.tsx88 lines · main
1import { useDocsSearch, type DocsSearchResult } from 'common'
2import { Book, Github, Loader2 } from 'lucide-react'
3import { cn } from 'ui'
4
5import { useChangedSync } from '@/hooks/misc/useChanged'
6import { DOCS_URL } from '@/lib/constants'
7
8function useDocsSuggestions(subject: string) {
9 const { handleDocsSearchDebounced, resetSearch, searchState } = useDocsSearch()
10
11 const trimmedSubject = subject.trim()
12 const subjectChanged = useChangedSync(trimmedSubject)
13
14 if (subjectChanged && trimmedSubject) {
15 handleDocsSearchDebounced(trimmedSubject)
16 } else if (subjectChanged && !trimmedSubject) {
17 resetSearch()
18 }
19
20 return searchState
21}
22
23interface DocsSuggestionsProps {
24 searchString: string
25}
26
27export function DocsSuggestions({ searchString }: DocsSuggestionsProps) {
28 const searchState = useDocsSuggestions(searchString)
29 const results =
30 'results' in searchState
31 ? searchState.results
32 : 'staleResults' in searchState
33 ? searchState.staleResults
34 : []
35 const resultsStale = searchState.status === 'loading'
36
37 return (
38 <>
39 {searchState.status === 'loading' && <DocsSuggestions_Loading />}
40 {results.length > 0 && <DocsSuggestions_Results results={results} isStale={resultsStale} />}
41 </>
42 )
43}
44
45function DocsSuggestions_Loading() {
46 return (
47 <div className="flex items-center gap-2 text-sm text-foreground-light">
48 <Loader2 className="animate-spin" size={14} />
49 <span>Searching for relevant resources...</span>
50 </div>
51 )
52}
53
54interface DocsSuggestions_ResultsProps {
55 results: DocsSearchResult[]
56 isStale: boolean
57}
58
59function DocsSuggestions_Results({ results, isStale }: DocsSuggestions_ResultsProps) {
60 return (
61 <ul
62 className={cn(
63 'flex flex-col gap-y-0.5 transition-opacity duration-200',
64 isStale ? 'opacity-50' : 'opacity-100'
65 )}
66 >
67 {results.slice(0, 5).map((page) => {
68 return (
69 <li key={page.id} className="flex items-center gap-x-1">
70 {page.type === 'github-discussions' ? (
71 <Github size={16} className="text-foreground-muted" />
72 ) : (
73 <Book size={16} className="text-foreground-muted" />
74 )}
75 <a
76 href={page.type === 'github-discussions' ? page.path : `${DOCS_URL}${page.path}`}
77 target="_blank"
78 rel="noreferrer"
79 className="text-sm text-foreground-light hover:text-foreground transition"
80 >
81 {page.title}
82 </a>
83 </li>
84 )
85 })}
86 </ul>
87 )
88}