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

472 lines (459 loc) 19.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.Draggable = Draggable; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _bindEventListener = require("bind-event-listener"); var _tinyInvariant = _interopRequireDefault(require("tiny-invariant")); var _combine = require("@atlaskit/pragmatic-drag-and-drop/combine"); var _adapter = require("@atlaskit/pragmatic-drag-and-drop/element/adapter"); var _disableNativeDragPreview = require("@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview"); var _getElementFromPointWithoutHoneyPot = require("@atlaskit/pragmatic-drag-and-drop/private/get-element-from-point-without-honey-pot"); var _useHiddenTextElement = require("../drag-drop-context/hooks/use-hidden-text-element"); var _internalContext = require("../drag-drop-context/internal-context"); var _lifecycleContext = require("../drag-drop-context/lifecycle-context"); var _rbdInvariant = require("../drag-drop-context/rbd-invariant"); var _droppableContext = require("../droppable/droppable-context"); var _useCapturedDimensions = require("../hooks/use-captured-dimensions"); var _useCleanupFn2 = require("../hooks/use-cleanup-fn"); var _useDropTargetForDraggable = require("../hooks/use-drop-target-for-draggable"); var _useKeyboardContext2 = require("../hooks/use-keyboard-context"); var _attributes = require("../utils/attributes"); var _findDragHandle = require("../utils/find-drag-handle"); var _findDropIndicator = require("../utils/find-drop-indicator"); var _findPlaceholder = require("../utils/find-placeholder"); var _useStable = require("../utils/use-stable"); var _data = require("./data"); var _getDraggableProvidedStyle = require("./get-draggable-provided-style"); var _isEventInInteractiveElement = _interopRequireWildcard(require("./is-event-in-interactive-element")); var _placeholder = require("./placeholder"); var _state = require("./state"); var _useDraggableStateSnapshot = require("./use-draggable-state-snapshot"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } // eslint-disable-next-line import/no-extraneous-dependencies var noop = function noop() {}; function Draggable(_ref) { var children = _ref.children, draggableId = _ref.draggableId, index = _ref.index, _ref$isDragDisabled = _ref.isDragDisabled, isDragDisabled = _ref$isDragDisabled === void 0 ? false : _ref$isDragDisabled, _ref$disableInteracti = _ref.disableInteractiveElementBlocking, disableInteractiveElementBlocking = _ref$disableInteracti === void 0 ? false : _ref$disableInteracti; var _useDroppableContext = (0, _droppableContext.useDroppableContext)(), direction = _useDroppableContext.direction, droppableId = _useDroppableContext.droppableId, type = _useDroppableContext.type, mode = _useDroppableContext.mode; var _useDragDropContext = (0, _internalContext.useDragDropContext)(), contextId = _useDragDropContext.contextId, getDragState = _useDragDropContext.getDragState; var elementRef = (0, _react.useRef)(null); var dragHandleRef = (0, _react.useRef)(null); var _useCleanupFn = (0, _useCleanupFn2.useCleanupFn)(), setCleanupFn = _useCleanupFn.setCleanupFn, runCleanupFn = _useCleanupFn.runCleanupFn; var setElement = (0, _react.useCallback)(function (element) { if (elementRef.current) { /** * Call the `setAttribute` clean up if the element changes */ runCleanupFn(); } if (element) { /** * The migration layer attaches some additional data attributes. * * These are required for querying elements in the DOM. * * These are not applied through render props, to avoid changing the type * interface of the migration layer. */ var cleanupFn = (0, _attributes.setAttributes)(element, (0, _defineProperty2.default)((0, _defineProperty2.default)({}, _attributes.customAttributes.draggable.droppableId, droppableId), _attributes.customAttributes.draggable.index, String(index))); setCleanupFn(cleanupFn); } elementRef.current = element; dragHandleRef.current = (0, _findDragHandle.findDragHandle)({ contextId: contextId, draggableId: draggableId }); }, [contextId, draggableId, droppableId, index, runCleanupFn, setCleanupFn]); var getIndex = (0, _useStable.useStable)(index); var _useReducer = (0, _react.useReducer)(_state.reducer, _state.idleState), _useReducer2 = (0, _slicedToArray2.default)(_useReducer, 2), state = _useReducer2[0], dispatch = _useReducer2[1]; var data = (0, _data.useDraggableData)({ draggableId: draggableId, droppableId: droppableId, getIndex: getIndex, contextId: contextId, type: type }); var isDragging = state.type === 'dragging'; var isHiding = state.type === 'hiding'; var _useDroppableContext2 = (0, _droppableContext.useDroppableContext)(), shouldRenderCloneWhileDragging = _useDroppableContext2.shouldRenderCloneWhileDragging, isDropDisabled = _useDroppableContext2.isDropDisabled; var monitorForLifecycle = (0, _lifecycleContext.useMonitorForLifecycle)(); var _useKeyboardContext = (0, _useKeyboardContext2.useKeyboardContext)(), startKeyboardDrag = _useKeyboardContext.startKeyboardDrag; /** * Binds the `keydown` listener to the drag handle which handles starting * keyboard drags. */ (0, _react.useEffect)(function () { if (state.type !== 'idle') { return; } if (isDragDisabled) { return; } var element = elementRef.current; (0, _tinyInvariant.default)(element instanceof HTMLElement); var dragHandle = dragHandleRef.current; (0, _tinyInvariant.default)(dragHandle instanceof HTMLElement); return (0, _bindEventListener.bindAll)(dragHandle, [{ type: 'keydown', listener: function listener(event) { if (event.key === ' ') { if (event.defaultPrevented) { return; } if (!disableInteractiveElementBlocking && (0, _isEventInInteractiveElement.default)(element, event)) { return; } // Only prevent default if we are consuming it event.preventDefault(); startKeyboardDrag({ event: event, draggableId: draggableId, type: type, getSourceLocation: function getSourceLocation() { return { droppableId: droppableId, index: getIndex() }; }, sourceElement: element }); } } }]); }, [disableInteractiveElementBlocking, draggableId, droppableId, getIndex, isDragDisabled, startKeyboardDrag, state.type, type]); /** * Sets up the pdnd draggable. */ (0, _react.useEffect)(function () { if (isHiding) { /** * If we render a clone, then we need to unmount the original element. * * Because of this, `elementRef.current` will become `null` and we will * no longer have a valid `element` reference. * * In this case, not having a valid `element` is expected, * instead of being an error. */ return; } if (isDragDisabled) { return; } var element = elementRef.current; (0, _rbdInvariant.rbdInvariant)(element instanceof HTMLElement); var dragHandle = dragHandleRef.current; (0, _rbdInvariant.rbdInvariant)(dragHandle instanceof HTMLElement); return (0, _adapter.draggable)({ canDrag: function canDrag(_ref2) { var input = _ref2.input; /** * Do not start a drag if any modifier key is pressed. * This matches the behavior of `react-beautiful-dnd`. */ if (input.ctrlKey || input.metaKey || input.shiftKey || input.altKey) { return false; } /** * To align with `react-beautiful-dnd` we are blocking drags * on interactive elements, unless the `disableInteractiveElementBlocking` * prop is provided. */ if (!disableInteractiveElementBlocking) { var elementUnderPointer = (0, _getElementFromPointWithoutHoneyPot.getElementFromPointWithoutHoneypot)({ x: input.clientX, y: input.clientY }); return !(0, _isEventInInteractiveElement.isAnInteractiveElement)(dragHandle, elementUnderPointer); } return !isDragging; }, element: element, dragHandle: dragHandle, getInitialData: function getInitialData() { return data; }, onGenerateDragPreview: _disableNativeDragPreview.disableNativeDragPreview }); }, [data, disableInteractiveElementBlocking, isDragDisabled, isDragging, isHiding]); var hasPlaceholder = state.type !== 'idle' && mode === 'standard'; var placeholderRef = (0, _react.useRef)(null); (0, _useDropTargetForDraggable.useDropTargetForDraggable)({ /** * Swapping the drop target to the placeholder is important * to ensure that hovering over where the item was won't result in a * drop at the end of the list. */ elementRef: hasPlaceholder ? placeholderRef : elementRef, data: data, direction: direction, contextId: contextId, isDropDisabled: isDropDisabled, type: type }); var isMountedRef = (0, _react.useRef)(true); (0, _react.useEffect)(function () { /** * React 18 strict mode will re-run effects in development mode. * https://react.dev/reference/react/StrictMode#fixing-bugs-found-by-re-running-effects-in-development * * Setting the ref value to `true` again in the effect to avoid the value staying `false` incorrectly after * the first cleanup. */ isMountedRef.current = true; return function () { isMountedRef.current = false; }; }, []); /** * If the draggable (re)mounts while it is being dragged (via a clone), * then it should hide itself. */ (0, _react.useEffect)(function () { var dragState = getDragState(); /** * If the draggable is not using a clone, then it doesn't need to be hidden. */ if (!shouldRenderCloneWhileDragging) { return; } /** * If there is no ongoing drag, then it doesn't need to be hidden. */ if (!dragState.isDragging) { return; } /** * Only the draggable being dragged (via a clone) needs to be hidden. */ if (dragState.draggableId !== data.draggableId) { return; } dispatch({ type: 'START_HIDING', payload: { mode: dragState.mode } }); }, [data.draggableId, getDragState, shouldRenderCloneWhileDragging]); var draggableDimensions = (0, _useCapturedDimensions.useDraggableDimensions)(); (0, _react.useEffect)(function () { /** * If the draggable should render a clone while dragging, * then it doesn't need to track any state, and it should be hidden. */ if (shouldRenderCloneWhileDragging) { return monitorForLifecycle({ onPendingDragStart: function onPendingDragStart(_ref3) { var start = _ref3.start; if (data.draggableId !== start.draggableId) { return; } dispatch({ type: 'START_HIDING', payload: { mode: start.mode } }); }, onBeforeDragEnd: function onBeforeDragEnd(_ref4) { var draggableId = _ref4.draggableId; if (draggableId !== data.draggableId) { return; } dispatch({ type: 'STOP_HIDING' }); } }); } /** * Drag events need to be monitored independently because the original * element can be unmounted for two (valid) reasons. * * The original element can be unmounted during the drag for two reasons: * * 1. A `renderClone` method has been provided to the containing * `<Droppable />` element. In this case the element is unmounted so * that it is not visible while the clone is. * * 2. The user portals the element while it is being dragged. This would * result in the original `HTMLElement` being unmounted. */ return (0, _combine.combine)(monitorForLifecycle({ onPendingDragStart: function onPendingDragStart(_ref5) { var start = _ref5.start, droppable = _ref5.droppable; if (data.draggableId !== start.draggableId) { return; } if (start.mode === 'FLUID') { return dispatch({ type: 'START_POINTER_DRAG', payload: { start: start } }); } if (start.mode === 'SNAP') { var dragState = getDragState(); (0, _rbdInvariant.rbdInvariant)(dragState.isDragging && dragState.draggableDimensions); return dispatch({ type: 'START_KEYBOARD_DRAG', payload: { start: start, draggableDimensions: dragState.draggableDimensions, droppable: droppable } }); } }, onPendingDragUpdate: function onPendingDragUpdate(_ref6) { var update = _ref6.update, droppable = _ref6.droppable; if (data.draggableId !== update.draggableId) { return; } dispatch({ type: 'UPDATE_DRAG', payload: { update: update } }); if (update.mode === 'SNAP') { /** * Updating the position in a microtask to resolve timing issues. * * When doing cross-axis dragging, the drop indicator in the new * droppable will mount and update in a `onPendingDragUpdate` too. * * The microtask ensures that the indicator will have updated by * the time this runs, so the preview will have the correct * location of the indicator. */ queueMicrotask(function () { /** * Because this update occurs in a microtask, we need to check * that the drag is still happening. * * If it has ended we should not try to update the preview. */ var dragState = getDragState(); if (!dragState.isDragging) { return; } /** * The placeholder might not exist if its associated * draggable unmounts in a virtual list. */ var placeholder = (0, _findPlaceholder.findPlaceholder)(contextId); var placeholderRect = placeholder ? placeholder.getBoundingClientRect() : null; /** * The drop indicator might not exist if the current target * is null */ var dropIndicator = (0, _findDropIndicator.findDropIndicator)(); var dropIndicatorRect = dropIndicator ? dropIndicator.getBoundingClientRect() : null; dispatch({ type: 'UPDATE_KEYBOARD_PREVIEW', payload: { update: update, draggableDimensions: draggableDimensions, droppable: droppable, placeholderRect: placeholderRect, dropIndicatorRect: dropIndicatorRect } }); }); } }, onBeforeDragEnd: function onBeforeDragEnd(_ref7) { var draggableId = _ref7.draggableId; if (draggableId !== data.draggableId) { return; } (0, _rbdInvariant.rbdInvariant)(isMountedRef.current, 'isMounted onBeforeDragEnd'); dispatch({ type: 'DROP' }); } }), (0, _adapter.monitorForElements)({ canMonitor: function canMonitor(_ref8) { var source = _ref8.source; if (!(0, _data.isDraggableData)(source.data)) { // not dragging something from the migration layer // we should not monitor it return false; } return source.data.contextId === data.contextId && source.data.draggableId === data.draggableId; }, onDrag: function onDrag(_ref9) { var location = _ref9.location; dispatch({ type: 'UPDATE_POINTER_PREVIEW', payload: { pointerLocation: location } }); } })); }, [data.draggableId, data.contextId, monitorForLifecycle, shouldRenderCloneWhileDragging, direction, contextId, draggableDimensions, getDragState]); var provided = (0, _react.useMemo)(function () { return { draggableProps: (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, _attributes.attributes.draggable.contextId, contextId), _attributes.attributes.draggable.id, draggableId), "style", (0, _getDraggableProvidedStyle.getDraggableProvidedStyle)({ draggableDimensions: draggableDimensions, draggableState: state })), dragHandleProps: (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({ role: 'button', 'aria-describedby': (0, _useHiddenTextElement.getHiddenTextElementId)(contextId) }, _attributes.attributes.dragHandle.contextId, contextId), _attributes.attributes.dragHandle.draggableId, draggableId), "tabIndex", 0), "draggable", false), "onDragStart", noop), innerRef: setElement }; }, [contextId, draggableId, draggableDimensions, state, setElement]); var snapshot = (0, _useDraggableStateSnapshot.useDraggableStateSnapshot)({ draggingOver: state.draggingOver, isClone: false, isDragging: isDragging, mode: isDragging ? state.mode : null }); var rubric = (0, _react.useMemo)(function () { return { draggableId: draggableId, type: type, source: { droppableId: droppableId, index: index } }; }, [draggableId, droppableId, index, type]); return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, isHiding ? null : children(provided, snapshot, rubric), hasPlaceholder && /*#__PURE__*/_react.default.createElement(_placeholder.Placeholder, { ref: placeholderRef })); }