mirror of
https://github.com/strapi/strapi.git
synced 2025-09-26 17:00:55 +00:00
refactor: repeatableComponents (make it simpler)
This commit is contained in:
parent
eef9fdb463
commit
ab5dac4ca2
@ -498,12 +498,12 @@ const EditViewDataManagerProvider = ({
|
|||||||
[shouldCheckDZErrors]
|
[shouldCheckDZErrors]
|
||||||
);
|
);
|
||||||
|
|
||||||
const moveComponentField = useCallback((pathToComponent, dragIndex, hoverIndex) => {
|
const moveComponentField = useCallback(({ name, newIndex, currentIndex }) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'MOVE_COMPONENT_FIELD',
|
type: 'MOVE_COMPONENT_FIELD',
|
||||||
pathToComponent,
|
keys: name.split('.'),
|
||||||
dragIndex,
|
newIndex,
|
||||||
hoverIndex,
|
oldIndex: currentIndex,
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -178,6 +178,7 @@ const reducer = (state, action) =>
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'MOVE_COMPONENT_FIELD':
|
||||||
case 'REORDER_RELATION': {
|
case 'REORDER_RELATION': {
|
||||||
const { oldIndex, newIndex, keys } = action;
|
const { oldIndex, newIndex, keys } = action;
|
||||||
const path = ['modifiedData', ...keys];
|
const path = ['modifiedData', ...keys];
|
||||||
@ -265,25 +266,6 @@ const reducer = (state, action) =>
|
|||||||
draftState.shouldCheckErrors = false;
|
draftState.shouldCheckErrors = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'MOVE_COMPONENT_FIELD': {
|
|
||||||
const currentValue = get(state, ['modifiedData', ...action.pathToComponent]);
|
|
||||||
const valueToInsert = get(state, [
|
|
||||||
'modifiedData',
|
|
||||||
...action.pathToComponent,
|
|
||||||
action.dragIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const updatedValue = moveFields(
|
|
||||||
currentValue,
|
|
||||||
action.dragIndex,
|
|
||||||
action.hoverIndex,
|
|
||||||
valueToInsert
|
|
||||||
);
|
|
||||||
|
|
||||||
set(draftState, ['modifiedData', ...action.pathToComponent], updatedValue);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'MOVE_COMPONENT_UP':
|
case 'MOVE_COMPONENT_UP':
|
||||||
case 'MOVE_COMPONENT_DOWN': {
|
case 'MOVE_COMPONENT_DOWN': {
|
||||||
const { currentIndex, dynamicZoneName, shouldCheckErrors } = action;
|
const { currentIndex, dynamicZoneName, shouldCheckErrors } = action;
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Stack } from '@strapi/design-system/Stack';
|
|
||||||
import { Flex } from '@strapi/design-system/Flex';
|
|
||||||
import { TextButton } from '@strapi/design-system/TextButton';
|
|
||||||
import { Icon } from '@strapi/design-system/Icon';
|
|
||||||
import { Typography } from '@strapi/design-system/Typography';
|
|
||||||
import Trash from '@strapi/icons/Trash';
|
|
||||||
import Drag from '@strapi/icons/Drag';
|
|
||||||
import DropdownIcon from '@strapi/icons/CarretDown';
|
|
||||||
import { CustomIconButtonSibling } from './IconButtonCustoms';
|
|
||||||
|
|
||||||
const SiblingWrapper = styled.span`
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-left: ${({ theme }) => theme.spaces[4]};
|
|
||||||
padding-right: ${({ theme }) => theme.spaces[4]};
|
|
||||||
background-color: ${({ theme }) => theme.colors.neutral0};
|
|
||||||
height: ${50 / 16}rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ToggleButton = styled(TextButton)`
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: ${14 / 16}rem;
|
|
||||||
height: ${14 / 16}rem;
|
|
||||||
|
|
||||||
path {
|
|
||||||
fill: ${({ theme, expanded }) =>
|
|
||||||
expanded ? theme.colors.primary600 : theme.colors.neutral500};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const DraggingSibling = ({ displayedValue }) => {
|
|
||||||
return (
|
|
||||||
<SiblingWrapper>
|
|
||||||
<Stack horizontal spacing={3} flex={1}>
|
|
||||||
<Flex
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="50%"
|
|
||||||
height={`${24 / 16}rem}`}
|
|
||||||
width={`${24 / 16}rem}`}
|
|
||||||
aria-hidden
|
|
||||||
as="span"
|
|
||||||
background="neutral200"
|
|
||||||
>
|
|
||||||
<Icon as={DropdownIcon} width={`${8 / 16}rem}`} color="neutral600" />
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<ToggleButton onClick={() => {}} flex={1}>
|
|
||||||
<Typography fontWeight="bold" textColor="neutral700">
|
|
||||||
{displayedValue}
|
|
||||||
</Typography>
|
|
||||||
</ToggleButton>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Stack horizontal spacing={0}>
|
|
||||||
<CustomIconButtonSibling noBorder onClick={() => {}} icon={<Trash />} />
|
|
||||||
<CustomIconButtonSibling icon={<Drag />} noBorder />
|
|
||||||
</Stack>
|
|
||||||
</SiblingWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DraggingSibling.propTypes = {
|
|
||||||
displayedValue: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DraggingSibling;
|
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
const StyledSpan = styled.span`
|
const StyledSpan = styled.span`
|
||||||
@ -9,8 +9,8 @@ const StyledSpan = styled.span`
|
|||||||
padding: ${({ theme }) => theme.spaces[6]};
|
padding: ${({ theme }) => theme.spaces[6]};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Preview = () => {
|
const Preview = forwardRef((_, ref) => {
|
||||||
return <StyledSpan padding={6} background="primary100" />;
|
return <StyledSpan ref={ref} padding={6} background="primary100" />;
|
||||||
};
|
});
|
||||||
|
|
||||||
export default Preview;
|
export default Preview;
|
||||||
|
@ -1,24 +1,27 @@
|
|||||||
/* eslint-disable import/no-cycle */
|
/* eslint-disable import/no-cycle */
|
||||||
import React, { memo, useEffect, useRef, useState } from 'react';
|
import React, { memo, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useDrag, useDrop } from 'react-dnd';
|
import { useDrag, useDrop } from 'react-dnd';
|
||||||
import { getEmptyImage } from 'react-dnd-html5-backend';
|
import { getEmptyImage } from 'react-dnd-html5-backend';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import toString from 'lodash/toString';
|
import toString from 'lodash/toString';
|
||||||
|
|
||||||
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion';
|
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion';
|
||||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||||
import { Stack } from '@strapi/design-system/Stack';
|
import { Stack } from '@strapi/design-system/Stack';
|
||||||
import { Box } from '@strapi/design-system/Box';
|
import { Box } from '@strapi/design-system/Box';
|
||||||
import { Tooltip } from '@strapi/design-system/Tooltip';
|
import { Tooltip } from '@strapi/design-system/Tooltip';
|
||||||
|
|
||||||
import Trash from '@strapi/icons/Trash';
|
import Trash from '@strapi/icons/Trash';
|
||||||
import Drag from '@strapi/icons/Drag';
|
import Drag from '@strapi/icons/Drag';
|
||||||
import ItemTypes from '../../../utils/ItemTypes';
|
|
||||||
import getTrad from '../../../utils/getTrad';
|
import { composeRefs, getTrad, ItemTypes } from '../../../utils';
|
||||||
|
|
||||||
import Inputs from '../../Inputs';
|
import Inputs from '../../Inputs';
|
||||||
import FieldComponent from '../../FieldComponent';
|
import FieldComponent from '../../FieldComponent';
|
||||||
|
|
||||||
import Preview from './Preview';
|
import Preview from './Preview';
|
||||||
import DraggingSibling from './DraggingSibling';
|
|
||||||
import { CustomIconButton } from './IconButtonCustoms';
|
import { CustomIconButton } from './IconButtonCustoms';
|
||||||
import { connect, select } from './utils';
|
import { connect, select } from './utils';
|
||||||
|
|
||||||
@ -48,7 +51,7 @@ const DraggedItem = ({
|
|||||||
// Errors are retrieved from the AccordionGroupCustom cloneElement
|
// Errors are retrieved from the AccordionGroupCustom cloneElement
|
||||||
hasErrorMessage,
|
hasErrorMessage,
|
||||||
hasErrors,
|
hasErrors,
|
||||||
isDraggingSibling,
|
index,
|
||||||
isOpen,
|
isOpen,
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
onClickToggle,
|
onClickToggle,
|
||||||
@ -57,90 +60,70 @@ const DraggedItem = ({
|
|||||||
// Retrieved from the select function
|
// Retrieved from the select function
|
||||||
moveComponentField,
|
moveComponentField,
|
||||||
removeRepeatableField,
|
removeRepeatableField,
|
||||||
setIsDraggingSibling,
|
|
||||||
triggerFormValidation,
|
triggerFormValidation,
|
||||||
// checkFormErrors,
|
|
||||||
displayedValue,
|
displayedValue,
|
||||||
}) => {
|
}) => {
|
||||||
const dragRef = useRef(null);
|
const accordionRef = useRef(null);
|
||||||
const dropRef = useRef(null);
|
const boxRef = useRef(null);
|
||||||
const [, forceRerenderAfterDnd] = useState(false);
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
const fields = schema.layouts.edit;
|
const fields = schema.layouts.edit;
|
||||||
|
|
||||||
const [, drop] = useDrop({
|
const [{ handlerId }, dropRef] = useDrop({
|
||||||
accept: ItemTypes.COMPONENT,
|
accept: ItemTypes.COMPONENT,
|
||||||
canDrop() {
|
collect(monitor) {
|
||||||
return false;
|
return {
|
||||||
|
handlerId: monitor.getHandlerId(),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
hover(item, monitor) {
|
hover(item, monitor) {
|
||||||
if (!dropRef.current) {
|
if (!boxRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dragPath = item.originalPath;
|
const dragIndex = item.index;
|
||||||
const hoverPath = componentFieldName;
|
const currentIndex = index;
|
||||||
const fullPathToComponentArray = dragPath.split('.');
|
|
||||||
const dragIndexString = fullPathToComponentArray.slice().splice(-1).join('');
|
|
||||||
const hoverIndexString = hoverPath.split('.').splice(-1).join('');
|
|
||||||
const pathToComponentArray = fullPathToComponentArray.slice(
|
|
||||||
0,
|
|
||||||
fullPathToComponentArray.length - 1
|
|
||||||
);
|
|
||||||
const dragIndex = parseInt(dragIndexString, 10);
|
|
||||||
const hoverIndex = parseInt(hoverIndexString, 10);
|
|
||||||
|
|
||||||
// Don't replace items with themselves
|
// Don't replace items with themselves
|
||||||
if (dragIndex === hoverIndex) {
|
if (dragIndex === currentIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine rectangle on screen
|
const hoverBoundingRect = boxRef.current.getBoundingClientRect();
|
||||||
const hoverBoundingRect = dropRef.current.getBoundingClientRect();
|
|
||||||
// Get vertical middle
|
|
||||||
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
||||||
// Determine mouse position
|
|
||||||
const clientOffset = monitor.getClientOffset();
|
const clientOffset = monitor.getClientOffset();
|
||||||
// Get pixels to the top
|
|
||||||
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
||||||
|
|
||||||
// Only perform the move when the mouse has crossed half of the items height
|
|
||||||
// When dragging downwards, only move when the cursor is below 50%
|
|
||||||
// When dragging upwards, only move when the cursor is above 50%
|
|
||||||
// Dragging downwards
|
// Dragging downwards
|
||||||
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
|
if (dragIndex < currentIndex && hoverClientY < hoverMiddleY) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Dragging upwards
|
|
||||||
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If They are not in the same level, should not move
|
|
||||||
if (dragPath.split('.').length !== hoverPath.split('.').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Time to actually perform the action in the data
|
|
||||||
moveComponentField(pathToComponentArray, dragIndex, hoverIndex);
|
|
||||||
|
|
||||||
item.originalPath = hoverPath;
|
// Dragging upwards
|
||||||
|
if (dragIndex > currentIndex && hoverClientY > hoverMiddleY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time to actually perform the action
|
||||||
|
moveComponentField(dragIndex, currentIndex);
|
||||||
|
|
||||||
|
item.index = currentIndex;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const [{ isDragging }, drag, preview] = useDrag({
|
const [{ isDragging }, dragRef, previewRef] = useDrag({
|
||||||
type: ItemTypes.COMPONENT,
|
type: ItemTypes.COMPONENT,
|
||||||
item() {
|
item() {
|
||||||
// Close all collapses
|
// Close all collapses
|
||||||
toggleCollapses(-1);
|
toggleCollapses(-1);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
displayedValue,
|
index,
|
||||||
originalPath: componentFieldName,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
end() {
|
end() {
|
||||||
// Update the errors
|
// Update the errors
|
||||||
triggerFormValidation();
|
triggerFormValidation();
|
||||||
setIsDraggingSibling(false);
|
// setIsDraggingSibling(false);
|
||||||
},
|
},
|
||||||
collect: (monitor) => ({
|
collect: (monitor) => ({
|
||||||
isDragging: monitor.isDragging(),
|
isDragging: monitor.isDragging(),
|
||||||
@ -148,43 +131,20 @@ const DraggedItem = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
preview(getEmptyImage(), { captureDraggingState: false });
|
previewRef(getEmptyImage(), { captureDraggingState: false });
|
||||||
}, [preview]);
|
}, [previewRef]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isDragging) {
|
|
||||||
setIsDraggingSibling(true);
|
|
||||||
}
|
|
||||||
}, [isDragging, setIsDraggingSibling]);
|
|
||||||
|
|
||||||
// Effect in order to force a rerender after reordering the components
|
|
||||||
// Since we are removing the Accordion when doing the DnD we are losing the dragRef, therefore the replaced element cannot be dragged
|
|
||||||
// anymore, this hack forces a rerender in order to apply the dragRef
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isDraggingSibling) {
|
|
||||||
forceRerenderAfterDnd((prev) => !prev);
|
|
||||||
}
|
|
||||||
}, [isDraggingSibling]);
|
|
||||||
|
|
||||||
// Create the refs
|
|
||||||
// We need 1 for the drop target
|
|
||||||
// 1 for the drag target
|
|
||||||
const refs = {
|
|
||||||
dragRef: drag(dragRef),
|
|
||||||
dropRef: drop(dropRef),
|
|
||||||
};
|
|
||||||
|
|
||||||
const accordionTitle = toString(displayedValue);
|
const accordionTitle = toString(displayedValue);
|
||||||
const accordionHasError = hasErrors ? 'error' : undefined;
|
const accordionHasError = hasErrors ? 'error' : undefined;
|
||||||
|
|
||||||
return (
|
const composedAccordionRefs = composeRefs(accordionRef, dragRef);
|
||||||
<Box ref={refs ? refs.dropRef : null}>
|
const composedBoxRefs = composeRefs(boxRef, dropRef);
|
||||||
{isDragging && <Preview />}
|
|
||||||
{!isDragging && isDraggingSibling && (
|
|
||||||
<DraggingSibling displayedValue={accordionTitle} componentFieldName={componentFieldName} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isDragging && !isDraggingSibling && (
|
return (
|
||||||
|
<Box ref={composedBoxRefs} style={{ border: '1px solid red' }}>
|
||||||
|
{isDragging ? (
|
||||||
|
<Preview ref={previewRef} />
|
||||||
|
) : (
|
||||||
<Accordion
|
<Accordion
|
||||||
error={accordionHasError}
|
error={accordionHasError}
|
||||||
hasErrorMessage={hasErrorMessage}
|
hasErrorMessage={hasErrorMessage}
|
||||||
@ -220,8 +180,9 @@ const DraggedItem = ({
|
|||||||
<DragButton
|
<DragButton
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
ref={refs.dragRef}
|
ref={composedAccordionRefs}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
data-handler-id={handlerId}
|
||||||
>
|
>
|
||||||
<Drag />
|
<Drag />
|
||||||
</DragButton>
|
</DragButton>
|
||||||
@ -290,9 +251,7 @@ const DraggedItem = ({
|
|||||||
|
|
||||||
DraggedItem.defaultProps = {
|
DraggedItem.defaultProps = {
|
||||||
componentUid: undefined,
|
componentUid: undefined,
|
||||||
isDraggingSibling: false,
|
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
setIsDraggingSibling() {},
|
|
||||||
toggleCollapses() {},
|
toggleCollapses() {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -301,7 +260,7 @@ DraggedItem.propTypes = {
|
|||||||
componentUid: PropTypes.string,
|
componentUid: PropTypes.string,
|
||||||
hasErrorMessage: PropTypes.bool.isRequired,
|
hasErrorMessage: PropTypes.bool.isRequired,
|
||||||
hasErrors: PropTypes.bool.isRequired,
|
hasErrors: PropTypes.bool.isRequired,
|
||||||
isDraggingSibling: PropTypes.bool,
|
index: PropTypes.number.isRequired,
|
||||||
isOpen: PropTypes.bool,
|
isOpen: PropTypes.bool,
|
||||||
isReadOnly: PropTypes.bool.isRequired,
|
isReadOnly: PropTypes.bool.isRequired,
|
||||||
onClickToggle: PropTypes.func.isRequired,
|
onClickToggle: PropTypes.func.isRequired,
|
||||||
@ -309,9 +268,7 @@ DraggedItem.propTypes = {
|
|||||||
toggleCollapses: PropTypes.func,
|
toggleCollapses: PropTypes.func,
|
||||||
moveComponentField: PropTypes.func.isRequired,
|
moveComponentField: PropTypes.func.isRequired,
|
||||||
removeRepeatableField: PropTypes.func.isRequired,
|
removeRepeatableField: PropTypes.func.isRequired,
|
||||||
setIsDraggingSibling: PropTypes.func,
|
|
||||||
triggerFormValidation: PropTypes.func.isRequired,
|
triggerFormValidation: PropTypes.func.isRequired,
|
||||||
// checkFormErrors: PropTypes.func.isRequired,
|
|
||||||
displayedValue: PropTypes.string.isRequired,
|
displayedValue: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,13 +3,7 @@ import { get, toString } from 'lodash';
|
|||||||
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
||||||
|
|
||||||
function useSelect({ schema, componentFieldName }) {
|
function useSelect({ schema, componentFieldName }) {
|
||||||
const {
|
const { modifiedData, removeRepeatableField, triggerFormValidation } = useCMEditViewDataManager();
|
||||||
checkFormErrors,
|
|
||||||
modifiedData,
|
|
||||||
moveComponentField,
|
|
||||||
removeRepeatableField,
|
|
||||||
triggerFormValidation,
|
|
||||||
} = useCMEditViewDataManager();
|
|
||||||
|
|
||||||
const mainField = useMemo(() => get(schema, ['settings', 'mainField'], 'id'), [schema]);
|
const mainField = useMemo(() => get(schema, ['settings', 'mainField'], 'id'), [schema]);
|
||||||
const displayedValue = toString(
|
const displayedValue = toString(
|
||||||
@ -19,8 +13,6 @@ function useSelect({ schema, componentFieldName }) {
|
|||||||
return {
|
return {
|
||||||
displayedValue,
|
displayedValue,
|
||||||
mainField,
|
mainField,
|
||||||
checkFormErrors,
|
|
||||||
moveComponentField,
|
|
||||||
removeRepeatableField,
|
removeRepeatableField,
|
||||||
schema,
|
schema,
|
||||||
triggerFormValidation,
|
triggerFormValidation,
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
import React, { memo, useCallback, useMemo, useState } from 'react';
|
|
||||||
/* eslint-disable import/no-cycle */
|
/* eslint-disable import/no-cycle */
|
||||||
import { useDrop } from 'react-dnd';
|
import React, { memo, useCallback, useMemo, useState } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { useNotification } from '@strapi/helper-plugin';
|
|
||||||
|
import { useNotification, useCMEditViewDataManager } from '@strapi/helper-plugin';
|
||||||
|
|
||||||
import { Box } from '@strapi/design-system/Box';
|
import { Box } from '@strapi/design-system/Box';
|
||||||
import { Flex } from '@strapi/design-system/Flex';
|
import { Flex } from '@strapi/design-system/Flex';
|
||||||
import { TextButton } from '@strapi/design-system/TextButton';
|
import { TextButton } from '@strapi/design-system/TextButton';
|
||||||
import Plus from '@strapi/icons/Plus';
|
import Plus from '@strapi/icons/Plus';
|
||||||
|
|
||||||
import { getMaxTempKey, getTrad } from '../../utils';
|
import { getMaxTempKey, getTrad } from '../../utils';
|
||||||
import { useContentTypeLayout } from '../../hooks';
|
import { useContentTypeLayout } from '../../hooks';
|
||||||
import ItemTypes from '../../utils/ItemTypes';
|
|
||||||
import ComponentInitializer from '../ComponentInitializer';
|
import ComponentInitializer from '../ComponentInitializer';
|
||||||
import connect from './utils/connect';
|
|
||||||
import select from './utils/select';
|
|
||||||
import getComponentErrorKeys from './utils/getComponentErrorKeys';
|
|
||||||
import DraggedItem from './DraggedItem';
|
import DraggedItem from './DraggedItem';
|
||||||
import AccordionGroupCustom from './AccordionGroupCustom';
|
import AccordionGroupCustom from './AccordionGroupCustom';
|
||||||
|
|
||||||
|
import getComponentErrorKeys from './utils/getComponentErrorKeys';
|
||||||
|
|
||||||
const TextButtonCustom = styled(TextButton)`
|
const TextButtonCustom = styled(TextButton)`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -33,8 +34,6 @@ const TextButtonCustom = styled(TextButton)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const RepeatableComponent = ({
|
const RepeatableComponent = ({
|
||||||
addRepeatableComponentToField,
|
|
||||||
formErrors,
|
|
||||||
componentUid,
|
componentUid,
|
||||||
componentValue,
|
componentValue,
|
||||||
componentValueLength,
|
componentValueLength,
|
||||||
@ -43,11 +42,11 @@ const RepeatableComponent = ({
|
|||||||
min,
|
min,
|
||||||
name,
|
name,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { addRepeatableComponentToField, formErrors, moveComponentField } =
|
||||||
|
useCMEditViewDataManager();
|
||||||
const toggleNotification = useNotification();
|
const toggleNotification = useNotification();
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const [collapseToOpen, setCollapseToOpen] = useState('');
|
const [collapseToOpen, setCollapseToOpen] = useState('');
|
||||||
const [isDraggingSibling, setIsDraggingSibling] = useState(false);
|
|
||||||
const [, drop] = useDrop({ accept: ItemTypes.COMPONENT });
|
|
||||||
const { getComponentLayout, components } = useContentTypeLayout();
|
const { getComponentLayout, components } = useContentTypeLayout();
|
||||||
const componentLayoutData = useMemo(
|
const componentLayoutData = useMemo(
|
||||||
() => getComponentLayout(componentUid),
|
() => getComponentLayout(componentUid),
|
||||||
@ -124,8 +123,16 @@ const RepeatableComponent = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMoveComponentField = (newIndex, currentIndex) => {
|
||||||
|
moveComponentField({
|
||||||
|
name,
|
||||||
|
newIndex,
|
||||||
|
currentIndex,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box hasRadius ref={drop}>
|
<Box hasRadius>
|
||||||
<AccordionGroupCustom
|
<AccordionGroupCustom
|
||||||
error={errorMessage}
|
error={errorMessage}
|
||||||
footer={
|
footer={
|
||||||
@ -151,7 +158,6 @@ const RepeatableComponent = ({
|
|||||||
componentUid={componentUid}
|
componentUid={componentUid}
|
||||||
hasErrors={hasErrors}
|
hasErrors={hasErrors}
|
||||||
hasMinError={hasMinError}
|
hasMinError={hasMinError}
|
||||||
isDraggingSibling={isDraggingSibling}
|
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
key={key}
|
key={key}
|
||||||
@ -162,10 +168,11 @@ const RepeatableComponent = ({
|
|||||||
setCollapseToOpen(key);
|
setCollapseToOpen(key);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
moveComponentField={handleMoveComponentField}
|
||||||
parentName={name}
|
parentName={name}
|
||||||
schema={componentLayoutData}
|
schema={componentLayoutData}
|
||||||
setIsDraggingSibling={setIsDraggingSibling}
|
|
||||||
toggleCollapses={toggleCollapses}
|
toggleCollapses={toggleCollapses}
|
||||||
|
index={index}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -177,25 +184,20 @@ const RepeatableComponent = ({
|
|||||||
RepeatableComponent.defaultProps = {
|
RepeatableComponent.defaultProps = {
|
||||||
componentValue: null,
|
componentValue: null,
|
||||||
componentValueLength: 0,
|
componentValueLength: 0,
|
||||||
formErrors: {},
|
|
||||||
max: Infinity,
|
max: Infinity,
|
||||||
min: 0,
|
min: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
RepeatableComponent.propTypes = {
|
RepeatableComponent.propTypes = {
|
||||||
addRepeatableComponentToField: PropTypes.func.isRequired,
|
|
||||||
componentUid: PropTypes.string.isRequired,
|
componentUid: PropTypes.string.isRequired,
|
||||||
componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||||
componentValueLength: PropTypes.number,
|
componentValueLength: PropTypes.number,
|
||||||
formErrors: PropTypes.object,
|
|
||||||
isReadOnly: PropTypes.bool.isRequired,
|
isReadOnly: PropTypes.bool.isRequired,
|
||||||
max: PropTypes.number,
|
max: PropTypes.number,
|
||||||
min: PropTypes.number,
|
min: PropTypes.number,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Memoized = memo(RepeatableComponent);
|
export default memo(RepeatableComponent);
|
||||||
|
|
||||||
export default connect(Memoized, select);
|
|
||||||
|
|
||||||
export { RepeatableComponent };
|
export { RepeatableComponent };
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
function connect(WrappedComponent, select) {
|
|
||||||
return (props) => {
|
|
||||||
const selectors = select(props);
|
|
||||||
|
|
||||||
return <WrappedComponent {...props} {...selectors} />;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect;
|
|
@ -1,12 +0,0 @@
|
|||||||
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
|
|
||||||
|
|
||||||
function useSelect() {
|
|
||||||
const { addRepeatableComponentToField, formErrors } = useCMEditViewDataManager();
|
|
||||||
|
|
||||||
return {
|
|
||||||
addRepeatableComponentToField,
|
|
||||||
formErrors,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useSelect;
|
|
Loading…
x
Reference in New Issue
Block a user