strapi-plugin-content-manager
Version:
A powerful UI to easily manage your data.
188 lines (171 loc) • 5.94 kB
JavaScript
import React, { memo, useCallback, useMemo, useState } from 'react';
/* eslint-disable import/no-cycle */
import { useDrop } from 'react-dnd';
import PropTypes from 'prop-types';
import { get, take } from 'lodash';
import { FormattedMessage } from 'react-intl';
import { ErrorMessage } from '@buffetjs/styles';
import pluginId from '../../pluginId';
import { getMaxTempKey } from '../../utils';
import { useContentTypeLayout } from '../../hooks';
import ItemTypes from '../../utils/ItemTypes';
import connect from './utils/connect';
import select from './utils/select';
import Button from './AddFieldButton';
import DraggedItem from './DraggedItem';
import EmptyComponent from './EmptyComponent';
const RepeatableComponent = ({
addRepeatableComponentToField,
formErrors,
componentUid,
componentValue,
componentValueLength,
isNested,
isReadOnly,
max,
min,
name,
}) => {
const [collapseToOpen, setCollapseToOpen] = useState('');
const [, drop] = useDrop({ accept: ItemTypes.COMPONENT });
const { getComponentLayout } = useContentTypeLayout();
const componentLayoutData = useMemo(() => getComponentLayout(componentUid), [
componentUid,
getComponentLayout,
]);
const nextTempKey = useMemo(() => {
return getMaxTempKey(componentValue || []) + 1;
}, [componentValue]);
const componentErrorKeys = Object.keys(formErrors)
.filter(errorKey => {
return take(errorKey.split('.'), isNested ? 3 : 1).join('.') === name;
})
.map(errorKey => {
return errorKey
.split('.')
.slice(0, name.split('.').length + 1)
.join('.');
});
const toggleCollapses = () => {
setCollapseToOpen('');
};
const missingComponentsValue = min - componentValueLength;
const errorsArray = componentErrorKeys.map(key => get(formErrors, [key, 'id'], ''));
const hasMinError = get(errorsArray, [0], '').includes('min');
const handleClick = useCallback(() => {
if (!isReadOnly) {
if (componentValueLength < max) {
const shouldCheckErrors = hasMinError;
addRepeatableComponentToField(name, componentUid, shouldCheckErrors);
setCollapseToOpen(nextTempKey);
} else if (componentValueLength >= max) {
strapi.notification.info(`${pluginId}.components.notification.info.maximum-requirement`);
}
}
}, [
addRepeatableComponentToField,
componentUid,
componentValueLength,
hasMinError,
isReadOnly,
max,
name,
nextTempKey,
]);
return (
<div>
{componentValueLength === 0 && (
<EmptyComponent hasMinError={hasMinError}>
<FormattedMessage id={`${pluginId}.components.empty-repeatable`}>
{msg => <p>{msg}</p>}
</FormattedMessage>
</EmptyComponent>
)}
<div ref={drop}>
{componentValueLength > 0 &&
componentValue.map((data, index) => {
const key = data.__temp_key__;
const isOpen = collapseToOpen === key;
const componentFieldName = `${name}.${index}`;
const previousComponentTempKey = get(componentValue, [index - 1, '__temp_key__']);
const doesPreviousFieldContainErrorsAndIsOpen =
componentErrorKeys.includes(`${name}.${index - 1}`) &&
index !== 0 &&
collapseToOpen === previousComponentTempKey;
const hasErrors = componentErrorKeys.includes(componentFieldName);
return (
<DraggedItem
componentFieldName={componentFieldName}
componentUid={componentUid}
doesPreviousFieldContainErrorsAndIsOpen={doesPreviousFieldContainErrorsAndIsOpen}
hasErrors={hasErrors}
hasMinError={hasMinError}
isFirst={index === 0}
isReadOnly={isReadOnly}
isOpen={isOpen}
key={key}
onClickToggle={() => {
if (isOpen) {
setCollapseToOpen('');
} else {
setCollapseToOpen(key);
}
}}
parentName={name}
schema={componentLayoutData}
toggleCollapses={toggleCollapses}
/>
);
})}
</div>
<Button
hasMinError={hasMinError}
disabled={isReadOnly}
withBorderRadius={false}
doesPreviousFieldContainErrorsAndIsClosed={
componentValueLength > 0 &&
componentErrorKeys.includes(`${name}.${componentValueLength - 1}`) &&
componentValue[componentValueLength - 1].__temp_key__ !== collapseToOpen
}
type="button"
onClick={handleClick}
>
<i className="fa fa-plus" />
<FormattedMessage id={`${pluginId}.containers.EditView.add.new`} />
</Button>
{hasMinError && (
<ErrorMessage>
<FormattedMessage
id={`${pluginId}.components.DynamicZone.missing${
missingComponentsValue > 1 ? '.plural' : '.singular'
}`}
values={{ count: missingComponentsValue }}
/>
</ErrorMessage>
)}
</div>
);
};
RepeatableComponent.defaultProps = {
componentValue: null,
componentValueLength: 0,
formErrors: {},
isNested: false,
max: Infinity,
min: -Infinity,
};
RepeatableComponent.propTypes = {
addRepeatableComponentToField: PropTypes.func.isRequired,
componentUid: PropTypes.string.isRequired,
componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
componentValueLength: PropTypes.number,
formErrors: PropTypes.object,
isNested: PropTypes.bool,
isReadOnly: PropTypes.bool.isRequired,
max: PropTypes.number,
min: PropTypes.number,
name: PropTypes.string.isRequired,
};
const Memoized = memo(RepeatableComponent);
export default connect(Memoized, select);
export { RepeatableComponent };