mirror of
https://github.com/strapi/strapi.git
synced 2025-11-11 15:49:50 +00:00
Add relation dnd
Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
parent
9bd2011f61
commit
6e77d7699e
@ -55,6 +55,7 @@ const CustomDragLayer = () => {
|
|||||||
<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">
|
||||||
{itemType === ItemTypes.FIELD && <CardPreview labelField={item.labelField} />}
|
{itemType === ItemTypes.FIELD && <CardPreview labelField={item.labelField} />}
|
||||||
|
{itemType === ItemTypes.EDIT_RELATION && <CardPreview labelField={item.labelField} />}
|
||||||
{itemType === ItemTypes.COMPONENT && (
|
{itemType === ItemTypes.COMPONENT && (
|
||||||
<RepeatableComponentPreview displayedValue={item.displayedValue} />
|
<RepeatableComponentPreview displayedValue={item.displayedValue} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -11,14 +11,14 @@ import { Flex } from '@strapi/design-system/Flex';
|
|||||||
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
import { VisuallyHidden } from '@strapi/design-system/VisuallyHidden';
|
||||||
import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
|
import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
|
||||||
import Plus from '@strapi/icons/Plus';
|
import Plus from '@strapi/icons/Plus';
|
||||||
import { getTrad } from '../../../utils';
|
import { getTrad, ItemTypes } from '../../../utils';
|
||||||
import { useLayoutDnd } from '../../../hooks';
|
import { useLayoutDnd } from '../../../hooks';
|
||||||
import FieldButton from './FieldButton';
|
import FieldButton from './FieldButton';
|
||||||
import LinkToCTB from './LinkToCTB';
|
import LinkToCTB from './LinkToCTB';
|
||||||
|
|
||||||
const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, onAddField }) => {
|
const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField, onAddField }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { setEditFieldToSelect, attributes, modifiedData } = useLayoutDnd();
|
const { setEditFieldToSelect, attributes, modifiedData, onMoveField } = useLayoutDnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack size={4}>
|
<Stack size={4}>
|
||||||
@ -32,15 +32,14 @@ const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField,
|
|||||||
})}
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{/* Since the drag n drop will not be available, this text will be hidden for the moment */}
|
<Box>
|
||||||
{/* <Box>
|
|
||||||
<Typography variant="pi" textColor="neutral600">
|
<Typography variant="pi" textColor="neutral600">
|
||||||
{formatMessage({
|
{formatMessage({
|
||||||
id: 'containers.SettingPage.editSettings.description',
|
id: 'containers.SettingPage.editSettings.description',
|
||||||
defaultMessage: 'Drag & drop the fields to build the layout',
|
defaultMessage: 'Drag & drop the fields to build the layout',
|
||||||
})}
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box> */}
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
<LinkToCTB />
|
<LinkToCTB />
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -63,6 +62,10 @@ const DisplayedFields = ({ editLayout, editLayoutRemainingFields, onRemoveField,
|
|||||||
onEditField={() => setEditFieldToSelect(rowItem.name)}
|
onEditField={() => setEditFieldToSelect(rowItem.name)}
|
||||||
onDeleteField={() => onRemoveField(row.rowId, index)}
|
onDeleteField={() => onRemoveField(row.rowId, index)}
|
||||||
attribute={attribute}
|
attribute={attribute}
|
||||||
|
itemType={ItemTypes.EDIT_RELATION}
|
||||||
|
index={index}
|
||||||
|
name={rowItem.name}
|
||||||
|
onMoveField={onMoveField}
|
||||||
>
|
>
|
||||||
{attributeLabel || rowItem.name}
|
{attributeLabel || rowItem.name}
|
||||||
</FieldButton>
|
</FieldButton>
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import React from 'react';
|
import React, { useRef, useEffect } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
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 { Box } from '@strapi/design-system/Box';
|
||||||
import { Flex } from '@strapi/design-system/Flex';
|
import { Flex } from '@strapi/design-system/Flex';
|
||||||
import { IconButton } from '@strapi/design-system/IconButton';
|
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 Drag from '@strapi/icons/Drag';
|
||||||
import Pencil from '@strapi/icons/Pencil';
|
import Pencil from '@strapi/icons/Pencil';
|
||||||
import Trash from '@strapi/icons/Trash';
|
import Trash from '@strapi/icons/Trash';
|
||||||
import { useIntl } from 'react-intl';
|
import { getTrad } from '../../../utils';
|
||||||
import ComponentFieldList from './ComponentFieldList';
|
import ComponentFieldList from './ComponentFieldList';
|
||||||
import DynamicZoneList from './DynamicZoneList';
|
import DynamicZoneList from './DynamicZoneList';
|
||||||
import getTrad from '../../../utils/getTrad';
|
|
||||||
|
|
||||||
const CustomIconButton = styled(IconButton)`
|
const CustomIconButton = styled(IconButton)`
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@ -27,10 +29,63 @@ const CustomDragIcon = styled(Drag)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const CustomFlex = styled(Flex)`
|
const CustomFlex = styled(Flex)`
|
||||||
|
opacity: ${({ isDragging }) => (isDragging ? 0 : 1)};
|
||||||
|
`;
|
||||||
|
const DragButton = styled(Flex)`
|
||||||
|
cursor: all-scroll;
|
||||||
border-right: 1px solid ${({ theme }) => theme.colors.neutral200};
|
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 { formatMessage } = useIntl();
|
||||||
const getHeight = () => {
|
const getHeight = () => {
|
||||||
const higherFields = ['json', 'text', 'file', 'media', 'component', 'richtext', 'dynamiczone'];
|
const higherFields = ['json', 'text', 'file', 'media', 'component', 'richtext', 'dynamiczone'];
|
||||||
@ -43,17 +98,28 @@ const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<CustomFlex
|
||||||
width="100%"
|
width="100%"
|
||||||
borderColor="neutral150"
|
borderColor="neutral150"
|
||||||
hasRadius
|
hasRadius
|
||||||
background="neutral100"
|
background="neutral100"
|
||||||
minHeight={getHeight()}
|
minHeight={getHeight()}
|
||||||
alignItems="stretch"
|
alignItems="stretch"
|
||||||
|
isDragging={isDragging}
|
||||||
>
|
>
|
||||||
<CustomFlex alignItems="center" paddingLeft={3} paddingRight={3}>
|
<DragButton
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
ref={dragButtonRef}
|
||||||
|
onClick={e => 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}
|
||||||
|
>
|
||||||
<CustomDragIcon />
|
<CustomDragIcon />
|
||||||
</CustomFlex>
|
</DragButton>
|
||||||
<Box overflow="hidden" width="100%">
|
<Box overflow="hidden" width="100%">
|
||||||
<Flex paddingLeft={3} alignItems="baseline" justifyContent="space-between">
|
<Flex paddingLeft={3} alignItems="baseline" justifyContent="space-between">
|
||||||
<Box>
|
<Box>
|
||||||
@ -96,7 +162,7 @@ const FieldButton = ({ attribute, onEditField, onDeleteField, children }) => {
|
|||||||
)}
|
)}
|
||||||
{attribute?.type === 'dynamiczone' && <DynamicZoneList components={attribute.components} />}
|
{attribute?.type === 'dynamiczone' && <DynamicZoneList components={attribute.components} />}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</CustomFlex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -113,6 +179,10 @@ FieldButton.propTypes = {
|
|||||||
onEditField: PropTypes.func.isRequired,
|
onEditField: PropTypes.func.isRequired,
|
||||||
onDeleteField: PropTypes.func.isRequired,
|
onDeleteField: PropTypes.func.isRequired,
|
||||||
children: PropTypes.string.isRequired,
|
children: PropTypes.string.isRequired,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
itemType: PropTypes.string.isRequired,
|
||||||
|
onMoveField: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default FieldButton;
|
export default FieldButton;
|
||||||
|
|||||||
@ -8,9 +8,9 @@ import { Typography } from '@strapi/design-system/Typography';
|
|||||||
import { Stack } from '@strapi/design-system/Stack';
|
import { Stack } from '@strapi/design-system/Stack';
|
||||||
import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
|
import { SimpleMenu, MenuItem } from '@strapi/design-system/SimpleMenu';
|
||||||
import Plus from '@strapi/icons/Plus';
|
import Plus from '@strapi/icons/Plus';
|
||||||
import { getTrad } from '../../../utils';
|
import { getTrad, ItemTypes } from '../../../utils';
|
||||||
import FieldButton from './FieldButton';
|
|
||||||
import { useLayoutDnd } from '../../../hooks';
|
import { useLayoutDnd } from '../../../hooks';
|
||||||
|
import FieldButton from './FieldButton';
|
||||||
|
|
||||||
const RelationalFields = ({
|
const RelationalFields = ({
|
||||||
relationsLayout,
|
relationsLayout,
|
||||||
@ -19,7 +19,7 @@ const RelationalFields = ({
|
|||||||
onAddField,
|
onAddField,
|
||||||
}) => {
|
}) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { setEditFieldToSelect, modifiedData } = useLayoutDnd();
|
const { setEditFieldToSelect, modifiedData, onMoveRelation } = useLayoutDnd();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack size={4}>
|
<Stack size={4}>
|
||||||
@ -56,6 +56,10 @@ const RelationalFields = ({
|
|||||||
onEditField={() => setEditFieldToSelect(relationName)}
|
onEditField={() => setEditFieldToSelect(relationName)}
|
||||||
onDeleteField={() => onRemoveField(index)}
|
onDeleteField={() => onRemoveField(index)}
|
||||||
key={relationName}
|
key={relationName}
|
||||||
|
index={index}
|
||||||
|
name={relationName}
|
||||||
|
onMoveField={onMoveRelation}
|
||||||
|
itemType={ItemTypes.EDIT_RELATION}
|
||||||
>
|
>
|
||||||
{relationLabel || relationName}
|
{relationLabel || relationName}
|
||||||
</FieldButton>
|
</FieldButton>
|
||||||
|
|||||||
@ -151,6 +151,22 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
|
|||||||
submitMutation.mutate(body);
|
submitMutation.mutate(body);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMoveRelation = (fromIndex, toIndex) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'MOVE_RELATION',
|
||||||
|
fromIndex,
|
||||||
|
toIndex,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMoveField = (fromIndex, toIndex) => {
|
||||||
|
dispatch({
|
||||||
|
type: 'MOVE_FIELD',
|
||||||
|
fromIndex,
|
||||||
|
toIndex,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutDndProvider
|
<LayoutDndProvider
|
||||||
isContentTypeView={isContentTypeView}
|
isContentTypeView={isContentTypeView}
|
||||||
@ -160,6 +176,8 @@ const EditSettingsView = ({ mainLayout, components, isContentTypeView, slug, upd
|
|||||||
componentLayouts={componentLayouts}
|
componentLayouts={componentLayouts}
|
||||||
selectedField={metaToEdit}
|
selectedField={metaToEdit}
|
||||||
fieldForm={metaForm}
|
fieldForm={metaForm}
|
||||||
|
onMoveRelation={handleMoveRelation}
|
||||||
|
onMoveField={handleMoveField}
|
||||||
setEditFieldToSelect={name => {
|
setEditFieldToSelect={name => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_FIELD_TO_EDIT',
|
type: 'SET_FIELD_TO_EDIT',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user