UNPKG

@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
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)); }