11import React , { FunctionComponent , useCallback , useRef , useState } from 'react' ;
22import Search from 'antd/lib/input/Search' ;
33import styles from './AppHeaderSearch.module.scss' ;
4- import { AutoComplete } from 'antd' ;
4+ import { AutoComplete , InputRef } from 'antd' ;
55import Text from 'antd/lib/typography/Text' ;
66import { DefaultOptionType , FilterFunc , SelectHandler } from 'rc-select/lib/Select' ;
77import { BaseSelectRef } from 'rc-select/lib/BaseSelect' ;
@@ -10,6 +10,8 @@ import { MenuRouteItem } from '../../../../utils/routeMenuItems';
1010import { useNavigate } from 'react-router-dom' ;
1111import { AutoCompleteProps } from 'antd/lib/auto-complete' ;
1212import classNames from 'classnames' ;
13+ import { useKey } from 'rooks' ;
14+ import { isEmpty } from 'lodash' ;
1315
1416interface OptionType extends DefaultOptionType {
1517 data : MenuRouteItem ;
@@ -28,12 +30,14 @@ const allSearchOptions: OptionType[] = menuRouteItems.map((item) => {
2830const filterOption : FilterFunc < OptionType > = ( inputValue , option ) => {
2931 const query = inputValue . trim ( ) . toLocaleLowerCase ( ) ;
3032
33+ const isEmptyQuery = isEmpty ( query ) ;
34+
3135 if ( option === undefined ) {
32- return ! query ;
36+ return isEmptyQuery ;
3337 }
3438
35- if ( ! query ) {
36- return false ;
39+ if ( isEmptyQuery ) {
40+ return true ;
3741 }
3842
3943 return String ( option . label ) . toLocaleLowerCase ( ) . includes ( query ) ;
@@ -46,36 +50,85 @@ interface Props extends Omit<AutoCompleteProps, 'options' | 'filterOption' | 'on
4650const AppHeaderSearch : FunctionComponent < Props > = ( { className, inputClassName, ...props } ) => {
4751 const navigate = useNavigate ( ) ;
4852
53+ const searchInputRef = useRef < InputRef > ( null ) ;
54+
4955 const [ query , setQuery ] = useState < string > ( '' ) ;
5056
5157 const autoCompleteRef = useRef < BaseSelectRef > ( null ) ;
5258
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+
5373 const handleSelect = useCallback < SelectHandler < string , OptionType > > (
5474 ( label : string , { data } : OptionType ) => {
55- const path = data . route . path ;
75+ selectOption ( data ) ;
76+ } ,
77+ [ navigate , selectOption ]
78+ ) ;
5679
57- navigate ( path ) ;
80+ const handleSearch = useCallback (
81+ ( value : string ) => {
82+ if ( ! query . trim ( ) . length ) {
83+ return ;
84+ }
5885
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 ) ;
6193 } ,
62- [ navigate ]
94+ [ selectOption , query ]
6395 ) ;
6496
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+
65111 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 >
79132 ) ;
80133} ;
81134
0 commit comments