diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js
new file mode 100644
index 0000000000..6f0cfadb6b
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/AccordionGroupCustom/index.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { Box } from '@strapi/design-system/Box';
+import { Text } from '@strapi/design-system/Text';
+import { Flex } from '@strapi/design-system/Flex';
+import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
+
+const AccordionFooter = styled(Box)`
+ border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};
+ border-right: 1px solid ${({ theme }) => theme.colors.neutral200};
+ border-left: 1px solid ${({ theme }) => theme.colors.neutral200};
+ border-radius: 0 0 ${({ theme }) => theme.borderRadius} ${({ theme }) => theme.borderRadius};
+`;
+
+const EnhancedGroup = styled(Box)`
+ > div {
+ & > * {
+ border-radius: unset;
+ border-right: 1px solid ${({ theme }) => theme.colors.neutral200};
+ border-left: 1px solid ${({ theme }) => theme.colors.neutral200};
+ border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};
+ }
+ }
+
+ > div:first-of-type {
+ > div {
+ border-radius: ${({ theme }) => theme.borderRadius} ${({ theme }) => theme.borderRadius} 0 0;
+ }
+
+ > div:not([data-strapi-expanded='true']) {
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral200};
+
+ &:hover {
+ border-top: 1px solid ${({ theme }) => theme.colors.primary600};
+ }
+ }
+
+ > span {
+ border-radius: ${({ theme }) => theme.borderRadius} ${({ theme }) => theme.borderRadius} 0 0;
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral200};
+ }
+ }
+
+ & [data-strapi-expanded='true'] {
+ border: 1px solid ${({ theme }) => theme.colors.primary600};
+ }
+
+ ${({ theme, footer }) => `
+ &:not(${footer}) {
+ & > *:last-of-type {
+ border-radius: 0 0 ${theme.borderRadius} ${theme.borderRadius};
+ }
+ }
+ `}
+`;
+
+const LabelAction = styled(Box)`
+ svg path {
+ fill: ${({ theme }) => theme.colors.neutral500};
+ }
+`;
+
+const AccordionGroupCustom = ({ children, footer, label, labelAction }) => {
+ return (
+
+ {label && (
+
+
+ {label}
+
+ {labelAction && {labelAction}}
+
+ )}
+ {children}
+ {footer && {footer}}
+
+ );
+};
+
+AccordionGroupCustom.defaultProps = {
+ footer: null,
+ label: null,
+ labelAction: undefined,
+};
+
+AccordionGroupCustom.propTypes = {
+ children: PropTypes.node.isRequired,
+ footer: PropTypes.node,
+ label: PropTypes.string,
+ labelAction: PropTypes.node,
+};
+
+export default AccordionGroupCustom;
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DragPreview.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DragPreview.js
index 32aeaf7372..523a0ed938 100644
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DragPreview.js
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DragPreview.js
@@ -44,7 +44,7 @@ const DragPreview = ({ displayedValue }) => {
paddingBottom={3}
hasRadius
background="neutral0"
- style={{ width: '20vw' }}
+ width="300px"
>
diff --git a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DragHandleWrapper.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DragHandleWrapper.js
deleted file mode 100644
index 058661680b..0000000000
--- a/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DragHandleWrapper.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import styled from 'styled-components';
-import { IconButton } from '@strapi/design-system/IconButton';
-
-const DragHandleWrapper = styled(IconButton)`
- cursor: move;
-`;
-
-export default DragHandleWrapper;
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
new file mode 100644
index 0000000000..cff81a95f6
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js
@@ -0,0 +1,72 @@
+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 { Text } from '@strapi/design-system/Text';
+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/IconButtonCustoms.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/IconButtonCustoms.js
new file mode 100644
index 0000000000..665bf9b7c8
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/DraggedItem/IconButtonCustoms.js
@@ -0,0 +1,36 @@
+import styled from 'styled-components';
+import { IconButton } from '@strapi/design-system/IconButton';
+
+export const CustomIconButton = styled(IconButton)`
+ background-color: transparent;
+
+ svg {
+ path {
+ fill: ${({ theme, expanded }) =>
+ expanded ? theme.colors.primary600 : theme.colors.neutral600};
+ }
+ }
+
+ &:hover {
+ svg {
+ path {
+ fill: ${({ theme }) => theme.colors.primary600};
+ }
+ }
+ }
+`;
+
+export const DragHandleWrapper = styled(CustomIconButton)`
+ cursor: move;
+`;
+
+export const CustomIconButtonSibling = styled(IconButton)`
+ background-color: transparent;
+
+ svg {
+ path {
+ fill: ${({ theme, expanded }) =>
+ expanded ? theme.colors.primary600 : theme.colors.neutral600};
+ }
+ }
+`;
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 aa46268519..08055e08b6 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,20 +1,16 @@
import React from 'react';
import styled from 'styled-components';
-import { Box } from '@strapi/design-system/Box';
-import { Flex } from '@strapi/design-system/Flex';
-const StyledBox = styled(Box)`
- border: 1px dashed ${({ theme }) => theme.colors.primary500};
+const StyledSpan = styled.span`
+ display: block;
+ background-color: ${({ theme }) => theme.colors.primary100};
+ outline: 1px dashed ${({ theme }) => theme.colors.primary500};
+ outline-offset: -1px;
+ padding: ${({ theme }) => theme.spaces[6]};
`;
const Preview = () => {
- return (
-
-
-
-
-
- );
+ 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 4922aa1f04..07964c41ef 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
@@ -5,32 +5,21 @@ import { useDrag, useDrop } from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import { useIntl } from 'react-intl';
import toString from 'lodash/toString';
-import styled from 'styled-components';
import { Accordion, AccordionToggle, AccordionContent } from '@strapi/design-system/Accordion';
-import { Box } from '@strapi/design-system/Box';
-import { Flex } from '@strapi/design-system/Flex';
-import { IconButton } from '@strapi/design-system/IconButton';
import { Grid, GridItem } from '@strapi/design-system/Grid';
import { Stack } from '@strapi/design-system/Stack';
+import { Box } from '@strapi/design-system/Box';
import Trash from '@strapi/icons/Trash';
-import DragHandle from '@strapi/icons/Drag';
+import Drag from '@strapi/icons/Drag';
import ItemTypes from '../../../utils/ItemTypes';
import getTrad from '../../../utils/getTrad';
import Inputs from '../../Inputs';
import FieldComponent from '../../FieldComponent';
-import DragHandleWrapper from './DragHandleWrapper';
import Preview from './Preview';
+import DraggingSibling from './DraggingSibling';
+import { CustomIconButton, DragHandleWrapper } from './IconButtonCustoms';
import { connect, select } from './utils';
-// FIXME
-// Temporary workaround to remove the overflow until we migrate the react-select for the relations
-// to the DS one
-const StyledBox = styled(Box)`
- > div {
- overflow: visible;
- }
-`;
-
/* eslint-disable react/no-array-index-key */
// Issues:
@@ -44,8 +33,7 @@ const DraggedItem = ({
// hasErrors,
// hasMinError,
// isFirst,
-
- isOdd,
+ isDraggingSibling,
isOpen,
isReadOnly,
onClickToggle,
@@ -54,6 +42,7 @@ const DraggedItem = ({
// Retrieved from the select function
moveComponentField,
removeRepeatableField,
+ setIsDraggingSiblig,
triggerFormValidation,
// checkFormErrors,
displayedValue,
@@ -137,6 +126,7 @@ const DraggedItem = ({
end: () => {
// Update the errors
triggerFormValidation();
+ setIsDraggingSiblig(false);
},
collect: monitor => ({
isDragging: monitor.isDragging(),
@@ -147,6 +137,12 @@ const DraggedItem = ({
preview(getEmptyImage(), { captureDraggingState: false });
}, [preview]);
+ useEffect(() => {
+ if (isDragging) {
+ setIsDraggingSiblig(true);
+ }
+ }, [isDragging, setIsDraggingSiblig]);
+
// Create the refs
// We need 1 for the drop target
// 1 for the drag target
@@ -155,102 +151,180 @@ const DraggedItem = ({
dropRef: drop(dropRef),
};
+ const accordionTitle = toString(displayedValue);
+
return (
-
+
{isDragging && }
- {!isDragging && (
-
+ {!isDragging && isDraggingSibling && (
+
+ )}
+ {!isDragging && !isDraggingSibling && (
+
-
+ {
removeRepeatableField(componentFieldName);
toggleCollapses();
}}
label={formatMessage({
id: getTrad('containers.Edit.delete'),
- defaultMessage: 'Edit',
+ defaultMessage: 'Delete',
})}
icon={}
/>
-
- }
- />
-
-
+ }
+ label={formatMessage({
+ id: getTrad('components.DragHandle-label'),
+ defaultMessage: 'Drag',
+ })}
+ noBorder
+ ref={refs.dragRef}
+ />
+
)
}
+ title={accordionTitle}
+ togglePosition="left"
/>
-
-
- {fields.map((fieldRow, key) => {
- return (
-
- {fieldRow.map(({ name, fieldSchema, metadatas, queryInfos, size }) => {
- const isComponent = fieldSchema.type === 'component';
- const keys = `${componentFieldName}.${name}`;
+
+ {fields.map((fieldRow, key) => {
+ return (
+
+ {fieldRow.map(({ name, fieldSchema, metadatas, queryInfos, size }) => {
+ const isComponent = fieldSchema.type === 'component';
+ const keys = `${componentFieldName}.${name}`;
- if (isComponent) {
- const componentUid = fieldSchema.component;
-
- return (
-
-
-
- );
- }
+ if (isComponent) {
+ const componentUid = fieldSchema.component;
return (
-
-
+
);
- })}
-
- );
- })}
-
-
+ }
+
+ return (
+
+
+
+ );
+ })}
+
+ );
+ })}
+
)}
-
+
+ //
+ // {isDragging && }
+ // {!isDragging && (
+ //
+ //
+ // }
+ // />
+ //
+ // }
+ // />
+ //
+ //
+ // )
+ // }
+ // />
+ //
+ //
+ //
+ // {fields.map((fieldRow, key) => {
+ // return (
+ //
+ // {fieldRow.map(({ name, fieldSchema, metadatas, queryInfos, size }) => {
+ // const isComponent = fieldSchema.type === 'component';
+ // const keys = `${componentFieldName}.${name}`;
+
+ // if (isComponent) {
+ // const componentUid = fieldSchema.component;
+
+ // return (
+ //
+ //
+ //
+ // );
+ // }
+
+ // return (
+ //
+ //
+ //
+ // );
+ // })}
+ //
+ // );
+ // })}
+ //
+ //
+ //
+ //
+ // )}
+ //
);
};
@@ -259,7 +333,9 @@ DraggedItem.defaultProps = {
// hasErrors: false,
// hasMinError: false,
// isFirst: false,
+ isDraggingSibling: false,
isOpen: false,
+ setIsDraggingSiblig: () => {},
toggleCollapses: () => {},
};
@@ -269,7 +345,7 @@ DraggedItem.propTypes = {
// hasErrors: PropTypes.bool,
// hasMinError: PropTypes.bool,
// isFirst: PropTypes.bool,
- isOdd: PropTypes.bool.isRequired,
+ isDraggingSibling: PropTypes.bool,
isOpen: PropTypes.bool,
isReadOnly: PropTypes.bool.isRequired,
onClickToggle: PropTypes.func.isRequired,
@@ -277,6 +353,7 @@ DraggedItem.propTypes = {
toggleCollapses: PropTypes.func,
moveComponentField: PropTypes.func.isRequired,
removeRepeatableField: PropTypes.func.isRequired,
+ setIsDraggingSiblig: 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/index.js b/packages/core/admin/admin/src/content-manager/components/RepeatableComponent/index.js
index 99180fe420..fd42a9936f 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,12 +1,17 @@
import React, { memo, useCallback, useMemo, useState } from 'react';
/* eslint-disable import/no-cycle */
import { useDrop } from 'react-dnd';
+import { useIntl } from 'react-intl';
+import styled from 'styled-components';
import PropTypes from 'prop-types';
import get from 'lodash/get';
import take from 'lodash/take';
// import { FormattedMessage } from 'react-intl';
import { useNotification } 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 { ErrorMessage } from '@buffetjs/styles';
import { getMaxTempKey, getTrad } from '../../utils';
import { useContentTypeLayout } from '../../hooks';
@@ -14,8 +19,20 @@ import ItemTypes from '../../utils/ItemTypes';
import ComponentInitializer from '../ComponentInitializer';
import connect from './utils/connect';
import select from './utils/select';
-import Button from './AddFieldButton';
import DraggedItem from './DraggedItem';
+import AccordionGroupCustom from './AccordionGroupCustom';
+
+const TextButtonCustom = styled(TextButton)`
+ height: 100%;
+ width: 100%;
+ border-radius: 0 0 4px 4px;
+ display: flex;
+ justify-content: center;
+ span {
+ font-weight: 600;
+ font-size: 14px;
+ }
+`;
const RepeatableComponent = ({
addRepeatableComponentToField,
@@ -30,7 +47,9 @@ const RepeatableComponent = ({
name,
}) => {
const toggleNotification = useNotification();
+ const { formatMessage } = useIntl();
const [collapseToOpen, setCollapseToOpen] = useState('');
+ const [isDraggingSibling, setIsDraggingSiblig] = useState(false);
const [, drop] = useDrop({ accept: ItemTypes.COMPONENT });
const { getComponentLayout } = useContentTypeLayout();
const componentLayoutData = useMemo(() => getComponentLayout(componentUid), [
@@ -94,8 +113,19 @@ const RepeatableComponent = ({
}
return (
-
-
+
+
+ }>
+ {formatMessage({
+ id: getTrad('containers.EditView.add.new-entry'),
+ defaultMessage: 'Add an entry',
+ })}
+
+
+ }
+ >
{componentValue.map((data, index) => {
const key = data.__temp_key__;
const isOpen = collapseToOpen === key;
@@ -116,8 +146,8 @@ const RepeatableComponent = ({
doesPreviousFieldContainErrorsAndIsOpen={doesPreviousFieldContainErrorsAndIsOpen}
hasErrors={hasErrors}
hasMinError={hasMinError}
+ isDraggingSibling={isDraggingSibling}
isFirst={index === 0}
- isOdd={index % 2 === 1}
isOpen={isOpen}
isReadOnly={isReadOnly}
key={key}
@@ -130,24 +160,67 @@ const RepeatableComponent = ({
}}
parentName={name}
schema={componentLayoutData}
+ setIsDraggingSiblig={setIsDraggingSiblig}
toggleCollapses={toggleCollapses}
/>
);
})}
-
-
+ //
+ //
+ // {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 (
+ // {
+ // if (isOpen) {
+ // setCollapseToOpen('');
+ // } else {
+ // setCollapseToOpen(key);
+ // }
+ // }}
+ // parentName={name}
+ // schema={componentLayoutData}
+ // toggleCollapses={toggleCollapses}
+ // />
+ // );
+ // })}
+ //
+ //
);
// return (