Files
mostlymatter/web/react/components/suggestion/suggestion_box.jsx
2015-12-09 09:14:46 -05:00

164 строки
5.5 KiB
JavaScript

// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import Constants from '../../utils/constants.jsx';
import * as EventHelpers from '../../dispatcher/event_helpers.jsx';
import SuggestionStore from '../../stores/suggestion_store.jsx';
import * as Utils from '../../utils/utils.jsx';
const KeyCodes = Constants.KeyCodes;
export default class SuggestionBox extends React.Component {
constructor(props) {
super(props);
this.handleDocumentClick = this.handleDocumentClick.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleCompleteWord = this.handleCompleteWord.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handlePretextChanged = this.handlePretextChanged.bind(this);
this.suggestionId = Utils.generateId();
}
componentDidMount() {
SuggestionStore.registerSuggestionBox(this.suggestionId);
$(document).on('click', this.handleDocumentClick);
SuggestionStore.addCompleteWordListener(this.suggestionId, this.handleCompleteWord);
SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
}
componentWillUnmount() {
SuggestionStore.removeCompleteWordListener(this.suggestionId, this.handleCompleteWord);
SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
SuggestionStore.unregisterSuggestionBox(this.suggestionId);
$(document).off('click', this.handleDocumentClick);
}
getTextbox() {
// this is to support old code that looks at the input/textarea DOM nodes
return ReactDOM.findDOMNode(this.refs.textbox);
}
handleDocumentClick(e) {
const container = $(ReactDOM.findDOMNode(this));
if (!(container.is(e.target) || container.has(e.target).length > 0)) {
// we can't just use blur for this because it fires and hides the children before
// their click handlers can be called
EventHelpers.emitClearSuggestions(this.suggestionId);
}
}
handleChange(e) {
const textbox = ReactDOM.findDOMNode(this.refs.textbox);
const caret = Utils.getCaretPosition(textbox);
const pretext = textbox.value.substring(0, caret);
EventHelpers.emitSuggestionPretextChanged(this.suggestionId, pretext);
if (this.props.onUserInput) {
this.props.onUserInput(textbox.value);
}
if (this.props.onChange) {
this.props.onChange(e);
}
}
handleCompleteWord(term) {
const textbox = ReactDOM.findDOMNode(this.refs.textbox);
const caret = Utils.getCaretPosition(textbox);
const text = this.props.value;
const prefix = text.substring(0, caret - SuggestionStore.getMatchedPretext(this.suggestionId).length);
const suffix = text.substring(caret);
if (this.props.onUserInput) {
this.props.onUserInput(prefix + term + ' ' + suffix);
}
// set the caret position after the next rendering
window.requestAnimationFrame(() => {
Utils.setCaretPosition(textbox, prefix.length + term.length + 1);
});
}
handleKeyDown(e) {
if (SuggestionStore.hasSuggestions(this.suggestionId)) {
if (e.which === KeyCodes.UP) {
EventHelpers.emitSelectPreviousSuggestion(this.suggestionId);
e.preventDefault();
} else if (e.which === KeyCodes.DOWN) {
EventHelpers.emitSelectNextSuggestion(this.suggestionId);
e.preventDefault();
} else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB) {
EventHelpers.emitCompleteWordSuggestion(this.suggestionId);
e.preventDefault();
} else if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
} else if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
}
handlePretextChanged(pretext) {
for (const provider of this.props.providers) {
provider.handlePretextChanged(this.suggestionId, pretext);
}
}
render() {
const newProps = Object.assign({}, this.props, {
onChange: this.handleChange,
onKeyDown: this.handleKeyDown
});
let textbox = null;
if (this.props.type === 'input') {
textbox = (
<input
ref='textbox'
type='text'
{...newProps}
/>
);
} else if (this.props.type === 'textarea') {
textbox = (
<textarea
ref='textbox'
{...newProps}
/>
);
}
const SuggestionListComponent = this.props.listComponent;
return (
<div>
{textbox}
<SuggestionListComponent suggestionId={this.suggestionId} />
</div>
);
}
}
SuggestionBox.defaultProps = {
type: 'input'
};
SuggestionBox.propTypes = {
listComponent: React.PropTypes.func.isRequired,
type: React.PropTypes.oneOf(['input', 'textarea']).isRequired,
value: React.PropTypes.string.isRequired,
onUserInput: React.PropTypes.func,
providers: React.PropTypes.arrayOf(React.PropTypes.object),
// explicitly name any input event handlers we override and need to manually call
onChange: React.PropTypes.func,
onKeyDown: React.PropTypes.func
};