Accepts command with no parameter

This commit is contained in:
Cássio Zen 2016-07-07 12:04:19 -03:00
parent 238e671f4f
commit 923678d74f
4 changed files with 49 additions and 28 deletions

View File

@ -1,7 +1,7 @@
import { configure } from '@kadira/storybook'; import { configure } from '@kadira/storybook';
import '../src/index.css';
function loadStories() { function loadStories() {
require('../src/index.css');
require('../src/containers/stories/'); require('../src/containers/stories/');
require('../src/components/stories/'); require('../src/components/stories/');
} }

View File

@ -1,5 +1,5 @@
export const RUN_COMMAND = 'RUN_COMMAND'; export const RUN_COMMAND = 'RUN_COMMAND';
export function runCommand(commandName, paramName, paramValue) { export function runCommand(commandName, payload) {
return { type: RUN_COMMAND, command: commandName, payload: { [paramName]: paramValue } }; return { type: RUN_COMMAND, command: commandName, payload };
} }

View File

@ -49,20 +49,29 @@ class FindBar extends Component {
return result.charAt(0).toUpperCase() + result.slice(1); return result.charAt(0).toUpperCase() + result.slice(1);
} }
// Generates a regexp and splits a token and param details for a command
compileCommand(command) { compileCommand(command) {
let regexp = ''; let regexp = '';
let param; let param = null;
const matcher = /\(:([a-zA-Z_$][a-zA-Z0-9_$]*)(?:(?: as )(.*))?\)/g; const matcher = /\(:([a-zA-Z_$][a-zA-Z0-9_$]*)(?:(?: as )(.*))?\)/g;
const match = matcher.exec(command.pattern); const match = matcher.exec(command.pattern);
const token = command.pattern.slice(0, match.index) || command.token; const matchIndex = match ? match.index : command.pattern.length;
regexp += this._escapeRegExp(command.pattern.slice(0, match.index));
if (match[1]) { const token = command.pattern.slice(0, matchIndex) || command.token;
regexp += this._escapeRegExp(command.pattern.slice(0, matchIndex));
if (match && match[1]) {
regexp += '(.*)'; regexp += '(.*)';
param = { name:match[1], display:match[2] || this._camelCaseToSpace(match[1]) }; param = { name:match[1], display:match[2] || this._camelCaseToSpace(match[1]) };
} }
console.log(Object.assign({}, command, {
regexp,
token,
param
}));
return Object.assign({}, command, { return Object.assign({}, command, {
regexp, regexp,
token, token,
@ -70,6 +79,8 @@ class FindBar extends Component {
}); });
} }
// Check if the entered string matches any command.
// adds a scope (so user can type param value) and dispatches action for fully matched commands
matchCommand() { matchCommand() {
const string = this.state.activeScope ? this.state.activeScope + this.state.value : this.state.value; const string = this.state.activeScope ? this.state.activeScope + this.state.value : this.state.value;
let match; let match;
@ -78,18 +89,27 @@ class FindBar extends Component {
return match; return match;
}); });
const param = match[1] && match[1].trim(); const paramName = command.param ? command.param.name : null;
const enteredParamValue = command.param && match[1] ? match[1].trim() : null;
if (!command) { if (!command) {
// No matched command
return null; return null;
} else if (command && !param) { } else if (command.param && !enteredParamValue) {
// Partial Match
// Command was partially matched: It requires a param, but param wasn't entered
// Set a scope so user can fill the param
this.setState({ this.setState({
value: '', value: '',
activeScope: command.token, activeScope: command.token,
placeholder: command.param.display placeholder: command.param.display
}); });
} else { } else {
this.props.dispatch(runCommand(command.token, command.param.name, param)); // Match
// Command was matched and either it doesn't require a param or it's required param was entered
// Dispatch action
const payload = paramName ? { [paramName]: enteredParamValue } : null;
this.props.dispatch(runCommand(command.token, payload));
} }
} }
@ -102,10 +122,18 @@ class FindBar extends Component {
} }
} }
handleChange(event) { getSuggestions() {
this.setState({ return this._getSuggestions(this.state.value, this.state.activeScope, this._compiledCommands);
value: event.target.value, }
// Memoized version
_getSuggestions(value, scope, commands) {
if (scope) return []; // No autocomplete for scoped input
const results = fuzzy.filter(value, commands, {
//pre: '<strong>',
//post: '</strong>',
extract: el => el.token
}); });
return results.slice(0, 5).map(result => result.original);
} }
handleKeyDown(event) { handleKeyDown(event) {
@ -171,6 +199,12 @@ class FindBar extends Component {
} }
} }
handleChange(event) {
this.setState({
value: event.target.value,
});
}
handleInputBlur() { handleInputBlur() {
if (this._ignoreBlur) return; if (this._ignoreBlur) return;
this.setState({ this.setState({
@ -189,20 +223,6 @@ class FindBar extends Component {
this.setState({ isOpen: true }); this.setState({ isOpen: true });
} }
_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.slice(0, 5).map(result => result.original);
}
getSuggestions() {
return this._getSuggestions(this.state.value, this.state.activeScope, this._compiledCommands);
}
highlightCommandFromMouse(index) { highlightCommandFromMouse(index) {
this.setState({ highlightedIndex: index }); this.setState({ highlightedIndex: index });
} }

View File

@ -10,7 +10,8 @@ const commands = [
{ pattern: 'Create new FAQ item(:faqName as FAQ item name)' }, { pattern: 'Create new FAQ item(:faqName as FAQ item name)' },
{ pattern: 'Add news item(:headline)' }, { pattern: 'Add news item(:headline)' },
{ pattern: 'Add new User(:userName as User name)' }, { pattern: 'Add new User(:userName as User name)' },
{ pattern: 'Search(:seachTerm as what?)' }, { pattern: 'Search(:searchTerm as what?)' },
{ pattern: 'Go to Settings' },
{ pattern: 'Find(:seachTerm as what?)' }, { pattern: 'Find(:seachTerm as what?)' },
{ pattern: '(:searchTerm as Find...)', token:'Find' } { pattern: '(:searchTerm as Find...)', token:'Find' }
]; ];