241 lines
6.4 KiB
JavaScript
Raw Normal View History

2019-11-13 18:54:10 +01:00
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useEffect, useRef, memo } from 'react';
2019-07-17 12:06:19 +02:00
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Link, useLocation } from 'react-router-dom';
2019-11-21 18:48:13 +01:00
import { cloneDeep, get, isArray, isEmpty } from 'lodash';
2019-07-17 12:06:19 +02:00
import { request } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
2019-10-30 14:47:12 +01:00
import useDataManager from '../../hooks/useDataManager';
import useEditView from '../../hooks/useEditView';
2019-07-17 12:06:19 +02:00
import SelectOne from '../SelectOne';
import SelectMany from '../SelectMany';
import { Nav, Wrapper } from './components';
function SelectWrapper({
2019-07-26 14:34:24 +02:00
description,
2019-07-25 13:44:35 +02:00
editable,
2019-07-17 12:06:19 +02:00
label,
mainField,
name,
relationType,
targetModel,
2019-07-25 12:06:40 +02:00
placeholder,
2019-07-17 12:06:19 +02:00
}) {
const { pathname, search } = useLocation();
// Disable the input in case of a polymorphic relation
const isMorph = relationType.toLowerCase().includes('morph');
const { addRelation, modifiedData, moveRelation, onChange, onRemoveRelation } = useDataManager();
const { isDraggingComponent } = useEditView();
2019-11-21 18:48:13 +01:00
2019-10-30 14:47:12 +01:00
const value = get(modifiedData, name, null);
2019-07-17 12:06:19 +02:00
const [state, setState] = useState({
_contains: '',
2019-07-26 12:24:15 +02:00
_limit: 20,
2019-07-17 12:06:19 +02:00
_start: 0,
});
const [options, setOptions] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const abortController = new AbortController();
const { signal } = abortController;
2019-07-17 12:06:19 +02:00
const ref = useRef();
const startRef = useRef();
2019-07-17 12:06:19 +02:00
startRef.current = state._start;
ref.current = async () => {
if (isMorph) {
setIsLoading(false);
return;
}
if (!isDraggingComponent) {
try {
const requestUrl = `/${pluginId}/explorer/${targetModel}`;
const containsKey = `${mainField}_contains`;
const { _contains, ...restState } = cloneDeep(state);
const params = isEmpty(state._contains)
? restState
: { [containsKey]: _contains, ...restState };
const data = await request(requestUrl, {
method: 'GET',
params,
signal,
});
const formattedData = data.map(obj => {
return { value: obj, label: obj[mainField] };
});
setOptions(prevState =>
prevState.concat(formattedData).filter((obj, index) => {
const objIndex = prevState.findIndex(el => el.value.id === obj.value.id);
if (objIndex === -1) {
return true;
}
return prevState.findIndex(el => el.value.id === obj.value.id) === index;
})
);
setIsLoading(false);
} catch (err) {
if (err.code !== 20) {
strapi.notification.error('notification.error');
}
2019-07-18 14:34:25 +02:00
}
2019-07-17 12:06:19 +02:00
}
};
useEffect(() => {
if (state._contains !== '') {
let timer = setTimeout(() => {
ref.current();
}, 300);
return () => clearTimeout(timer);
}
2019-07-17 12:06:19 +02:00
ref.current();
return () => {
abortController.abort();
};
}, [state._contains]);
2019-07-17 12:06:19 +02:00
useEffect(() => {
if (state._start !== 0) {
ref.current();
}
return () => {
abortController.abort();
};
}, [state._start]);
const onInputChange = (inputValue, { action }) => {
if (action === 'input-change') {
setState(prevState => {
if (prevState._contains === inputValue) {
return prevState;
}
return { ...prevState, _contains: inputValue, _start: 0 };
});
}
2019-07-18 14:07:22 +02:00
2019-07-17 12:06:19 +02:00
return inputValue;
};
const onMenuScrollToBottom = () => {
setState(prevState => ({ ...prevState, _start: prevState._start + 20 }));
2019-07-17 12:06:19 +02:00
};
const isSingle = ['oneWay', 'oneToOne', 'manyToOne', 'oneToManyMorph', 'oneToOneMorph'].includes(
relationType
);
2019-07-17 12:06:19 +02:00
const nextSearch = `${pathname}${search}`;
const to = `/plugins/${pluginId}/collectionType/${targetModel}/${
2019-07-17 12:06:19 +02:00
value ? value.id : null
2019-11-21 18:48:13 +01:00
}?redirectUrl=${nextSearch}`;
2019-07-17 12:06:19 +02:00
const link =
value === null ||
value === undefined ||
['plugins::users-permissions.role', 'plugins::users-permissions.permission'].includes(
targetModel
) ? null : (
2019-07-17 12:06:19 +02:00
<Link to={to}>
<FormattedMessage id="content-manager.containers.Edit.seeDetails" />
</Link>
);
const Component = isSingle ? SelectOne : SelectMany;
2019-07-29 17:11:53 +02:00
const associationsLength = isArray(value) ? value.length : 0;
2019-07-17 12:06:19 +02:00
const customStyles = {
option: provided => {
return {
...provided,
maxWidth: '100% !important',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
};
},
};
2019-07-17 12:06:19 +02:00
return (
<Wrapper className="form-group">
<Nav>
2019-07-29 17:11:53 +02:00
<div>
<label htmlFor={name}>
{label}
{!isSingle && (
<span style={{ fontWeight: 400, fontSize: 12 }}>&nbsp;({associationsLength})</span>
2019-07-29 17:11:53 +02:00
)}
</label>
{isSingle && link}
</div>
{!isEmpty(description) && <p className="description">{description}</p>}
2019-07-17 12:06:19 +02:00
</Nav>
<Component
addRelation={value => {
addRelation({ target: { name, value } });
}}
id={name}
isDisabled={!editable || isMorph}
2019-07-17 12:06:19 +02:00
isLoading={isLoading}
isClearable
mainField={mainField}
move={moveRelation}
2019-07-17 12:06:19 +02:00
name={name}
nextSearch={nextSearch}
2019-07-17 12:06:19 +02:00
options={options}
onChange={value => {
onChange({ target: { name, value: value ? value.value : value } });
}}
onInputChange={onInputChange}
onMenuClose={() => {
setState(prevState => ({ ...prevState, _contains: '' }));
2019-07-17 12:06:19 +02:00
}}
onMenuScrollToBottom={onMenuScrollToBottom}
2019-10-30 15:06:38 +01:00
onRemove={onRemoveRelation}
2019-07-17 12:06:19 +02:00
placeholder={
2019-07-25 12:06:40 +02:00
isEmpty(placeholder) ? (
<FormattedMessage id={`${pluginId}.containers.Edit.addAnItem`} />
) : (
placeholder
)
2019-07-17 12:06:19 +02:00
}
styles={customStyles}
targetModel={targetModel}
2019-07-17 12:06:19 +02:00
value={value}
/>
2019-07-22 11:41:27 +02:00
<div style={{ marginBottom: 18 }} />
2019-07-17 12:06:19 +02:00
</Wrapper>
);
}
SelectWrapper.defaultProps = {
2019-07-25 13:44:35 +02:00
editable: true,
2019-07-17 12:06:19 +02:00
description: '',
label: '',
2019-07-25 12:06:40 +02:00
placeholder: '',
2019-07-17 12:06:19 +02:00
};
SelectWrapper.propTypes = {
2019-07-25 13:44:35 +02:00
editable: PropTypes.bool,
2019-07-17 12:06:19 +02:00
description: PropTypes.string,
label: PropTypes.string,
mainField: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
2019-07-25 12:06:40 +02:00
placeholder: PropTypes.string,
2019-07-17 12:06:19 +02:00
relationType: PropTypes.string.isRequired,
targetModel: PropTypes.string.isRequired,
};
export default memo(SelectWrapper);