@@ -15,7 +15,13 @@ import {
1515 SpaceBetweenVerticallyIcon ,
1616 TextIcon
1717} from '@radix-ui/react-icons' ;
18- import { Command , Dialog , EmptyState , IconButton } from '@raystack/apsara' ;
18+ import {
19+ Command ,
20+ Dialog ,
21+ EmptyState ,
22+ IconButton ,
23+ Spinner
24+ } from '@raystack/apsara' ;
1925import {
2026 flattenTree ,
2127 type Item as PageItem ,
@@ -36,10 +42,7 @@ type SearchItems = {
3642 items : Item [ ] ;
3743} ;
3844
39- // Docs pages carry no icon in their data, so map known page slugs to icons
40- // (overview + foundations pages get meaningful ones). Everything else —
41- // components and any future pages — falls back to a generic icon so every
42- // top-level row stays icon-aligned.
45+ /* Map known page slugs to icons; everything else falls back to a generic one. */
4346const PAGE_ICONS : Record < string , typeof MagnifyingGlassIcon > = {
4447 docs : ReaderIcon ,
4548 'getting-started' : RocketIcon ,
@@ -59,8 +62,7 @@ const getPageIcon = (url: string): typeof MagnifyingGlassIcon =>
5962export default function DocsSearch ( { pageTree } : { pageTree : Root } ) {
6063 const router = useRouter ( ) ;
6164 const [ open , setOpen ] = useState ( false ) ;
62- // Per-result manual expand/collapse overrides; reset whenever the query
63- // changes so the auto-open rule re-applies for the fresh result set.
65+ /* Manual expand/collapse overrides; reset on query change. */
6466 const [ openOverrides , setOpenOverrides ] = useState < Record < string , boolean > > (
6567 { }
6668 ) ;
@@ -86,9 +88,8 @@ export default function DocsSearch({ pageTree }: { pageTree: Root }) {
8688 const flattened = flattenTree ( pageTree . children ) ;
8789 if ( ! flattened . length ) return [ ] ;
8890
89- // Default view shows the overview + foundations (theme) pages grouped by
90- // folder. Components are intentionally excluded — there are dozens of them,
91- // so they only surface once the user actually searches.
91+ /* Default view: overview + foundations grouped by folder; components are
92+ excluded until the user searches. */
9293 const items = flattened . reduce < Record < string , Item [ ] > > ( ( acc , item ) => {
9394 const folder = getFolderFromUrl ( item . url ) ;
9495 if ( folder === 'components' ) return acc ;
@@ -106,6 +107,7 @@ export default function DocsSearch({ pageTree }: { pageTree: Root }) {
106107
107108 const trimmedQuery = search . trim ( ) ;
108109 const isSearching = trimmedQuery . length > 0 ;
110+ const isLoading = isSearching && query . isLoading ;
109111 const results =
110112 isSearching && query . data && query . data !== 'empty' ? query . data : [ ] ;
111113
@@ -152,10 +154,8 @@ export default function DocsSearch({ pageTree }: { pageTree: Root }) {
152154
153155 const items = ! isSearching ? defaultItems : searchResults ;
154156
155- // The `items` prop opts the Command out of its built-in per-item filtering
156- // and group-unwrapping: results are already filtered by fumadocs, and the
157- // grouped layout must stay intact while searching. An empty array is still
158- // truthy, so `hasItems` stays true even when there are no results.
157+ /* The `items` prop opts Command out of built-in filtering/unwrapping —
158+ results are pre-filtered by fumadocs and the grouped layout stays intact. */
159159 const itemValues = useMemo (
160160 ( ) =>
161161 items . flatMap ( section =>
@@ -167,10 +167,8 @@ export default function DocsSearch({ pageTree }: { pageTree: Root }) {
167167 [ items ]
168168 ) ;
169169
170- // A result's sub-details collapse behaves like an accordion: open it only
171- // when the match came from a sub-detail rather than the page title (e.g.
172- // "Toolbar" surfacing for "button" via its Button section). When the title
173- // itself matches, the page is the hit, so keep its sub-details collapsed.
170+ /* Auto-open sub-details only when the match came from a sub-detail, not the
171+ page title; a title match keeps its sub-details collapsed. */
174172 const queryLower = trimmedQuery . toLowerCase ( ) ;
175173 const titleMatches = ( item : Item ) =>
176174 typeof item . name === 'string' &&
@@ -229,12 +227,16 @@ export default function DocsSearch({ pageTree }: { pageTree: Root }) {
229227 </ div >
230228 < Command . Content className = { styles . searchList } >
231229 < Command . Empty className = { styles . searchEmpty } >
232- < EmptyState
233- variant = 'empty1'
234- heading = 'No result found'
235- subHeading = 'The keyword you’re searching for isn’t in the document—try using a different term.'
236- icon = { < ExclamationTriangleIcon /> }
237- />
230+ { isLoading ? (
231+ < Spinner size = { 5 } aria-label = 'Searching docs' />
232+ ) : (
233+ < EmptyState
234+ variant = 'empty1'
235+ heading = 'No result found'
236+ subHeading = 'The keyword you’re searching for isn’t in the document—try using a different term.'
237+ icon = { < ExclamationTriangleIcon /> }
238+ />
239+ ) }
238240 </ Command . Empty >
239241 { items . map ( ( section , index ) => (
240242 < Fragment key = { section . heading } >
0 commit comments