Skip to content

Commit 049c7b6

Browse files
committed
Fix search page navigation and improve performance
- Fix 404 error by adding trailing slash to search URLs - Use siteConfig.baseUrl instead of hardcoded paths for PREVIEW_PATH support - Prevent page reload by using React Router navigation with startTransition - Cache search documents to eliminate network delays on subsequent searches - Fix icon overlap on search page with proper padding (2.5rem left) - Remove webkit search decorations for cleaner appearance Resolves screen blink issue when searching by performing instant searches on cached data instead of fetching on every query.
1 parent 9a9ec40 commit 049c7b6

File tree

2 files changed

+95
-52
lines changed

2 files changed

+95
-52
lines changed

src/css/custom.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,24 @@ select {
4747
margin-right: 0.4em;
4848
vertical-align: middle;
4949
}
50+
51+
/* stylelint-disable selector-class-pattern */
52+
.search-page-input.navbar__search-input {
53+
padding: 0.5rem !important;
54+
padding-left: 2.5rem !important;
55+
border: 1px solid var(--ifm-color-emphasis-300);
56+
border-radius: var(--ifm-global-radius);
57+
}
58+
59+
[data-theme="dark"] .search-page-input.navbar__search-input {
60+
background: var(--ifm-background-surface-color) !important;
61+
border-color: var(--ifm-color-emphasis-300);
62+
}
63+
/* stylelint-enable selector-class-pattern */
64+
65+
.search-page-input::-webkit-search-decoration,
66+
.search-page-input::-webkit-search-cancel-button,
67+
.search-page-input::-webkit-search-results-button,
68+
.search-page-input::-webkit-search-results-decoration {
69+
display: none !important;
70+
}

src/pages/search.tsx

Lines changed: 74 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import Link from "@docusaurus/Link";
2-
import { useLocation } from "@docusaurus/router";
2+
import { useHistory, useLocation } from "@docusaurus/router";
33
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
44
import { usePluginData } from "@docusaurus/useGlobalData";
55
import Heading from "@theme/Heading";
66
import Layout from "@theme/Layout";
7-
import { useEffect, useMemo, useState } from "react";
7+
import { startTransition, useEffect, useMemo, useRef, useState } from "react";
88

99
interface 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

Comments
 (0)