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} /> ); })} - -