command scope interface

This commit is contained in:
Cássio Zen 2016-07-06 18:10:13 -03:00
parent cc505a4025
commit fb6fd4762b
2 changed files with 102 additions and 53 deletions

View File

@ -1,18 +1,36 @@
.root {
width: 350px;
}
.input {
background: none transparent;
border: 0 none;
font-size: inherit;
outline: none;
width: 100%;
}
.inputArea {
border: solid 1px #000;
display: table;
width: 100%;
border: 1px solid #ddd;
border-radius: 3px;
box-shadow: inset 0 1px 2px rgba(0,0,0,0.075);
}
.inputScope {
display: table-cell;
width: 1%;
padding-right: 6px;
padding-left: 8px;
color: #767676;
white-space: nowrap;
vertical-align: middle;
border-right: 1px solid #eee;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
.inputField {
display: table-cell;
width: 99%;
background: none transparent;
border: 0 none;
box-shadow: none;
outline: none;
font-size: inherit;
}
.menu {
@ -20,9 +38,8 @@
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
background: rgba(255, 255, 255, 0.9);
padding: 2px 0;
overflow: auto;
width: 350px;
height: 100px;
height: 100%;
}
.command {

View File

@ -9,11 +9,15 @@ class FindBar extends Component {
super(props);
this._compiledCommands = {};
this.state = {
value: this.props.initialValue,
value: '',
placeholder: '',
activeScope: null,
isOpen: false,
highlightedIndex: 0,
};
this._getSuggestions = _.memoize(this._getSuggestions, (value, activeScope) => value + activeScope);
this.compileCommand = this.compileCommand.bind(this);
this.matchCommand = this.matchCommand.bind(this);
this.handleChange = this.handleChange.bind(this);
@ -21,7 +25,7 @@ class FindBar extends Component {
this.handleInputBlur = this.handleInputBlur.bind(this);
this.handleInputFocus = this.handleInputFocus.bind(this);
this.handleInputClick = this.handleInputClick.bind(this);
this.getSuggestions = _.memoize(this.getSuggestions);
this.getSuggestions = this.getSuggestions.bind(this);
this.highlightCommandFromMouse = this.highlightCommandFromMouse.bind(this);
this.selectCommandFromMouse = this.selectCommandFromMouse.bind(this);
this.setIgnoreBlur = this.setIgnoreBlur.bind(this);
@ -66,36 +70,57 @@ class FindBar extends Component {
});
}
matchCommand(string) {
matchCommand() {
const string = this.state.activeScope ? this.state.activeScope + this.state.value : this.state.value;
let match;
const command = this.compiledCommands.find(command => {
const command = this._compiledCommands.find(command => {
match = string.match(RegExp(`^${command.regexp}`, 'i'));
return match;
});
if (command === null) return null;
const param = match[1] && match[1].trim();
if (!command) {
return null;
} else if (command && !param) {
this.setState({
value: '',
activeScope: command.token,
placeholder: command.param.display
});
}
console.log({
command,
param
})
return {
command,
param: match[1] && match[1].trim()
param
};
}
handleChange(event) {
this.setState({
value: event.target.value,
}, () => {
this.props.onChange(event, this.state.value);
});
}
handleKeyDown(event) {
let highlightedIndex, index;
switch (event.key) {
case 'Backspace':
if (this.state.value.length === 0 && this.state.activeScope) {
this.setState({
activeScope: null,
placeholder: ''
});
}
break;
case 'ArrowDown':
event.preventDefault();
let { highlightedIndex } = this.state;
let index = (
highlightedIndex === this.getSuggestions(this.state.value, this._compiledCommands).length - 1 ||
highlightedIndex = this.state.highlightedIndex;
index = (
highlightedIndex === this.getSuggestions().length - 1 ||
this.state.isOpen === false
) ? 0 : highlightedIndex + 1;
this.setState({
@ -105,10 +130,10 @@ class FindBar extends Component {
break;
case 'ArrowUp':
event.preventDefault();
let { highlightedIndex } = this.state;
let index = (
highlightedIndex = this.state.highlightedIndex;
index = (
highlightedIndex === 0
) ? this.getSuggestions(this.state.value, this._compiledCommands).length - 1 : highlightedIndex - 1;
) ? this.getSuggestions().length - 1 : highlightedIndex - 1;
this.setState({
highlightedIndex: index,
isOpen: true,
@ -116,18 +141,21 @@ class FindBar extends Component {
break;
case 'Enter':
if (this.state.isOpen) {
const command = this.getSuggestions(this.state.value, this._compiledCommands)[this.state.highlightedIndex];
this.setState({
value: command.token,
const command = this.getSuggestions()[this.state.highlightedIndex];
const newState = {
isOpen: false,
highlightedIndex: 0
}, () => {
this.refs.input.focus();
this.refs.input.setSelectionRange(
};
if (command) {
newState.value = command.token;
}
this.setState(newState, () => {
this._input.focus();
this._input.setSelectionRange(
this.state.value.length,
this.state.value.length
);
this.props.onSelect(this.state.value, command);
this.matchCommand();
});
}
break;
@ -163,15 +191,19 @@ class FindBar extends Component {
this.setState({ isOpen: true });
}
getSuggestions(value, commands) {
_getSuggestions(value, scope, commands) {
if (scope) return []; // TODO: Prepare for multiple params & search suggestions
const results = fuzzy.filter(value, commands, {
//pre: '<strong>',
//post: '</strong>',
extract: el => el.token
});
return results.map(result => result.original);
return results.slice(0, 5).map(result => result.original);
}
getSuggestions() {
return this._getSuggestions(this.state.value, this.state.activeScope, this._compiledCommands);
}
highlightCommandFromMouse(index) {
this.setState({ highlightedIndex: index });
@ -183,8 +215,8 @@ class FindBar extends Component {
isOpen: false,
highlightedIndex: 0
}, () => {
this.props.onSelect(this.state.value, command);
this.refs.input.focus();
this.matchCommand();
this._input.focus();
this.setIgnoreBlur(false);
});
}
@ -194,7 +226,7 @@ class FindBar extends Component {
}
renderMenu() {
const commands = this.getSuggestions(this.state.value, this._compiledCommands).map((command, index) => {
const commands = this.getSuggestions().map((command, index) => {
return (
<div
className={this.state.highlightedIndex === index ? styles.highlightedCommand : styles.command}
@ -207,40 +239,40 @@ class FindBar extends Component {
);
});
return <div className={styles.menu} children={commands} ref='menu'/>;
return commands.length > 0 ? <div className={styles.menu} children={commands} /> : null;
}
renderActiveScope() {
return <div className={styles.inputScope}>{this.state.activeScope}</div>;
}
render() {
const menu = this.state.isOpen && this.renderMenu();
const scope = this.state.activeScope && this.renderActiveScope();
return (
<div className={styles.root}>
<div className={styles.inputArea}>
<label className={styles.inputArea}>
{scope}
<input
ref="input"
className={styles.input}
className={styles.inputField}
ref={(c) => this._input = c}
onFocus={this.handleInputFocus}
onBlur={this.handleInputBlur}
onChange={(event) => this.handleChange(event)}
onKeyDown={(event) => this.handleKeyDown(event)}
onClick={this.handleInputClick}
placeholder={this.state.placeholder}
value={this.state.value}
/>
</div>
{this.state.isOpen && this.renderMenu()}
</label>
{menu}
</div>
);
}
}
FindBar.propTypes = {
commands: PropTypes.array,
initialValue: PropTypes.any,
onChange: PropTypes.func,
onSelect: PropTypes.func,
commands: PropTypes.array.isRequired
};
FindBar.defaultProps = {
initialValue: '',
onChange() {},
onSelect(value, command) {}
};
module.exports = FindBar;