sickdyd / react-search-autocomplete

A search box that filters the provided array of objects
https://sickdyd.github.io/react-search-autocomplete
MIT License
216 stars 82 forks source link

onBlur Event #104

Open jwj777 opened 1 year ago

jwj777 commented 1 year ago

Would be nice to have a config option for onBlur event to trigger changes if a user clicks outside of the search component.

If you look at Google's search input, the border radius decreases when the drop-down appears. This improves the look of the drop-down a bit. It goes back to full border-radius if you click somewhere else (onBlur) when the drop-down goes away.

I'm using the handleOnSearch config to decrease border-radius, but can't add on onBlur event handler since the input isn't exposed in the config options.

KedalenDev commented 1 year ago

Hey, I also needed a way of accessing the blur and focus event on the internal input. So I created a simple hook, it does require you to wrap the AutoComplete Component in a div or a similar container so we can access that ref. This is the hook:

const useAutoCompleteFocus= ({
    parentRef,
    onFocused,
    onBlurred
}:{
    parentRef: React.RefObject<HTMLDivElement>,
    onFocused?: () => void,
    onBlurred?: () => void
}) => {

    const [isFocused, setIsFocused] = React.useState(false);

    const handleFocus = () => {
        setIsFocused(true);
        if(onFocused) {
            onFocused();
        }
    }

    const handleBlur = () => {
        setIsFocused(false);
        if(onBlurred) {
            onBlurred();
        }
    }

    useEffect(() => {

        if(parentRef.current){
            //parent is found find the input
            const autoCompleteInput = parentRef.current.querySelector('input');
            if(autoCompleteInput) {
                autoCompleteInput.addEventListener('focus', handleFocus);
                autoCompleteInput.addEventListener('blur', handleBlur);
            }
        }

        return () => {
            if(parentRef.current){
                /*We could probably store the input in a ref to avoid this querySelector but if the input is removed from the dom this would fail
                so i'll leave it like this for now*/

                const autoCompleteInput = parentRef.current.querySelector('input');
                if(autoCompleteInput) {
                    autoCompleteInput.removeEventListener('focus', handleFocus);
                    autoCompleteInput.removeEventListener('blur', handleBlur);
                }
            }
        }
    },[parentRef.current])

    return {
        isFocused
    }
}

Then this is how you would use it:

const component = () => {
  const parentRef = useRef<HTMLElement>(null);
  const {isFocused} = useAutoCompleteFocus({
  parentRef,
  onFocus: () => {
  //whatever you want to do here 
 },
 onBlur: () => {
  //whatever you want to do here 
 }
})

 return (<div ref={parentRef}>
      <ReactSearchAutocomplete />
    </div>)

}

It would obviously be nicer if we were to get an actual onBlur event but this works for now (at least for me)

ziudeso commented 10 months ago

If anyone wants to loose the focus (e.g. of the keyboard on mobile) when selecting here's what I've done:

  1. const parentAutocompleteRef = useRef(null);
  2. surround ReactSearchAutocomplete with a div and give it the ref above
    <div ref={parentAutocompleteRef}>
    <ReactSearchAutocomplete items={enabledCompanies} onSearch={handleOnSearch} onSelect={handleOnSelect} autoFocus formatResult={formatSearchResult}/>
    </div>
  3. Edit handleOnSelect like so
    const handleOnSelect = (item) => {
    ...
    parentAutocompleteRef.current.querySelector('input').blur();
    }

And in mobile you'll unfocus keyboard when selecting an item