@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
224 lines (215 loc) • 5.82 kB
JavaScript
import { attributes } from '../../utils/attributes';
import { findClosestScrollContainer } from '../../utils/find-closest-scroll-container';
import { getElement } from '../../utils/find-element';
import { findPlaceholder } from '../../utils/find-placeholder';
import { getClosestPositionedElement } from '../../utils/get-closest-positioned-element';
import { getElementByDraggableLocation } from '../../utils/get-element-by-draggable-location';
import { getGapOffset } from '../gap';
import { directionMapping, lineOffset } from './constants';
/**
* Returns the dimensions for a drop indicator either before or after a
* draggable.
*
* `isForwardEdge` determines whether it is before or after.
*/
function measureDraggable({
element,
isForwardEdge,
mode,
direction,
contextId
}) {
const {
mainAxis,
crossAxis
} = directionMapping[direction];
const offsetElement = getClosestPositionedElement({
element,
mode
});
const gapOffset = getGapOffset({
element,
where: isForwardEdge ? 'after' : 'before',
direction,
contextId
});
const baseOffset = offsetElement[mainAxis.offset] - lineOffset;
const mainAxisOffset = isForwardEdge ? baseOffset + element[mainAxis.length] : baseOffset;
return {
mainAxis: {
offset: mainAxisOffset + gapOffset
},
crossAxis: {
offset: offsetElement[crossAxis.offset],
length: offsetElement[crossAxis.length]
}
};
}
/**
* This will return an indicator size and offset corresponding to a line
* through the middle of the placeholder.
*
* The reason this is a special case, instead of just falling back to the
* standard positioning logic, is to avoid measuring the drag preview.
*/
function measurePlaceholder({
element,
direction
}) {
const {
mainAxis,
crossAxis
} = directionMapping[direction];
/**
* This function measures against the `element` directly instead of an
* `offsetElement` because:
* - For standard lists, that is already the behavior.
* - For virtual lists, we know that the `element` is being absolutely
* positioned (and not an ancestor).
*/
const baseOffset = element[mainAxis.offset] - lineOffset;
const mainAxisOffset = baseOffset + element[mainAxis.length] / 2;
return {
mainAxis: {
offset: mainAxisOffset
},
crossAxis: {
offset: element[crossAxis.offset],
length: element[crossAxis.length]
}
};
}
function getDroppableOffset({
element,
direction
}) {
const {
mainAxis
} = directionMapping[direction];
const scrollContainer = findClosestScrollContainer(element);
if (!scrollContainer) {
return 0;
}
/**
* If the scroll container has static positioning,
* then we need to add the scroll container's offset as well.
*/
const {
position
} = getComputedStyle(scrollContainer);
if (position !== 'static') {
return 0;
}
return scrollContainer[mainAxis.offset];
}
/**
* Returns the dimensions for a drop indicator in an empty list.
*/
function measureDroppable({
droppableId,
direction
}) {
const element = getElement({
attribute: attributes.droppable.id,
value: droppableId
});
const mainAxisOffset = getDroppableOffset({
element,
direction
});
return {
mainAxis: {
offset: mainAxisOffset
},
crossAxis: {
offset: 0,
length: '100%'
}
};
}
export function getIndicatorSizeAndOffset({
targetLocation,
isInHomeLocation,
direction,
mode,
contextId
}) {
if (isInHomeLocation) {
/**
* If we are in the home location (source === destination) then the
* indicator is centered in the placeholder.
*
* It isn't visible, but is used to scroll to.
*
* This is a special case, because the standard logic will not work
* correctly when measuring the drag preview,
* which occurs when in the home location.
*
* This is because the drag preview:
*
* 1. Has `position: fixed; top: 0; left: 0;` so its `offsetTop` and `offsetLeft`
* will always be `0`, which result in the indicator being at the start of the list.
* 2. Is in the wrong location anyway.
*
* `measurePlaceholder()` is specifically designed for this case.
*/
const element = findPlaceholder(contextId);
if (!element) {
return null;
}
return measurePlaceholder({
element,
direction
});
}
if (targetLocation.index === 0) {
/**
* If the target is the 0th index, there are two situations:
*
* 1. Targeting an empty list
* 2. Targeting before the first item in the list
*/
const element = getElementByDraggableLocation(contextId, targetLocation);
if (!element) {
/**
* If there's no element in the location, it is because the list is empty.
* In this case, we measure the droppable itself to draw the indicator.
*/
return measureDroppable({
droppableId: targetLocation.droppableId,
direction
});
}
/**
* Otherwise, there is a reference element we can use to measure.
*/
return measureDraggable({
element,
// `false` because the line is before the item
isForwardEdge: false,
mode,
direction,
contextId
});
}
/**
* Otherwise, for any other index, we can measure the draggable above where
* we would be dropping.
*/
const element = getElementByDraggableLocation(contextId, {
droppableId: targetLocation.droppableId,
// subtracting one because it is the draggable above
index: targetLocation.index - 1
});
if (!element) {
return null;
}
return measureDraggable({
element,
// `true` because the line is after the item
isForwardEdge: true,
mode,
direction,
contextId
});
}