@@ -72,15 +72,17 @@ const SearchResultsContainer = styled(OverlayScrollbarsComponent)`
7272 border-top-right-radius: 0;
7373` ;
7474
75- const StyledCard = styled ( Card ) < { selected : boolean } > `
75+ const StyledCard = styled ( Card ) < { selected : boolean ; keyboardSelected : boolean } > `
7676 ${ hoverShortTransitionTiming }
7777 height: auto;
7878 width: 100%;
79- padding: ${ ( { selected } ) => ( selected ? "16px 13px" : "16px" ) } ;
79+ padding: ${ ( { selected, keyboardSelected } ) => ( selected || keyboardSelected ? "16px 13px" : "16px" ) } ;
8080 cursor: pointer;
8181 border: none;
82- border-left: ${ ( { selected, theme } ) => ( selected ? `3px solid ${ theme . primaryBlue } ` : "none" ) } ;
83- background-color: ${ ( { selected, theme } ) => ( selected ? theme . mediumBlue : "transparent" ) } ;
82+ border-left: ${ ( { selected, keyboardSelected, theme } ) =>
83+ selected || keyboardSelected ? `3px solid ${ theme . primaryBlue } ` : "none" } ;
84+ background-color: ${ ( { selected, keyboardSelected, theme } ) =>
85+ selected || keyboardSelected ? theme . mediumBlue : "transparent" } ;
8486 border-radius: 0;
8587
8688 :hover {
@@ -112,6 +114,7 @@ const TopSearch: React.FC = () => {
112114 const items = useMemo ( ( ) => ! isUndefined ( data ?. court ) && [ rootCourtToItems ( data . court ) ] , [ data ] ) ;
113115 const isUniversity = isKlerosUniversity ( ) ;
114116 const [ search , setSearch ] = useState ( "" ) ;
117+ const [ selectedIndex , setSelectedIndex ] = useState ( - 1 ) ;
115118
116119 const filteredCourts = useMemo ( ( ) => {
117120 if ( ! data ?. court ) return [ ] ;
@@ -122,6 +125,39 @@ const TopSearch: React.FC = () => {
122125 return [ selectedCourt , ...courts . filter ( ( c ) => c . id !== currentCourtId ) ] ;
123126 } , [ data , search , currentCourtId ] ) ;
124127
128+ const handleKeyDown = ( e : React . KeyboardEvent ) => {
129+ if ( ! search || filteredCourts . length === 0 ) return ;
130+
131+ switch ( e . key ) {
132+ case "ArrowDown" :
133+ e . preventDefault ( ) ;
134+ setSelectedIndex ( ( prev ) => Math . min ( prev + 1 , filteredCourts . length - 1 ) ) ;
135+ break ;
136+ case "ArrowUp" :
137+ e . preventDefault ( ) ;
138+ setSelectedIndex ( ( prev ) => Math . max ( prev - 1 , - 1 ) ) ;
139+ break ;
140+ case "Enter" :
141+ if ( selectedIndex >= 0 ) {
142+ navigate ( `/courts/${ filteredCourts [ selectedIndex ] . id } ` ) ;
143+ setSearch ( "" ) ;
144+ setSelectedIndex ( - 1 ) ;
145+ }
146+ break ;
147+ }
148+ } ;
149+
150+ const handleSearchChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
151+ setSearch ( e . target . value ) ;
152+ setSelectedIndex ( - 1 ) ;
153+ } ;
154+
155+ const handleCourtClick = ( courtId : string ) => {
156+ navigate ( `/courts/${ courtId } ` ) ;
157+ setSearch ( "" ) ;
158+ setSelectedIndex ( - 1 ) ;
159+ } ;
160+
125161 return (
126162 < Container >
127163 { items ? (
@@ -137,18 +173,17 @@ const TopSearch: React.FC = () => {
137173 type = "text"
138174 placeholder = "Search"
139175 value = { search }
140- onChange = { ( e ) => setSearch ( e . target . value ) }
176+ onChange = { handleSearchChange }
177+ onKeyDown = { handleKeyDown }
141178 />
142179 { search && filteredCourts . length > 0 && (
143180 < SearchResultsContainer >
144- { filteredCourts . map ( ( court ) => (
181+ { filteredCourts . map ( ( court , index ) => (
145182 < StyledCard
146183 key = { court . id }
147- selected = { court . id === currentCourtId }
148- onClick = { ( ) => {
149- navigate ( `/courts/${ court . id } ` ) ;
150- setSearch ( "" ) ;
151- } }
184+ selected = { selectedIndex === - 1 && court . id === currentCourtId }
185+ keyboardSelected = { index === selectedIndex }
186+ onClick = { ( ) => handleCourtClick ( court . id ) }
152187 >
153188 { court . parentName && < CourtParentSpan > { court . parentName } / </ CourtParentSpan > }
154189 < CourtNameSpan > { court . name } </ CourtNameSpan >
0 commit comments