import type { Rect } from 'css-box-model'; import type { DraggableId, Axis, DraggableDimension, DroppableDimension, DragImpact, LiftEffect, DisplacedBy, } from '../../types'; import getDidStartAfterCritical from '../did-start-after-critical'; import getDisplacedBy from '../get-displaced-by'; import getIsDisplaced from '../get-is-displaced'; import removeDraggableFromList from '../remove-draggable-from-list'; interface Args { draggable: DraggableDimension; pageBorderBoxWithDroppableScroll: Rect; previousImpact: DragImpact; destination: DroppableDimension; insideDestination: DraggableDimension[]; afterCritical: LiftEffect; } // exported for testing export const combineThresholdDivisor = 4; export default ({ draggable, pageBorderBoxWithDroppableScroll: targetRect, previousImpact, destination, insideDestination, afterCritical, }: Args): DragImpact | null => { if (!destination.isCombineEnabled) { return null; } const axis: Axis = destination.axis; const displacedBy: DisplacedBy = getDisplacedBy( destination.axis, draggable.displaceBy, ); const displacement: number = displacedBy.value; const targetStart: number = targetRect[axis.start]; const targetEnd: number = targetRect[axis.end]; const withoutDragging: DraggableDimension[] = removeDraggableFromList( draggable, insideDestination, ); const combineWith = withoutDragging.find((child): boolean => { const id: DraggableId = child.descriptor.id; const childRect: Rect = child.page.borderBox; const childSize: number = childRect[axis.size]; const threshold: number = childSize / combineThresholdDivisor; const didStartAfterCritical: boolean = getDidStartAfterCritical( id, afterCritical, ); const isDisplaced: boolean = getIsDisplaced({ displaced: previousImpact.displaced, id, }); /* Only combining when in the combine region As soon as a boundary is hit then no longer combining */ if (didStartAfterCritical) { // In original position // Will combine with item when inside a band if (isDisplaced) { return ( targetEnd > childRect[axis.start] + threshold && targetEnd < childRect[axis.end] - threshold ); } // child is now 'displaced' backwards from where it started // want to combine when we move backwards onto it return ( targetStart > childRect[axis.start] - displacement + threshold && targetStart < childRect[axis.end] - displacement - threshold ); } // item has moved forwards if (isDisplaced) { return ( targetEnd > childRect[axis.start] + displacement + threshold && targetEnd < childRect[axis.end] + displacement - threshold ); } // is in resting position - being moved backwards on to return ( targetStart > childRect[axis.start] + threshold && targetStart < childRect[axis.end] - threshold ); }); if (!combineWith) { return null; } const impact: DragImpact = { // no change to displacement when combining displacedBy, displaced: previousImpact.displaced, at: { type: 'COMBINE', combine: { draggableId: combineWith.descriptor.id, droppableId: destination.descriptor.id, }, }, }; return impact; };