import * as React from 'react'; import * as _ from 'lodash'; import * as DefaultStyles from './style'; import { DropDownDrawer } from './DropDownDrawer'; import { KEY_CODE } from '../../constants/keyCodes'; export namespace InputDropDownDrawer { export interface Props { /** * Custom input component */ customInput?: any; customInputProps?: any, /** * Custom component to show when no options */ customEmptyStateMessage?: JSX.Element; /** * Message to show when no options */ emptyStateMessage?: string; /** * Chunk to be highlighted inside the options */ highlightedChunk?: string; /** * Value to display inside the input */ inputValue: string; /** * Function called when an event happens inside the drop down (Arrow Up, Arrow Down, Enter, etc) * It passes the event and a boolean indicating if the dropdown should be showed or not */ onDropDownEvent: (event, show: boolean) => void; /** * 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?: (event) => void; /** * Placeholder of the input */ placeholder?: string; /** * Indicates if the input is read only */ readOnly?: boolean; /** * Indicates if the dropdown needs to be displayed */ showDropDown: boolean; /** * Indicates if the input will have an icon at the right, as a combo box */ showSelectIcon?: boolean; /** * List of options to be displayed */ suggestOptions: { toString: () => string }[]; /** * Prop to customize styles */ styles?: Styles; id?: string; name?: string; } export interface State { /** * Indicates the current selected option */ selectedOption: number; } export interface Styles { /** * Styles for the container of the entire component */ Container?: any; /** * Styles for the component that renders the dropdown */ DropDownDrawer?: DropDownDrawer.Styles; /** * Styles for the input */ Input?: any; /** * Styles for the input icon on collapse */ InputSelectIconOnCollapse?: any; /** * Styles for the input icon on expand */ InputSelectIconOnExpand?: any; /** * Styles for the wrapper that contains both input and icon */ InputWrapper?: any; } } /** * Component that displays an input with a dropdown, depending on the passed props * The only logic it has is to handle clicks and key presses * It changes the selected option or notifies when to open/close the dropdown * * @version 1.0.0 * */ export class InputDropDownDrawer extends React.Component { /** * This div is injected in the component to know if a click is done inside it or not */ private containerDiv: any; constructor(props: InputDropDownDrawer.Props) { super(props); this.state = { selectedOption: 0 }; } componentDidMount(): void { document.addEventListener('mousedown', this.handleClickOutside); } componentWillUnmount(): void { document.removeEventListener('mousedown', this.handleClickOutside); } /** * This function catches any click done outside of the component * It notifies that the dropdown should be not displayed anymore */ handleClickOutside = (event): void => { if (this.containerDiv && !this.containerDiv.contains(event.target)) { this.props.onDropDownEvent(event, false); } }; getNextOption = (currentOption: number, maxOptions: number): number => ( (currentOption + 1) % maxOptions ); getPreviousOption = (currentOption: number, maxOptions: number): number => ( (currentOption + maxOptions - 1) % maxOptions ); /** * This function handles important key press events */ keyPressHandle = (event): void => { if (event.key === KEY_CODE.ARROW_DOWN) { const newSelectedOption = this.getNextOption(this.state.selectedOption, this.props.suggestOptions.length); this.props.onDropDownEvent(event, true); this.setState({ selectedOption: newSelectedOption }); } if (event.key === KEY_CODE.ARROW_UP) { const newSelectedOption = this.getPreviousOption(this.state.selectedOption, this.props.suggestOptions.length); this.props.onDropDownEvent(event, true); this.setState({ selectedOption: newSelectedOption }); } if (event.key === KEY_CODE.ENTER) { if (this.props.showDropDown && this.props.suggestOptions.length > 0) { this.props.onSelectOption(event, this.props.suggestOptions[this.state.selectedOption]); } else { if (this.props.onEnterKeyPressed) { this.props.onEnterKeyPressed(); } } } if ((event.key === KEY_CODE.ESCAPE) || (event.key === KEY_CODE.TAB)) { if (this.props.showDropDown) { this.props.onDropDownEvent(event, false); } } } handleInputClick = (): void => { if (this.props.showSelectIcon) { this.props.onDropDownEvent('CLICK', !this.props.showDropDown); } }; handleOnInputChange = (event): void => { if (this.props.onValueChange) { this.props.onValueChange(event); } this.setState({ selectedOption: 0 }); }; selectInput = (Custom, Styles): JSX.Element => { if (Custom) { return ; } else { return } } handleOnHoverOption = (event, value: { toString: () => string }, position: number) => { this.setState({ selectedOption: position }); } render(): JSX.Element { const Styles: InputDropDownDrawer.Styles = _.merge(DefaultStyles, this.props.styles); return ( this.containerDiv = div} onKeyDown={this.keyPressHandle} id={this.props.id && this.props.id + '-input-drop-down-drawer-container-div'} name={this.props.name && this.props.name + '-input-drop-down-drawer-container-div'} > {this.selectInput(this.props.customInput, Styles)} { this.props.showSelectIcon && ( this.props.showDropDown ? : ) } { this.props.showDropDown && } ); } }