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