react-beautiful-dnd-next
Version:
Beautiful and accessible drag and drop for lists with React
132 lines (118 loc) • 3.63 kB
JavaScript
// @flow
import type { Position } from 'css-box-model';
import type {
DraggableDimension,
DraggingState,
ClientPositions,
PagePositions,
DragPositions,
DragImpact,
Viewport,
DimensionMap,
UserDirection,
StateWhenUpdatesAllowed,
DroppableDimensionMap,
} from '../../../types';
import getDragImpact from '../../get-drag-impact';
import { add, subtract } from '../../position';
import getUserDirection from '../../user-direction/get-user-direction';
import recomputePlaceholders from '../../recompute-placeholders';
type Args = {|
state: StateWhenUpdatesAllowed,
clientSelection?: Position,
dimensions?: DimensionMap,
viewport?: Viewport,
// force a custom drag impact
impact?: ?DragImpact,
// provide a scroll jump request (optionally provided - and can be null)
scrollJumpRequest?: ?Position,
|};
export default ({
state,
clientSelection: forcedClientSelection,
dimensions: forcedDimensions,
viewport: forcedViewport,
impact: forcedImpact,
scrollJumpRequest,
}: Args): StateWhenUpdatesAllowed => {
// DRAGGING: can update position and impact
// COLLECTING: can update position but cannot update impact
const viewport: Viewport = forcedViewport || state.viewport;
const currentWindowScroll: Position = viewport.scroll.current;
const dimensions: DimensionMap = forcedDimensions || state.dimensions;
const clientSelection: Position =
forcedClientSelection || state.current.client.selection;
const offset: Position = subtract(
clientSelection,
state.initial.client.selection,
);
const client: ClientPositions = {
offset,
selection: clientSelection,
borderBoxCenter: add(state.initial.client.borderBoxCenter, offset),
};
const page: PagePositions = {
selection: add(client.selection, currentWindowScroll),
borderBoxCenter: add(client.borderBoxCenter, currentWindowScroll),
};
const current: DragPositions = {
client,
page,
};
const userDirection: UserDirection = getUserDirection(
state.userDirection,
state.current.page.borderBoxCenter,
current.page.borderBoxCenter,
);
// Not updating impact while bulk collecting
if (state.phase === 'COLLECTING') {
return {
// adding phase to appease flow (even though it will be overwritten by spread)
phase: 'COLLECTING',
...state,
dimensions,
viewport,
current,
userDirection,
};
}
const draggable: DraggableDimension =
dimensions.draggables[state.critical.draggable.id];
const newImpact: DragImpact =
forcedImpact ||
getDragImpact({
pageBorderBoxCenter: page.borderBoxCenter,
draggable,
draggables: dimensions.draggables,
droppables: dimensions.droppables,
previousImpact: state.impact,
viewport,
userDirection,
onLift: state.onLift,
});
const withUpdatedPlaceholders: DroppableDimensionMap = recomputePlaceholders({
draggable,
impact: newImpact,
previousImpact: state.impact,
draggables: dimensions.draggables,
droppables: dimensions.droppables,
});
// dragging!
const result: DraggingState = {
...state,
current,
userDirection,
dimensions: {
draggables: dimensions.draggables,
droppables: withUpdatedPlaceholders,
},
impact: newImpact,
viewport,
scrollJumpRequest: scrollJumpRequest || null,
// client updates can be applied as a part of a jump scroll
// this can be to immediately reverse movement to allow for a nice animation
// into the final position
forceShouldAnimate: scrollJumpRequest ? false : null,
};
return result;
};