|
|
@@ -1,112 +0,0 @@
|
|
|
-import React from 'react';
|
|
|
-import PropTypes from 'prop-types';
|
|
|
-import { AsyncTypeahead } from 'react-bootstrap-typeahead';
|
|
|
-
|
|
|
-import { withUnstatedContainers } from '../UnstatedUtils';
|
|
|
-import AppContainer from '~/client/services/AppContainer';
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author Yuki Takei <yuki@weseek.co.jp>
|
|
|
- *
|
|
|
- * @export
|
|
|
- * @class TagsInput
|
|
|
- * @extends {React.Component}
|
|
|
- */
|
|
|
-
|
|
|
-class TagsInput extends React.Component {
|
|
|
-
|
|
|
- constructor(props) {
|
|
|
- super(props);
|
|
|
-
|
|
|
- this.state = {
|
|
|
- resultTags: [],
|
|
|
- isLoading: false,
|
|
|
- selected: this.props.tags,
|
|
|
- defaultPageTags: this.props.tags,
|
|
|
- };
|
|
|
-
|
|
|
- this.tagsInput = React.createRef();
|
|
|
-
|
|
|
- this.handleChange = this.handleChange.bind(this);
|
|
|
- this.handleSearch = this.handleSearch.bind(this);
|
|
|
- this.handleSelect = this.handleSelect.bind(this);
|
|
|
- }
|
|
|
-
|
|
|
- componentDidMount() {
|
|
|
- this.tagsInput.current.focus();
|
|
|
- }
|
|
|
-
|
|
|
- handleChange(selected) {
|
|
|
- // send tags to TagLabel Component when user add tag to form everytime
|
|
|
- this.setState({ selected }, () => {
|
|
|
- this.props.onTagsUpdated(this.state.selected);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- async handleSearch(query) {
|
|
|
- this.setState({ isLoading: true });
|
|
|
- const res = await this.props.appContainer.apiGet('/tags.search', { q: query });
|
|
|
- res.tags.unshift(query); // selectable new tag whose name equals query
|
|
|
- this.setState({
|
|
|
- resultTags: Array.from(new Set(res.tags)), // use Set for de-duplication
|
|
|
- isLoading: false,
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- handleSelect(e) {
|
|
|
- if (e.keyCode === 32 || e.keyCode === 13) { // '32' means ASCII code of 'space'
|
|
|
- e.preventDefault();
|
|
|
- const { initialItem, selected } = this.tagsInput.current.state;
|
|
|
- if (initialItem != null) {
|
|
|
- this.handleChange([...selected, initialItem]);
|
|
|
- this.forceUpdate();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- render() {
|
|
|
- return (
|
|
|
- <div className="tag-typeahead">
|
|
|
- <AsyncTypeahead
|
|
|
- id="tag-typeahead-asynctypeahead"
|
|
|
- ref={this.tagsInput}
|
|
|
- caseSensitive={false}
|
|
|
- defaultSelected={this.state.defaultPageTags}
|
|
|
- isLoading={this.state.isLoading}
|
|
|
- minLength={1}
|
|
|
- multiple
|
|
|
- newSelectionPrefix=""
|
|
|
- onChange={this.handleChange}
|
|
|
- onSearch={this.handleSearch}
|
|
|
- onKeyDown={this.handleSelect}
|
|
|
- options={this.state.resultTags} // Search result (Some tag names)
|
|
|
- placeholder="tag name"
|
|
|
- inputProps={this.inputProps}
|
|
|
- autoFocus={this.props.autoFocus}
|
|
|
- />
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Wrapper component for using unstated
|
|
|
- */
|
|
|
-const TagsInputWrapper = withUnstatedContainers(TagsInput, [AppContainer]);
|
|
|
-
|
|
|
-TagsInput.propTypes = {
|
|
|
- appContainer: PropTypes.instanceOf(AppContainer).isRequired,
|
|
|
-
|
|
|
- tags: PropTypes.array.isRequired,
|
|
|
- onTagsUpdated: PropTypes.func.isRequired,
|
|
|
- autoFocus: PropTypes.bool,
|
|
|
-};
|
|
|
-
|
|
|
-TagsInput.defaultProps = {
|
|
|
- autoFocus: false,
|
|
|
-};
|
|
|
-
|
|
|
-export default TagsInputWrapper;
|