import React, { FormEvent, ReactNode, useEffect, useState } from 'react'; import AutoSuggest, { GetSuggestionValue, OnSuggestionsClearRequested, OnSuggestionSelected, RenderSuggestion, RenderSuggestionsContainerParams, SuggestionSelectedEventData, SuggestionsFetchRequested, } from 'react-autosuggest'; import styled from 'styled-components'; import { space, WidthProps } from 'styled-system'; import { useDebouncedCallback } from 'use-debounce'; import { ThemeProps } from '@t3n/theme'; import Box from '../Box'; import Text from '../Text'; import SearchBoxWrapper, { SearchBoxVariantType } from './SearchBoxWrapper'; export interface GroupedSuggestions { title: string; suggestions: S[]; } export interface SearchBoxProps extends WidthProps { variant: SearchBoxVariantType; placeholder: string; isLoading: boolean; defaultValue?: string; multiSection?: boolean; suggestions: GroupedSuggestions[] | S[] | null; getSuggestionValue: GetSuggestionValue; handleSuggestionFetchRequested: SuggestionsFetchRequested; handleSuggestionClearRequested: OnSuggestionsClearRequested; renderSuggestion: RenderSuggestion; renderSuggestionsEmpty?: React.ReactNode; onSelect: OnSuggestionSelected; clearOnSelect?: boolean; onSearchTermChange?: (term: string) => void; children?: ReactNode; } const SuggestionItem = styled.div` ${({ theme }) => space({ theme, p: [2, 3] })} color: ${({ theme }: ThemeProps) => theme.colors.text.primary}; border-bottom: 1px solid ${({ theme }: ThemeProps) => theme.colors.shades.grey244}; &:hover { cursor: pointer; background-color: ${({ theme }: ThemeProps) => theme.colors.background.secondary}; } `; const SuggestionContainer = styled.div` background-color: ${({ theme }: ThemeProps) => theme.colors.background.primary}; box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.1); position: absolute; left: 0; right: 0; z-index: 200; .react-autosuggest__suggestion--highlighted { background-color: ${({ theme }: ThemeProps) => theme.colors.background.secondary}; } ul { margin: 0; padding: 0; list-style: none; } `; // eslint-disable-next-line react/function-component-definition function SearchBox({ variant: variantProp = 'highlight', placeholder = 'Suche', defaultValue, multiSection, isLoading = true, renderSuggestion, renderSuggestionsEmpty: SuggestionsEmpty, suggestions = null, onSelect, clearOnSelect = true, onSearchTermChange, getSuggestionValue, handleSuggestionFetchRequested, handleSuggestionClearRequested, }: SearchBoxProps) { const [term, setTerm] = useState(defaultValue || ''); const debounced = useDebouncedCallback(handleSuggestionFetchRequested, 400); useEffect(() => { if (onSearchTermChange) { onSearchTermChange(term); } }, [onSearchTermChange, term]); const handleOnChange = ( event: FormEvent, { newValue }: { newValue: string }, ) => { setTerm(newValue); }; const renderSuggestionContainer = ({ children, containerProps, }: RenderSuggestionsContainerParams) => { return ( term.length >= 3 && ( {children} {suggestions !== null && suggestions.length === 0 && !isLoading && (SuggestionsEmpty || ( Keine Treffer gefunden ))} ) ); }; const handleSuggestionSelected = ( event: React.FormEvent, data: SuggestionSelectedEventData, ) => { if (clearOnSelect) setTerm(''); onSelect(event, data); }; return ( {multiSection ? ( multiSection renderSuggestionsContainer={renderSuggestionContainer} getSuggestionValue={getSuggestionValue} focusInputOnSuggestionClick={false} suggestions={suggestions === null ? [] : (suggestions as S[])} getSectionSuggestions={(section: GroupedSuggestions) => section.suggestions } shouldRenderSuggestions={(value) => value.length > 1} renderSectionTitle={(section: GroupedSuggestions) => ( {section.title} )} onSuggestionsFetchRequested={debounced} onSuggestionsClearRequested={handleSuggestionClearRequested} onSuggestionSelected={handleSuggestionSelected} renderSuggestion={(suggestion, params) => ( {renderSuggestion(suggestion, params)} )} inputProps={{ value: term, onChange: handleOnChange, placeholder, }} /> ) : ( renderSuggestionsContainer={renderSuggestionContainer} getSuggestionValue={getSuggestionValue} focusInputOnSuggestionClick={false} suggestions={suggestions === null ? [] : (suggestions as S[])} shouldRenderSuggestions={(value) => value.length > 1} onSuggestionsFetchRequested={debounced} onSuggestionsClearRequested={handleSuggestionClearRequested} onSuggestionSelected={handleSuggestionSelected} renderSuggestion={(suggestion, params) => ( {renderSuggestion(suggestion, params)} )} inputProps={{ value: term, onChange: handleOnChange, placeholder, }} /> )} ); } export default SearchBox;