react-beautiful-dnd
Version:
Beautiful, accessible drag and drop for lists with React.js
112 lines (95 loc) • 2.87 kB
Flow
// @flow
import type {
State,
Hooks,
DragStart,
DropResult,
} from '../types';
export default (hooks: Hooks, current: State, previous: State): void => {
const { onDragStart, onDragEnd } = hooks;
const currentPhase = current.phase;
const previousPhase = previous.phase;
// Exit early if phase in unchanged
if (currentPhase === previousPhase) {
return;
}
// Drag start
if (currentPhase === 'DRAGGING' && previousPhase !== 'DRAGGING') {
// onDragStart is optional
if (!onDragStart) {
return;
}
if (!current.drag) {
console.error('cannot fire onDragStart hook without drag state', { current, previous });
return;
}
const start: DragStart = {
draggableId: current.drag.current.id,
type: current.drag.current.type,
source: current.drag.initial.source,
};
onDragStart(start);
return;
}
// Drag end
if (currentPhase === 'DROP_COMPLETE' && previousPhase !== 'DROP_COMPLETE') {
if (!current.drop || !current.drop.result) {
console.error('cannot fire onDragEnd hook without drag state', { current, previous });
return;
}
const {
source,
destination,
draggableId,
type,
} = current.drop.result;
// Could be a cancel or a drop nowhere
if (!destination) {
onDragEnd(current.drop.result);
return;
}
// Do not publish a result.destination where nothing moved
const didMove: boolean = source.droppableId !== destination.droppableId ||
source.index !== destination.index;
if (didMove) {
onDragEnd(current.drop.result);
return;
}
const muted: DropResult = {
draggableId,
type,
source,
destination: null,
};
onDragEnd(muted);
}
// Drag ended while dragging
if (currentPhase === 'IDLE' && previousPhase === 'DRAGGING') {
if (!previous.drag) {
console.error('cannot fire onDragEnd for cancel because cannot find previous drag');
return;
}
const result: DropResult = {
draggableId: previous.drag.current.id,
type: previous.drag.current.type,
source: previous.drag.initial.source,
destination: null,
};
onDragEnd(result);
}
// Drag ended during a drop animation. Not super sure how this can even happen.
// This is being really safe
if (currentPhase === 'IDLE' && previousPhase === 'DROP_ANIMATING') {
if (!previous.drop || !previous.drop.pending) {
console.error('cannot fire onDragEnd for cancel because cannot find previous pending drop');
return;
}
const result: DropResult = {
draggableId: previous.drop.pending.result.draggableId,
type: previous.drop.pending.result.type,
source: previous.drop.pending.result.source,
destination: null,
};
onDragEnd(result);
}
};