import React, {
useCallback,
useState,
useEffect,
useMemo,
// useRef,
memo,
} from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Link, useLocation } from 'react-router-dom';
import {
// cloneDeep,
findIndex,
get,
isArray,
isEmpty,
// set,
} from 'lodash';
import { request } from 'strapi-helper-plugin';
import { Flex, Text, Padded } from '@buffetjs/core';
import pluginId from '../../pluginId';
import useDataManager from '../../hooks/useDataManager';
// import { getFieldName } from '../../utils';
import NotAllowedInput from '../NotAllowedInput';
import SelectOne from '../SelectOne';
import SelectMany from '../SelectMany';
import ClearIndicator from './ClearIndicator';
import DropdownIndicator from './DropdownIndicator';
import IndicatorSeparator from './IndicatorSeparator';
import Option from './Option';
import { A, BaselineAlignment } from './components';
import { connect, select, styles } from './utils';
function SelectWrapper({
// componentUid,
description,
displayNavigationLink,
editable,
label,
isCreatingEntry,
isFieldAllowed,
isFieldReadable,
mainField,
name,
relationType,
// slug,
targetModel,
placeholder,
//
queryInfos,
}) {
// Disable the input in case of a polymorphic relation
const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]);
const { addRelation, modifiedData, moveRelation, onChange, onRemoveRelation } = useDataManager();
const { pathname } = useLocation();
const value = get(modifiedData, name, null);
const [state, setState] = useState({
_contains: '',
_limit: 20,
_start: 0,
});
const [options, setOptions] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const filteredOptions = useMemo(() => {
return options.filter(option => {
if (!isEmpty(value)) {
// SelectMany
if (Array.isArray(value)) {
return findIndex(value, o => o.id === option.value.id) === -1;
}
// SelectOne
return get(value, 'id', '') !== option.value.id;
}
return true;
});
}, [options, value]);
const endPoint = useMemo(() => queryInfos.endPoint, [queryInfos]);
const containsKey = useMemo(() => queryInfos.containsKey, [queryInfos]);
const defaultParams = useMemo(() => queryInfos.defaultParams, [queryInfos]);
const getData = useCallback(
async signal => {
// Currently polymorphic relations are not handled
if (isMorph) {
setIsLoading(false);
return;
}
if (!isFieldAllowed) {
setIsLoading(false);
return;
}
const params = { _limit: state._limit, _start: state._start, ...defaultParams };
if (state._contains) {
params[containsKey] = state._contains;
}
try {
const data = await request(endPoint, { 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) {
// Silent
}
},
[
containsKey,
defaultParams,
endPoint,
isFieldAllowed,
isMorph,
mainField,
state._limit,
state._start,
state._contains,
]
);
useEffect(() => {
const abortController = new AbortController();
const { signal } = abortController;
getData(signal);
return () => abortController.abort();
}, [getData]);
const onInputChange = (inputValue, { action }) => {
if (action === 'input-change') {
setState(prevState => {
if (prevState._contains === inputValue) {
return prevState;
}
return { ...prevState, _contains: inputValue, _start: 0 };
});
}
return inputValue;
};
const onMenuScrollToBottom = () => {
setState(prevState => ({ ...prevState, _start: prevState._start + 20 }));
};
const isSingle = ['oneWay', 'oneToOne', 'manyToOne', 'oneToManyMorph', 'oneToOneMorph'].includes(
relationType
);
const to = `/plugins/${pluginId}/collectionType/${targetModel}/${value ? value.id : null}`;
const link = useMemo(() => {
if (!value) {
return null;
}
if (!displayNavigationLink) {
return null;
}
return (
{msg => {msg}}
);
}, [displayNavigationLink, pathname, to, value]);
const Component = isSingle ? SelectOne : SelectMany;
const associationsLength = isArray(value) ? value.length : 0;
const isDisabled = useMemo(() => {
if (isMorph) {
return true;
}
if (!isCreatingEntry) {
return (!isFieldAllowed && isFieldReadable) || !editable;
}
return !editable;
}, [isMorph, isCreatingEntry, editable, isFieldAllowed, isFieldReadable]);
if (!isFieldAllowed && isCreatingEntry) {
return ;
}
if (!isCreatingEntry && !isFieldAllowed && !isFieldReadable) {
return ;
}
return (
{label}
{!isSingle && ` (${associationsLength})`}
{isSingle && link}
{!isEmpty(description) && (
{description}
)}
{
addRelation({ target: { name, value } });
}}
components={{ ClearIndicator, DropdownIndicator, IndicatorSeparator, Option }}
displayNavigationLink={displayNavigationLink}
id={name}
isDisabled={isDisabled}
isLoading={isLoading}
isClearable
mainField={mainField}
move={moveRelation}
name={name}
options={filteredOptions}
onChange={value => {
onChange({ target: { name, value: value ? value.value : value } });
}}
onInputChange={onInputChange}
onMenuClose={() => {
setState(prevState => ({ ...prevState, _contains: '' }));
}}
onMenuScrollToBottom={onMenuScrollToBottom}
onRemove={onRemoveRelation}
placeholder={
isEmpty(placeholder) ? (
) : (
placeholder
)
}
styles={styles}
targetModel={targetModel}
value={value}
/>
);
}
SelectWrapper.defaultProps = {
// componentUid: null,
editable: true,
description: '',
label: '',
isFieldAllowed: true,
placeholder: '',
};
SelectWrapper.propTypes = {
// componentUid: PropTypes.string,
displayNavigationLink: PropTypes.bool.isRequired,
editable: PropTypes.bool,
description: PropTypes.string,
label: PropTypes.string,
isCreatingEntry: PropTypes.bool.isRequired,
isFieldAllowed: PropTypes.bool,
isFieldReadable: PropTypes.bool.isRequired,
mainField: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
placeholder: PropTypes.string,
relationType: PropTypes.string.isRequired,
// slug: PropTypes.string.isRequired,
targetModel: PropTypes.string.isRequired,
queryInfos: PropTypes.exact({
containsKey: PropTypes.string.isRequired,
defaultParams: PropTypes.object,
endPoint: PropTypes.string.isRequired,
}).isRequired,
};
const Memoized = memo(SelectWrapper);
export default connect(Memoized, select);