From ab5dac4ca2be12f10bfefe6afb9e62dcbdb077a2 Mon Sep 17 00:00:00 2001
From: Josh <37798644+joshuaellis@users.noreply.github.com>
Date: Mon, 7 Nov 2022 11:29:03 +0000
Subject: [PATCH] refactor: repeatableComponents (make it simpler)
---
.../EditViewDataManagerProvider/index.js | 8 +-
.../EditViewDataManagerProvider/reducer.js | 20 +--
.../DraggedItem/DraggingSibling.js | 72 ----------
.../DraggedItem/Preview.js | 8 +-
.../RepeatableComponent/DraggedItem/index.js | 133 ++++++------------
.../DraggedItem/utils/select.js | 10 +-
.../components/RepeatableComponent/index.js | 42 +++---
.../RepeatableComponent/utils/connect.js | 11 --
.../RepeatableComponent/utils/select.js | 12 --
9 files changed, 77 insertions(+), 239 deletions(-)
delete mode 100644 packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js
delete mode 100644 packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/connect.js
delete mode 100644 packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/select.js
diff --git a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js
index 6975e24b33..9f2d5bf301 100644
--- a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js
@@ -498,12 +498,12 @@ const EditViewDataManagerProvider = ({
[shouldCheckDZErrors]
);
- const moveComponentField = useCallback((pathToComponent, dragIndex, hoverIndex) => {
+ const moveComponentField = useCallback(({ name, newIndex, currentIndex }) => {
dispatch({
type: 'MOVE_COMPONENT_FIELD',
- pathToComponent,
- dragIndex,
- hoverIndex,
+ keys: name.split('.'),
+ newIndex,
+ oldIndex: currentIndex,
});
}, []);
diff --git a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js
index d8e58893f2..80849ad2eb 100644
--- a/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js
+++ b/packages/core/admin/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js
@@ -178,6 +178,7 @@ const reducer = (state, action) =>
break;
}
+ case 'MOVE_COMPONENT_FIELD':
case 'REORDER_RELATION': {
const { oldIndex, newIndex, keys } = action;
const path = ['modifiedData', ...keys];
@@ -265,25 +266,6 @@ const reducer = (state, action) =>
draftState.shouldCheckErrors = false;
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_DOWN': {
const { currentIndex, dynamicZoneName, shouldCheckErrors } = action;
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js
deleted file mode 100644
index ad42753867..0000000000
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js
+++ /dev/null
@@ -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 (
-
-
-
-
-
-
- {}} flex={1}>
-
- {displayedValue}
-
-
-
-
-
- {}} icon={} />
- } noBorder />
-
-
- );
-};
-
-DraggingSibling.propTypes = {
- displayedValue: PropTypes.string.isRequired,
-};
-
-export default DraggingSibling;
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/Preview.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/Preview.js
index 08055e08b6..d008d08119 100644
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/Preview.js
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/Preview.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { forwardRef } from 'react';
import styled from 'styled-components';
const StyledSpan = styled.span`
@@ -9,8 +9,8 @@ const StyledSpan = styled.span`
padding: ${({ theme }) => theme.spaces[6]};
`;
-const Preview = () => {
- return ;
-};
+const Preview = forwardRef((_, ref) => {
+ return ;
+});
export default Preview;
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js
index 41fcb02fc1..34e7ceafbf 100644
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js
@@ -1,24 +1,27 @@
/* 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 { useDrag, useDrop } from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import styled from 'styled-components';
import { useIntl } from 'react-intl';
import toString from 'lodash/toString';
+
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion';
import { Grid, GridItem } from '@strapi/design-system/Grid';
import { Stack } from '@strapi/design-system/Stack';
import { Box } from '@strapi/design-system/Box';
import { Tooltip } from '@strapi/design-system/Tooltip';
+
import Trash from '@strapi/icons/Trash';
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 FieldComponent from '../../FieldComponent';
+
import Preview from './Preview';
-import DraggingSibling from './DraggingSibling';
import { CustomIconButton } from './IconButtonCustoms';
import { connect, select } from './utils';
@@ -48,7 +51,7 @@ const DraggedItem = ({
// Errors are retrieved from the AccordionGroupCustom cloneElement
hasErrorMessage,
hasErrors,
- isDraggingSibling,
+ index,
isOpen,
isReadOnly,
onClickToggle,
@@ -57,90 +60,70 @@ const DraggedItem = ({
// Retrieved from the select function
moveComponentField,
removeRepeatableField,
- setIsDraggingSibling,
triggerFormValidation,
- // checkFormErrors,
displayedValue,
}) => {
- const dragRef = useRef(null);
- const dropRef = useRef(null);
- const [, forceRerenderAfterDnd] = useState(false);
+ const accordionRef = useRef(null);
+ const boxRef = useRef(null);
const { formatMessage } = useIntl();
const fields = schema.layouts.edit;
- const [, drop] = useDrop({
+ const [{ handlerId }, dropRef] = useDrop({
accept: ItemTypes.COMPONENT,
- canDrop() {
- return false;
+ collect(monitor) {
+ return {
+ handlerId: monitor.getHandlerId(),
+ };
},
hover(item, monitor) {
- if (!dropRef.current) {
+ if (!boxRef.current) {
return;
}
- const dragPath = item.originalPath;
- const hoverPath = componentFieldName;
- 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);
+ const dragIndex = item.index;
+ const currentIndex = index;
// Don't replace items with themselves
- if (dragIndex === hoverIndex) {
+ if (dragIndex === currentIndex) {
return;
}
- // Determine rectangle on screen
- const hoverBoundingRect = dropRef.current.getBoundingClientRect();
- // Get vertical middle
+ const hoverBoundingRect = boxRef.current.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
- // Determine mouse position
const clientOffset = monitor.getClientOffset();
- // Get pixels to the 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
- if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
+ if (dragIndex < currentIndex && hoverClientY < hoverMiddleY) {
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,
item() {
// Close all collapses
toggleCollapses(-1);
return {
- displayedValue,
- originalPath: componentFieldName,
+ index,
};
},
end() {
// Update the errors
triggerFormValidation();
- setIsDraggingSibling(false);
+ // setIsDraggingSibling(false);
},
collect: (monitor) => ({
isDragging: monitor.isDragging(),
@@ -148,43 +131,20 @@ const DraggedItem = ({
});
useEffect(() => {
- preview(getEmptyImage(), { captureDraggingState: false });
- }, [preview]);
-
- 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),
- };
+ previewRef(getEmptyImage(), { captureDraggingState: false });
+ }, [previewRef]);
const accordionTitle = toString(displayedValue);
const accordionHasError = hasErrors ? 'error' : undefined;
- return (
-
- {isDragging && }
- {!isDragging && isDraggingSibling && (
-
- )}
+ const composedAccordionRefs = composeRefs(accordionRef, dragRef);
+ const composedBoxRefs = composeRefs(boxRef, dropRef);
- {!isDragging && !isDraggingSibling && (
+ return (
+
+ {isDragging ? (
+
+ ) : (
e.stopPropagation()}
+ data-handler-id={handlerId}
>
@@ -290,9 +251,7 @@ const DraggedItem = ({
DraggedItem.defaultProps = {
componentUid: undefined,
- isDraggingSibling: false,
isOpen: false,
- setIsDraggingSibling() {},
toggleCollapses() {},
};
@@ -301,7 +260,7 @@ DraggedItem.propTypes = {
componentUid: PropTypes.string,
hasErrorMessage: PropTypes.bool.isRequired,
hasErrors: PropTypes.bool.isRequired,
- isDraggingSibling: PropTypes.bool,
+ index: PropTypes.number.isRequired,
isOpen: PropTypes.bool,
isReadOnly: PropTypes.bool.isRequired,
onClickToggle: PropTypes.func.isRequired,
@@ -309,9 +268,7 @@ DraggedItem.propTypes = {
toggleCollapses: PropTypes.func,
moveComponentField: PropTypes.func.isRequired,
removeRepeatableField: PropTypes.func.isRequired,
- setIsDraggingSibling: PropTypes.func,
triggerFormValidation: PropTypes.func.isRequired,
- // checkFormErrors: PropTypes.func.isRequired,
displayedValue: PropTypes.string.isRequired,
};
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js
index bd158c6ddb..10209a6569 100644
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/utils/select.js
@@ -3,13 +3,7 @@ import { get, toString } from 'lodash';
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
function useSelect({ schema, componentFieldName }) {
- const {
- checkFormErrors,
- modifiedData,
- moveComponentField,
- removeRepeatableField,
- triggerFormValidation,
- } = useCMEditViewDataManager();
+ const { modifiedData, removeRepeatableField, triggerFormValidation } = useCMEditViewDataManager();
const mainField = useMemo(() => get(schema, ['settings', 'mainField'], 'id'), [schema]);
const displayedValue = toString(
@@ -19,8 +13,6 @@ function useSelect({ schema, componentFieldName }) {
return {
displayedValue,
mainField,
- checkFormErrors,
- moveComponentField,
removeRepeatableField,
schema,
triggerFormValidation,
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/index.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/index.js
index d71f45b6ff..8caf3dd591 100644
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/index.js
@@ -1,25 +1,26 @@
-import React, { memo, useCallback, useMemo, useState } from 'react';
/* eslint-disable import/no-cycle */
-import { useDrop } from 'react-dnd';
+import React, { memo, useCallback, useMemo, useState } from 'react';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import PropTypes from 'prop-types';
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 { Flex } from '@strapi/design-system/Flex';
import { TextButton } from '@strapi/design-system/TextButton';
import Plus from '@strapi/icons/Plus';
+
import { getMaxTempKey, getTrad } from '../../utils';
import { useContentTypeLayout } from '../../hooks';
-import ItemTypes from '../../utils/ItemTypes';
+
import ComponentInitializer from '../ComponentInitializer';
-import connect from './utils/connect';
-import select from './utils/select';
-import getComponentErrorKeys from './utils/getComponentErrorKeys';
import DraggedItem from './DraggedItem';
import AccordionGroupCustom from './AccordionGroupCustom';
+import getComponentErrorKeys from './utils/getComponentErrorKeys';
+
const TextButtonCustom = styled(TextButton)`
height: 100%;
width: 100%;
@@ -33,8 +34,6 @@ const TextButtonCustom = styled(TextButton)`
`;
const RepeatableComponent = ({
- addRepeatableComponentToField,
- formErrors,
componentUid,
componentValue,
componentValueLength,
@@ -43,11 +42,11 @@ const RepeatableComponent = ({
min,
name,
}) => {
+ const { addRepeatableComponentToField, formErrors, moveComponentField } =
+ useCMEditViewDataManager();
const toggleNotification = useNotification();
const { formatMessage } = useIntl();
const [collapseToOpen, setCollapseToOpen] = useState('');
- const [isDraggingSibling, setIsDraggingSibling] = useState(false);
- const [, drop] = useDrop({ accept: ItemTypes.COMPONENT });
const { getComponentLayout, components } = useContentTypeLayout();
const componentLayoutData = useMemo(
() => getComponentLayout(componentUid),
@@ -124,8 +123,16 @@ const RepeatableComponent = ({
};
}
+ const handleMoveComponentField = (newIndex, currentIndex) => {
+ moveComponentField({
+ name,
+ newIndex,
+ currentIndex,
+ });
+ };
+
return (
-
+
);
})}
@@ -177,25 +184,20 @@ const RepeatableComponent = ({
RepeatableComponent.defaultProps = {
componentValue: null,
componentValueLength: 0,
- formErrors: {},
max: Infinity,
min: 0,
};
RepeatableComponent.propTypes = {
- addRepeatableComponentToField: PropTypes.func.isRequired,
componentUid: PropTypes.string.isRequired,
componentValue: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
componentValueLength: PropTypes.number,
- formErrors: PropTypes.object,
isReadOnly: PropTypes.bool.isRequired,
max: PropTypes.number,
min: PropTypes.number,
name: PropTypes.string.isRequired,
};
-const Memoized = memo(RepeatableComponent);
-
-export default connect(Memoized, select);
+export default memo(RepeatableComponent);
export { RepeatableComponent };
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/connect.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/connect.js
deleted file mode 100644
index f763338c32..0000000000
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/connect.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-
-function connect(WrappedComponent, select) {
- return (props) => {
- const selectors = select(props);
-
- return ;
- };
-}
-
-export default connect;
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/select.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/select.js
deleted file mode 100644
index 93d746e76c..0000000000
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/utils/select.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { useCMEditViewDataManager } from '@strapi/helper-plugin';
-
-function useSelect() {
- const { addRepeatableComponentToField, formErrors } = useCMEditViewDataManager();
-
- return {
- addRepeatableComponentToField,
- formErrors,
- };
-}
-
-export default useSelect;