diff --git a/.eslintrc b/.eslintrc index 73de372a..99f891d9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -70,7 +70,6 @@ rules: # ECMAScript 6 # http://eslint.org/docs/rules/#ecmascript-6 - arrow-parens: [2, "always"] arrow-spacing: [2, {"before": true, "after": true}] no-arrow-condition: 2 prefer-const: 2 diff --git a/package.json b/package.json index e5bac462..dadad656 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "draft-js": "^0.7.0", "draft-js-export-markdown": "^0.2.0", "draft-js-import-markdown": "^0.1.6", + "fuzzy": "^0.1.1", "json-loader": "^0.5.4", "localforage": "^1.4.2" } diff --git a/src/containers/FindBar.js b/src/containers/FindBar.js new file mode 100644 index 00000000..99d073f2 --- /dev/null +++ b/src/containers/FindBar.js @@ -0,0 +1,103 @@ +import React, { PropTypes } from 'react'; +import fuzzy from 'fuzzy'; +import { connect } from 'react-redux'; + +class FindBar extends React.Component { + constructor() { + this.compiledCommands = {}; + this.state = { + prompt: '', + value: '', + suggestions: [] + }; + + this.compileCommand = this.compileCommand.bind(this); + this.matchCommand = this.matchCommand.bind(this); + this.getSuggestions = this.getSuggestions.bind(this); + this.handleInputChange = this.handleInputChange.bind(this); + } + + componentDidMount() { + this.compiledCommands = this.props.commands.map(this.compileCommand); + } + + _escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + + _camelCaseToSpace(string) { + var result = string.replace(/([A-Z])/g, ' $1'); + return result.charAt(0).toUpperCase() + result.slice(1); + } + + compileCommand(command) { + let regexp = ''; + let param; + + const matcher = /\(:([a-zA-Z_$][a-zA-Z0-9_$]*)(?:(?: as )(.*))?\)/g; + const match = matcher.exec(command.pattern); + const token = command.pattern.slice(0, match.index); + regexp += this._escapeRegExp(command.pattern.slice(0, match.index)); + + if (match[1]) { + regexp += '(.*)'; + param = { name:match[1], display:match[2] || this._camelCaseToSpace(match[1]) }; + } + + return Object.assign({}, command, { + regexp, + token, + param + }); + } + + matchCommand(string) { + let match; + const command = this.compiledCommands.find(command => { + match = string.match(RegExp(`^${command.regexp}`, 'i')); + return match; + }); + + if (command === null) return null; + + return { + command, + param: match[1] && match[1].trim() + }; + } + + getSuggestions(value) { + const options = { + //pre: '', + //post: '', + extract: el => el. token + }; + const results = fuzzy.filter(value, this.compiledCommands, options); + return results; + } + + handleInputChange(e) { + const newValue = e.target.value; + this.setState({ + value: newValue, + suggestions: this.getSuggestions(newValue) + }); + } + + render() { + return ( + + ); + } +} + +FindBar.propTypes = { + commands: PropTypes.array.isRequired +}; + +export default connect(null)(FindBar);