@@ -6,7 +6,7 @@ import { useSidebar } from '@/contexts/SidebarContext';
66import { useSettings } from '@/contexts/SettingsContext' ;
77import { useIsMobile } from '@/hooks/use-mobile' ;
88import { Box , CircularProgress , Divider , IconButton , InputAdornment , InputBase , ListItemIcon , ListItemText , Menu , MenuItem , MenuList , Paper , TextField , Typography } from '@mui/material' ;
9- import { ClearRounded , CloseRounded , InfoRounded , NavigateBeforeRounded , NavigateNextRounded , SearchRounded } from '@mui/icons-material' ;
9+ import { ClearRounded , CloseRounded , InfoRounded , KeyboardArrowDownRounded , KeyboardArrowUpRounded , NavigateBeforeRounded , NavigateNextRounded , SearchRounded } from '@mui/icons-material' ;
1010
1111interface TimeSlicedSearchProps {
1212 onSearch : ( value : string ) => void ;
@@ -45,6 +45,39 @@ export const TimeSlicedSearch = ({
4545 // Hide search on mobile when sidebar is open, or when settings are open
4646 const shouldHideSearch = ( isMobile && isOpen ) || isSettingsOpen ;
4747
48+ useEffect ( ( ) => {
49+ const handleGlobalKeyDown = ( e : KeyboardEvent ) => {
50+ if ( localValue . length === 0 ) return ;
51+ if ( e . key === 'Escape' ) {
52+ e . preventDefault ( ) ;
53+ setLocalValue ( '' ) ;
54+ onSearch ( '' ) ; // Only clear when explicitly using ESC
55+ setIsTyping ( false ) ;
56+ onLoadingChange ( false ) ;
57+ } else if ( e . key === 'Enter' && ! e . shiftKey ) {
58+ e . preventDefault ( ) ;
59+ onNavigateNext ?.( ) ;
60+ if ( 'vibrate' in navigator ) {
61+ navigator . vibrate ( 50 ) ;
62+ }
63+ } else if ( e . key === 'Enter' && e . shiftKey ) {
64+ e . preventDefault ( ) ;
65+ onNavigatePrevious ?.( ) ;
66+ if ( 'vibrate' in navigator ) {
67+ navigator . vibrate ( 50 ) ;
68+ }
69+ } else if ( e . key === 'ArrowDown' && e . ctrlKey ) {
70+ e . preventDefault ( ) ;
71+ onNavigateNext ?.( ) ;
72+ } else if ( e . key === 'ArrowUp' && e . ctrlKey ) {
73+ e . preventDefault ( ) ;
74+ onNavigatePrevious ?.( ) ;
75+ }
76+ } ;
77+ window . addEventListener ( 'keydown' , handleGlobalKeyDown ) ;
78+ return ( ) => window . removeEventListener ( 'keydown' , handleGlobalKeyDown ) ;
79+ } , [ localValue , onSearch , onLoadingChange , onNavigateNext , onNavigatePrevious ] ) ;
80+
4881 // Time-sliced debouncing using requestAnimationFrame
4982 const scheduleSearch = useCallback ( ( value : string ) => {
5083 if ( searchTimeoutRef . current ) {
@@ -139,35 +172,6 @@ export const TimeSlicedSearch = ({
139172 }
140173 } , [ onSearch , onLoadingChange ] ) ;
141174
142- // Handle keyboard navigation
143- const handleKeyDown = useCallback ( ( e : React . KeyboardEvent < HTMLInputElement > ) => {
144- if ( e . key === 'Escape' ) {
145- e . preventDefault ( ) ;
146- setLocalValue ( '' ) ;
147- onSearch ( '' ) ; // Only clear when explicitly using ESC
148- setIsTyping ( false ) ;
149- onLoadingChange ( false ) ;
150- } else if ( e . key === 'Enter' && ! e . shiftKey ) {
151- e . preventDefault ( ) ;
152- onNavigateNext ?.( ) ;
153- if ( 'vibrate' in navigator ) {
154- navigator . vibrate ( 50 ) ;
155- }
156- } else if ( e . key === 'Enter' && e . shiftKey ) {
157- e . preventDefault ( ) ;
158- onNavigatePrevious ?.( ) ;
159- if ( 'vibrate' in navigator ) {
160- navigator . vibrate ( 50 ) ;
161- }
162- } else if ( e . key === 'ArrowDown' && e . ctrlKey ) {
163- e . preventDefault ( ) ;
164- onNavigateNext ?.( ) ;
165- } else if ( e . key === 'ArrowUp' && e . ctrlKey ) {
166- e . preventDefault ( ) ;
167- onNavigatePrevious ?.( ) ;
168- }
169- } , [ onNavigateNext , onNavigatePrevious , onSearch , onLoadingChange ] ) ;
170-
171175 // Cleanup
172176 useEffect ( ( ) => {
173177 return ( ) => {
@@ -240,7 +244,6 @@ export const TimeSlicedSearch = ({
240244 aria-label = "Search attributes in tables"
241245 value = { localValue }
242246 onChange = { handleChange }
243- onKeyDown = { handleKeyDown }
244247 spellCheck = { false }
245248 autoComplete = "off"
246249 autoCapitalize = "off"
@@ -286,6 +289,20 @@ export const TimeSlicedSearch = ({
286289 < ListItemText > Previous</ ListItemText >
287290 < Typography variant = 'body2' color = "text.secondary" > Shift + Enter</ Typography >
288291 </ MenuItem >
292+ < MenuItem onClick = { onNavigateNext } >
293+ < ListItemIcon >
294+ < NavigateNextRounded />
295+ </ ListItemIcon >
296+ < ListItemText > Next</ ListItemText >
297+ < Typography variant = 'body2' color = "text.secondary" > Ctrl + < KeyboardArrowDownRounded /> </ Typography >
298+ </ MenuItem >
299+ < MenuItem onClick = { onNavigatePrevious } >
300+ < ListItemIcon >
301+ < NavigateBeforeRounded />
302+ </ ListItemIcon >
303+ < ListItemText > Previous</ ListItemText >
304+ < Typography variant = 'body2' color = "text.secondary" > Ctrl + < KeyboardArrowUpRounded /> </ Typography >
305+ </ MenuItem >
289306 < Divider />
290307 < MenuItem onClick = { handleClear } >
291308 < ListItemIcon >
0 commit comments