mirror of
https://github.com/strapi/strapi.git
synced 2025-11-15 09:39:15 +00:00
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:
parent
11f3519710
commit
bb53ba8638
@ -54,7 +54,7 @@ const CustomDragLayer = () => {
|
|||||||
<LayoutDndProvider>
|
<LayoutDndProvider>
|
||||||
<div style={layerStyles}>
|
<div style={layerStyles}>
|
||||||
<div style={getItemStyles(initialOffset, currentOffset, mouseOffset)} className="col-md-2">
|
<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} />
|
<CardPreview labelField={item.labelField} />
|
||||||
)}
|
)}
|
||||||
{itemType === ItemTypes.COMPONENT && (
|
{itemType === ItemTypes.COMPONENT && (
|
||||||
|
|||||||
@ -31,36 +31,40 @@ const DragButton = styled(ActionBox)`
|
|||||||
const FieldContainer = styled(Flex)`
|
const FieldContainer = styled(Flex)`
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
max-height: ${32 / 16}rem;
|
max-height: ${32 / 16}rem;
|
||||||
background-color: ${({ theme }) => theme.colors.primary100};
|
opacity: ${({ transparent }) => (transparent ? 0 : 1)};
|
||||||
border-color: ${({ theme }) => theme.colors.primary200};
|
background-color: ${({ theme, isSibling }) =>
|
||||||
|
isSibling ? theme.colors.neutral100 : theme.colors.primary100};
|
||||||
|
border: 1px solid
|
||||||
|
${({ theme, isSibling }) => (isSibling ? theme.colors.neutral150 : theme.colors.primary200)};
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: ${10 / 16}rem;
|
width: ${10 / 16}rem;
|
||||||
height: ${10 / 16}rem;
|
height: ${10 / 16}rem;
|
||||||
|
|
||||||
path {
|
path {
|
||||||
fill: ${({ theme }) => theme.colors.primary600};
|
fill: ${({ theme, isSibling }) => (isSibling ? undefined : theme.colors.primary600)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${Typography} {
|
${Typography} {
|
||||||
color: ${({ theme }) => theme.colors.primary600};
|
color: ${({ theme, isSibling }) => (isSibling ? undefined : theme.colors.primary600)};
|
||||||
}
|
}
|
||||||
|
|
||||||
${DragButton} {
|
${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);
|
const cardEllipsisTitle = ellipsisCardTitle(labelField);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FieldContainer
|
<FieldContainer
|
||||||
borderColor="neutral150"
|
|
||||||
background="neutral100"
|
|
||||||
hasRadius
|
hasRadius
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
|
transparent={transparent}
|
||||||
|
isSibling={isSibling}
|
||||||
>
|
>
|
||||||
<Stack horizontal size={3}>
|
<Stack horizontal size={3}>
|
||||||
<DragButton alignItems="center">
|
<DragButton alignItems="center">
|
||||||
@ -80,8 +84,15 @@ const CardPreview = ({ labelField }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CardPreview.defaultProps = {
|
||||||
|
isSibling: false,
|
||||||
|
transparent: false,
|
||||||
|
};
|
||||||
|
|
||||||
CardPreview.propTypes = {
|
CardPreview.propTypes = {
|
||||||
|
isSibling: PropTypes.bool,
|
||||||
labelField: PropTypes.string.isRequired,
|
labelField: PropTypes.string.isRequired,
|
||||||
|
transparent: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CardPreview;
|
export default CardPreview;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useDrag, useDrop } from 'react-dnd';
|
import { useDrag, useDrop } from 'react-dnd';
|
||||||
@ -11,6 +11,7 @@ import { Stack } from '@strapi/design-system/Stack';
|
|||||||
import Pencil from '@strapi/icons/Pencil';
|
import Pencil from '@strapi/icons/Pencil';
|
||||||
import Cross from '@strapi/icons/Cross';
|
import Cross from '@strapi/icons/Cross';
|
||||||
import Drag from '@strapi/icons/Drag';
|
import Drag from '@strapi/icons/Drag';
|
||||||
|
import CardPreview from './CardPreview';
|
||||||
import ellipsisCardTitle from '../utils/ellipsisCardTitle';
|
import ellipsisCardTitle from '../utils/ellipsisCardTitle';
|
||||||
import { getTrad, ItemTypes } from '../../../utils';
|
import { getTrad, ItemTypes } from '../../../utils';
|
||||||
|
|
||||||
@ -38,7 +39,6 @@ const DragButton = styled(ActionButton)`
|
|||||||
const FieldContainer = styled(Flex)`
|
const FieldContainer = styled(Flex)`
|
||||||
max-height: ${32 / 16}rem;
|
max-height: ${32 / 16}rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: ${({ isDragging }) => (isDragging ? 0 : 1)};
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: ${10 / 16}rem;
|
width: ${10 / 16}rem;
|
||||||
@ -77,14 +77,18 @@ const FieldWrapper = styled(Box)`
|
|||||||
|
|
||||||
const DraggableCard = ({
|
const DraggableCard = ({
|
||||||
index,
|
index,
|
||||||
|
isDraggingSibling,
|
||||||
labelField,
|
labelField,
|
||||||
onClickEditField,
|
onClickEditField,
|
||||||
onMoveField,
|
onMoveField,
|
||||||
onRemoveField,
|
onRemoveField,
|
||||||
name,
|
name,
|
||||||
|
setIsDraggingSibling,
|
||||||
}) => {
|
}) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const ref = useRef(null);
|
const dragRef = useRef(null);
|
||||||
|
const dropRef = useRef(null);
|
||||||
|
const [, forceRerenderAfterDnd] = useState(false);
|
||||||
const editButtonRef = useRef();
|
const editButtonRef = useRef();
|
||||||
const cardEllipsisTitle = ellipsisCardTitle(labelField);
|
const cardEllipsisTitle = ellipsisCardTitle(labelField);
|
||||||
|
|
||||||
@ -96,8 +100,8 @@ const DraggableCard = ({
|
|||||||
|
|
||||||
const [, drop] = useDrop({
|
const [, drop] = useDrop({
|
||||||
accept: ItemTypes.FIELD,
|
accept: ItemTypes.FIELD,
|
||||||
hover(item) {
|
hover(item, monitor) {
|
||||||
if (!ref.current) {
|
if (!dropRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dragIndex = item.index;
|
const dragIndex = item.index;
|
||||||
@ -108,6 +112,27 @@ const DraggableCard = ({
|
|||||||
return;
|
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);
|
onMoveField(dragIndex, hoverIndex);
|
||||||
|
|
||||||
item.index = hoverIndex;
|
item.index = hoverIndex;
|
||||||
@ -122,86 +147,117 @@ const DraggableCard = ({
|
|||||||
collect: monitor => ({
|
collect: monitor => ({
|
||||||
isDragging: monitor.isDragging(),
|
isDragging: monitor.isDragging(),
|
||||||
}),
|
}),
|
||||||
|
end: () => {
|
||||||
|
setIsDraggingSibling(false);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
preview(getEmptyImage(), { captureDraggingState: true });
|
preview(getEmptyImage(), { captureDraggingState: false });
|
||||||
}, [preview]);
|
}, [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 (
|
return (
|
||||||
<FieldWrapper>
|
<FieldWrapper ref={refs ? refs.dropRef : null}>
|
||||||
<FieldContainer
|
{isDragging && <CardPreview transparent labelField={cardEllipsisTitle} />}
|
||||||
borderColor="neutral150"
|
{!isDragging && isDraggingSibling && <CardPreview isSibling labelField={cardEllipsisTitle} />}
|
||||||
background="neutral100"
|
|
||||||
hasRadius
|
{!isDragging && !isDraggingSibling && (
|
||||||
justifyContent="space-between"
|
<FieldContainer
|
||||||
onClick={handleClickEditRow}
|
borderColor="neutral150"
|
||||||
isDragging={isDragging}
|
background="neutral100"
|
||||||
>
|
hasRadius
|
||||||
<Stack horizontal size={3}>
|
justifyContent="space-between"
|
||||||
<DragButton
|
onClick={handleClickEditRow}
|
||||||
aria-label={formatMessage(
|
isDragging={isDragging}
|
||||||
{
|
>
|
||||||
id: getTrad('components.DraggableCard.move.field'),
|
<Stack horizontal size={3}>
|
||||||
defaultMessage: 'Move {item}',
|
<DragButton
|
||||||
},
|
aria-label={formatMessage(
|
||||||
{ item: name }
|
{
|
||||||
)}
|
id: getTrad('components.DraggableCard.move.field'),
|
||||||
onClick={e => e.stopPropagation()}
|
defaultMessage: 'Move {item}',
|
||||||
ref={ref}
|
},
|
||||||
type="button"
|
{ item: name }
|
||||||
>
|
)}
|
||||||
<Drag />
|
onClick={e => e.stopPropagation()}
|
||||||
</DragButton>
|
ref={refs.dragRef}
|
||||||
<Typography fontWeight="bold">{cardEllipsisTitle}</Typography>
|
type="button"
|
||||||
</Stack>
|
>
|
||||||
<Flex paddingLeft={3}>
|
<Drag />
|
||||||
<ActionButton
|
</DragButton>
|
||||||
ref={editButtonRef}
|
<Typography fontWeight="bold">{cardEllipsisTitle}</Typography>
|
||||||
onClick={e => {
|
</Stack>
|
||||||
e.stopPropagation();
|
<Flex paddingLeft={3}>
|
||||||
onClickEditField(name);
|
<ActionButton
|
||||||
}}
|
ref={editButtonRef}
|
||||||
aria-label={formatMessage(
|
onClick={e => {
|
||||||
{
|
e.stopPropagation();
|
||||||
id: getTrad('components.DraggableCard.edit.field'),
|
onClickEditField(name);
|
||||||
defaultMessage: 'Edit {item}',
|
}}
|
||||||
},
|
aria-label={formatMessage(
|
||||||
{ item: name }
|
{
|
||||||
)}
|
id: getTrad('components.DraggableCard.edit.field'),
|
||||||
type="button"
|
defaultMessage: 'Edit {item}',
|
||||||
>
|
},
|
||||||
<Pencil />
|
{ item: name }
|
||||||
</ActionButton>
|
)}
|
||||||
<ActionButton
|
type="button"
|
||||||
onClick={onRemoveField}
|
>
|
||||||
data-testid={`delete-${name}`}
|
<Pencil />
|
||||||
aria-label={formatMessage(
|
</ActionButton>
|
||||||
{
|
<ActionButton
|
||||||
id: getTrad('components.DraggableCard.delete.field'),
|
onClick={onRemoveField}
|
||||||
defaultMessage: 'Delete {item}',
|
data-testid={`delete-${name}`}
|
||||||
},
|
aria-label={formatMessage(
|
||||||
{ item: name }
|
{
|
||||||
)}
|
id: getTrad('components.DraggableCard.delete.field'),
|
||||||
type="button"
|
defaultMessage: 'Delete {item}',
|
||||||
>
|
},
|
||||||
<Cross />
|
{ item: name }
|
||||||
</ActionButton>
|
)}
|
||||||
</Flex>
|
type="button"
|
||||||
</FieldContainer>
|
>
|
||||||
|
<Cross />
|
||||||
|
</ActionButton>
|
||||||
|
</Flex>
|
||||||
|
</FieldContainer>
|
||||||
|
)}
|
||||||
</FieldWrapper>
|
</FieldWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
DraggableCard.propTypes = {
|
DraggableCard.propTypes = {
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
|
isDraggingSibling: PropTypes.bool.isRequired,
|
||||||
labelField: PropTypes.string.isRequired,
|
labelField: PropTypes.string.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
onClickEditField: PropTypes.func.isRequired,
|
onClickEditField: PropTypes.func.isRequired,
|
||||||
onMoveField: PropTypes.func.isRequired,
|
onMoveField: PropTypes.func.isRequired,
|
||||||
onRemoveField: PropTypes.func.isRequired,
|
onRemoveField: PropTypes.func.isRequired,
|
||||||
|
setIsDraggingSibling: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DraggableCard;
|
export default DraggableCard;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { PropTypes } from 'prop-types';
|
import { PropTypes } from 'prop-types';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
@ -35,6 +35,7 @@ const SortDisplayedFields = ({
|
|||||||
onRemoveField,
|
onRemoveField,
|
||||||
}) => {
|
}) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const [isDraggingSibling, setIsDraggingSibling] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -61,11 +62,13 @@ const SortDisplayedFields = ({
|
|||||||
<DraggableCard
|
<DraggableCard
|
||||||
key={field}
|
key={field}
|
||||||
index={index}
|
index={index}
|
||||||
|
isDraggingSibling={isDraggingSibling}
|
||||||
onMoveField={onMoveField}
|
onMoveField={onMoveField}
|
||||||
onClickEditField={onClickEditField}
|
onClickEditField={onClickEditField}
|
||||||
onRemoveField={e => onRemoveField(e, index)}
|
onRemoveField={e => onRemoveField(e, index)}
|
||||||
name={field}
|
name={field}
|
||||||
labelField={metadatas[field].list.label || field}
|
labelField={metadatas[field].list.label || field}
|
||||||
|
setIsDraggingSibling={setIsDraggingSibling}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@ -878,7 +878,6 @@ exports[`ADMIN | CM | LV | Configure the view renders and matches the snapshot 1
|
|||||||
.c73 {
|
.c73 {
|
||||||
max-height: 2rem;
|
max-height: 2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.c73 svg {
|
.c73 svg {
|
||||||
@ -2766,7 +2765,6 @@ exports[`ADMIN | CM | LV | Configure the view should add field 1`] = `
|
|||||||
.c73 {
|
.c73 {
|
||||||
max-height: 2rem;
|
max-height: 2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.c73 svg {
|
.c73 svg {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user