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

232 lines (223 loc) 7.23 kB
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)); };