33import React , { useState , useRef , useCallback , useEffect } from 'react' ;
44import { createPortal } from 'react-dom' ;
55import { Input } from '../ui/input' ;
6- import { Search , ChevronUp , ChevronDown } from 'lucide-react' ;
6+ import { Search , ChevronUp , ChevronDown , X } from 'lucide-react' ;
7+ import { useSidebar } from '@/contexts/SidebarContext' ;
8+ import { useIsMobile } from '@/hooks/use-mobile' ;
79
810interface TimeSlicedSearchProps {
911 onSearch : ( value : string ) => void ;
@@ -28,11 +30,16 @@ export const TimeSlicedSearch = ({
2830 const [ localValue , setLocalValue ] = useState ( '' ) ;
2931 const [ isTyping , setIsTyping ] = useState ( false ) ;
3032 const [ portalRoot , setPortalRoot ] = useState < HTMLElement | null > ( null ) ;
33+ const { isOpen } = useSidebar ( ) ;
34+ const isMobile = useIsMobile ( ) ;
3135
3236 const searchTimeoutRef = useRef < number > ( ) ;
3337 const typingTimeoutRef = useRef < number > ( ) ;
3438 const frameRef = useRef < number > ( ) ;
3539
40+ // Hide search on mobile when sidebar is open
41+ const shouldHideSearch = isMobile && isOpen ;
42+
3643 // Time-sliced debouncing using requestAnimationFrame
3744 const scheduleSearch = useCallback ( ( value : string ) => {
3845 if ( searchTimeoutRef . current ) {
@@ -81,6 +88,22 @@ export const TimeSlicedSearch = ({
8188
8289 } , [ isTyping , onLoadingChange , scheduleSearch ] ) ;
8390
91+ // Handle clear button
92+ const handleClear = useCallback ( ( ) => {
93+ setLocalValue ( '' ) ;
94+ onSearch ( '' ) ; // Clear search immediately
95+ setIsTyping ( false ) ;
96+ onLoadingChange ( false ) ;
97+
98+ // Clear any pending timeouts
99+ if ( searchTimeoutRef . current ) {
100+ clearTimeout ( searchTimeoutRef . current ) ;
101+ }
102+ if ( typingTimeoutRef . current ) {
103+ clearTimeout ( typingTimeoutRef . current ) ;
104+ }
105+ } , [ onSearch , onLoadingChange ] ) ;
106+
84107 // Handle keyboard navigation
85108 const handleKeyDown = useCallback ( ( e : React . KeyboardEvent < HTMLInputElement > ) => {
86109 if ( e . key === 'Enter' && ! e . shiftKey ) {
@@ -151,7 +174,7 @@ export const TimeSlicedSearch = ({
151174 } , [ ] ) ;
152175
153176 const searchInput = (
154- < div className = " fixed top-4 right-8 z-50 w-80" >
177+ < div className = { ` fixed top-4 right-8 z-50 w-[280px] transition-opacity duration-200 ${ shouldHideSearch ? 'opacity-0 pointer-events-none' : 'opacity-100' } ` } >
155178 { /* Search Input Container */ }
156179 < div className = "flex items-center gap-2" >
157180 < div className = "relative flex-1" >
@@ -163,16 +186,26 @@ export const TimeSlicedSearch = ({
163186 value = { localValue }
164187 onChange = { handleChange }
165188 onKeyDown = { handleKeyDown }
166- className = "pl-10 pr-8 h-9 text-sm"
189+ className = "pl-10 pr-10 h-9 text-sm"
167190 spellCheck = { false }
168191 autoComplete = "off"
169192 autoCapitalize = "off"
170193 />
171- { isTyping && (
172- < div className = "absolute right-3 top-1/2 transform -translate-y-1/2" >
194+ { /* Clear button or loading indicator */ }
195+ < div className = "absolute right-3 top-1/2 transform -translate-y-1/2 flex justify-center items-center" >
196+ { isTyping ? (
173197 < div className = "w-4 h-4 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" > </ div >
174- </ div >
175- ) }
198+ ) : localValue ? (
199+ < button
200+ onClick = { handleClear }
201+ className = "w-4 h-4 text-gray-400 hover:text-gray-600 transition-colors"
202+ title = "Clear search"
203+ aria-label = "Clear search"
204+ >
205+ < X className = "w-4 h-4" />
206+ </ button >
207+ ) : null }
208+ </ div >
176209 </ div >
177210
178211 { /* Navigation Buttons */ }
0 commit comments