diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js index fe4d18c691..5ee06e12e3 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/index.js @@ -1,267 +1,69 @@ -/** - * - * SelectMany - * - */ - -/* eslint-disable */ import React from 'react'; -import Select from 'react-select'; -import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; -import { - cloneDeep, - includes, - isArray, - isNull, - isUndefined, - get, - findIndex, - isEmpty, -} from 'lodash'; +import { get, isEmpty } from 'lodash'; -// Utils. -import { request, templateObject } from 'strapi-helper-plugin'; +import Select from 'react-select'; -// Component. -import SortableList from './SortableList'; -// CSS. -import styles from './styles.scss'; - -class SelectMany extends React.PureComponent { - state = { - isLoading: true, - options: [], - start: 0, - }; - - componentDidMount() { - this.getOptions(''); - } - - componentDidUpdate(prevProps, prevState) { - if (isEmpty(prevProps.record) && !isEmpty(this.props.record)) { - const values = ( - get(this.props.record, this.props.relation.alias) || [] - ).map(el => el.id || el._id); - - const options = this.state.options.filter(el => { - return !values.includes(el.value.id || el.value._id); - }); - - this.state.options = options; - } - - if (prevState.start !== this.state.start) { - this.getOptions(''); - } - } - - getOptions = query => { - const params = { - _limit: 20, - _start: this.state.start, - source: this.props.relation.plugin || 'content-manager', - }; - - // Set `query` parameter if necessary - if (query) { - delete params._limit; - delete params._skip; - params[`${this.props.relation.displayedAttribute}_contains`] = query; - } - // Request URL - const requestUrl = `/content-manager/explorer/${this.props.relation.model || - this.props.relation.collection}`; - - // Call our request helper (see 'utils/request') - return request(requestUrl, { - method: 'GET', - params, - }) - .then(response => { - /* eslint-disable indent */ - const options = isArray(response) - ? response.map(item => ({ - value: item, - label: templateObject( - { mainField: this.props.relation.displayedAttribute }, - item - ).mainField, - })) - : [ - { - value: response, - label: response[this.props.relation.displayedAttribute], - }, - ]; - /* eslint-enable indent */ - const newOptions = cloneDeep(this.state.options); - options.map(option => { - // Don't add the values when searching - if ( - findIndex(newOptions, o => o.value.id === option.value.id) === -1 - ) { - return newOptions.push(option); +function SelectMany({ + addRelation, + mainField, + name, + isLoading, + onInputChange, + onMenuClose, + onMenuScrollToBottom, + options, + placeholder, + value, +}) { + return ( + <> + - } - /> - { - if (item) { - return { - value: get(item, 'value') || item, - label: - get(item, 'label') || - templateObject( - { mainField: this.props.relation.displayedAttribute }, - item - ).mainField || - item.id, - }; - } - }) - } - /* eslint-enable indent */ - isDraggingSibling={this.props.isDraggingSibling} - keys={this.props.relation.alias} - moveAttr={this.props.moveAttr} - moveAttrEnd={this.props.moveAttrEnd} - name={this.props.relation.alias} - onRemove={this.handleRemove} - distance={1} - onClick={this.handleClick} - /> - - ); - /* eslint-disable jsx-a11y/label-has-for */ - } + return value.findIndex(obj => obj.id === el.value.id) === -1; + }} + isLoading={isLoading} + isMulti + isSearchable + options={options} + onChange={addRelation} + onInputChange={onInputChange} + onMenuClose={onMenuClose} + onMenuScrollToBottom={onMenuScrollToBottom} + placeholder={placeholder} + value={[]} + /> + {!isEmpty(value) && ( + + )} + + ); } SelectMany.defaultProps = { - isDraggingSibling: false, - moveAttr: () => {}, - moveAttrEnd: () => {}, + value: null, }; SelectMany.propTypes = { - isDraggingSibling: PropTypes.bool, - moveAttr: PropTypes.func, - moveAttrEnd: PropTypes.func, - onAddRelationalItem: PropTypes.func.isRequired, - onRedirect: PropTypes.func.isRequired, - onRemoveRelationItem: PropTypes.func.isRequired, - record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired, - relation: PropTypes.object.isRequired, + addRelation: PropTypes.func.isRequired, + mainField: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + isLoading: PropTypes.bool.isRequired, + onInputChange: PropTypes.func.isRequired, + onMenuClose: PropTypes.func.isRequired, + onMenuScrollToBottom: PropTypes.func.isRequired, + options: PropTypes.array.isRequired, + placeholder: PropTypes.node.isRequired, + value: PropTypes.array, }; export default SelectMany; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/main.js b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/main.js new file mode 100644 index 0000000000..fe4d18c691 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/components/SelectMany/main.js @@ -0,0 +1,267 @@ +/** + * + * SelectMany + * + */ + +/* eslint-disable */ +import React from 'react'; +import Select from 'react-select'; +import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; +import { + cloneDeep, + includes, + isArray, + isNull, + isUndefined, + get, + findIndex, + isEmpty, +} from 'lodash'; + +// Utils. +import { request, templateObject } from 'strapi-helper-plugin'; + +// Component. +import SortableList from './SortableList'; +// CSS. +import styles from './styles.scss'; + +class SelectMany extends React.PureComponent { + state = { + isLoading: true, + options: [], + start: 0, + }; + + componentDidMount() { + this.getOptions(''); + } + + componentDidUpdate(prevProps, prevState) { + if (isEmpty(prevProps.record) && !isEmpty(this.props.record)) { + const values = ( + get(this.props.record, this.props.relation.alias) || [] + ).map(el => el.id || el._id); + + const options = this.state.options.filter(el => { + return !values.includes(el.value.id || el.value._id); + }); + + this.state.options = options; + } + + if (prevState.start !== this.state.start) { + this.getOptions(''); + } + } + + getOptions = query => { + const params = { + _limit: 20, + _start: this.state.start, + source: this.props.relation.plugin || 'content-manager', + }; + + // Set `query` parameter if necessary + if (query) { + delete params._limit; + delete params._skip; + params[`${this.props.relation.displayedAttribute}_contains`] = query; + } + // Request URL + const requestUrl = `/content-manager/explorer/${this.props.relation.model || + this.props.relation.collection}`; + + // Call our request helper (see 'utils/request') + return request(requestUrl, { + method: 'GET', + params, + }) + .then(response => { + /* eslint-disable indent */ + const options = isArray(response) + ? response.map(item => ({ + value: item, + label: templateObject( + { mainField: this.props.relation.displayedAttribute }, + item + ).mainField, + })) + : [ + { + value: response, + label: response[this.props.relation.displayedAttribute], + }, + ]; + /* eslint-enable indent */ + const newOptions = cloneDeep(this.state.options); + options.map(option => { + // Don't add the values when searching + if ( + findIndex(newOptions, o => o.value.id === option.value.id) === -1 + ) { + return newOptions.push(option); + } + }); + + return this.setState({ + options: newOptions, + isLoading: false, + }); + }) + .catch(() => { + strapi.notification.error( + 'content-manager.notification.error.relationship.fetch' + ); + }); + }; + + handleInputChange = value => { + const clonedOptions = this.state.options; + const filteredValues = clonedOptions.filter(data => + includes(data.label, value) + ); + + if (filteredValues.length === 0) { + return this.getOptions(value); + } + }; + + handleChange = value => { + // Remove new added value from available option; + this.state.options = this.state.options.filter( + el => + !((el.value._id || el.value.id) === (value.value.id || value.value._id)) + ); + + this.props.onAddRelationalItem({ + key: this.props.relation.alias, + value: value.value, + }); + }; + + handleBottomScroll = () => { + this.setState(prevState => { + return { + start: prevState.start + 1, + }; + }); + }; + + handleRemove = index => { + const values = get(this.props.record, this.props.relation.alias); + + // Add removed value from available option; + const toAdd = { + value: values[index], + label: templateObject( + { mainField: this.props.relation.displayedAttribute }, + values[index] + ).mainField, + }; + + this.setState(prevState => ({ + options: prevState.options.concat([toAdd]), + })); + + this.props.onRemoveRelationItem({ + key: this.props.relation.alias, + index, + }); + }; + + // Redirect to the edit page + handleClick = (item = {}) => { + this.props.onRedirect({ + model: this.props.relation.collection || this.props.relation.model, + id: item.value.id || item.value._id, + source: this.props.relation.plugin, + }); + }; + + render() { + const description = this.props.relation.description ? ( +

{this.props.relation.description}

+ ) : ( + '' + ); + const value = get(this.props.record, this.props.relation.alias) || []; + + /* eslint-disable jsx-a11y/label-has-for */ + return ( +
4 && + styles.selectManyUpdate}`} + > + + {description} + { - onChange({ target: { name, value: value ? value.value : value } }); - }} - onInputChange={onInputChange} - onMenuClose={() => { - setState(prevState => ({ ...prevState, _start: 0 })); - }} - onMenuScrollToBottom={onMenuScrollToBottom} - value={ - isNull(value) ? null : { label: get(value, [mainField], ''), value } - } - /> - +