11import Link from "@docusaurus/Link" ;
2- import { useLocation } from "@docusaurus/router" ;
2+ import { useHistory , useLocation } from "@docusaurus/router" ;
33import useDocusaurusContext from "@docusaurus/useDocusaurusContext" ;
44import { usePluginData } from "@docusaurus/useGlobalData" ;
55import Heading from "@theme/Heading" ;
66import Layout from "@theme/Layout" ;
7- import { useEffect , useMemo , useState } from "react" ;
7+ import { startTransition , useEffect , useMemo , useRef , useState } from "react" ;
88
99interface SearchDocument {
1010 url : string ;
@@ -35,22 +35,31 @@ export default function SearchPage() {
3535 fileNames ?: { searchDoc ?: string } ;
3636 } ;
3737
38+ const history = useHistory ( ) ;
39+ const searchInputRef = useRef < HTMLInputElement > ( null ) ;
3840 const [ isReady , setIsReady ] = useState ( false ) ;
3941 const [ results , setResults ] = useState < SearchResult [ ] > ( [ ] ) ;
4042 const [ error , setError ] = useState < string | null > ( null ) ;
43+ const searchDocumentsRef = useRef < SearchDocument [ ] | null > ( null ) ;
44+
45+ const handleSearchSubmit = ( event : React . FormEvent < HTMLFormElement > ) => {
46+ event . preventDefault ( ) ;
47+ const searchQuery = searchInputRef . current ?. value . trim ( ) ?? "" ;
48+ if ( searchQuery ) {
49+ startTransition ( ( ) => {
50+ history . push (
51+ `${ siteConfig . baseUrl } search/?q=${ encodeURIComponent ( searchQuery ) } ` ,
52+ ) ;
53+ } ) ;
54+ }
55+ } ;
4156
4257 useEffect ( ( ) => {
43- if ( ! query ) {
44- setIsReady ( true ) ;
45- setResults ( [ ] ) ;
58+ if ( process . env . NODE_ENV !== "production" ) {
4659 return ;
4760 }
4861
49- if ( process . env . NODE_ENV !== "production" ) {
50- setError (
51- "Search is only available in production mode. Run 'pnpm build && pnpm serve' to test search." ,
52- ) ;
53- setIsReady ( true ) ;
62+ if ( searchDocumentsRef . current ) {
5463 return ;
5564 }
5665
@@ -67,67 +76,80 @@ export default function SearchPage() {
6776 } > ;
6877 } )
6978 . then ( ( searchData ) => {
70- const documents : SearchDocument [ ] = searchData . searchDocs ?? [ ] ;
71-
72- if ( documents . length === 0 ) {
73- throw new Error ( "No documents found in search index" ) ;
74- }
75-
76- const filtered = documents
77- . filter ( ( document : SearchDocument ) => {
78- const searchText = (
79- ( document . title ?? "" ) +
80- " " +
81- ( document . pageTitle ?? "" ) +
82- " " +
83- ( document . content ?? "" )
84- ) . toLowerCase ( ) ;
85- return searchText . includes ( query . toLowerCase ( ) ) ;
86- } )
87- . slice ( 0 , 50 )
88- . map ( ( document : SearchDocument ) => {
89- const excerpt =
90- ( document . content ?? "" ) . slice ( 0 , 220 ) +
91- ( ( document . content ?? "" ) . length > 220 ? "…" : "" ) ;
92-
93- return {
94- title : document . pageTitle ?? document . title ?? document . url ,
95- route : document . url ,
96- excerpt,
97- } ;
98- } ) ;
99-
100- setResults ( filtered ) ;
101- setIsReady ( true ) ;
79+ searchDocumentsRef . current = searchData . searchDocs ?? [ ] ;
10280 } )
10381 . catch ( ( error_ : Error ) => {
104- console . error ( "Search error :" , error_ ) ;
82+ console . error ( "Failed to load search index :" , error_ ) ;
10583 setError ( error_ . message ) ;
106- setIsReady ( true ) ;
10784 } ) ;
108- } , [ query , pluginData , siteConfig ] ) ;
85+ } , [ pluginData , siteConfig ] ) ;
86+
87+ useEffect ( ( ) => {
88+ if ( ! query ) {
89+ setIsReady ( true ) ;
90+ setResults ( [ ] ) ;
91+ return ;
92+ }
93+
94+ if ( process . env . NODE_ENV !== "production" ) {
95+ setError (
96+ "Search is only available in production mode. Run 'pnpm build && pnpm serve' to test search." ,
97+ ) ;
98+ setIsReady ( true ) ;
99+ return ;
100+ }
101+
102+ if ( ! searchDocumentsRef . current ) {
103+ return ;
104+ }
105+
106+ const documents = searchDocumentsRef . current ;
107+ const filtered = documents
108+ . filter ( ( document : SearchDocument ) => {
109+ const searchText = (
110+ ( document . title ?? "" ) +
111+ " " +
112+ ( document . pageTitle ?? "" ) +
113+ " " +
114+ ( document . content ?? "" )
115+ ) . toLowerCase ( ) ;
116+ return searchText . includes ( query . toLowerCase ( ) ) ;
117+ } )
118+ . slice ( 0 , 50 )
119+ . map ( ( document : SearchDocument ) => {
120+ const excerpt =
121+ ( document . content ?? "" ) . slice ( 0 , 220 ) +
122+ ( ( document . content ?? "" ) . length > 220 ? "…" : "" ) ;
123+
124+ return {
125+ title : document . pageTitle ?? document . title ?? document . url ,
126+ route : document . url ,
127+ excerpt,
128+ } ;
129+ } ) ;
130+
131+ setResults ( filtered ) ;
132+ setIsReady ( true ) ;
133+ } , [ query ] ) ;
109134
110135 return (
111136 < Layout description = "Search documentation results" title = "Search Results" >
112137 < main className = "container margin-vert--lg" >
113138 < Heading as = "h1" > Search Results</ Heading >
114139
115- < form
116- action = { `${ siteConfig . baseUrl } search/` }
117- method = "get"
118- style = { { marginBottom : "1.5rem" } }
119- >
140+ < form style = { { marginBottom : "1.5rem" } } onSubmit = { handleSearchSubmit } >
120141 < input
142+ key = { rawQuery }
143+ ref = { searchInputRef }
121144 aria-label = "Search docs"
122- className = "navbar__search-input"
145+ className = "navbar__search-input search-page-input "
123146 defaultValue = { rawQuery }
124147 name = "q"
125148 placeholder = "Search docs"
126149 type = "search"
127150 style = { {
128151 width : "100%" ,
129152 maxWidth : 560 ,
130- padding : "0.5rem 1rem" ,
131153 fontSize : "1rem" ,
132154 } }
133155 />
0 commit comments