CM LV Settings Dnd fixes (#11755)

* fixed draglayer for dnd

* used drag sibling logic to remove hover state bug

* updated snapshots
This commit is contained in:
ronronscelestes 2021-12-03 09:31:52 +01:00 committed by GitHub
parent 11f3519710
commit bb53ba8638
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 147 additions and 79 deletions

View File

@ -54,7 +54,7 @@ const CustomDragLayer = () => {
<LayoutDndProvider>
<div style={layerStyles}>
<div style={getItemStyles(initialOffset, currentOffset, mouseOffset)} className="col-md-2">
{[ItemTypes.EDIT_RELATION, ItemTypes.EDIT_FIELD].includes(itemType) && (
{[ItemTypes.EDIT_RELATION, ItemTypes.EDIT_FIELD, ItemTypes.FIELD].includes(itemType) && (
<CardPreview labelField={item.labelField} />
)}
{itemType === ItemTypes.COMPONENT && (

View File

@ -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 (
<FieldContainer
borderColor="neutral150"
background="neutral100"
hasRadius
justifyContent="space-between"
transparent={transparent}
isSibling={isSibling}
>
<Stack horizontal size={3}>
<DragButton alignItems="center">
@ -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;

View File

@ -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 (
<FieldWrapper>
<FieldContainer
borderColor="neutral150"
background="neutral100"
hasRadius
justifyContent="space-between"
onClick={handleClickEditRow}
isDragging={isDragging}
>
<Stack horizontal size={3}>
<DragButton
aria-label={formatMessage(
{
id: getTrad('components.DraggableCard.move.field'),
defaultMessage: 'Move {item}',
},
{ item: name }
)}
onClick={e => e.stopPropagation()}
ref={ref}
type="button"
>
<Drag />
</DragButton>
<Typography fontWeight="bold">{cardEllipsisTitle}</Typography>
</Stack>
<Flex paddingLeft={3}>
<ActionButton
ref={editButtonRef}
onClick={e => {
e.stopPropagation();
onClickEditField(name);
}}
aria-label={formatMessage(
{
id: getTrad('components.DraggableCard.edit.field'),
defaultMessage: 'Edit {item}',
},
{ item: name }
)}
type="button"
>
<Pencil />
</ActionButton>
<ActionButton
onClick={onRemoveField}
data-testid={`delete-${name}`}
aria-label={formatMessage(
{
id: getTrad('components.DraggableCard.delete.field'),
defaultMessage: 'Delete {item}',
},
{ item: name }
)}
type="button"
>
<Cross />
</ActionButton>
</Flex>
</FieldContainer>
<FieldWrapper ref={refs ? refs.dropRef : null}>
{isDragging && <CardPreview transparent labelField={cardEllipsisTitle} />}
{!isDragging && isDraggingSibling && <CardPreview isSibling labelField={cardEllipsisTitle} />}
{!isDragging && !isDraggingSibling && (
<FieldContainer
borderColor="neutral150"
background="neutral100"
hasRadius
justifyContent="space-between"
onClick={handleClickEditRow}
isDragging={isDragging}
>
<Stack horizontal size={3}>
<DragButton
aria-label={formatMessage(
{
id: getTrad('components.DraggableCard.move.field'),
defaultMessage: 'Move {item}',
},
{ item: name }
)}
onClick={e => e.stopPropagation()}
ref={refs.dragRef}
type="button"
>
<Drag />
</DragButton>
<Typography fontWeight="bold">{cardEllipsisTitle}</Typography>
</Stack>
<Flex paddingLeft={3}>
<ActionButton
ref={editButtonRef}
onClick={e => {
e.stopPropagation();
onClickEditField(name);
}}
aria-label={formatMessage(
{
id: getTrad('components.DraggableCard.edit.field'),
defaultMessage: 'Edit {item}',
},
{ item: name }
)}
type="button"
>
<Pencil />
</ActionButton>
<ActionButton
onClick={onRemoveField}
data-testid={`delete-${name}`}
aria-label={formatMessage(
{
id: getTrad('components.DraggableCard.delete.field'),
defaultMessage: 'Delete {item}',
},
{ item: name }
)}
type="button"
>
<Cross />
</ActionButton>
</Flex>
</FieldContainer>
)}
</FieldWrapper>
);
};
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;

View File

@ -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 = ({
<DraggableCard
key={field}
index={index}
isDraggingSibling={isDraggingSibling}
onMoveField={onMoveField}
onClickEditField={onClickEditField}
onRemoveField={e => onRemoveField(e, index)}
name={field}
labelField={metadatas[field].list.label || field}
setIsDraggingSibling={setIsDraggingSibling}
/>
))}
</Stack>

View File

@ -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 {