1- import { FC , useState , FocusEvent } from 'react' ;
1+ import { FC , useState , useRef , useEffect } from 'react' ;
22import Link from 'next/link' ;
33import algoliasearch from 'algoliasearch/lite' ;
44import {
55 Configure ,
66 Highlight ,
77 Hits ,
88 InstantSearch ,
9- SearchBox ,
10- } from 'react-instantsearch-hooks-web ' ;
11- import type { Hit } from 'instantsearch.js' ;
9+ useSearchBox ,
10+ } from 'react-instantsearch' ;
11+ import type { Hit , BaseHit } from 'instantsearch.js' ;
1212import { Card } from '@components/layout' ;
1313import classNames from 'classnames' ;
14- import { AlgoliaSearchHelper } from 'algoliasearch-helper' ;
15- import { useDocumentEvent } from 'hooks' ;
1614
1715const searchClient = algoliasearch (
1816 'V0X7Z4KE9D' ,
1917 '544bec33383dc791bcbca3e1ceaec11b' ,
2018) ;
2119
22- interface ToolHit extends Hit {
20+ interface ToolHit extends Hit < BaseHit > {
2321 fields : {
2422 slug : string ;
2523 name : string ;
@@ -39,57 +37,65 @@ const Hit = (result: SearchResult) => {
3937 ) ;
4038} ;
4139
42- const AutocompleteSearch : FC = ( ) => {
43- const [ showResults , setShow ] = useState ( false ) ;
40+ const CustomSearchBox : FC < {
41+ onFocus : ( ) => void ;
42+ onChange : ( query : string ) => void ;
43+ } > = ( { onFocus, onChange } ) => {
44+ const { query, refine } = useSearchBox ( ) ;
4445
45- const handleShowResults = ( e : FocusEvent < HTMLInputElement > ) => {
46- if ( ! ! e . target . value ) {
47- setShow ( true ) ;
48- }
49- } ;
46+ return (
47+ < div className = "ais-SearchBox" >
48+ < input
49+ type = "search"
50+ placeholder = "Find analysis tools, formatters, linters.."
51+ value = { query }
52+ onFocus = { onFocus }
53+ onChange = { ( e ) => {
54+ refine ( e . target . value ) ;
55+ onChange ( e . target . value ) ;
56+ } }
57+ className = "ais-SearchBox-input"
58+ />
59+ </ div >
60+ ) ;
61+ } ;
5062
51- const handleSearch = ( e : AlgoliaSearchHelper ) => {
52- const shouldShow =
53- e . state . query && e . state . query . length > 0 ? true : false ;
54- setShow ( shouldShow ) ;
55- if ( shouldShow ) {
56- e . search ( ) ;
57- }
58- } ;
63+ const AutocompleteSearch : FC = ( ) => {
64+ const [ showResults , setShowResults ] = useState ( false ) ;
65+ const searchRef = useRef < HTMLDivElement > ( null ) ;
5966
60- const handleHideDropdown = ( event : KeyboardEvent ) => {
61- if ( event . key === 'Escape' ) {
62- setShow ( false ) ;
63- }
64- } ;
67+ useEffect ( ( ) => {
68+ const handleClickOutside = ( event : MouseEvent ) => {
69+ if (
70+ searchRef . current &&
71+ ! searchRef . current . contains ( event . target as Node )
72+ ) {
73+ setShowResults ( false ) ;
74+ }
75+ } ;
6576
66- const handleClickOutside = ( event : MouseEvent ) => {
67- const composedPath = event . composedPath ?.( ) || [ ] ;
68- const hasSearchContainer = composedPath . some (
69- ( el ) => ( el as Element ) . className === 'autocomplete-search' ,
70- ) ;
71- if ( ! hasSearchContainer ) {
72- setShow ( false ) ;
73- }
74- } ;
77+ const handleEscape = ( event : KeyboardEvent ) => {
78+ if ( event . key === 'Escape' ) {
79+ setShowResults ( false ) ;
80+ }
81+ } ;
7582
76- useDocumentEvent ( [
77- { type : 'click' , callback : handleClickOutside } ,
78- { type : 'keydown' , callback : handleHideDropdown } ,
79- ] ) ;
83+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
84+ document . addEventListener ( 'keydown' , handleEscape ) ;
85+
86+ return ( ) => {
87+ document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
88+ document . removeEventListener ( 'keydown' , handleEscape ) ;
89+ } ;
90+ } , [ ] ) ;
8091
8192 return (
82- < InstantSearch
83- searchClient = { searchClient }
84- indexName = "tools"
85- searchFunction = { handleSearch } >
86- < div className = "autocomplete-search" >
87- < Configure
88- { ...( { hitsPerPage : 10 , typoTolerance : true } as any ) }
89- />
90- < SearchBox
91- placeholder = "Find analysis tools, formatters, linters.."
92- onFocus = { handleShowResults }
93+ < InstantSearch searchClient = { searchClient } indexName = "tools" >
94+ < div className = "autocomplete-search" ref = { searchRef } >
95+ < Configure hitsPerPage = { 10 } typoTolerance = { true } />
96+ < CustomSearchBox
97+ onFocus = { ( ) => setShowResults ( true ) }
98+ onChange = { ( query ) => setShowResults ( query . length > 0 ) }
9399 />
94100 < div className = "relative" >
95101 < Card
0 commit comments