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

refs issue#238
- Modified SearchForm using SearchTypeahead

Ryu Sato 8 лет назад
Родитель
Сommit
8d83f39569

+ 20 - 71
resource/js/components/HeaderSearchBox/SearchForm.js

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import { FormGroup, Button, InputGroup } from 'react-bootstrap';
 import { FormGroup, Button, InputGroup } from 'react-bootstrap';
 
 
-import { AsyncTypeahead } from 'react-bootstrap-typeahead';
+import SearchTypeahead from '../SearchTypeahead';
 
 
 import UserPicture from '../User/UserPicture';
 import UserPicture from '../User/UserPicture';
 import PageListMeta from '../PageList/PageListMeta';
 import PageListMeta from '../PageList/PageListMeta';
@@ -19,16 +19,13 @@ export default class SearchForm extends React.Component {
     this.state = {
     this.state = {
       input: '',
       input: '',
       keyword: '',
       keyword: '',
-      searchedKeyword: '',
       pages: [],
       pages: [],
       isLoading: false,
       isLoading: false,
       searchError: null,
       searchError: null,
     };
     };
 
 
-    this.search = this.search.bind(this);
-    this.clearForm = this.clearForm.bind(this);
-    this.getFormClearComponent = this.getFormClearComponent.bind(this);
-    this.renderMenuItemChildren = this.renderMenuItemChildren.bind(this);
+    this.onSearchSuccess = this.onSearchSuccess.bind(this);
+    this.onSearchError = this.onSearchError.bind(this);
     this.onInputChange = this.onInputChange.bind(this);
     this.onInputChange = this.onInputChange.bind(this);
     this.onChange = this.onChange.bind(this);
     this.onChange = this.onChange.bind(this);
   }
   }
@@ -39,47 +36,19 @@ export default class SearchForm extends React.Component {
   componentWillUnmount() {
   componentWillUnmount() {
   }
   }
 
 
-  search(keyword) {
-
-    if (keyword === '') {
-      this.setState({
-        keyword: '',
-        searchedKeyword: '',
-      });
-      return;
-    }
-
-    this.setState({isLoading: true});
-
-    this.crowi.apiGet('/search', {q: keyword})
-      .then(res => {
-        this.setState({
-          isLoading: false,
-          keyword: '',
-          pages: res.data,
-        });
-      })
-      .catch(err => {
-        this.setState({
-          isLoading: false,
-          searchError: err,
-        });
-      });
-  }
-
-  getFormClearComponent() {
-    let isHidden = (this.state.input.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>
-    );
+  onSearchSuccess(res) {
+    this.setState({
+      isLoading: false,
+      keyword: '',
+      pages: res.data,
+    });
   }
   }
 
 
-  clearForm() {
-    this._typeahead.getInstance().clear();
-    this.setState({keyword: ''});
+  onSearchError(err) {
+    this.setState({
+      isLoading: false,
+      searchError: err,
+    });
   }
   }
 
 
   onInputChange(text) {
   onInputChange(text) {
@@ -95,22 +64,10 @@ export default class SearchForm extends React.Component {
     }
     }
   }
   }
 
 
-  renderMenuItemChildren(option, props, index) {
-    const page = option;
-    return (
-      <span>
-        <UserPicture user={page.revision.author} />
-        <PagePath page={page} />
-        <PageListMeta page={page} />
-      </span>
-    );
-  }
-
   render() {
   render() {
     const emptyLabel = (this.state.searchError !== null)
     const emptyLabel = (this.state.searchError !== null)
         ? 'Error on searching.'
         ? 'Error on searching.'
         : 'No matches found on title... Hit [Enter] key so that search on contents.';
         : 'No matches found on title... Hit [Enter] key so that search on contents.';
-    const formClear = this.getFormClearComponent();
 
 
     return (
     return (
       <form
       <form
@@ -119,23 +76,15 @@ export default class SearchForm extends React.Component {
       >
       >
         <FormGroup>
         <FormGroup>
           <InputGroup>
           <InputGroup>
-            <AsyncTypeahead
-              ref={ref => this._typeahead = ref}
-              inputProps={{name: "q", autoComplete: "off"}}
-              isLoading={this.state.isLoading}
-              labelKey="path"
-              minLength={2}
-              options={this.state.pages}
-              placeholder="Search ..."
-              emptyLabel={emptyLabel}
-              align='left'
-              submitFormOnEnter={true}
-              onSearch={this.search}
+            <SearchTypeahead
+              crowi={this.crowi}
+              onSearchSuccess={this.onSearchSuccess}
+              onSearchError={this.onSearchError}
               onInputChange={this.onInputChange}
               onInputChange={this.onInputChange}
               onChange={this.onChange}
               onChange={this.onChange}
-              renderMenuItemChildren={this.renderMenuItemChildren}
+              emptyLabel={emptyLabel}
+              placeholder="Search ..."
             />
             />
-            {formClear}
             <InputGroup.Button>
             <InputGroup.Button>
               <Button type="submit">
               <Button type="submit">
                 <i className="search-top-icon fa fa-search"></i>
                 <i className="search-top-icon fa fa-search"></i>

+ 15 - 10
resource/js/components/NewPageNameInputter.js

@@ -1,8 +1,6 @@
 import React from 'react';
 import React from 'react';
 import { FormGroup, Button, InputGroup } from 'react-bootstrap';
 import { FormGroup, Button, InputGroup } from 'react-bootstrap';
 
 
-import { AsyncTypeahead } from 'react-bootstrap-typeahead';
-
 import UserPicture from './User/UserPicture';
 import UserPicture from './User/UserPicture';
 import PageListMeta from './PageList/PageListMeta';
 import PageListMeta from './PageList/PageListMeta';
 import PagePath from './PageList/PagePath';
 import PagePath from './PageList/PagePath';
@@ -67,21 +65,28 @@ export default class NewPageNameInputter extends React.Component {
       : 'No matches found on title...';
       : 'No matches found on title...';
 
 
     return (
     return (
-      <SearchTypeahead
-        crowi={this.crowi}
-        onSearchSuccess={this.onSearchSuccess}
-        onSearchError={this.onSearchError}
-        emptyLabel={emptyLabel}
-        keywordOnInit={this.getParentPageName(this.props.parentPageName)}
-      />
+      <form
+        action="/_search"
+        className=""
+      >
+        <SearchTypeahead
+          crowi={this.crowi}
+          onSearchSuccess={this.onSearchSuccess}
+          onSearchError={this.onSearchError}
+          emptyLabel={emptyLabel}
+          placeholder="Input page name"
+          keywordOnInit={this.getParentPageName(this.props.parentPageName)}
+        />
+      </form>
     );
     );
   }
   }
 }
 }
 
 
 NewPageNameInputter.propTypes = {
 NewPageNameInputter.propTypes = {
   crowi:          PropTypes.object.isRequired,
   crowi:          PropTypes.object.isRequired,
-  parentPageName: PropTypes.string.isRequired,
+  parentPageName: PropTypes.string,
 };
 };
 
 
 NewPageNameInputter.defaultProps = {
 NewPageNameInputter.defaultProps = {
+  parentPageName: '',
 };
 };

+ 39 - 27
resource/js/components/SearchTypeahead.js

@@ -1,3 +1,4 @@
+import {noop} from 'lodash';
 import React from 'react';
 import React from 'react';
 
 
 import { AsyncTypeahead } from 'react-bootstrap-typeahead';
 import { AsyncTypeahead } from 'react-bootstrap-typeahead';
@@ -58,6 +59,7 @@ export default class SearchTypeahead extends React.Component {
    */
    */
   forceToFocus() {
   forceToFocus() {
     const typeahead = this.getTypeahead();
     const typeahead = this.getTypeahead();
+    if (typeahead == null) return;
     const intervalId = setInterval(() => {
     const intervalId = setInterval(() => {
       this.getTypeahead().focus();
       this.getTypeahead().focus();
       if (this.state.isFocused) {
       if (this.state.isFocused) {
@@ -166,33 +168,37 @@ export default class SearchTypeahead extends React.Component {
       ? 'Error on searching.'
       ? 'Error on searching.'
       : 'No matches found on title...';
       : 'No matches found on title...';
     const restoreFormButton = this.getRestoreFormButton();
     const restoreFormButton = this.getRestoreFormButton();
+    const defaultSelected = (this.props.keywordOnInit != "")
+      ? [{path: this.props.keywordOnInit}]
+      : [];
+    const {ref, inputProps, isLoading, labelKey, minLength, options, emptyLabel,
+           align, submitFormOnEnter, onSearch, onInputChange, renderMenuItemChildren,
+           caseSensitive, defaultSelected, onBlur, onFocus, keywordOnInit,
+           ...otherProps} = this.props;
 
 
     return (
     return (
-      <form
-        action="/_search"
-        className=""
-        >
-      <AsyncTypeahead
-        ref="typeahead"
-        inputProps={{name: "q", autoComplete: "off"}}
-        isLoading={this.state.isLoading}
-        labelKey="path"
-        minLength={2}
-        options={this.state.pages} // Search result (Some page names)
-        placeholder="Input page name"
-        emptyLabel={this.emptyLabel ? this.emptyLabel : emptyLabel}
-        align='left'
-        submitFormOnEnter={true}
-        onSearch={this.search}
-        onInputChange={this.onInputChange}
-        renderMenuItemChildren={this.renderMenuItemChildren}
-        caseSensitive={false}
-        defaultSelected={[{path: this.props.keywordOnInit}]}
-        onBlur={this.onInputBlur}
-        onFocus={this.onInputFocus}
-      />
-      {restoreFormButton}
-      </form>
+      <span>
+        <AsyncTypeahead
+          {...otherProps}
+          ref="typeahead"
+          inputProps={{name: "q", autoComplete: "off"}}
+          isLoading={this.state.isLoading}
+          labelKey="path"
+          minLength={2}
+          options={this.state.pages} // Search result (Some page names)
+          emptyLabel={this.emptyLabel ? this.emptyLabel : emptyLabel}
+          align='left'
+          submitFormOnEnter={true}
+          onSearch={this.search}
+          onInputChange={this.onInputChange}
+          renderMenuItemChildren={this.renderMenuItemChildren}
+          caseSensitive={false}
+          defaultSelected={defaultSelected}
+          onBlur={this.onInputBlur}
+          onFocus={this.onInputFocus}
+        />
+        {restoreFormButton}
+      </span>
     );
     );
   }
   }
 }
 }
@@ -204,7 +210,10 @@ SearchTypeahead.propTypes = {
   crowi:           PropTypes.object.isRequired,
   crowi:           PropTypes.object.isRequired,
   onSearchSuccess: PropTypes.func,
   onSearchSuccess: PropTypes.func,
   onSearchError:   PropTypes.func,
   onSearchError:   PropTypes.func,
+  onChange:        PropTypes.func,
+  onInputChange:   PropTypes.func,
   emptyLabel:      PropTypes.string,
   emptyLabel:      PropTypes.string,
+  placeholder:     PropTypes.string,
   keywordOnInit:   PropTypes.string,
   keywordOnInit:   PropTypes.string,
 };
 };
 
 
@@ -212,8 +221,11 @@ SearchTypeahead.propTypes = {
  * Properties
  * Properties
  */
  */
 SearchTypeahead.defaultProps = {
 SearchTypeahead.defaultProps = {
-  onSearchSuccess: {},
-  onSearchError:   {},
+  onSearchSuccess: noop,
+  onSearchError:   noop,
+  onChange:        noop,
+  onInputChange:   noop,
   emptyLabel:      null,
   emptyLabel:      null,
+  placeholder:     "",
   keywordOnInit:   "",
   keywordOnInit:   "",
 };
 };

+ 0 - 1
resource/js/legacy/crowi.js

@@ -239,7 +239,6 @@ $(function() {
       - $('#create-page-today .page-today-suffix').outerWidth()
       - $('#create-page-today .page-today-suffix').outerWidth()
       - 42
       - 42
       ;
       ;
-    //$('#page-name-inputter .form-control').focus(); // focus() を実行しないと placeholder が表示されない。[TODO] jQuery から focus() を実行せずにフォーカスを当てる
     $('#create-page-today .form-control.page-today-input2').css({width: newWidth}).focus();
     $('#create-page-today .form-control.page-today-input2').css({width: newWidth}).focus();
 
 
   });
   });