@@ -5,21 +5,42 @@ import { debounce } from 'perfect-debounce'
55const pagesWithLocalFilter = new Set ( [ '~username' , 'org' ] )
66
77export function useGlobalSearch ( place : 'header' | 'content' = 'content' ) {
8+ const { settings } = useSettings ( )
89 const { searchProvider } = useSearchProvider ( )
910 const searchProviderValue = computed ( ( ) => {
1011 const p = normalizeSearchParam ( route . query . p )
1112 if ( p === 'npm' || searchProvider . value === 'npm' ) return 'npm'
1213 return 'algolia'
1314 } )
15+
1416 const router = useRouter ( )
1517 const route = useRoute ( )
18+ // Internally used searchQuery state
1619 const searchQuery = useState < string > ( 'search-query' , ( ) => {
1720 if ( pagesWithLocalFilter . has ( route . name as string ) ) {
1821 return ''
1922 }
2023 return normalizeSearchParam ( route . query . q )
2124 } )
2225
26+ // Committed searchQuery: last value submitted by user
27+ // Syncs instantly when instant search is on VS only on Enter presswhen off
28+ const committedSearchQuery = useState < string > ( 'committed-search-query' , ( ) => {
29+ if ( pagesWithLocalFilter . has ( route . name as string ) ) {
30+ return ''
31+ }
32+ return normalizeSearchParam ( route . query . q )
33+ } )
34+
35+ watch ( searchQuery , val => {
36+ if ( settings . value . instantSearch ) {
37+ committedSearchQuery . value = val
38+ } else if ( ! val ) {
39+ // Only clear committed query when input is cleared
40+ committedSearchQuery . value = ''
41+ }
42+ } )
43+
2344 // clean search input when navigating away from search page
2445 watch (
2546 ( ) => route . query . q ,
@@ -29,6 +50,8 @@ export function useGlobalSearch(place: 'header' | 'content' = 'content') {
2950 if ( ! searchQuery . value ) searchQuery . value = value
3051 } ,
3152 )
53+
54+ // Updates URL when search query changes (immediately for instantSearch or after Enter hit otherwise)
3255 const updateUrlQueryImpl = ( value : string , provider : 'npm' | 'algolia' ) => {
3356 const isSameQuery = route . query . q === value && route . query . p === provider
3457 // Don't navigate away from pages that use ?q for local filtering
@@ -54,23 +77,40 @@ export function useGlobalSearch(place: 'header' | 'content' = 'content') {
5477 } ,
5578 } )
5679 }
80+
5781 const updateUrlQuery = debounce ( updateUrlQueryImpl , 250 )
5882
5983 function flushUpdateUrlQuery ( ) {
60- updateUrlQuery . flush ( )
84+ // Commit the current query when explicitly submitted (Enter pressed)
85+ committedSearchQuery . value = searchQuery . value
86+ // When instant search is off the debounce queue is empty, so call directly
87+ if ( ! settings . value . instantSearch ) {
88+ updateUrlQueryImpl ( searchQuery . value , searchProvider . value )
89+ } else {
90+ updateUrlQuery . flush ( )
91+ }
6192 }
6293
6394 const searchQueryValue = computed ( {
6495 get : ( ) => searchQuery . value ,
6596 set : async ( value : string ) => {
6697 searchQuery . value = value
6798
99+ // When instant search is off, skip debounced URL updates — only flushUpdateUrlQuery commits and navigates
100+ if ( ! settings . value . instantSearch ) return
101+
68102 // Leading debounce implementation as it doesn't work properly out of the box (https://github.com/unjs/perfect-debounce/issues/43)
69103 if ( ! updateUrlQuery . isPending ( ) ) {
70104 updateUrlQueryImpl ( value , searchProvider . value )
71105 }
72106 updateUrlQuery ( value , searchProvider . value )
73107 } ,
74108 } )
75- return { model : searchQueryValue , provider : searchProviderValue , flushUpdateUrlQuery }
109+
110+ return {
111+ model : searchQueryValue ,
112+ committedModel : committedSearchQuery ,
113+ provider : searchProviderValue ,
114+ startSearch : flushUpdateUrlQuery ,
115+ }
76116}
0 commit comments