import type { DimensionMap, DraggingState, CollectingState, DropPendingState, Published, DraggableId, DraggableDimension, DroppableDimensionMap, DraggableDimensionMap, DroppableDimension, DragImpact, DroppablePublish, DroppableId, } from '../../types'; import * as timings from '../../debug/timings'; import getDragImpact from '../get-drag-impact'; import adjustAdditionsForScrollChanges from './adjust-additions-for-scroll-changes'; import { toDraggableMap, toDroppableMap } from '../dimension-structures'; import getLiftEffect from '../get-lift-effect'; import scrollDroppable from '../droppable/scroll-droppable'; import whatIsDraggedOver from '../droppable/what-is-dragged-over'; interface Args { state: CollectingState | DropPendingState; published: Published; } const timingsKey = 'Processing dynamic changes'; export default ({ state, published, }: Args): DraggingState | DropPendingState => { timings.start(timingsKey); // TODO: update window scroll (needs to be a part of the published object) // TODO: validate. // - Check that all additions / removals have a droppable // - Check that all droppables are virtual // The scroll might be different to what is currently in the state // We want to ensure the new draggables are in step with the state const withScrollChange: DroppableDimension[] = published.modified.map( (update: DroppablePublish): DroppableDimension => { const existing: DroppableDimension = state.dimensions.droppables[update.droppableId]; const scrolled: DroppableDimension = scrollDroppable( existing, update.scroll, ); return scrolled; }, ); const droppables: DroppableDimensionMap = { ...state.dimensions.droppables, ...toDroppableMap(withScrollChange), }; const updatedAdditions: DraggableDimensionMap = toDraggableMap( adjustAdditionsForScrollChanges({ additions: published.additions, updatedDroppables: droppables, viewport: state.viewport, }), ); const draggables: DraggableDimensionMap = { ...state.dimensions.draggables, ...updatedAdditions, }; // remove all the old ones (except for the critical) // we do this so that list operations remain fast // TODO: need to test the impact of this like crazy published.removals.forEach((id: DraggableId) => { delete draggables[id]; }); const dimensions: DimensionMap = { droppables, draggables, }; const wasOverId: DroppableId | null = whatIsDraggedOver(state.impact); const wasOver: DroppableDimension | null = wasOverId ? dimensions.droppables[wasOverId] : null; const draggable: DraggableDimension = dimensions.draggables[state.critical.draggable.id]; const home: DroppableDimension = dimensions.droppables[state.critical.droppable.id]; const { impact: onLiftImpact, afterCritical } = getLiftEffect({ draggable, home, draggables, viewport: state.viewport, }); const previousImpact: DragImpact = wasOver && wasOver.isCombineEnabled ? // Cheating here // TODO: pursue a more robust approach state.impact : onLiftImpact; const impact: DragImpact = getDragImpact({ pageOffset: state.current.page.offset, draggable: dimensions.draggables[state.critical.draggable.id], draggables: dimensions.draggables, droppables: dimensions.droppables, previousImpact, viewport: state.viewport, afterCritical, }); timings.finish(timingsKey); const draggingState: DraggingState = { ...state, // eslint-disable-next-line phase: 'DRAGGING', impact, onLiftImpact, dimensions, afterCritical, // not animating this movement forceShouldAnimate: false, }; if (state.phase === 'COLLECTING') { return draggingState; } // There was a DROP_PENDING // Staying in the DROP_PENDING phase // setting isWaiting for false const dropPending: DropPendingState = { ...draggingState, // eslint-disable-next-line phase: 'DROP_PENDING', // No longer waiting reason: state.reason, isWaiting: false, }; return dropPending; };