2017-06-18 17:23:58 +02:00
|
|
|
/**
|
|
|
|
*
|
2017-09-07 17:16:31 +02:00
|
|
|
* SelectMany
|
2017-06-18 17:23:58 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2017-09-26 16:36:28 +02:00
|
|
|
import React from 'react';
|
2017-06-18 17:23:58 +02:00
|
|
|
import Select from 'react-select';
|
2018-07-31 16:57:16 +02:00
|
|
|
import { FormattedMessage } from 'react-intl';
|
2017-09-18 09:34:29 +02:00
|
|
|
import PropTypes from 'prop-types';
|
2018-09-17 12:46:22 +02:00
|
|
|
import { cloneDeep, includes, isArray, isNull, isUndefined, get, findIndex, isEmpty } from 'lodash';
|
2017-06-18 17:23:58 +02:00
|
|
|
|
2018-07-30 16:33:50 +02:00
|
|
|
// Utils.
|
2017-06-18 17:23:58 +02:00
|
|
|
import request from 'utils/request';
|
2017-09-07 17:16:31 +02:00
|
|
|
import templateObject from 'utils/templateObject';
|
2017-06-18 17:23:58 +02:00
|
|
|
|
2018-07-30 16:33:50 +02:00
|
|
|
// CSS.
|
2018-06-18 09:41:31 -07:00
|
|
|
import 'react-select/dist/react-select.css';
|
2018-08-02 15:27:30 +02:00
|
|
|
// Component.
|
|
|
|
import SortableList from './SortableList';
|
|
|
|
// CSS.
|
2017-09-07 17:16:31 +02:00
|
|
|
import styles from './styles.scss';
|
|
|
|
|
2018-08-01 13:58:57 +02:00
|
|
|
class SelectMany extends React.PureComponent {
|
|
|
|
state = {
|
|
|
|
isLoading: true,
|
|
|
|
options: [],
|
|
|
|
toSkip: 0,
|
|
|
|
};
|
2017-06-18 17:23:58 +02:00
|
|
|
|
2018-04-19 17:19:56 +02:00
|
|
|
componentDidMount() {
|
|
|
|
this.getOptions('');
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps, prevState) {
|
2018-08-01 13:58:57 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-04-19 17:19:56 +02:00
|
|
|
if (prevState.toSkip !== this.state.toSkip) {
|
|
|
|
this.getOptions('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-19 15:45:35 +02:00
|
|
|
getOptions = query => {
|
2017-09-07 17:16:31 +02:00
|
|
|
const params = {
|
2018-05-23 10:57:51 +02:00
|
|
|
_limit: 20,
|
|
|
|
_start: this.state.toSkip,
|
2017-12-11 18:23:15 +01:00
|
|
|
source: this.props.relation.plugin || 'content-manager',
|
2017-09-07 17:16:31 +02:00
|
|
|
};
|
2017-06-18 17:23:58 +02:00
|
|
|
|
|
|
|
// Set `query` parameter if necessary
|
|
|
|
if (query) {
|
2018-05-23 10:57:51 +02:00
|
|
|
delete params._limit;
|
|
|
|
delete params._skip;
|
2018-04-19 15:45:35 +02:00
|
|
|
params[`${this.props.relation.displayedAttribute}_contains`] = query;
|
2017-06-18 17:23:58 +02:00
|
|
|
}
|
|
|
|
// Request URL
|
2018-04-19 15:45:35 +02:00
|
|
|
const requestUrl = `/content-manager/explorer/${this.props.relation.model ||
|
2018-04-23 11:36:21 +02:00
|
|
|
this.props.relation.collection}`;
|
2017-06-18 17:23:58 +02:00
|
|
|
|
|
|
|
// Call our request helper (see 'utils/request')
|
2017-06-19 19:47:38 +02:00
|
|
|
return request(requestUrl, {
|
2017-06-18 17:23:58 +02:00
|
|
|
method: 'GET',
|
|
|
|
params,
|
|
|
|
})
|
|
|
|
.then(response => {
|
2018-04-19 15:45:35 +02:00
|
|
|
const options = isArray(response)
|
|
|
|
? response.map(item => ({
|
2017-09-07 17:16:31 +02:00
|
|
|
value: item,
|
2018-04-19 15:45:35 +02:00
|
|
|
label: templateObject({ mainField: this.props.relation.displayedAttribute }, item)
|
|
|
|
.mainField,
|
|
|
|
}))
|
|
|
|
: [
|
|
|
|
{
|
|
|
|
value: response,
|
|
|
|
label: response[this.props.relation.displayedAttribute],
|
|
|
|
},
|
|
|
|
];
|
2017-06-18 17:23:58 +02:00
|
|
|
|
2018-04-19 17:19:56 +02:00
|
|
|
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,
|
|
|
|
});
|
2017-09-25 15:35:27 +02:00
|
|
|
})
|
|
|
|
.catch(() => {
|
2017-12-07 13:26:25 +01:00
|
|
|
strapi.notification.error('content-manager.notification.error.relationship.fetch');
|
2017-06-18 17:23:58 +02:00
|
|
|
});
|
2018-04-19 15:45:35 +02:00
|
|
|
};
|
2017-06-18 17:23:58 +02:00
|
|
|
|
2018-11-19 19:07:17 +01:00
|
|
|
handleInputChange = (value) => {
|
|
|
|
const clonedOptions = this.state.options;
|
|
|
|
const filteredValues = clonedOptions.filter(data => includes(data.label, value));
|
|
|
|
|
|
|
|
if (filteredValues.length === 0) {
|
|
|
|
return this.getOptions(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-19 15:45:35 +02:00
|
|
|
handleChange = value => {
|
2018-08-01 13:58:57 +02:00
|
|
|
// Remove new added value from available option;
|
2018-08-02 15:27:30 +02:00
|
|
|
this.state.options = this.state.options.filter(el =>
|
|
|
|
!((el.value._id || el.value.id) === (value.value.id || value.value._id))
|
2018-04-19 15:45:35 +02:00
|
|
|
);
|
2017-10-21 15:25:00 +02:00
|
|
|
|
2018-08-02 15:27:30 +02:00
|
|
|
this.props.onAddRelationalItem({
|
|
|
|
key: this.props.relation.alias,
|
|
|
|
value: value.value,
|
2018-08-01 13:58:57 +02:00
|
|
|
});
|
2018-04-19 15:45:35 +02:00
|
|
|
};
|
2017-09-19 14:07:58 +02:00
|
|
|
|
2018-04-19 17:19:56 +02:00
|
|
|
handleBottomScroll = () => {
|
|
|
|
this.setState(prevState => {
|
|
|
|
return {
|
|
|
|
toSkip: prevState.toSkip + 20,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-06-18 09:41:31 -07:00
|
|
|
handleRemove = (index) => {
|
|
|
|
const values = get(this.props.record, this.props.relation.alias);
|
2018-08-01 13:58:57 +02:00
|
|
|
|
|
|
|
// Add removed value from available option;
|
2018-08-09 17:02:15 +02:00
|
|
|
const toAdd = {
|
2018-08-01 13:58:57 +02:00
|
|
|
value: values[index],
|
2018-08-09 17:02:15 +02:00
|
|
|
label: templateObject({ mainField: this.props.relation.displayedAttribute }, values[index]).mainField,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.setState(prevState => ({
|
|
|
|
options: prevState.options.concat([toAdd]),
|
|
|
|
}));
|
2018-08-01 13:58:57 +02:00
|
|
|
|
2018-08-02 15:27:30 +02:00
|
|
|
this.props.onRemoveRelationItem({
|
|
|
|
key: this.props.relation.alias,
|
|
|
|
index,
|
|
|
|
});
|
2018-06-18 09:41:31 -07:00
|
|
|
}
|
|
|
|
|
2018-07-30 16:33:50 +02:00
|
|
|
// 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,
|
|
|
|
});
|
2018-04-19 17:19:56 +02:00
|
|
|
}
|
|
|
|
|
2017-06-18 17:23:58 +02:00
|
|
|
render() {
|
2018-04-19 15:45:35 +02:00
|
|
|
const description = this.props.relation.description ? (
|
|
|
|
<p>{this.props.relation.description}</p>
|
|
|
|
) : (
|
|
|
|
''
|
|
|
|
);
|
2018-06-18 09:41:31 -07:00
|
|
|
const value = get(this.props.record, this.props.relation.alias) || [];
|
2018-07-16 17:26:51 +02:00
|
|
|
|
2017-08-29 17:32:48 +02:00
|
|
|
/* eslint-disable jsx-a11y/label-has-for */
|
2017-06-18 17:23:58 +02:00
|
|
|
return (
|
2018-07-31 16:57:16 +02:00
|
|
|
<div className={`form-group ${styles.selectMany} ${value.length > 4 && styles.selectManyUpdate}`}>
|
|
|
|
<label htmlFor={this.props.relation.alias}>{this.props.relation.alias} <span>({value.length})</span></label>
|
2017-06-20 19:10:23 +02:00
|
|
|
{description}
|
2018-04-19 17:19:56 +02:00
|
|
|
<Select
|
2018-08-02 15:27:30 +02:00
|
|
|
className={`${styles.select}`}
|
2017-10-21 15:25:00 +02:00
|
|
|
id={this.props.relation.alias}
|
2018-04-19 17:19:56 +02:00
|
|
|
isLoading={this.state.isLoading}
|
2018-08-02 15:27:30 +02:00
|
|
|
onChange={this.handleChange}
|
2018-09-17 12:46:22 +02:00
|
|
|
onInputChange={this.handleInputChange}
|
2018-04-19 17:19:56 +02:00
|
|
|
onMenuScrollToBottom={this.handleBottomScroll}
|
2018-08-02 15:27:30 +02:00
|
|
|
options={this.state.options}
|
2018-07-31 16:57:16 +02:00
|
|
|
placeholder={<FormattedMessage id='content-manager.containers.Edit.addAnItem' />}
|
2018-06-18 09:41:31 -07:00
|
|
|
/>
|
|
|
|
<SortableList
|
|
|
|
items={
|
2018-04-19 15:45:35 +02:00
|
|
|
isNull(value) || isUndefined(value) || value.size === 0
|
|
|
|
? null
|
|
|
|
: value.map(item => {
|
2018-07-16 17:26:51 +02:00
|
|
|
|
2018-04-19 15:45:35 +02:00
|
|
|
if (item) {
|
|
|
|
return {
|
|
|
|
value: get(item, 'value') || item,
|
|
|
|
label:
|
|
|
|
get(item, 'label') ||
|
|
|
|
templateObject({ mainField: this.props.relation.displayedAttribute }, item)
|
|
|
|
.mainField ||
|
2018-07-16 17:26:51 +02:00
|
|
|
item.id,
|
2018-04-19 15:45:35 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2018-08-09 17:02:15 +02:00
|
|
|
isDraggingSibling={this.props.isDraggingSibling}
|
2018-08-09 14:59:33 +02:00
|
|
|
keys={this.props.relation.alias}
|
|
|
|
moveAttr={this.props.moveAttr}
|
2018-08-09 17:02:15 +02:00
|
|
|
moveAttrEnd={this.props.moveAttrEnd}
|
2018-06-18 09:41:31 -07:00
|
|
|
onRemove={this.handleRemove}
|
2018-07-30 16:33:50 +02:00
|
|
|
distance={1}
|
|
|
|
onClick={this.handleClick}
|
2017-06-18 17:23:58 +02:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
2017-08-29 17:32:48 +02:00
|
|
|
/* eslint-disable jsx-a11y/label-has-for */
|
2017-06-18 17:23:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 14:59:33 +02:00
|
|
|
SelectMany.defaultProps = {
|
2018-08-09 17:02:15 +02:00
|
|
|
isDraggingSibling: false,
|
2018-08-09 14:59:33 +02:00
|
|
|
moveAttr: () => {},
|
2018-08-09 17:02:15 +02:00
|
|
|
moveAttrEnd: () => {},
|
2018-08-09 14:59:33 +02:00
|
|
|
};
|
|
|
|
|
2017-09-07 17:16:31 +02:00
|
|
|
SelectMany.propTypes = {
|
2018-08-09 17:02:15 +02:00
|
|
|
isDraggingSibling: PropTypes.bool,
|
2018-08-09 14:59:33 +02:00
|
|
|
moveAttr: PropTypes.func,
|
2018-08-09 17:02:15 +02:00
|
|
|
moveAttrEnd: PropTypes.func,
|
2018-08-02 15:27:30 +02:00
|
|
|
onAddRelationalItem: PropTypes.func.isRequired,
|
2018-07-30 16:33:50 +02:00
|
|
|
onRedirect: PropTypes.func.isRequired,
|
2018-08-02 15:27:30 +02:00
|
|
|
onRemoveRelationItem: PropTypes.func.isRequired,
|
2018-04-19 15:45:35 +02:00
|
|
|
record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired,
|
2017-09-18 09:34:29 +02:00
|
|
|
relation: PropTypes.object.isRequired,
|
2017-06-18 17:23:58 +02:00
|
|
|
};
|
|
|
|
|
2017-09-07 17:16:31 +02:00
|
|
|
export default SelectMany;
|