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);