@@ -72,15 +72,17 @@ const SearchResultsContainer = styled(OverlayScrollbarsComponent)`
72
72
border-top-right-radius: 0;
73
73
` ;
74
74
75
- const StyledCard = styled ( Card ) < { selected : boolean } > `
75
+ const StyledCard = styled ( Card ) < { selected : boolean ; keyboardSelected : boolean } > `
76
76
${ hoverShortTransitionTiming }
77
77
height: auto;
78
78
width: 100%;
79
- padding: ${ ( { selected } ) => ( selected ? "16px 13px" : "16px" ) } ;
79
+ padding: ${ ( { selected, keyboardSelected } ) => ( selected || keyboardSelected ? "16px 13px" : "16px" ) } ;
80
80
cursor: pointer;
81
81
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" } ;
84
86
border-radius: 0;
85
87
86
88
:hover {
@@ -112,6 +114,7 @@ const TopSearch: React.FC = () => {
112
114
const items = useMemo ( ( ) => ! isUndefined ( data ?. court ) && [ rootCourtToItems ( data . court ) ] , [ data ] ) ;
113
115
const isUniversity = isKlerosUniversity ( ) ;
114
116
const [ search , setSearch ] = useState ( "" ) ;
117
+ const [ selectedIndex , setSelectedIndex ] = useState ( - 1 ) ;
115
118
116
119
const filteredCourts = useMemo ( ( ) => {
117
120
if ( ! data ?. court ) return [ ] ;
@@ -122,6 +125,39 @@ const TopSearch: React.FC = () => {
122
125
return [ selectedCourt , ...courts . filter ( ( c ) => c . id !== currentCourtId ) ] ;
123
126
} , [ data , search , currentCourtId ] ) ;
124
127
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
+
125
161
return (
126
162
< Container >
127
163
{ items ? (
@@ -137,18 +173,17 @@ const TopSearch: React.FC = () => {
137
173
type = "text"
138
174
placeholder = "Search"
139
175
value = { search }
140
- onChange = { ( e ) => setSearch ( e . target . value ) }
176
+ onChange = { handleSearchChange }
177
+ onKeyDown = { handleKeyDown }
141
178
/>
142
179
{ search && filteredCourts . length > 0 && (
143
180
< SearchResultsContainer >
144
- { filteredCourts . map ( ( court ) => (
181
+ { filteredCourts . map ( ( court , index ) => (
145
182
< StyledCard
146
183
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 ) }
152
187
>
153
188
{ court . parentName && < CourtParentSpan > { court . parentName } / </ CourtParentSpan > }
154
189
< CourtNameSpan > { court . name } </ CourtNameSpan >
0 commit comments