@@ -10,23 +10,54 @@ export function DocsSearch() {
1010 const [ query , setQuery ] = state ( '' ) ;
1111 const [ loading , setLoading ] = state ( false ) ;
1212 const [ results , setResults ] = state < readonly DocsSearchRecord [ ] > ( [ ] ) ;
13+ const [ error , setError ] = state ( false ) ;
14+ let searchGeneration = 0 ;
15+
16+ const focusInput = ( element : HTMLElement | null ) => {
17+ window . setTimeout (
18+ ( ) =>
19+ element
20+ ?. querySelector < HTMLInputElement > ( '[data-docs-search-input]' )
21+ ?. focus ( ) ,
22+ 0
23+ ) ;
24+ } ;
25+
26+ const openSearch = ( element ?: HTMLElement | null ) => {
27+ setOpen ( true ) ;
28+ focusInput ( element ?? null ) ;
29+ } ;
1330
1431 const runSearch = async ( value : string ) => {
32+ const generation = ++ searchGeneration ;
1533 setQuery ( value ) ;
34+ setError ( false ) ;
1635 if ( ! value . trim ( ) ) {
1736 setResults ( [ ] ) ;
37+ setLoading ( false ) ;
1838 return ;
1939 }
2040 setLoading ( true ) ;
21- const { searchDocs } = await import ( './search-index' ) ;
22- setResults ( searchDocs ( value ) ) ;
23- setLoading ( false ) ;
41+ try {
42+ const { searchDocs } = await import ( './search-index' ) ;
43+ if ( generation !== searchGeneration ) return ;
44+ setResults ( searchDocs ( value ) ) ;
45+ } catch {
46+ if ( generation !== searchGeneration ) return ;
47+ setResults ( [ ] ) ;
48+ setError ( true ) ;
49+ } finally {
50+ if ( generation === searchGeneration ) setLoading ( false ) ;
51+ }
2452 } ;
2553
2654 const close = ( ) => {
55+ searchGeneration += 1 ;
2756 setOpen ( false ) ;
2857 setQuery ( '' ) ;
2958 setResults ( [ ] ) ;
59+ setLoading ( false ) ;
60+ setError ( false ) ;
3061 } ;
3162 return (
3263 < div
@@ -44,14 +75,7 @@ export function DocsSearch() {
4475 ) )
4576 ) {
4677 event . preventDefault ( ) ;
47- setOpen ( true ) ;
48- window . setTimeout (
49- ( ) =>
50- element
51- . querySelector < HTMLInputElement > ( '[data-docs-search-input]' )
52- ?. focus ( ) ,
53- 0
54- ) ;
78+ openSearch ( element ) ;
5579 }
5680 if ( event . key === 'Escape' && open ( ) ) close ( ) ;
5781 } ) ;
@@ -60,7 +84,9 @@ export function DocsSearch() {
6084 < button
6185 class = "docs-search__trigger"
6286 type = "button"
63- onClick = { ( ) => setOpen ( true ) }
87+ onClick = { ( event : Event ) =>
88+ openSearch ( ( event . currentTarget as HTMLElement ) . parentElement )
89+ }
6490 aria-haspopup = "dialog"
6591 >
6692 < SearchIcon size = { 16 } aria-hidden = "true" />
@@ -106,6 +132,8 @@ export function DocsSearch() {
106132 Search page titles, component aliases, package imports, CLI
107133 commands, and every published API symbol.
108134 </ p >
135+ ) : error ( ) ? (
136+ < p > Search is temporarily unavailable. Please try again.</ p >
109137 ) : results ( ) . length ? (
110138 < ul >
111139 { results ( ) . map ( ( result ) => (
0 commit comments