From bb53ba86383be9ea8fc859ade08f1e9d7af28364 Mon Sep 17 00:00:00 2001 From: ronronscelestes <71838159+ronronscelestes@users.noreply.github.com> Date: Fri, 3 Dec 2021 09:31:52 +0100 Subject: [PATCH] CM LV Settings Dnd fixes (#11755) * fixed draglayer for dnd * used drag sibling logic to remove hover state bug * updated snapshots --- .../components/DragLayer/index.js | 2 +- .../components/CardPreview.js | 27 ++- .../components/DraggableCard.js | 190 ++++++++++++------ .../components/SortDisplayedFields.js | 5 +- .../tests/__snapshots__/index.test.js.snap | 2 - 5 files changed, 147 insertions(+), 79 deletions(-) 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 da2cbbf586..69ccb9a67c 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 @@ -54,7 +54,7 @@ const CustomDragLayer = () => {
- {[ItemTypes.EDIT_RELATION, ItemTypes.EDIT_FIELD].includes(itemType) && ( + {[ItemTypes.EDIT_RELATION, ItemTypes.EDIT_FIELD, ItemTypes.FIELD].includes(itemType) && ( )} {itemType === ItemTypes.COMPONENT && ( diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/CardPreview.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/CardPreview.js index e4cdb4dec6..84f74baaff 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/CardPreview.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/CardPreview.js @@ -31,36 +31,40 @@ const DragButton = styled(ActionBox)` const FieldContainer = styled(Flex)` display: inline-flex; max-height: ${32 / 16}rem; - background-color: ${({ theme }) => theme.colors.primary100}; - border-color: ${({ theme }) => theme.colors.primary200}; + opacity: ${({ transparent }) => (transparent ? 0 : 1)}; + background-color: ${({ theme, isSibling }) => + isSibling ? theme.colors.neutral100 : theme.colors.primary100}; + border: 1px solid + ${({ theme, isSibling }) => (isSibling ? theme.colors.neutral150 : theme.colors.primary200)}; svg { width: ${10 / 16}rem; height: ${10 / 16}rem; path { - fill: ${({ theme }) => theme.colors.primary600}; + fill: ${({ theme, isSibling }) => (isSibling ? undefined : theme.colors.primary600)}; } } ${Typography} { - color: ${({ theme }) => theme.colors.primary600}; + color: ${({ theme, isSibling }) => (isSibling ? undefined : theme.colors.primary600)}; } ${DragButton} { - border-right: 1px solid ${({ theme }) => theme.colors.primary200}; + border-right: 1px solid + ${({ theme, isSibling }) => (isSibling ? theme.colors.neutral150 : theme.colors.primary200)}; } `; -const CardPreview = ({ labelField }) => { +const CardPreview = ({ labelField, transparent, isSibling }) => { const cardEllipsisTitle = ellipsisCardTitle(labelField); return ( @@ -80,8 +84,15 @@ const CardPreview = ({ labelField }) => { ); }; +CardPreview.defaultProps = { + isSibling: false, + transparent: false, +}; + CardPreview.propTypes = { + isSibling: PropTypes.bool, labelField: PropTypes.string.isRequired, + transparent: PropTypes.bool, }; export default CardPreview; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js index d35b094fcd..df3988998f 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; import PropTypes from 'prop-types'; import { useDrag, useDrop } from 'react-dnd'; @@ -11,6 +11,7 @@ import { Stack } from '@strapi/design-system/Stack'; import Pencil from '@strapi/icons/Pencil'; import Cross from '@strapi/icons/Cross'; import Drag from '@strapi/icons/Drag'; +import CardPreview from './CardPreview'; import ellipsisCardTitle from '../utils/ellipsisCardTitle'; import { getTrad, ItemTypes } from '../../../utils'; @@ -38,7 +39,6 @@ const DragButton = styled(ActionButton)` const FieldContainer = styled(Flex)` max-height: ${32 / 16}rem; cursor: pointer; - opacity: ${({ isDragging }) => (isDragging ? 0 : 1)}; svg { width: ${10 / 16}rem; @@ -77,14 +77,18 @@ const FieldWrapper = styled(Box)` const DraggableCard = ({ index, + isDraggingSibling, labelField, onClickEditField, onMoveField, onRemoveField, name, + setIsDraggingSibling, }) => { const { formatMessage } = useIntl(); - const ref = useRef(null); + const dragRef = useRef(null); + const dropRef = useRef(null); + const [, forceRerenderAfterDnd] = useState(false); const editButtonRef = useRef(); const cardEllipsisTitle = ellipsisCardTitle(labelField); @@ -96,8 +100,8 @@ const DraggableCard = ({ const [, drop] = useDrop({ accept: ItemTypes.FIELD, - hover(item) { - if (!ref.current) { + hover(item, monitor) { + if (!dropRef.current) { return; } const dragIndex = item.index; @@ -108,6 +112,27 @@ const DraggableCard = ({ return; } + // Determine rectangle on screen + const hoverBoundingRect = dropRef.current.getBoundingClientRect(); + // Get vertical middle + const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2; + // Determine mouse position + const clientOffset = monitor.getClientOffset(); + // Get pixels to the top + const hoverClientX = clientOffset.x - hoverBoundingRect.left; + + // 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 && hoverClientX > hoverMiddleX) { + return; + } + // Dragging upwards + if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) { + return; + } + onMoveField(dragIndex, hoverIndex); item.index = hoverIndex; @@ -122,86 +147,117 @@ const DraggableCard = ({ collect: monitor => ({ isDragging: monitor.isDragging(), }), + end: () => { + setIsDraggingSibling(false); + }, }); useEffect(() => { - preview(getEmptyImage(), { captureDraggingState: true }); + preview(getEmptyImage(), { captureDraggingState: false }); }, [preview]); - drag(drop(ref)); + 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), + }; return ( - - - - e.stopPropagation()} - ref={ref} - type="button" - > - - - {cardEllipsisTitle} - - - { - e.stopPropagation(); - onClickEditField(name); - }} - aria-label={formatMessage( - { - id: getTrad('components.DraggableCard.edit.field'), - defaultMessage: 'Edit {item}', - }, - { item: name } - )} - type="button" - > - - - - - - - + + {isDragging && } + {!isDragging && isDraggingSibling && } + + {!isDragging && !isDraggingSibling && ( + + + e.stopPropagation()} + ref={refs.dragRef} + type="button" + > + + + {cardEllipsisTitle} + + + { + e.stopPropagation(); + onClickEditField(name); + }} + aria-label={formatMessage( + { + id: getTrad('components.DraggableCard.edit.field'), + defaultMessage: 'Edit {item}', + }, + { item: name } + )} + type="button" + > + + + + + + + + )} ); }; DraggableCard.propTypes = { index: PropTypes.number.isRequired, + isDraggingSibling: PropTypes.bool.isRequired, labelField: PropTypes.string.isRequired, name: PropTypes.string.isRequired, onClickEditField: PropTypes.func.isRequired, onMoveField: PropTypes.func.isRequired, onRemoveField: PropTypes.func.isRequired, + setIsDraggingSibling: PropTypes.func.isRequired, }; export default DraggableCard; diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js index 264aacc273..e68ba58aba 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components'; import { PropTypes } from 'prop-types'; import { useIntl } from 'react-intl'; @@ -35,6 +35,7 @@ const SortDisplayedFields = ({ onRemoveField, }) => { const { formatMessage } = useIntl(); + const [isDraggingSibling, setIsDraggingSibling] = useState(false); return ( <> @@ -61,11 +62,13 @@ const SortDisplayedFields = ({ onRemoveField(e, index)} name={field} labelField={metadatas[field].list.label || field} + setIsDraggingSibling={setIsDraggingSibling} /> ))} diff --git a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/__snapshots__/index.test.js.snap index 5db9666a84..160935fe7b 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/__snapshots__/index.test.js.snap +++ b/packages/core/admin/admin/src/content-manager/pages/ListSettingsView/tests/__snapshots__/index.test.js.snap @@ -878,7 +878,6 @@ exports[`ADMIN | CM | LV | Configure the view renders and matches the snapshot 1 .c73 { max-height: 2rem; cursor: pointer; - opacity: 1; } .c73 svg { @@ -2766,7 +2765,6 @@ exports[`ADMIN | CM | LV | Configure the view should add field 1`] = ` .c73 { max-height: 2rem; cursor: pointer; - opacity: 1; } .c73 svg {