UNPKG

react-beautiful-dnd-next

Version:

Beautiful and accessible drag and drop for lists with React

163 lines (143 loc) 4.74 kB
// @flow import invariant from 'tiny-invariant'; import type { DimensionMap, DraggingState, CollectingState, DropPendingState, Published, Critical, DraggableDimension, DraggableDimensionMap, DroppableDimensionMap, DragImpact, } from '../../types'; import * as timings from '../../debug/timings'; import whatIsDraggedOver from '../droppable/what-is-dragged-over'; import getDragImpact from '../get-drag-impact'; import getHomeOnLift from '../get-home-on-lift'; import getDragPositions from './get-drag-positions'; import updateDraggables from './update-draggables'; import updateDroppables from './update-droppables'; import withNoAnimatedDisplacement from './with-no-animated-displacement'; import recomputePlaceholders from '../recompute-placeholders'; type Args = {| state: CollectingState | DropPendingState, published: Published, |}; const timingsKey: string = 'Processing dynamic changes'; export default ({ state, published, }: Args): DraggingState | DropPendingState => { timings.start(timingsKey); // Change the subject size and scroll of droppables // will remove any subject.withPlaceholder const updatedDroppables: DroppableDimensionMap = updateDroppables({ modified: published.modified, existing: state.dimensions.droppables, viewport: state.viewport, }); const draggables: DraggableDimensionMap = updateDraggables({ updatedDroppables, // will not change during a drag criticalId: state.critical.draggable.id, existing: state.dimensions.draggables, additions: published.additions, removals: published.removals, viewport: state.viewport, }); const critical: Critical = { draggable: draggables[state.critical.draggable.id].descriptor, droppable: updatedDroppables[state.critical.droppable.id].descriptor, }; const original: DraggableDimension = state.dimensions.draggables[critical.draggable.id]; const updated: DraggableDimension = draggables[critical.draggable.id]; // TODO: this is a bit of a chicken and egg problem, but it will use the old impact for placeholders const droppables: DroppableDimensionMap = recomputePlaceholders({ draggable: updated, draggables, droppables: updatedDroppables, previousImpact: state.impact, impact: state.impact, }); const dimensions: DimensionMap = { draggables, droppables, }; // Get the updated drag positions to account for any // shift to the critical draggable const { initial, current } = getDragPositions({ initial: state.initial, current: state.current, oldClientBorderBoxCenter: original.client.borderBox.center, newClientBorderBoxCenter: updated.client.borderBox.center, viewport: state.viewport, }); // Get the impact of all of our changes // this could result in a strange snap placement (will be fixed on next move) const { impact: homeImpact, onLift } = getHomeOnLift({ draggable: updated, home: dimensions.droppables[critical.droppable.id], draggables: dimensions.draggables, viewport: state.viewport, }); // now need to calculate the impact for the current pageBorderBoxCenter const impact: DragImpact = withNoAnimatedDisplacement( getDragImpact({ pageBorderBoxCenter: current.page.borderBoxCenter, draggable: updated, draggables: dimensions.draggables, droppables: dimensions.droppables, // starting from a fresh slate previousImpact: homeImpact, viewport: state.viewport, userDirection: state.userDirection, onLift, }), ); const isOrphaned: boolean = Boolean( state.movementMode === 'SNAP' && !whatIsDraggedOver(impact), ); // TODO: try and recover? invariant( !isOrphaned, 'Dragging item no longer has a valid merge/destination after a dynamic update. This is not supported', ); // TODO: move into move visually pleasing position if using JUMP auto scrolling timings.finish(timingsKey); const draggingState: DraggingState = { // appeasing flow phase: 'DRAGGING', ...state, // eslint-disable-next-line phase: 'DRAGGING', critical, current, initial, impact, dimensions, onLift, onLiftImpact: homeImpact, // 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 = { // appeasing flow phase: 'DROP_PENDING', ...draggingState, // eslint-disable-next-line phase: 'DROP_PENDING', // No longer waiting reason: state.reason, isWaiting: false, }; return dropPending; };