Allow items re-order
[MAILPOET-2452]
This commit is contained in:
committed by
Rostislav Wolný
parent
72fe96f822
commit
c4012a54e9
@@ -103,7 +103,7 @@ SegmentSelectSettings.propTypes = {
|
|||||||
setNewSelection: PropTypes.func.isRequired,
|
setNewSelection: PropTypes.func.isRequired,
|
||||||
segmentsAddedIntoSelection: PropTypes.arrayOf(PropTypes.shape({
|
segmentsAddedIntoSelection: PropTypes.arrayOf(PropTypes.shape({
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
isChecked: PropTypes.boolean,
|
isChecked: PropTypes.bool,
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
}).isRequired).isRequired,
|
}).isRequired).isRequired,
|
||||||
};
|
};
|
||||||
|
@@ -1,28 +1,73 @@
|
|||||||
import React from 'react';
|
import React, { useRef, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CheckboxControl, Dashicon } from '@wordpress/components';
|
import { CheckboxControl, Dashicon } from '@wordpress/components';
|
||||||
import { partial } from 'lodash';
|
import { partial } from 'lodash';
|
||||||
|
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||||
|
import Backend from 'react-dnd-html5-backend';
|
||||||
|
|
||||||
const Preview = ({
|
const PreviewItem = ({
|
||||||
segments,
|
segment,
|
||||||
updateSegment,
|
index,
|
||||||
|
moveItem,
|
||||||
removeSegment,
|
removeSegment,
|
||||||
onSegmentsReorder,
|
onCheck,
|
||||||
}) => {
|
}) => {
|
||||||
if (segments.length === 0) {
|
const ref = useRef(null);
|
||||||
return null;
|
const [, drop] = useDrop({
|
||||||
|
accept: 'item',
|
||||||
|
hover(item, monitor) {
|
||||||
|
if (!ref.current) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
const dragIndex = item.index;
|
||||||
const onCheck = (segmentId, isChecked) => {
|
const hoverIndex = index;
|
||||||
const segment = segments.find((s) => s.id === segmentId);
|
// Don't replace items with themselves
|
||||||
segment.isChecked = isChecked;
|
if (dragIndex === hoverIndex) {
|
||||||
updateSegment(segment);
|
return;
|
||||||
};
|
}
|
||||||
|
// Determine rectangle on screen
|
||||||
return segments.map((segment) => (
|
const hoverBoundingRect = ref.current.getBoundingClientRect();
|
||||||
|
// Get vertical middle
|
||||||
|
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
||||||
|
// Determine mouse position
|
||||||
|
const clientOffset = monitor.getClientOffset();
|
||||||
|
// Get pixels to the top
|
||||||
|
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
||||||
|
// 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 && hoverClientY < hoverMiddleY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Dragging upwards
|
||||||
|
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Time to actually perform the action
|
||||||
|
moveItem(dragIndex, hoverIndex);
|
||||||
|
// Note: we're mutating the monitor item here!
|
||||||
|
// Generally it's better to avoid mutations,
|
||||||
|
// but it's good here for the sake of performance
|
||||||
|
// to avoid expensive index searches.
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
item.index = hoverIndex;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [{ isDragging }, drag] = useDrag({
|
||||||
|
item: { type: 'item', id: segment.id, index },
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragging: monitor.isDragging(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const opacity = isDragging ? 0.2 : 1;
|
||||||
|
drag(drop(ref));
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
className="mailpoet-form-segments-settings-list"
|
className="mailpoet-form-segments-settings-list"
|
||||||
key={segment.id}
|
key={segment.id}
|
||||||
|
ref={ref}
|
||||||
|
style={{ opacity }}
|
||||||
>
|
>
|
||||||
<CheckboxControl
|
<CheckboxControl
|
||||||
label={segment.name}
|
label={segment.name}
|
||||||
@@ -37,13 +82,68 @@ const Preview = ({
|
|||||||
onClick={partial(removeSegment, segment.id)}
|
onClick={partial(removeSegment, segment.id)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
));
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PreviewItem.propTypes = {
|
||||||
|
segment: PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
isChecked: PropTypes.bool,
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
onCheck: PropTypes.func.isRequired,
|
||||||
|
moveItem: PropTypes.func.isRequired,
|
||||||
|
removeSegment: PropTypes.func.isRequired,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Preview = ({
|
||||||
|
segments,
|
||||||
|
updateSegment,
|
||||||
|
removeSegment,
|
||||||
|
onSegmentsReorder,
|
||||||
|
}) => {
|
||||||
|
const moveItem = useCallback(
|
||||||
|
(dragIndex, hoverIndex) => {
|
||||||
|
const result = Array.from(segments);
|
||||||
|
const [removed] = result.splice(dragIndex, 1);
|
||||||
|
result.splice(hoverIndex, 0, removed);
|
||||||
|
|
||||||
|
onSegmentsReorder(result);
|
||||||
|
},
|
||||||
|
[segments, onSegmentsReorder],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (segments.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCheck = (segmentId, isChecked) => {
|
||||||
|
const segment = segments.find((s) => s.id === segmentId);
|
||||||
|
segment.isChecked = isChecked;
|
||||||
|
updateSegment(segment);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DndProvider backend={Backend}>
|
||||||
|
{segments.map((segment, index) => (
|
||||||
|
<PreviewItem
|
||||||
|
key={segment.id}
|
||||||
|
index={index}
|
||||||
|
segment={segment}
|
||||||
|
moveItem={moveItem}
|
||||||
|
onCheck={onCheck}
|
||||||
|
removeSegment={removeSegment}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</DndProvider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Preview.propTypes = {
|
Preview.propTypes = {
|
||||||
segments: PropTypes.arrayOf(PropTypes.shape({
|
segments: PropTypes.arrayOf(PropTypes.shape({
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
isChecked: PropTypes.boolean,
|
isChecked: PropTypes.bool,
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
}).isRequired).isRequired,
|
}).isRequired).isRequired,
|
||||||
updateSegment: PropTypes.func.isRequired,
|
updateSegment: PropTypes.func.isRequired,
|
||||||
|
Reference in New Issue
Block a user