@@ -5,21 +5,35 @@ 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 search query: last value submitted by user
27+ // Syncs instantly when instantSearch is on, but only on Enter press when off
28+ const committedSearchQuery = useState < string > ( 'committed-search-query' , ( ) => searchQuery . value )
29+
30+ // This is basically doing instant search as user types
31+ watch ( searchQuery , val => {
32+ if ( settings . value . instantSearch ) {
33+ committedSearchQuery . value = val
34+ }
35+ } )
36+
2337 // clean search input when navigating away from search page
2438 watch (
2539 ( ) => route . query . q ,
@@ -29,6 +43,8 @@ export function useGlobalSearch(place: 'header' | 'content' = 'content') {
2943 if ( ! searchQuery . value ) searchQuery . value = value
3044 } ,
3145 )
46+
47+ // Updates URL when search query changes (immediately for instantSearch or after Enter hit otherwise)
3248 const updateUrlQueryImpl = ( value : string , provider : 'npm' | 'algolia' ) => {
3349 const isSameQuery = route . query . q === value && route . query . p === provider
3450 // Don't navigate away from pages that use ?q for local filtering
@@ -54,23 +70,41 @@ export function useGlobalSearch(place: 'header' | 'content' = 'content') {
5470 } ,
5571 } )
5672 }
73+
5774 const updateUrlQuery = debounce ( updateUrlQueryImpl , 250 )
5875
5976 function flushUpdateUrlQuery ( ) {
60- updateUrlQuery . flush ( )
77+ // Commit the current query when explicitly submitted (Enter pressed)
78+ committedSearchQuery . value = searchQuery . value
79+ // When instant search is off the debounce queue is empty, so call directly
80+ if ( ! settings . value . instantSearch ) {
81+ updateUrlQueryImpl ( searchQuery . value , searchProvider . value )
82+ } else {
83+ updateUrlQuery . flush ( )
84+ }
6185 }
6286
6387 const searchQueryValue = computed ( {
6488 get : ( ) => searchQuery . value ,
6589 set : async ( value : string ) => {
6690 searchQuery . value = value
6791
92+ // When instant search is off, skip debounced URL updates
93+ // Only explicitly called flushUpdateUrlQuery commits and navigates
94+ if ( ! settings . value . instantSearch ) return
95+
6896 // Leading debounce implementation as it doesn't work properly out of the box (https://github.com/unjs/perfect-debounce/issues/43)
6997 if ( ! updateUrlQuery . isPending ( ) ) {
7098 updateUrlQueryImpl ( value , searchProvider . value )
7199 }
72100 updateUrlQuery ( value , searchProvider . value )
73101 } ,
74102 } )
75- return { model : searchQueryValue , provider : searchProviderValue , flushUpdateUrlQuery }
103+
104+ return {
105+ model : searchQueryValue ,
106+ committedModel : committedSearchQuery ,
107+ provider : searchProviderValue ,
108+ startSearch : flushUpdateUrlQuery ,
109+ }
76110}
0 commit comments