import * as React from 'react'; import * as _ from 'lodash'; import { InputDropDownDrawer } from '../InputDropDownDrawer'; export namespace InputSuggest { export interface Props { /** * Custom input */ customInput?: any; customInputProps?: any; /** * Custom component to show when no options */ customEmptyStateMessage?: JSX.Element; /** * Message to show when no options */ emptyStateMessage?: string; /** * List of options to be displayed initially (the state will manage them) */ initialSuggestOptions: { toString: () => string }[]; /** * Value to display inside the input */ inputValue: string; /** * Function called when the user hits Enter and the dropdown is closed * This function is not executed when the Enter is used to select an option */ onEnterKeyPressed?: () => void; /** * Function called when an option is selected, passing it */ onSelectOption?: (event, option: { toString: () => string }) => void; /** * Function called when the value of the input changes */ onValueChange: (value: string) => void; /** * Function called when rendering the options, being responsible of saying whether the value matches the option or not * By default function includes is used (case insensitive) */ optionMatches?: (option: { toString: () => string }, currentValue: string) => boolean; /** * Placeholder of the input */ placeholder?: string; /** * Indicates if the input is read only */ readOnly?: boolean; /** * Indicates if the dropdown is displayed when there are no options matching the value * It must be true to display emptyStateMessage or customEmptyStateMessage */ showDropDownOnEmpty?: boolean; /** * Prop to customize styles */ styles?: Styles; id?: string; name?: string; } export interface State { /** * Indicates the current options to be displayed */ suggestOptions: { toString: () => string }[]; /** * Indicates whether the dropdown is shown or not */ showDropDown: boolean; } export interface Styles { /** * Styles for the component that renders the input and the dropdown */ InputDropDownDrawer?: InputDropDownDrawer.Styles; } } /** * Component that displays an input that drops down a list of suggestions that match with what the user is typing * * @version 1.0.0 * */ export default class InputSuggest extends React.Component { constructor(props: InputSuggest.Props) { super(props); this.state = { showDropDown: false, suggestOptions: this.filterOptions(props.initialSuggestOptions, props.inputValue.toString()) }; } /** * Function called when rendering the options, being responsible of saying whether the value matches the option or not * It calls prop optionsMatches is passed * By default function includes is used (case insensitive) */ filterOptions = (options: { toString: () => string }[], value: string) => ( options.filter((option) => ( this.props.optionMatches ? this.props.optionMatches(option, value) : option.toString().toUpperCase().includes(value.toUpperCase()) )) ); handleChangeValue = (event) => { const newValue = event.target.value; this.props.onValueChange(newValue); const newOptions = this.filterOptions(this.props.initialSuggestOptions, newValue.toString()); this.setState((prevState: InputSuggest.State, props: InputSuggest.Props) => ({ showDropDown: newOptions.length > 0 || Boolean(props.showDropDownOnEmpty), suggestOptions: newOptions })); }; handleDropDownEvent = (event, show: boolean) => { this.setState((prevState: InputSuggest.State, props: InputSuggest.Props) => ({ showDropDown: show && (prevState.suggestOptions.length > 0 || Boolean(props.showDropDownOnEmpty)) })); }; handleSelectOption = (event: any, option: string) => { const newOptions = this.filterOptions(this.props.initialSuggestOptions, option.toString()); this.props.onValueChange(option); if (this.props.onSelectOption) { this.props.onSelectOption(event, option); } this.setState({ showDropDown: false, suggestOptions: newOptions }); }; render() { const Styles: InputSuggest.Styles = _.merge({}, this.props.styles); return ( ); } }