@atlaskit/pragmatic-drag-and-drop-react-beautiful-dnd-migration
Version:
An optional Pragmatic drag and drop package that enables rapid migration from react-beautiful-dnd to Pragmatic drag and drop
246 lines (241 loc) • 9.67 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React, { useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createPortal } from 'react-dom';
import invariant from 'tiny-invariant';
import { attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { useDragDropContext } from '../drag-drop-context/internal-context';
import { useMonitorForLifecycle } from '../drag-drop-context/lifecycle-context';
import { isDraggableData } from '../draggable/data';
import { useLayoutEffect } from '../hooks/use-isomorphic-layout-effect';
import { attributes, customAttributes, setAttributes } from '../utils/attributes';
import { useStable } from '../utils/use-stable';
import { useDroppableData } from './data';
import { DraggableClone } from './draggable-clone';
import { DropIndicator } from './drop-indicator';
import { DroppableContextProvider, useParentDroppableId } from './droppable-context';
import { idleState, reducer } from './state';
import { VirtualPlaceholder } from './virtual-placeholder';
export function Droppable(_ref) {
var children = _ref.children,
droppableId = _ref.droppableId,
_ref$type = _ref.type,
type = _ref$type === void 0 ? 'DEFAULT' : _ref$type,
_ref$direction = _ref.direction,
direction = _ref$direction === void 0 ? 'vertical' : _ref$direction,
_ref$mode = _ref.mode,
mode = _ref$mode === void 0 ? 'standard' : _ref$mode,
renderClone = _ref.renderClone,
getContainerForClone = _ref.getContainerForClone,
_ref$isDropDisabled = _ref.isDropDisabled,
isDropDisabled = _ref$isDropDisabled === void 0 ? false : _ref$isDropDisabled;
var getIsDropDisabled = useStable(isDropDisabled);
var _useDragDropContext = useDragDropContext(),
contextId = _useDragDropContext.contextId,
droppableRegistry = _useDragDropContext.droppableRegistry;
var data = useDroppableData({
contextId: contextId,
droppableId: droppableId,
getIsDropDisabled: getIsDropDisabled
});
var elementRef = useRef(null);
var setElement = useCallback(function (element) {
if (element) {
setAttributes(element, _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, customAttributes.droppable.type, type), customAttributes.droppable.direction, direction), attributes.droppable.id, droppableId), attributes.droppable.contextId, contextId));
}
elementRef.current = element;
}, [contextId, direction, droppableId, type]);
var _useReducer = useReducer(reducer, idleState),
_useReducer2 = _slicedToArray(_useReducer, 2),
state = _useReducer2[0],
dispatch = _useReducer2[1];
var draggingFromThisWith = state.draggingFromThisWith,
draggingOverWith = state.draggingOverWith,
isDraggingOver = state.isDraggingOver;
var parentDroppableId = useParentDroppableId();
useEffect(function () {
var element = elementRef.current;
invariant(element instanceof HTMLElement, 'innerRef must provide an `HTMLElement`');
return combine(droppableRegistry.register({
droppableId: droppableId,
type: type,
isDropDisabled: isDropDisabled,
parentDroppableId: parentDroppableId,
element: element,
direction: direction,
mode: mode
}), dropTargetForElements({
element: element,
getData: function getData(_ref2) {
var input = _ref2.input;
return attachClosestEdge(data, {
element: element,
input: input,
allowedEdges: direction === 'vertical' ? ['top', 'bottom'] : ['left', 'right']
});
},
canDrop: function canDrop(_ref3) {
var source = _ref3.source;
if (!isDraggableData(source.data)) {
// not dragging something from the migration layer
// we should not allow dropping
return false;
}
if (isDropDisabled) {
return false;
}
return source.data.contextId === contextId && source.data.type === type;
},
onDragLeave: function onDragLeave() {
dispatch({
type: 'DRAG_CLEAR'
});
}
}));
}, [data, droppableId, contextId, isDropDisabled, type, droppableRegistry, parentDroppableId, direction, mode]);
var monitorForLifecycle = useMonitorForLifecycle();
useEffect(function () {
function isEventRelevant(data) {
var _data$destination;
/**
* If the draggable is of a different type to this droppable,
* then we can ignore it.
*/
var isSameType = data.type === type;
var isOverAfterUpdate = ((_data$destination = data.destination) === null || _data$destination === void 0 ? void 0 : _data$destination.droppableId) === droppableId;
var isDragEnter = !isDraggingOver && isOverAfterUpdate;
var isDragLeave = isDraggingOver && !isOverAfterUpdate;
/**
* A droppable will only have a meaningful state update if the user is entering or exiting it.
*/
var isDragEnterOrLeave = isDragEnter || isDragLeave;
return isSameType && isDragEnterOrLeave;
}
return monitorForLifecycle({
onPendingDragStart: function onPendingDragStart(_ref4) {
var start = _ref4.start;
if (!isEventRelevant({
destination: start.source,
type: start.type
})) {
return;
}
dispatch({
type: 'DRAG_START',
payload: {
droppableId: droppableId,
start: start
}
});
},
onPendingDragUpdate: function onPendingDragUpdate(_ref5) {
var update = _ref5.update;
if (!isEventRelevant(update)) {
return;
}
dispatch({
type: 'DRAG_UPDATE',
payload: {
droppableId: droppableId,
update: update
}
});
},
onBeforeDragEnd: function onBeforeDragEnd() {
/**
* This is safe to call optimistically as it uses a stable idle state.
*
* If the droppable is already idle, it will not rerender.
*/
dispatch({
type: 'DRAG_CLEAR'
});
}
});
}, [droppableId, isDraggingOver, monitorForLifecycle, type]);
var dropIndicator = useMemo(function () {
if (!isDraggingOver) {
return null;
}
return /*#__PURE__*/React.createElement(DropIndicator, {
direction: direction,
mode: mode
});
}, [direction, isDraggingOver, mode]);
var provided = useMemo(function () {
return {
innerRef: setElement,
droppableProps: _defineProperty(_defineProperty({}, attributes.droppable.contextId, contextId), attributes.droppable.id, droppableId),
/**
* We only provide a drop indicator as the placeholder for
* non-virtual lists. Otherwise it is portalled in.
*/
placeholder: mode === 'standard' ? dropIndicator : null
};
}, [contextId, dropIndicator, droppableId, mode, setElement]);
var snapshot = useMemo(function () {
return {
draggingFromThisWith: draggingFromThisWith,
draggingOverWith: draggingOverWith,
isDraggingOver: isDraggingOver,
isUsingPlaceholder: isDraggingOver
};
}, [draggingFromThisWith, draggingOverWith, isDraggingOver]);
var element = elementRef.current;
var shouldPortalDropIndicator = isDraggingOver && mode === 'virtual' && element;
/**
* Assumes that the ref points to the scroll container.
*/
useLayoutEffect(function () {
if (!shouldPortalDropIndicator) {
return;
}
var _window$getComputedSt = window.getComputedStyle(element),
position = _window$getComputedSt.position;
if (position !== 'static') {
return;
}
var prevStyle = element.style.position;
element.style.position = 'relative';
return function () {
element.style.position = prevStyle;
};
}, [element, shouldPortalDropIndicator]);
/**
* Used to disable the dragging style for the real draggable.
*/
var shouldRenderCloneWhileDragging = Boolean(renderClone);
var contextValue = useMemo(function () {
return {
direction: direction,
droppableId: droppableId,
shouldRenderCloneWhileDragging: shouldRenderCloneWhileDragging,
isDropDisabled: isDropDisabled,
type: type,
mode: mode
};
}, [direction, droppableId, shouldRenderCloneWhileDragging, isDropDisabled, type, mode]);
/**
* For virtual lists we portal a placeholder in when dragging from the list.
*
* This is because `<Draggable />`'s can be unmounted at any time, so we
* cannot rely on rendering the placeholder as a sibling.
*/
var shouldPortalPlaceholder = draggingFromThisWith && mode === 'virtual' && element;
return /*#__PURE__*/React.createElement(DroppableContextProvider, {
value: contextValue
}, children(provided, snapshot), shouldPortalDropIndicator && /*#__PURE__*/createPortal(dropIndicator, element), shouldPortalPlaceholder && /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(VirtualPlaceholder, {
droppableId: droppableId,
draggableId: draggingFromThisWith,
type: type,
direction: direction,
isDropDisabled: isDropDisabled
}), element), renderClone && /*#__PURE__*/React.createElement(DraggableClone, {
droppableId: droppableId,
type: type,
getContainerForClone: getContainerForClone
}, renderClone));
}