frontend/app/common/AutocompletingEditBox.jsx (60 lines of code) (raw):

import Autocomplete from "react-autocomplete"; import React from "react"; import PropTypes from "prop-types"; import ClickableIcon from "./ClickableIcon.jsx"; /** * wraps the Autocomplete component to provide clickable enter/cancel buttons */ class AutocompletingEditBox extends React.Component { static propTypes = { items: PropTypes.array.isRequired, initialValue: PropTypes.string.isRequired, newValueOkayed: PropTypes.func.isRequired, filterOptions: PropTypes.boolean }; constructor(props){ super(props); this.state = { currentValue: props.initialValue ? props.initialValue : "", showButtons: false }; this.cancelClicked = this.cancelClicked.bind(this); } filteredItems(){ if(this.props.filterOptions) return this.props.items.filter(item=>item.startsWith(this.state.currentValue)); else return this.props.items; } componentDidUpdate(prevProps, prevState, snapshot) { if( !this.state.showButtons && this.state.currentValue!==prevState.currentValue || prevProps.initialValue!==this.props.initialValue ) this.setState({showButtons: this.state.currentValue!==this.props.initialValue}); } cancelClicked(){ this.setState({currentValue: this.props.initialValue, showButtons:false}); } render() { return <span><Autocomplete getItemValue={item=>item} items={this.filteredItems()} renderItem={(item, isHighlighted) => <div style={{ background: isHighlighted ? 'lightgray' : 'white', color: "black" }}> {item} </div> } value={this.state.currentValue} onChange={evt=>this.setState({currentValue: evt.target.value})} onSelect={val=>this.setState({currentValue: val})} /> <span style={{display: this.state.showButtons ? "inline" : "none"}}> <ClickableIcon onClick={evt=>this.props.newValueOkayed(this.state.currentValue)} icon="check-circle"/> <ClickableIcon onClick={this.cancelClicked} icon="times-circle"/> </span> </span> } } export default AutocompletingEditBox;