Allow items re-order

[MAILPOET-2452]
This commit is contained in:
Pavel Dohnal
2019-12-10 14:22:58 +01:00
committed by Rostislav Wolný
parent 72fe96f822
commit c4012a54e9
2 changed files with 119 additions and 19 deletions

View File

@@ -103,7 +103,7 @@ SegmentSelectSettings.propTypes = {
setNewSelection: PropTypes.func.isRequired,
segmentsAddedIntoSelection: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
isChecked: PropTypes.boolean,
isChecked: PropTypes.bool,
id: PropTypes.string.isRequired,
}).isRequired).isRequired,
};

View File

@@ -1,28 +1,73 @@
import React from 'react';
import React, { useRef, useCallback } from 'react';
import PropTypes from 'prop-types';
import { CheckboxControl, Dashicon } from '@wordpress/components';
import { partial } from 'lodash';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import Backend from 'react-dnd-html5-backend';
const Preview = ({
segments,
updateSegment,
const PreviewItem = ({
segment,
index,
moveItem,
removeSegment,
onSegmentsReorder,
onCheck,
}) => {
if (segments.length === 0) {
return null;
}
const onCheck = (segmentId, isChecked) => {
const segment = segments.find((s) => s.id === segmentId);
segment.isChecked = isChecked;
updateSegment(segment);
};
return segments.map((segment) => (
const ref = useRef(null);
const [, drop] = useDrop({
accept: 'item',
hover(item, monitor) {
if (!ref.current) {
return;
}
const dragIndex = item.index;
const hoverIndex = index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Determine rectangle on screen
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
className="mailpoet-form-segments-settings-list"
key={segment.id}
ref={ref}
style={{ opacity }}
>
<CheckboxControl
label={segment.name}
@@ -37,13 +82,68 @@ const Preview = ({
onClick={partial(removeSegment, segment.id)}
/>
</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 = {
segments: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
isChecked: PropTypes.boolean,
isChecked: PropTypes.bool,
id: PropTypes.string.isRequired,
}).isRequired).isRequired,
updateSegment: PropTypes.func.isRequired,