11import { MagnifyingGlassIcon , XMarkIcon } from "@heroicons/react/20/solid" ;
2- import { useNavigate } from "@remix-run/react" ;
32import { motion } from "framer-motion" ;
43import { useCallback , useEffect , useRef , useState } from "react" ;
54import { Input } from "~/components/primitives/Input" ;
65import { ShortcutKey } from "~/components/primitives/ShortcutKey" ;
76import { cn } from "~/utils/cn" ;
87import { useOptimisticLocation } from "~/hooks/useOptimisticLocation" ;
8+ import { useSearchParams } from "~/hooks/useSearchParam" ;
99
1010export function LogsSearchInput ( ) {
1111 const location = useOptimisticLocation ( ) ;
12- const navigate = useNavigate ( ) ;
1312 const inputRef = useRef < HTMLInputElement > ( null ) ;
1413
14+ const { value, replace, del } = useSearchParams ( ) ;
15+
1516 // Get initial search value from URL
16- const searchParams = new URLSearchParams ( location . search ) ;
17- const initialSearch = searchParams . get ( "search" ) ?? "" ;
17+ const initialSearch = value ( "search" ) ?? "" ;
1818
1919 const [ text , setText ] = useState ( initialSearch ) ;
2020 const [ isFocused , setIsFocused ] = useState ( false ) ;
2121
2222 // Update text when URL search param changes (only when not focused to avoid overwriting user input)
2323 useEffect ( ( ) => {
24- const params = new URLSearchParams ( location . search ) ;
25- const urlSearch = params . get ( "search" ) ?? "" ;
24+ const urlSearch = value ( "search" ) ?? "" ;
2625 if ( urlSearch !== text && ! isFocused ) {
2726 setText ( urlSearch ) ;
2827 }
29- // eslint-disable-next-line react-hooks/exhaustive-deps
30- } , [ location . search ] ) ;
28+ } , [ value , text , isFocused ] ) ;
3129
3230 const handleSubmit = useCallback ( ( ) => {
33- const params = new URLSearchParams ( location . search ) ;
3431 if ( text . trim ( ) ) {
35- params . set ( " search" , text . trim ( ) ) ;
32+ replace ( { search : text . trim ( ) } ) ;
3633 } else {
37- params . delete ( "search" ) ;
34+ del ( "search" ) ;
3835 }
39- // Reset cursor when searching
40- params . delete ( "cursor" ) ;
41- params . delete ( "direction" ) ;
42- navigate ( `${ location . pathname } ?${ params . toString ( ) } ` , { replace : true } ) ;
43- } , [ text , location . pathname , location . search , navigate ] ) ;
36+ } , [ text , replace , del ] ) ;
4437
45- const handleClear = useCallback ( ( ) => {
38+ const handleClear = useCallback ( ( e : React . MouseEvent < HTMLButtonElement > ) => {
39+ e . preventDefault ( ) ;
40+ e . stopPropagation ( ) ;
4641 setText ( "" ) ;
47- const params = new URLSearchParams ( location . search ) ;
48- params . delete ( "search" ) ;
49- params . delete ( "cursor" ) ;
50- params . delete ( "direction" ) ;
51- navigate ( `${ location . pathname } ?${ params . toString ( ) } ` , { replace : true } ) ;
52- } , [ location . pathname , location . search , navigate ] ) ;
42+ del ( [ "search" , "cursor" , "direction" ] ) ;
43+ } , [ ] ) ;
5344
5445 return (
5546 < div className = "flex items-center gap-1" >
@@ -71,7 +62,7 @@ export function LogsSearchInput() {
7162 value = { text }
7263 onChange = { ( e ) => setText ( e . target . value ) }
7364 fullWidth
74- className = { cn ( isFocused && "placeholder:text-text-dimmed/70" ) }
65+ className = { cn ( "" , isFocused && "placeholder:text-text-dimmed/70" ) }
7566 onKeyDown = { ( e ) => {
7667 if ( e . key === "Enter" ) {
7768 e . preventDefault ( ) ;
@@ -86,22 +77,21 @@ export function LogsSearchInput() {
8677 icon = { < MagnifyingGlassIcon className = "size-4" /> }
8778 accessory = {
8879 text . length > 0 ? (
89- < ShortcutKey shortcut = { { key : "enter" } } variant = "small" />
80+ < div className = "-mr-1 flex items-center gap-1" >
81+ < ShortcutKey shortcut = { { key : "enter" } } variant = "small" />
82+ < button
83+ type = "button"
84+ onClick = { handleClear }
85+ className = "flex size-4.5 items-center justify-center rounded-[2px] border border-text-dimmed/40 text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
86+ title = "Clear search"
87+ >
88+ < XMarkIcon className = "size-3" />
89+ </ button >
90+ </ div >
9091 ) : undefined
9192 }
9293 />
9394 </ motion . div >
94-
95- { text . length > 0 && (
96- < button
97- type = "button"
98- onClick = { handleClear }
99- className = "flex size-6 items-center justify-center rounded text-text-dimmed hover:bg-charcoal-700 hover:text-text-bright"
100- title = "Clear search"
101- >
102- < XMarkIcon className = "size-4" />
103- </ button >
104- ) }
10595 </ div >
10696 ) ;
10797}
0 commit comments