Просмотр исходного кода

applying react-bootstrap-typeahead

Yuki Takei 9 лет назад
Родитель
Сommit
23e176b274

+ 2 - 0
package.json

@@ -103,6 +103,8 @@
     "normalize-path": "^2.1.1",
     "optimize-js-plugin": "0.0.4",
     "react": "^15.4.2",
+    "react-bootstrap": "^0.31.0",
+    "react-bootstrap-typeahead": "^1.3.0",
     "react-clipboard.js": "^1.0.1",
     "react-dom": "^15.4.2",
     "redis": "^2.7.1",

+ 6 - 3
resource/css/_search.scss

@@ -23,10 +23,12 @@
   vertical-align: bottom;
 }
 
-
 .search-top {
   .search-top-input-group {
-    .search-top-input {
+
+    // using react-bootstrap-typeahead
+    // see: https://github.com/ericgio/react-bootstrap-typeahead
+    .bootstrap-typeahead-input input {
       width: 400px;
     }
 
@@ -36,8 +38,9 @@
       z-index: 10;
       width: 22px;
       height: 22px;
+      top: 7px;
       color: #ccc;
-      padding: 8px;
+      padding: 0;
     }
   }
 }

+ 6 - 0
resource/css/crowi.scss

@@ -5,6 +5,12 @@
 $bootstrap-sass-asset-helper: true;
 @import "~bootstrap-sass/assets/stylesheets/bootstrap";
 
+// import react-bootstrap-typeahead styles
+@import '~react-bootstrap-typeahead/css/ClearButton';
+@import '~react-bootstrap-typeahead/css/Loader';
+@import '~react-bootstrap-typeahead/css/Token';
+@import '~react-bootstrap-typeahead/css/Typeahead';
+
 // crowi component
 @import 'admin';
 @import 'comment';

+ 2 - 6
resource/js/components/HeaderSearchBox.js

@@ -22,11 +22,6 @@ export default class SearchBox extends React.Component {
     }
 
     this.search = this.search.bind(this);
-    this.isShown = this.isShown.bind(this);
-  }
-
-  isShown(focused) {
-    this.setState({focused: !!focused});
   }
 
   search(data) {
@@ -66,8 +61,8 @@ export default class SearchBox extends React.Component {
       <div className="search-box">
         <SearchForm
           onSearchFormChanged={this.search}
-          isShown={this.isShown}
           />
+        {/* omit since using react-bootstrap-typeahead in SearchForm
         <SearchSuggest
           searchingKeyword={this.state.searchingKeyword}
           searchedPages={this.state.searchedPages}
@@ -75,6 +70,7 @@ export default class SearchBox extends React.Component {
           searching={this.state.searching}
           focused={this.state.focused}
           />
+        */}
       </div>
     );
   }

+ 34 - 55
resource/js/components/HeaderSearchBox/SearchForm.js

@@ -1,4 +1,7 @@
 import React from 'react';
+import { FormGroup, FormControl, DropdownButton, MenuItem, Button, Checkbox, InputGroup } from 'react-bootstrap';
+
+import { AsyncTypeahead } from 'react-bootstrap-typeahead';
 
 // Header.SearchForm
 export default class SearchForm extends React.Component {
@@ -11,57 +14,39 @@ export default class SearchForm extends React.Component {
       searchedKeyword: '',
     };
 
-    this.handleChange = this.handleChange.bind(this);
-    this.handleFocus = this.handleFocus.bind(this);
-    this.handleBlur = this.handleBlur.bind(this);
+    this.search = this.search.bind(this);
     this.clearForm = this.clearForm.bind(this);
-    this.ticker = null;
+    this.getFormClearComponent = this.getFormClearComponent.bind(this);
   }
 
   componentDidMount() {
-    this.ticker = setInterval(this.searchFieldTicker.bind(this), this.props.pollInterval);
   }
 
   componentWillUnmount() {
-    clearInterval(this.ticker);
   }
 
-  search() {
-    if (this.state.searchedKeyword != this.state.keyword) {
-      this.props.onSearchFormChanged({keyword: this.state.keyword});
-      this.setState({searchedKeyword: this.state.keyword});
+  search(keyword) {
+    this.setState({keyword});
+
+    if (this.state.searchedKeyword != keyword) {
+      this.props.onSearchFormChanged({keyword});
+      this.setState({searchedKeyword: keyword});
     }
   }
 
   getFormClearComponent() {
-    let isHidden = (this.state.keyword === '');
-    return (
-      <a className="search-top-clear" onClick={this.clearForm} hidden={isHidden}>
+    let isHidden = (this.state.keyword.length === 0);
+
+    return isHidden ? <span></span> : (
+      <a className="btn btn-link search-top-clear" onClick={this.clearForm} hidden={isHidden}>
         <i className="fa fa-times-circle" />
       </a>
     );
   }
 
   clearForm() {
+    this._typeahead.getInstance().clear();
     this.setState({keyword: ''});
-    this.search();
-  }
-
-  searchFieldTicker() {
-    this.search();
-  }
-
-  handleFocus(event) {
-    this.props.isShown(true);
-  }
-
-  handleBlur(event) {
-    //this.props.isShown(false);
-  }
-
-  handleChange(event) {
-    const keyword = event.target.value;
-    this.setState({keyword});
   }
 
   render() {
@@ -72,36 +57,30 @@ export default class SearchForm extends React.Component {
         action="/_search"
         className="search-form form-group input-group search-top-input-group"
       >
-        <div className="input-group">
-          <input
-            autocomplete="off"
-            type="text"
-            className="search-top-input form-control"
-            placeholder="Search ... Page Title (Path) and Content"
-            name="q"
-            value={this.state.keyword}
-            onFocus={this.handleFocus}
-            onBlur={this.handleBlur}
-            onChange={this.handleChange}
-          />
-          {formClear}
-          <span className="input-group-btn">
-            <button type="submit" className="btn btn-default">
-              <i className="search-top-icon fa fa-search"></i>
-            </button>
-          </span>
-        </div>// /.input-group
+        <FormGroup>
+          <InputGroup>
+            <AsyncTypeahead
+              ref={ref => this._typeahead = ref}
+              name="q"
+              placeholder="Search ... Page Title (Path) and Content"
+              submitFormOnEnter={true}
+              onSearch={this.search}
+            />
+            {formClear}
+            <InputGroup.Button>
+              <Button type="submit">
+                <i className="search-top-icon fa fa-search"></i>
+              </Button >
+            </InputGroup.Button>
+          </InputGroup>
+        </FormGroup>
 
       </form>
+
     );
   }
 }
 
 SearchForm.propTypes = {
   onSearchFormChanged: React.PropTypes.func.isRequired,
-  isShown: React.PropTypes.func.isRequired,
-  pollInterval: React.PropTypes.number,
-};
-SearchForm.defaultProps = {
-  pollInterval: 1000,
 };

+ 112 - 6
yarn.lock

@@ -714,7 +714,7 @@ babel-register@^6.24.1:
     mkdirp "^0.5.1"
     source-map-support "^0.4.2"
 
-babel-runtime@^6.18.0, babel-runtime@^6.22.0:
+babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0:
   version "6.23.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
   dependencies:
@@ -833,6 +833,10 @@ bl@~1.1.2:
   dependencies:
     readable-stream "~2.0.5"
 
+blacklist@^1.1.2:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/blacklist/-/blacklist-1.1.4.tgz#b2dd09d6177625b2caa69835a37b28995fa9a2f2"
+
 blob@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
@@ -1164,6 +1168,10 @@ clap@^1.0.9:
   dependencies:
     chalk "^1.1.3"
 
+classnames@^2.2.0, classnames@^2.2.5:
+  version "2.2.5"
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
+
 cli-table@^0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
@@ -1534,6 +1542,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2:
     create-hash "^1.1.0"
     inherits "^2.0.1"
 
+create-react-class@^15.5.x:
+  version "15.5.2"
+  resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.5.2.tgz#6a8758348df660b88326a0e764d569f274aad681"
+  dependencies:
+    fbjs "^0.8.9"
+    object-assign "^4.1.1"
+
 cross-spawn@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@@ -1824,6 +1839,10 @@ diffie-hellman@^5.0.0:
     miller-rabin "^4.0.0"
     randombytes "^2.0.0"
 
+dom-helpers@^3.2.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
+
 domain-browser@^1.1.1:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
@@ -1988,7 +2007,7 @@ escape-html@~1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
 
-escape-string-regexp@1.0.5, escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3:
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
 
@@ -2790,7 +2809,7 @@ interpret@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.2.tgz#f4f623f0bb7122f15f5717c8e254b8161b5c5b2d"
 
-invariant@^2.2.0:
+invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1:
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
   dependencies:
@@ -3103,6 +3122,10 @@ kareem@1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/kareem/-/kareem-1.2.1.tgz#acdb8c8119845834abbfa58ade1cf9dea63dc752"
 
+keycode@^2.1.2:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.8.tgz#94d2b7098215eff0e8f9a8931d5a59076c4532fb"
+
 keygrip@~1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.1.tgz#b02fa4816eef21a8c4b35ca9e52921ffc89a30e9"
@@ -3388,7 +3411,7 @@ lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
 
-lodash@^4.0.0, lodash@^4.12.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.8.0:
+lodash@^4.0.0, lodash@^4.12.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.8.0:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
 
@@ -4019,7 +4042,7 @@ object-assign@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
 
-object-assign@^4.0.1, object-assign@^4.1.0:
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
 
@@ -4510,7 +4533,7 @@ promise@^7.1.1:
   dependencies:
     asap "~2.0.3"
 
-prop-types@^15.5.7, prop-types@~15.5.7:
+prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
   version "15.5.8"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
   dependencies:
@@ -4624,6 +4647,35 @@ rc@^1.1.7:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
+react-bootstrap-typeahead@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/react-bootstrap-typeahead/-/react-bootstrap-typeahead-1.3.0.tgz#dc7383584d2f68b17f3c6d96b82ab32020934801"
+  dependencies:
+    classnames "^2.2.0"
+    invariant "^2.2.1"
+    lodash "^4.17.2"
+    react-highlighter "^0.3.3"
+    react-input-autosize "^1.1.0"
+    react-onclickoutside "^5.7.0"
+    react-overlays "^0.6.10"
+    react-prop-types "^0.4.0"
+    warning "^3.0.0"
+
+react-bootstrap@^0.31.0:
+  version "0.31.0"
+  resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-0.31.0.tgz#bbca804c0404d9c640102b2b656ae4cd5bea35c8"
+  dependencies:
+    babel-runtime "^6.11.6"
+    classnames "^2.2.5"
+    dom-helpers "^3.2.0"
+    invariant "^2.2.1"
+    keycode "^2.1.2"
+    prop-types "^15.5.6"
+    react-overlays "^0.7.0"
+    react-prop-types "^0.4.0"
+    uncontrollable "^4.1.0"
+    warning "^3.0.0"
+
 react-clipboard.js@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/react-clipboard.js/-/react-clipboard.js-1.0.1.tgz#56dea0547c13977cd1a1650fc740e0349aa90bdf"
@@ -4639,6 +4691,48 @@ react-dom@^15.4.2:
     object-assign "^4.1.0"
     prop-types "~15.5.7"
 
+react-highlighter@^0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/react-highlighter/-/react-highlighter-0.3.3.tgz#92f8a9e3948c503a215d8eb9a778cabac2fd0ea4"
+  dependencies:
+    blacklist "^1.1.2"
+    escape-string-regexp "^1.0.5"
+
+react-input-autosize@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.1.0.tgz#3fe1ac832387d8abab85f6051ceab1c9e5570853"
+
+react-onclickoutside@^5.7.0:
+  version "5.11.1"
+  resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-5.11.1.tgz#00314e52567cf55faba94cabbacd119619070623"
+  dependencies:
+    create-react-class "^15.5.x"
+
+react-overlays@^0.6.10:
+  version "0.6.12"
+  resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.6.12.tgz#a079c750cc429d7db4c7474a95b4b54033e255c3"
+  dependencies:
+    classnames "^2.2.5"
+    dom-helpers "^3.2.0"
+    react-prop-types "^0.4.0"
+    warning "^3.0.0"
+
+react-overlays@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.7.0.tgz#531898ff566c7e5c7226ead2863b8cf9fbb5a981"
+  dependencies:
+    classnames "^2.2.5"
+    dom-helpers "^3.2.0"
+    prop-types "^15.5.8"
+    react-prop-types "^0.4.0"
+    warning "^3.0.0"
+
+react-prop-types@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/react-prop-types/-/react-prop-types-0.4.0.tgz#f99b0bfb4006929c9af2051e7c1414a5c75b93d0"
+  dependencies:
+    warning "^3.0.0"
+
 react@^15.4.2:
   version "15.5.4"
   resolved "https://registry.yarnpkg.com/react/-/react-15.5.4.tgz#fa83eb01506ab237cdc1c8c3b1cea8de012bf047"
@@ -5633,6 +5727,12 @@ ultron@1.0.x:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
 
+uncontrollable@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.1.0.tgz#e0358291252e1865222d90939b19f2f49f81c1a9"
+  dependencies:
+    invariant "^2.1.0"
+
 underscore@~1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
@@ -5721,6 +5821,12 @@ ware@^1.3.0:
   dependencies:
     wrap-fn "^0.1.0"
 
+warning@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
+  dependencies:
+    loose-envify "^1.0.0"
+
 watchpack@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87"