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

325 lines (318 loc) 11.5 kB
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import { useCallback } from 'react'; import { bindAll } from 'bind-event-listener'; // eslint-disable-next-line import/no-extraneous-dependencies import { attributes, customAttributes, getAttribute } from '../../utils/attributes'; import { findClosestScrollContainer } from '../../utils/find-closest-scroll-container'; import { getElement } from '../../utils/find-element'; import { getBestCrossAxisDroppable } from '../../utils/get-best-cross-axis-droppable'; import { getElementByDraggableLocation } from '../../utils/get-element-by-draggable-location'; import { isSameLocation } from '../draggable-location'; import { getActualDestination } from '../get-destination'; import { rbdInvariant } from '../rbd-invariant'; /** * Finds the element's scroll container and scrolls it to the top. * * This is used for cross-axis drags to provide a consistent starting point * in the list. * * The behavior differs to `react-beautiful-dnd` which would find the * index closest to the current visual position. That was not preserved for * performance cost reasons. */ function scrollToTop(element) { var scrollContainer = findClosestScrollContainer(element); if (!scrollContainer) { return; } scrollContainer.scrollTo(0, 0); } var moveHandlers = { mainAxis: { prev: function prev(event, _ref) { var dragController = _ref.dragController; /** * Preventing default to stop scrolling caused by arrow key press. */ event.preventDefault(); var dragState = dragController.getDragState(); rbdInvariant(dragState.isDragging); var sourceLocation = dragState.sourceLocation, targetLocation = dragState.targetLocation; if (!targetLocation) { return; } if (targetLocation.index === 0) { return; } var nextLocation = _objectSpread(_objectSpread({}, targetLocation), {}, { index: targetLocation.index - 1 }); var nextDestination = getActualDestination({ start: sourceLocation, target: nextLocation }); /** * There are two target indexes that correspond to a drop in the source location: * * 1. source.index (before the source, but after previous item) * 2. source.index + 1 (after the source, but before next item) * * We decrement by 2 when going over the source location, * so the user only perceives one of these indexes. */ if (isSameLocation(sourceLocation, nextDestination)) { nextLocation.index = targetLocation.index - 2; } dragController.updateDrag({ targetLocation: nextLocation }); }, next: function next(event, _ref2) { var dragController = _ref2.dragController, contextId = _ref2.contextId; /** * Preventing default to stop scrolling caused by arrow key press. */ event.preventDefault(); var dragState = dragController.getDragState(); rbdInvariant(dragState.isDragging); var sourceLocation = dragState.sourceLocation, targetLocation = dragState.targetLocation; if (!targetLocation) { return; } /** * Checks if we can move to the next position. * * Reasoning: if there is already a draggable with the current index, * then it is a possible target. */ var element = getElementByDraggableLocation(contextId, targetLocation); /** * This check is for virtual lists, and is a special case. * * When dragging, the element will unmount and we won't be able to find * the element for that index. * * This means for virtual lists, the normal check (element check) will fail. */ var isSame = isSameLocation(sourceLocation, targetLocation); if (!isSame && !element) { return; } var nextLocation = _objectSpread(_objectSpread({}, targetLocation), {}, { index: targetLocation.index + 1 }); var nextDestination = getActualDestination({ start: sourceLocation, target: nextLocation }); /** * There are two target indexes that correspond to a drop in the source location: * * 1. source.index (before the source, but after previous item) * 2. source.index + 1 (after the source, but before next item) * * We increment by 2 when going over the source location, * so the user only perceives one of these indexes. */ if (isSameLocation(sourceLocation, nextDestination)) { nextLocation.index = targetLocation.index + 2; } dragController.updateDrag({ targetLocation: nextLocation }); } }, crossAxis: { prev: function prev(event, _ref3) { var dragController = _ref3.dragController, droppableRegistry = _ref3.droppableRegistry, contextId = _ref3.contextId; /** * Preventing default to stop scrolling caused by arrow key press. */ event.preventDefault(); var dragState = dragController.getDragState(); rbdInvariant(dragState.isDragging); var targetLocation = dragState.targetLocation, type = dragState.type; if (!targetLocation) { return; } var before = getBestCrossAxisDroppable({ droppableId: targetLocation.droppableId, type: type, isMovingForward: false, contextId: contextId, droppableRegistry: droppableRegistry }); if (!before) { return; } scrollToTop(before); var nextLocation = { droppableId: getAttribute(before, attributes.droppable.id), index: 0 }; dragController.updateDrag({ targetLocation: nextLocation }); }, next: function next(event, _ref4) { var dragController = _ref4.dragController, droppableRegistry = _ref4.droppableRegistry, contextId = _ref4.contextId; /** * Preventing default to stop scrolling caused by arrow key press. */ event.preventDefault(); var dragState = dragController.getDragState(); rbdInvariant(dragState.isDragging); var targetLocation = dragState.targetLocation, type = dragState.type; if (!targetLocation) { return; } var after = getBestCrossAxisDroppable({ droppableId: targetLocation.droppableId, type: type, isMovingForward: true, contextId: contextId, droppableRegistry: droppableRegistry }); if (!after) { return; } scrollToTop(after); var nextLocation = { droppableId: getAttribute(after, attributes.droppable.id), index: 0 }; dragController.updateDrag({ targetLocation: nextLocation }); } } }; function preventDefault(event) { event.preventDefault(); } /** * These keys mostly have their default behavior prevented to stop scrolling. * * The tab key is prevented to lock focus in place. */ var commonKeyHandlers = { PageUp: preventDefault, PageDown: preventDefault, Home: preventDefault, End: preventDefault, Enter: preventDefault, Tab: preventDefault }; /** * Maps actions to keys. */ var keyHandlers = { vertical: _objectSpread(_objectSpread({}, commonKeyHandlers), {}, { ArrowUp: moveHandlers.mainAxis.prev, ArrowDown: moveHandlers.mainAxis.next, ArrowLeft: moveHandlers.crossAxis.prev, ArrowRight: moveHandlers.crossAxis.next }), horizontal: _objectSpread(_objectSpread({}, commonKeyHandlers), {}, { ArrowUp: moveHandlers.crossAxis.prev, ArrowDown: moveHandlers.crossAxis.next, ArrowLeft: moveHandlers.mainAxis.prev, ArrowRight: moveHandlers.mainAxis.next }) }; export function useKeyboardControls(_ref5) { var dragController = _ref5.dragController, droppableRegistry = _ref5.droppableRegistry, contextId = _ref5.contextId, setKeyboardCleanupFn = _ref5.setKeyboardCleanupFn; var startKeyboardDrag = useCallback(function (_ref6) { var startEvent = _ref6.event, draggableId = _ref6.draggableId, type = _ref6.type, getSourceLocation = _ref6.getSourceLocation, sourceElement = _ref6.sourceElement; dragController.startDrag({ draggableId: draggableId, type: type, getSourceLocation: getSourceLocation, sourceElement: sourceElement, mode: 'SNAP' }); var sourceLocation = getSourceLocation(); var droppable = getElement({ attribute: attributes.droppable.id, value: sourceLocation.droppableId }); var direction = getAttribute(droppable, customAttributes.droppable.direction); rbdInvariant(direction === 'vertical' || direction === 'horizontal'); function cancelDrag() { dragController.stopDrag({ reason: 'CANCEL' }); } /** * All of these events should cancel the drag. * * These events were taken from `react-beautiful-dnd`. */ var cancelBindings = ['mousedown', 'mouseup', 'click', 'touchstart', 'resize', 'wheel', 'visibilitychange'].map(function (type) { return { type: type, listener: cancelDrag }; }); var cleanupFn = bindAll(window, [{ type: 'keydown', listener: function listener(event) { var _keyHandlers$directio, _keyHandlers$directio2; /** * Ignores the keydown event which triggered the drag start, * so it doesn't trigger an immediate drop. */ if (event === startEvent) { return; } var _dragController$getDr = dragController.getDragState(), isDragging = _dragController$getDr.isDragging; if (!isDragging) { return; } if (event.key === ' ') { event.preventDefault(); dragController.stopDrag({ reason: 'DROP' }); return; } if (event.key === 'Escape') { event.preventDefault(); dragController.stopDrag({ reason: 'CANCEL' }); return; } (_keyHandlers$directio = (_keyHandlers$directio2 = keyHandlers[direction])[event.key]) === null || _keyHandlers$directio === void 0 || _keyHandlers$directio.call(_keyHandlers$directio2, event, { dragController: dragController, droppableRegistry: droppableRegistry, contextId: contextId }); } }].concat(_toConsumableArray(cancelBindings))); setKeyboardCleanupFn(cleanupFn); }, [contextId, dragController, droppableRegistry, setKeyboardCleanupFn]); return { startKeyboardDrag: startKeyboardDrag }; }