@@ -32,11 +32,16 @@ import { createTheme } from '@uiw/codemirror-themes';
32
32
import CodeMirror , { Annotation , EditorView , KeyBinding , keymap , ReactCodeMirrorRef } from "@uiw/react-codemirror" ;
33
33
import { cva } from "class-variance-authority" ;
34
34
import { useRouter } from "next/navigation" ;
35
- import { useCallback , useMemo , useRef , useState } from "react" ;
35
+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
36
36
import { useHotkeys } from 'react-hotkeys-hook' ;
37
- import { SearchSuggestionsBox , SuggestionMode } from "./searchSuggestionsBox" ;
37
+ import { SearchSuggestionsBox } from "./searchSuggestionsBox" ;
38
38
import { useSuggestionsData } from "./useSuggestionsData" ;
39
39
import { zoekt } from "./zoektLanguageExtension" ;
40
+ import { CounterClockwiseClockIcon } from "@radix-ui/react-icons" ;
41
+ import { useSuggestionModeAndQuery } from "./useSuggestionModeAndQuery" ;
42
+ import { Separator } from "@/components/ui/separator" ;
43
+ import { Tooltip , TooltipTrigger , TooltipContent } from "@/components/ui/tooltip" ;
44
+ import { Toggle } from "@/components/ui/toggle" ;
40
45
41
46
interface SearchBarProps {
42
47
className ?: string ;
@@ -66,7 +71,7 @@ const searchBarKeymap: readonly KeyBinding[] = ([
66
71
] as KeyBinding [ ] ) . concat ( historyKeymap ) ;
67
72
68
73
const searchBarContainerVariants = cva (
69
- "search-bar-container flex items-center p -0.5 border rounded-md relative" ,
74
+ "search-bar-container flex items-center py -0.5 px-1 border rounded-md relative" ,
70
75
{
71
76
variants : {
72
77
size : {
@@ -91,13 +96,12 @@ export const SearchBar = ({
91
96
const suggestionBoxRef = useRef < HTMLDivElement > ( null ) ;
92
97
const editorRef = useRef < ReactCodeMirrorRef > ( null ) ;
93
98
const [ cursorPosition , setCursorPosition ] = useState ( 0 ) ;
94
- const [ isSuggestionsBoxEnabled , setIsSuggestionsBoxEnabled ] = useState ( false ) ;
99
+ const [ isSuggestionsEnabled , setIsSuggestionsEnabled ] = useState ( false ) ;
95
100
const [ isSuggestionsBoxFocused , setIsSuggestionsBoxFocused ] = useState ( false ) ;
96
-
101
+ const [ isHistorySearchEnabled , setIsHistorySearchEnabled ] = useState ( false ) ;
102
+
97
103
const focusEditor = useCallback ( ( ) => editorRef . current ?. view ?. focus ( ) , [ ] ) ;
98
104
const focusSuggestionsBox = useCallback ( ( ) => suggestionBoxRef . current ?. focus ( ) , [ ] ) ;
99
- const [ suggestionMode , setSuggestionMode ] = useState < SuggestionMode > ( "refine" ) ;
100
- const [ suggestionQuery , setSuggestionQuery ] = useState ( "" ) ;
101
105
102
106
const [ _query , setQuery ] = useState ( defaultQuery ?? "" ) ;
103
107
const query = useMemo ( ( ) => {
@@ -106,6 +110,22 @@ export const SearchBar = ({
106
110
return _query . replaceAll ( / \n / g, " " ) ;
107
111
} , [ _query ] ) ;
108
112
113
+ // When the user navigates backwards/forwards while on the
114
+ // search page (causing the `query` search param to change),
115
+ // we want to update what query is displayed in the search bar.
116
+ useEffect ( ( ) => {
117
+ if ( defaultQuery ) {
118
+ setQuery ( defaultQuery ) ;
119
+ }
120
+ } , [ defaultQuery ] )
121
+
122
+ const { suggestionMode, suggestionQuery } = useSuggestionModeAndQuery ( {
123
+ isSuggestionsEnabled,
124
+ isHistorySearchEnabled,
125
+ cursorPosition,
126
+ query,
127
+ } ) ;
128
+
109
129
const suggestionData = useSuggestionsData ( {
110
130
suggestionMode,
111
131
suggestionQuery,
@@ -152,7 +172,7 @@ export const SearchBar = ({
152
172
useHotkeys ( '/' , ( event ) => {
153
173
event . preventDefault ( ) ;
154
174
focusEditor ( ) ;
155
- setIsSuggestionsBoxEnabled ( true ) ;
175
+ setIsSuggestionsEnabled ( true ) ;
156
176
if ( editorRef . current ?. view ) {
157
177
cursorDocEnd ( {
158
178
state : editorRef . current . view . state ,
@@ -164,37 +184,40 @@ export const SearchBar = ({
164
184
// Collapse the suggestions box if the user clicks outside of the search bar container.
165
185
useClickListener ( '.search-bar-container' , ( isElementClicked ) => {
166
186
if ( ! isElementClicked ) {
167
- setIsSuggestionsBoxEnabled ( false ) ;
187
+ setIsSuggestionsEnabled ( false ) ;
168
188
} else {
169
- setIsSuggestionsBoxEnabled ( true ) ;
189
+ setIsSuggestionsEnabled ( true ) ;
170
190
}
171
191
} ) ;
172
192
173
- const onSubmit = ( ) => {
193
+ const onSubmit = useCallback ( ( query : string ) => {
194
+ setIsSuggestionsEnabled ( false ) ;
195
+ setIsHistorySearchEnabled ( false ) ;
196
+
174
197
const url = createPathWithQueryParams ( '/search' ,
175
198
[ SearchQueryParams . query , query ] ,
176
- )
199
+ ) ;
177
200
router . push ( url ) ;
178
- }
201
+ } , [ router ] ) ;
179
202
180
203
return (
181
204
< div
182
205
className = { cn ( searchBarContainerVariants ( { size, className } ) ) }
183
206
onKeyDown = { ( e ) => {
184
207
if ( e . key === 'Enter' ) {
185
208
e . preventDefault ( ) ;
186
- setIsSuggestionsBoxEnabled ( false ) ;
187
- onSubmit ( ) ;
209
+ setIsSuggestionsEnabled ( false ) ;
210
+ onSubmit ( query ) ;
188
211
}
189
212
190
213
if ( e . key === 'Escape' ) {
191
214
e . preventDefault ( ) ;
192
- setIsSuggestionsBoxEnabled ( false ) ;
215
+ setIsSuggestionsEnabled ( false ) ;
193
216
}
194
217
195
218
if ( e . key === 'ArrowDown' ) {
196
219
e . preventDefault ( ) ;
197
- setIsSuggestionsBoxEnabled ( true ) ;
220
+ setIsSuggestionsEnabled ( true ) ;
198
221
focusSuggestionsBox ( ) ;
199
222
}
200
223
@@ -203,16 +226,29 @@ export const SearchBar = ({
203
226
}
204
227
} }
205
228
>
229
+ < SearchHistoryButton
230
+ isToggled = { isHistorySearchEnabled }
231
+ onClick = { ( ) => {
232
+ setQuery ( "" ) ;
233
+ setIsHistorySearchEnabled ( ! isHistorySearchEnabled ) ;
234
+ setIsSuggestionsEnabled ( true ) ;
235
+ focusEditor ( ) ;
236
+ } }
237
+ />
238
+ < Separator
239
+ className = "mx-1 h-6"
240
+ orientation = "vertical"
241
+ />
206
242
< CodeMirror
207
243
ref = { editorRef }
208
244
className = "overflow-x-auto scrollbar-hide w-full"
209
- placeholder = { "Search..." }
245
+ placeholder = { isHistorySearchEnabled ? "Filter history..." : "Search..." }
210
246
value = { query }
211
247
onChange = { ( value ) => {
212
248
setQuery ( value ) ;
213
249
// Whenever the user types, we want to re-enable
214
250
// the suggestions box.
215
- setIsSuggestionsBoxEnabled ( true ) ;
251
+ setIsSuggestionsEnabled ( true ) ;
216
252
} }
217
253
theme = { theme }
218
254
basicSetup = { false }
@@ -223,7 +259,9 @@ export const SearchBar = ({
223
259
< SearchSuggestionsBox
224
260
ref = { suggestionBoxRef }
225
261
query = { query }
226
- onCompletion = { ( newQuery : string , newCursorPosition : number ) => {
262
+ suggestionQuery = { suggestionQuery }
263
+ suggestionMode = { suggestionMode }
264
+ onCompletion = { ( newQuery : string , newCursorPosition : number , autoSubmit = false ) => {
227
265
setQuery ( newQuery ) ;
228
266
229
267
// Move the cursor to it's new position.
@@ -242,8 +280,12 @@ export const SearchBar = ({
242
280
243
281
// Re-focus the editor since suggestions cause focus to be lost (both click & keyboard)
244
282
editorRef . current ?. view ?. focus ( ) ;
283
+
284
+ if ( autoSubmit ) {
285
+ onSubmit ( newQuery ) ;
286
+ }
245
287
} }
246
- isEnabled = { isSuggestionsBoxEnabled }
288
+ isEnabled = { isSuggestionsEnabled }
247
289
onReturnFocus = { ( ) => {
248
290
focusEditor ( ) ;
249
291
} }
@@ -255,17 +297,40 @@ export const SearchBar = ({
255
297
setIsSuggestionsBoxFocused ( document . activeElement === suggestionBoxRef . current ) ;
256
298
} }
257
299
cursorPosition = { cursorPosition }
258
- onSuggestionModeChanged = { ( newSuggestionMode ) => {
259
- if ( suggestionMode !== newSuggestionMode ) {
260
- console . debug ( `Suggestion mode changed: ${ suggestionMode } -> ${ newSuggestionMode } ` ) ;
261
- }
262
- setSuggestionMode ( newSuggestionMode ) ;
263
- } }
264
- onSuggestionQueryChanged = { ( suggestionQuery ) => {
265
- setSuggestionQuery ( suggestionQuery ) ;
266
- } }
267
300
{ ...suggestionData }
268
301
/>
269
302
</ div >
270
303
)
304
+ }
305
+
306
+ const SearchHistoryButton = ( {
307
+ isToggled,
308
+ onClick,
309
+ } : {
310
+ isToggled : boolean ,
311
+ onClick : ( ) => void
312
+ } ) => {
313
+ return (
314
+ < Tooltip >
315
+ < TooltipTrigger
316
+ asChild = { true }
317
+ >
318
+ { /* @see : https://github.com/shadcn-ui/ui/issues/1988#issuecomment-1980597269 */ }
319
+ < div >
320
+ < Toggle
321
+ pressed = { isToggled }
322
+ className = "h-6 w-6 min-w-6 px-0 p-1 cursor-pointer"
323
+ onClick = { onClick }
324
+ >
325
+ < CounterClockwiseClockIcon />
326
+ </ Toggle >
327
+ </ div >
328
+ </ TooltipTrigger >
329
+ < TooltipContent
330
+ side = "bottom"
331
+ >
332
+ Search history
333
+ </ TooltipContent >
334
+ </ Tooltip >
335
+ )
271
336
}
0 commit comments