@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
232 lines (223 loc) • 7.23 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
/**
* @jsxRuntime classic
* @jsx jsx
*/
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
import { css, jsx } from '@emotion/react';
import { isSameLocation } from '../../drag-drop-context/draggable-location';
import { getActualDestination } from '../../drag-drop-context/get-destination';
import { useDragDropContext } from '../../drag-drop-context/internal-context';
import { useMonitorForLifecycle } from '../../drag-drop-context/lifecycle-context';
import { rbdInvariant } from '../../drag-drop-context/rbd-invariant';
import { customAttributes } from '../../utils/attributes';
import { directionMapping, lineOffset, lineThickness } from './constants';
import { getIndicatorSizeAndOffset } from './get-dimensions';
const scrollMarginTop = lineThickness + 2 * lineOffset;
const baseStyles = css({
background: "var(--ds-border-brand, #1868DB)",
/**
* Ensures that when the indicator is scrolled into view there is visual
* space around it.
*
* Otherwise it will hug the edge of the container and be hard to see.
*/
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
scrollMarginTop,
/**
* The bottom margin needs to be slightly bigger for the gap to look
* the same visually.
*
* It's unclear why, this was found through testing.
*/
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
scrollMarginBottom: scrollMarginTop + lineOffset
});
/**
* For virtual lists, the indicator might not be a sibling of the contents.
* This can lead to issues like wrapping.
*
* This style 'resets' it so that there is a consistent initial position.
*/
const virtualStyles = css({
position: 'absolute',
top: 0,
left: 0
});
/**
* When targeting the source location, we hide the drop indicator.
* But it should still be scrolled to, so we only want to hide it visually,
* instead of not rendering it.
*/
const visuallyHiddenStyles = css({
opacity: 0
});
const directionStyles = {
horizontal: css({
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
width: lineThickness,
height: '100%',
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
marginLeft: -lineThickness
}),
vertical: css({
width: '100%',
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
height: lineThickness,
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
marginTop: -lineThickness
})
};
function getDynamicStyles({
direction,
dimensions,
indicatorOffset
}) {
if (dimensions === null) {
/**
* We hide the indicator initially until dimensions can be taken.
*/
return {
opacity: 0
};
}
const {
mainAxis,
crossAxis
} = directionMapping[direction];
return {
transform: `${mainAxis.style.transform}(${dimensions.mainAxis.offset - indicatorOffset}px)`,
[crossAxis.style.length]: dimensions.crossAxis.length,
[crossAxis.style.offset]: dimensions.crossAxis.offset
};
}
const dropIndicatorData = {
[customAttributes.dropIndicator]: ''
};
export const DropIndicator = ({
direction,
mode
}) => {
const {
contextId,
getDragState
} = useDragDropContext();
const ref = useRef(null);
const [dimensions, setDimensions] = useState(null);
const [isHidden, setIsHidden] = useState(false);
const monitorForLifecycle = useMonitorForLifecycle();
const updateIndicator = useCallback(({
targetLocation,
source,
destination
}) => {
if (!targetLocation) {
return setDimensions(null);
}
const isInHomeLocation = isSameLocation(source, destination);
/**
* Determines if the drop indicator should be hidden.
*
* This is desired when the current drop target would not change the position
* of the draggable.
*/
setIsHidden(isInHomeLocation);
return setDimensions(getIndicatorSizeAndOffset({
targetLocation,
isInHomeLocation,
direction,
mode,
contextId
}));
}, [contextId, direction, mode]);
/**
* This is in a `useLayoutEffect` for immediacy.
*
* When mounting (cross-axis movement) the indicator should update into
* its correct position right away, so that the drag preview can be placed
* correctly.
*/
useLayoutEffect(() => {
const dragState = getDragState();
if (!dragState.isDragging) {
return;
}
/**
* If the indicator is only just mounting then it needs an immediate
* update to have it appear in the correct position.
*/
const {
targetLocation,
sourceLocation
} = dragState;
const destination = getActualDestination({
start: sourceLocation,
target: targetLocation
});
updateIndicator({
targetLocation,
destination,
source: sourceLocation
});
return monitorForLifecycle({
onPrePendingDragUpdate({
update,
targetLocation
}) {
const {
destination = null,
source
} = update;
updateIndicator({
targetLocation,
source,
destination
});
}
});
}, [contextId, direction, getDragState, mode, monitorForLifecycle, updateIndicator]);
/**
* Scroll the indicator into view.
*
* This is in a `useLayoutEffect` for immediacy.
* Otherwise the keyboard drag preview can appear in the wrong (old) location.
*/
useLayoutEffect(() => {
if (dimensions === null) {
return;
}
/**
* If we are doing a mouse drag,
* then we don't want to scroll to the indicator.
*
* Otherwise, it will conflict with the auto-scroll addon.
*/
const dragState = getDragState();
if (!dragState.isDragging || dragState.mode !== 'SNAP') {
return;
}
const element = ref.current;
rbdInvariant(element instanceof HTMLElement);
element.scrollIntoView({
block: 'nearest'
});
}, [dimensions, getDragState]);
const {
mainAxis
} = directionMapping[direction];
const indicatorOffset = ref.current ? ref.current[mainAxis.offset] : 0;
const dynamicStyles = getDynamicStyles({
direction,
dimensions,
indicatorOffset
});
const isVirtual = mode === 'virtual';
return jsx("div", _extends({
ref: ref,
css: [baseStyles, directionStyles[direction], isVirtual && virtualStyles, isHidden && visuallyHiddenStyles]
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
,
style: dynamicStyles
}, dropIndicatorData));
};