diff --git a/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js b/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js index d77e66ac21..1923965456 100644 --- a/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js +++ b/packages/core/admin/admin/src/content-manager/components/DragLayer/index.js @@ -55,6 +55,7 @@ const CustomDragLayer = () => {
{itemType === ItemTypes.FIELD && } + {itemType === ItemTypes.EDIT_RELATION && } {itemType === ItemTypes.COMPONENT && ( )} diff --git a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js index c68ae73695..0cfa51b2a5 100644 --- a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +++ b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js @@ -11,14 +11,14 @@ import { Flex } from '@strapi/design-system/Flex'; import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden'; import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu'; import Plus from '@strapi/icons/Plus'; -import { getTrad } from '../../../utils'; +import { getTrad, ItemTypes } from '../../../utils'; import { useLayoutDnd } from '../../../hooks'; import FieldButton from './FieldButton'; import LinkToCTB from './LinkToCTB'; const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, onAddField }) => { const { formatMessage } = useIntl(); - const { setEditFieldToSelect, attributes, modifiedData } = useLayoutDnd(); + const { setEditFieldToSelect, attributes, modifiedData, onMoveField } = useLayoutDnd(); return ( @@ -32,15 +32,14 @@ const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, })} - {/* Since the drag n drop will not be available, this text will be hidden for the moment */} - {/* + {formatMessage({ id: 'containers.SettingPage.editSettings.description', defaultMessage: 'Drag & drop the fields to build the layout', })} - */} +
@@ -63,6 +62,10 @@ const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, onEditField={() => setEditFieldToSelect(rowItem.name)} onDeleteField={() => onRemoveField(row.rowId, index)} attribute={attribute} + itemType={ItemTypes.EDIT_RELATION} + index={index} + name={rowItem.name} + onMoveField={onMoveField} > {attributeLabel || rowItem.name} diff --git a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/FieldButton.js b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/FieldButton.js index 1800daeabb..8913dc1499 100644 --- a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/FieldButton.js +++ b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/FieldButton.js @@ -1,6 +1,9 @@ -import React from 'react'; +import React, { useRef, useEffect } from 'react'; import styled from 'styled-components'; import PropTypes from 'prop-types'; +import { useDrop, useDrag } from 'react-dnd'; +import { getEmptyImage } from 'react-dnd-html5-backend'; +import { useIntl } from 'react-intl'; import { Box } from '@strapi/design-system/Box'; import { Flex } from '@strapi/design-system/Flex'; import { IconButton } from '@strapi/design-system/IconButton'; @@ -8,10 +11,9 @@ import { Typography } from '@strapi/design-system/Typography'; import Drag from '@strapi/icons/Drag'; import Pencil from '@strapi/icons/Pencil'; import Trash from '@strapi/icons/Trash'; -import { useIntl } from 'react-intl'; +import { getTrad } from '../../../utils'; import ComponentFieldList from './ComponentFieldList'; import DynamicZoneList from './DynamicZoneList'; -import getTrad from '../../../utils/getTrad'; const CustomIconButton = styled(IconButton)` background-color: transparent; @@ -27,10 +29,63 @@ const CustomDragIcon = styled(Drag)` } `; const CustomFlex = styled(Flex)` + opacity: ${({ isDragging }) => (isDragging ? 0 : 1)}; +`; +const DragButton = styled(Flex)` + cursor: all-scroll; border-right: 1px solid ${({ theme }) => theme.colors.neutral200}; `; -const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => { +const FieldButton = ({ + attribute, + onEditField, + onDeleteField, + children, + index, + itemType, + name, + onMoveField, +}) => { + const dragButtonRef = useRef(); + + const [, drop] = useDrop({ + accept: itemType, + hover(item) { + if (!dragButtonRef.current) { + return; + } + const dragIndex = item.index; + const hoverIndex = index; + + // Don't replace items with themselves + if (dragIndex === hoverIndex) { + return; + } + + console.log(hoverIndex); + + onMoveField(dragIndex, hoverIndex); + + item.index = hoverIndex; + }, + }); + + const [{ isDragging }, drag, dragPreview] = useDrag({ + type: itemType, + item: () => { + return { index, labelField: children, name }; + }, + collect: monitor => ({ + isDragging: monitor.isDragging(), + }), + }); + + useEffect(() => { + dragPreview(getEmptyImage(), { captureDraggingState: true }); + }, [dragPreview]); + + drag(drop(dragButtonRef)); + const { formatMessage } = useIntl(); const getHeight = () => { const higherFields = ['json', 'text', 'file', 'media', 'component', 'richtext', 'dynamiczone']; @@ -43,17 +98,28 @@ const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => { }; return ( - - + e.stopPropagation()} + alignItems="center" + paddingLeft={3} + paddingRight={3} + // Disable the keyboard navigation since the drag n drop isn't accessible with the keyboard for the moment + tabIndex={-1} + > - + @@ -96,7 +162,7 @@ const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => { )} {attribute?.type === 'dynamiczone' && } - + ); }; @@ -113,6 +179,10 @@ FieldButton.propTypes = { onEditField: PropTypes.func.isRequired, onDeleteField: PropTypes.func.isRequired, children: PropTypes.string.isRequired, + index: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + itemType: PropTypes.string.isRequired, + onMoveField: PropTypes.func.isRequired, }; export default FieldButton; diff --git a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js index 639c1aa438..c4f05d1fb3 100644 --- a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js +++ b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js @@ -8,9 +8,9 @@ import { Typography } from '@strapi/design-system/Typography'; import { Stack } from '@strapi/design-system/Stack'; import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu'; import Plus from '@strapi/icons/Plus'; -import { getTrad } from '../../../utils'; -import FieldButton from './FieldButton'; +import { getTrad, ItemTypes } from '../../../utils'; import { useLayoutDnd } from '../../../hooks'; +import FieldButton from './FieldButton'; const RelationalFields = ({ relationsLayout, @@ -19,7 +19,7 @@ const RelationalFields = ({ onAddField, }) => { const { formatMessage } = useIntl(); - const { setEditFieldToSelect, modifiedData } = useLayoutDnd(); + const { setEditFieldToSelect, modifiedData, onMoveRelation } = useLayoutDnd(); return ( @@ -56,6 +56,10 @@ const RelationalFields = ({ onEditField={() => setEditFieldToSelect(relationName)} onDeleteField={() => onRemoveField(index)} key={relationName} + index={index} + name={relationName} + onMoveField={onMoveRelation} + itemType={ItemTypes.EDIT_RELATION} > {relationLabel || relationName} diff --git a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/index.js b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/index.js index 137b2d10d6..04c236083c 100644 --- a/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/EditSettingsView/index.js @@ -151,6 +151,22 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd submitMutation.mutate(body); }; + const handleMoveRelation = (fromIndex, toIndex) => { + dispatch({ + type: 'MOVE_RELATION', + fromIndex, + toIndex, + }); + }; + + const handleMoveField = (fromIndex, toIndex) => { + dispatch({ + type: 'MOVE_FIELD', + fromIndex, + toIndex, + }); + }; + return ( { dispatch({ type: 'SET_FIELD_TO_EDIT',