194 lines
6.9 KiB
JavaScript
194 lines
6.9 KiB
JavaScript
'use strict';
|
|
|
|
var React = require('react');
|
|
var reactDnd = require('react-dnd');
|
|
var useKeyboardDragAndDrop = require('./useKeyboardDragAndDrop.js');
|
|
|
|
function _interopNamespaceDefault(e) {
|
|
var n = Object.create(null);
|
|
if (e) {
|
|
Object.keys(e).forEach(function (k) {
|
|
if (k !== 'default') {
|
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
Object.defineProperty(n, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function () { return e[k]; }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
n.default = e;
|
|
return Object.freeze(n);
|
|
}
|
|
|
|
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
|
|
const DIRECTIONS = {
|
|
UPWARD: 'upward',
|
|
DOWNWARD: 'downward'
|
|
};
|
|
const DROP_SENSITIVITY = {
|
|
REGULAR: 'regular',
|
|
IMMEDIATE: 'immediate'
|
|
};
|
|
/**
|
|
* A utility hook abstracting the general drag and drop hooks from react-dnd.
|
|
* Centralising the same behaviours and by default offering keyboard support.
|
|
*/ const useDragAndDrop = (active, { type = 'STRAPI_DND', index, item, onStart, onEnd, onGrabItem, onDropItem, onCancel, onMoveItem, dropSensitivity = DROP_SENSITIVITY.REGULAR })=>{
|
|
const objectRef = React__namespace.useRef(null);
|
|
const [{ handlerId, isOver }, dropRef] = reactDnd.useDrop({
|
|
accept: type,
|
|
collect (monitor) {
|
|
return {
|
|
handlerId: monitor.getHandlerId(),
|
|
isOver: monitor.isOver({
|
|
shallow: true
|
|
})
|
|
};
|
|
},
|
|
drop (item) {
|
|
const draggedIndex = item.index;
|
|
const newIndex = index;
|
|
if (isOver && onDropItem) {
|
|
onDropItem(draggedIndex, newIndex);
|
|
}
|
|
},
|
|
hover (item, monitor) {
|
|
if (!objectRef.current || !onMoveItem) {
|
|
return;
|
|
}
|
|
const dragIndex = item.index;
|
|
const newIndex = index;
|
|
const hoverBoundingRect = objectRef.current?.getBoundingClientRect();
|
|
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
const clientOffset = monitor.getClientOffset();
|
|
if (!clientOffset) return;
|
|
const hoverClientY = clientOffset && clientOffset.y - hoverBoundingRect.top;
|
|
if (typeof dragIndex === 'number' && typeof newIndex === 'number') {
|
|
if (dragIndex === newIndex) {
|
|
// Don't replace items with themselves
|
|
return;
|
|
}
|
|
if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
|
|
// Dragging downwards
|
|
if (dragIndex < newIndex && hoverClientY < hoverMiddleY) {
|
|
return;
|
|
}
|
|
// Dragging upwards
|
|
if (dragIndex > newIndex && hoverClientY > hoverMiddleY) {
|
|
return;
|
|
}
|
|
}
|
|
// Time to actually perform the action
|
|
onMoveItem(newIndex, dragIndex);
|
|
item.index = newIndex;
|
|
} else {
|
|
// Using numbers as indices doesn't work for nested list items with path like [1, 1, 0]
|
|
if (Array.isArray(dragIndex) && Array.isArray(newIndex)) {
|
|
// Indices comparison to find item position in nested list
|
|
const minLength = Math.min(dragIndex.length, newIndex.length);
|
|
let areEqual = true;
|
|
let isLessThan = false;
|
|
let isGreaterThan = false;
|
|
for(let i = 0; i < minLength; i++){
|
|
if (dragIndex[i] < newIndex[i]) {
|
|
isLessThan = true;
|
|
areEqual = false;
|
|
break;
|
|
} else if (dragIndex[i] > newIndex[i]) {
|
|
isGreaterThan = true;
|
|
areEqual = false;
|
|
break;
|
|
}
|
|
}
|
|
// Don't replace items with themselves
|
|
if (areEqual && dragIndex.length === newIndex.length) {
|
|
return;
|
|
}
|
|
if (dropSensitivity === DROP_SENSITIVITY.REGULAR) {
|
|
// Dragging downwards
|
|
if (isLessThan && !isGreaterThan && hoverClientY < hoverMiddleY) {
|
|
return;
|
|
}
|
|
// Dragging upwards
|
|
if (isGreaterThan && !isLessThan && hoverClientY > hoverMiddleY) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
onMoveItem(newIndex, dragIndex);
|
|
item.index = newIndex;
|
|
}
|
|
}
|
|
});
|
|
const getDragDirection = (monitor)=>{
|
|
if (monitor && monitor.isDragging() && !monitor.didDrop() && monitor.getInitialClientOffset() && monitor.getClientOffset()) {
|
|
const deltaY = monitor.getInitialClientOffset().y - monitor.getClientOffset().y;
|
|
if (deltaY > 0) return DIRECTIONS.UPWARD;
|
|
if (deltaY < 0) return DIRECTIONS.DOWNWARD;
|
|
return null;
|
|
}
|
|
return null;
|
|
};
|
|
const [{ isDragging, direction }, dragRef, dragPreviewRef] = reactDnd.useDrag({
|
|
type,
|
|
item () {
|
|
if (onStart) {
|
|
onStart();
|
|
}
|
|
/**
|
|
* This will be attached and it helps define the preview sizes
|
|
* when a component is flexy e.g. Relations
|
|
*/ const { width } = objectRef.current?.getBoundingClientRect() ?? {};
|
|
return {
|
|
index,
|
|
width,
|
|
...item
|
|
};
|
|
},
|
|
end () {
|
|
if (onEnd) {
|
|
onEnd();
|
|
}
|
|
},
|
|
canDrag: active,
|
|
/**
|
|
* This is useful when the item is in a virtualized list.
|
|
* However, if we don't have an ID then we want the libraries
|
|
* defaults to take care of this.
|
|
*/ isDragging: item?.id ? (monitor)=>{
|
|
return item.id === monitor.getItem().id;
|
|
} : undefined,
|
|
collect: (monitor)=>({
|
|
isDragging: monitor.isDragging(),
|
|
initialOffset: monitor.getInitialClientOffset(),
|
|
currentOffset: monitor.getClientOffset(),
|
|
direction: getDragDirection(monitor)
|
|
})
|
|
});
|
|
const handleKeyDown = useKeyboardDragAndDrop.useKeyboardDragAndDrop(active, index, {
|
|
onGrabItem,
|
|
onDropItem,
|
|
onCancel,
|
|
onMoveItem
|
|
});
|
|
return [
|
|
{
|
|
handlerId,
|
|
isDragging,
|
|
handleKeyDown,
|
|
isOverDropTarget: isOver,
|
|
direction
|
|
},
|
|
objectRef,
|
|
dropRef,
|
|
dragRef,
|
|
dragPreviewRef
|
|
];
|
|
};
|
|
|
|
exports.DIRECTIONS = DIRECTIONS;
|
|
exports.DROP_SENSITIVITY = DROP_SENSITIVITY;
|
|
exports.useDragAndDrop = useDragAndDrop;
|
|
//# sourceMappingURL=useDragAndDrop.js.map
|