2017-06-08_509bba0/509bba0_unpacked_with_node_modules/discord_app/components/AutocompletePopout.js
2022-07-26 10:06:20 -07:00

166 lines
4.6 KiB
JavaScript
Executable file

import React from 'react';
import classNames from 'classnames';
import LazyScroller, {Themes} from './common/LazyScroller';
import i18n from '../i18n';
import RegexUtils from '../utils/RegexUtils';
import '../styles/autocomplete_popout.styl';
const INPUT_REF = 'input';
const AutocompletePopout = React.createClass({
getDefaultProps() {
return {
sections: [null],
};
},
getInitialState() {
return this.createState();
},
componentWillReceiveProps() {
this.setState(this.createState(this.state.prefix));
},
createState(prefix = '') {
const regex = new RegExp(`^${RegexUtils.escape(prefix.trim())}`, 'i');
const filter = v => regex.test(v);
const rows = this.props.sections.map((_, i) => this.props.onFilterResults(filter, i));
let selectedSection = 0;
for (; selectedSection < rows.length; selectedSection++) {
if (rows[selectedSection].length > 0) {
break;
}
}
return {
prefix,
selectedSection,
selectedRow: 0,
rows,
};
},
componentDidMount() {
process.nextTick(this.focus);
},
handleChange(e) {
const prefix = e.target.value;
this.props.onQueryChange && this.props.onQueryChange(prefix);
this.setState(this.createState(prefix));
},
handleMouseEnter(section, row) {
this.setState({
selectedSection: section,
selectedRow: row,
});
},
handleClick(section, row) {
this.props.onSelect(this.state.rows[section][row], section);
this.props.onClose();
},
handleKeyDown(e) {
let {selectedSection, selectedRow} = this.state;
switch (e.keyCode) {
case 9: // TAB
case 40: // DOWN
e.preventDefault();
if (this.props.sections.length > selectedSection && ++selectedRow >= this.state.rows[selectedSection].length) {
if (++selectedSection >= this.props.sections.length) {
selectedSection = 0;
}
selectedRow = 0;
}
this.setState({selectedSection, selectedRow});
break;
case 38: // UP
e.preventDefault();
if (--selectedRow < 0) {
if (--selectedSection < 0) {
selectedSection = this.props.sections.length - 1;
}
selectedRow = this.state.rows[selectedSection].length - 1;
}
this.setState({selectedSection, selectedRow});
break;
case 13: // ENTER
e.preventDefault();
if (this.props.sections.length > selectedSection && this.state.rows[selectedSection].length > selectedRow) {
this.props.onSelect(this.state.rows[selectedSection][selectedRow], selectedSection);
this.props.onClose();
}
break;
case 27: // ESCAPE
e.preventDefault();
this.props.onSelect(null, null);
this.props.onClose();
break;
}
},
render() {
const results = [];
if (this.state.rows.reduce((acc, rows) => acc + rows.length, 0) === 0) {
results.push(
<div key="empty" className="empty">
<h4>{i18n.Messages.AUTOCOMPLETE_NO_RESULTS_HEADER}</h4>
<p>{i18n.Messages.AUTOCOMPLETE_NO_RESULTS_BODY}</p>
</div>
);
} else {
const {selectedSection, selectedRow} = this.state;
this.props.sections.forEach((section, i) => {
const rows = this.state.rows[i];
if (rows.length === 0) return;
results.push(<div key={i} className="section">{section}</div>);
rows.forEach((result, j) => {
results.push(
<div
key={`${i}-${j}`}
onClick={this.handleClick.bind(this, i, j)}
onMouseEnter={this.handleMouseEnter.bind(this, i, j)}
className={classNames('row', {selected: selectedSection === i && selectedRow === j})}>
<a>{this.props.onRenderResult(result, i)}</a>
</div>
);
});
});
}
return (
<div id="autocomplete-popout" className={this.props.className}>
<header>
<h3>{this.props.label}</h3>
<input
type="text"
placeholder={this.props.placeholder}
ref={INPUT_REF}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
/>
</header>
<section>
<LazyScroller fade theme={Themes.LIGHT} elementHeight={44}>
{results}
</LazyScroller>
</section>
</div>
);
},
focus() {
if (this.isMounted()) {
this.refs[INPUT_REF].focus();
}
},
});
export default AutocompletePopout;
// WEBPACK FOOTER //
// ./discord_app/components/AutocompletePopout.js