1
1
import React , { FunctionComponent , useCallback , useRef , useState } from 'react' ;
2
2
import Search from 'antd/lib/input/Search' ;
3
3
import styles from './AppHeaderSearch.module.scss' ;
4
- import { AutoComplete } from 'antd' ;
4
+ import { AutoComplete , InputRef } from 'antd' ;
5
5
import Text from 'antd/lib/typography/Text' ;
6
6
import { DefaultOptionType , FilterFunc , SelectHandler } from 'rc-select/lib/Select' ;
7
7
import { BaseSelectRef } from 'rc-select/lib/BaseSelect' ;
@@ -10,6 +10,8 @@ import { MenuRouteItem } from '../../../../utils/routeMenuItems';
10
10
import { useNavigate } from 'react-router-dom' ;
11
11
import { AutoCompleteProps } from 'antd/lib/auto-complete' ;
12
12
import classNames from 'classnames' ;
13
+ import { useKey } from 'rooks' ;
14
+ import { isEmpty } from 'lodash' ;
13
15
14
16
interface OptionType extends DefaultOptionType {
15
17
data : MenuRouteItem ;
@@ -28,12 +30,14 @@ const allSearchOptions: OptionType[] = menuRouteItems.map((item) => {
28
30
const filterOption : FilterFunc < OptionType > = ( inputValue , option ) => {
29
31
const query = inputValue . trim ( ) . toLocaleLowerCase ( ) ;
30
32
33
+ const isEmptyQuery = isEmpty ( query ) ;
34
+
31
35
if ( option === undefined ) {
32
- return ! query ;
36
+ return isEmptyQuery ;
33
37
}
34
38
35
- if ( ! query ) {
36
- return false ;
39
+ if ( isEmptyQuery ) {
40
+ return true ;
37
41
}
38
42
39
43
return String ( option . label ) . toLocaleLowerCase ( ) . includes ( query ) ;
@@ -46,36 +50,85 @@ interface Props extends Omit<AutoCompleteProps, 'options' | 'filterOption' | 'on
46
50
const AppHeaderSearch : FunctionComponent < Props > = ( { className, inputClassName, ...props } ) => {
47
51
const navigate = useNavigate ( ) ;
48
52
53
+ const searchInputRef = useRef < InputRef > ( null ) ;
54
+
49
55
const [ query , setQuery ] = useState < string > ( '' ) ;
50
56
51
57
const autoCompleteRef = useRef < BaseSelectRef > ( null ) ;
52
58
59
+ const selectOption = useCallback ( ( option : MenuRouteItem ) => {
60
+ const path = option . route . path ;
61
+
62
+ navigate ( path ) ;
63
+
64
+ setTimeout ( ( ) => {
65
+ setQuery ( ' ' ) ;
66
+ } ) ;
67
+ setTimeout ( ( ) => {
68
+ setQuery ( '' ) ;
69
+ } ) ;
70
+ autoCompleteRef . current ?. blur ( ) ;
71
+ } , [ ] ) ;
72
+
53
73
const handleSelect = useCallback < SelectHandler < string , OptionType > > (
54
74
( label : string , { data } : OptionType ) => {
55
- const path = data . route . path ;
75
+ selectOption ( data ) ;
76
+ } ,
77
+ [ navigate , selectOption ]
78
+ ) ;
56
79
57
- navigate ( path ) ;
80
+ const handleSearch = useCallback (
81
+ ( value : string ) => {
82
+ if ( ! query . trim ( ) . length ) {
83
+ return ;
84
+ }
58
85
59
- setQuery ( '' ) ;
60
- autoCompleteRef . current ?. blur ( ) ;
86
+ const matchingOptions = allSearchOptions . filter ( ( option ) => filterOption ( value , option ) ) ;
87
+ if ( ! matchingOptions . length ) {
88
+ return ;
89
+ }
90
+
91
+ const option = matchingOptions [ 0 ] ;
92
+ selectOption ( option . data ) ;
61
93
} ,
62
- [ navigate ]
94
+ [ selectOption , query ]
63
95
) ;
64
96
97
+ useKey ( [ '/' ] , ( event ) => {
98
+ const activeTag = document . activeElement ?. tagName ;
99
+ if ( activeTag && [ 'input' , 'textarea' ] . includes ( activeTag ) ) {
100
+ return ;
101
+ }
102
+
103
+ if ( event . altKey || event . ctrlKey || event . shiftKey || event . metaKey ) {
104
+ return ;
105
+ }
106
+
107
+ searchInputRef . current ?. focus ( ) ;
108
+ event . preventDefault ( ) ;
109
+ } ) ;
110
+
65
111
return (
66
- < AutoComplete
67
- options = { allSearchOptions }
68
- className = { classNames ( styles . container , className ) }
69
- filterOption = { filterOption }
70
- onSelect = { handleSelect }
71
- notFoundContent = { < Text type = "secondary" > No results</ Text > }
72
- value = { query }
73
- onChange = { setQuery }
74
- ref = { autoCompleteRef }
75
- { ...props }
76
- >
77
- < Search placeholder = "Search" className = { inputClassName } />
78
- </ AutoComplete >
112
+ < div className = { styles . container } >
113
+ < AutoComplete
114
+ options = { allSearchOptions }
115
+ className = { classNames ( styles . autoComplete , className ) }
116
+ filterOption = { filterOption }
117
+ onSelect = { handleSelect }
118
+ notFoundContent = { < Text type = "secondary" > No results</ Text > }
119
+ value = { query }
120
+ onChange = { setQuery }
121
+ ref = { autoCompleteRef }
122
+ { ...props }
123
+ >
124
+ < Search ref = { searchInputRef } onSearch = { handleSearch } placeholder = "Search" className = { inputClassName } />
125
+ </ AutoComplete >
126
+ { ! query . length && (
127
+ < Text keyboard className = { styles . keyHint } type = "secondary" >
128
+ /
129
+ </ Text >
130
+ ) }
131
+ </ div >
79
132
) ;
80
133
} ;
81
134
0 commit comments