Skip to content

Commit

Permalink
Refactor measuring and collision detection
Browse files Browse the repository at this point in the history
  • Loading branch information
Clauderic Demers committed Jan 4, 2022
1 parent 60b608a commit 9719ab8
Show file tree
Hide file tree
Showing 68 changed files with 708 additions and 597 deletions.
84 changes: 45 additions & 39 deletions packages/core/src/components/DndContext/DndContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
getInitialState,
reducer,
} from '../../store';
import type {ViewRect} from '../../types';
import {DndMonitorContext, DndMonitorState} from '../../hooks/monitor';
import {
useAutoScroller,
Expand All @@ -36,6 +35,7 @@ import {
useSensorSetup,
useClientRect,
useClientRects,
useWindowRect,
useRect,
useScrollOffsets,
} from '../../hooks/utilities';
Expand All @@ -59,12 +59,13 @@ import {
defaultCoordinates,
getAdjustedRect,
getRectDelta,
getViewRect,
rectIntersection,
} from '../../utilities';
import {getTransformAgnosticClientRect} from '../../utilities/rect';
import {applyModifiers, Modifiers} from '../../modifiers';
import type {Active, DataRef, Over} from '../../store/types';
import type {
ClientRect,
DragStartEvent,
DragCancelEvent,
DragEndEvent,
Expand Down Expand Up @@ -97,13 +98,18 @@ export interface Props {
onDragCancel?(event: DragCancelEvent): void;
}

export interface DraggableMeasuring {
measure(node: HTMLElement): ViewRect;
interface Measuring {
measure(node: HTMLElement): ClientRect;
}

export interface DraggableMeasuring extends Measuring {}

export interface DragOverlayMeasuring extends Measuring {}

export interface MeasuringConfiguration {
draggable?: Partial<DraggableMeasuring>;
droppable?: Partial<DroppableMeasuring>;
dragOverlay?: Partial<DragOverlayMeasuring>;
}

export interface CancelDropArguments extends DragEndEvent {}
Expand Down Expand Up @@ -180,7 +186,7 @@ export const DndContext = memo(function DndContext({
return droppableContainers.getEnabled();
}, [droppableContainers]);
const {
layoutRectMap: droppableRects,
rectMap: droppableRects,
recomputeLayouts,
willRecomputeLayouts,
} = useDroppableMeasuring(enabledDroppableContainers, {
Expand All @@ -194,45 +200,37 @@ export const DndContext = memo(function DndContext({
: null;
const activeNodeRect = useRect(
activeNode,
measuring?.draggable?.measure ?? getViewRect
measuring?.draggable?.measure ?? getTransformAgnosticClientRect
);
const containerNodeRect = useClientRect(
activeNode ? activeNode.parentElement : null
);
const activeNodeClientRect = useClientRect(activeNode);
const initialActiveNodeRectRef = useRef<ViewRect | null>(null);
const initialActiveNodeRect = initialActiveNodeRectRef.current;
const sensorContext = useRef<SensorContext>({
active: null,
activeNode,
collisionRect: null,
droppableRects,
draggableNodes,
draggingNode: null,
draggingNodeRect: null,
droppableContainers,
over: null,
scrollableAncestors: [],
scrollAdjustedTranslate: null,
translatedRect: null,
});
const overNode = droppableContainers.getNodeFor(
sensorContext.current.over?.id
);
const windowRect = useClientRect(
activeNode ? activeNode.ownerDocument.defaultView : null
);
const containerNodeRect = useClientRect(
activeNode ? activeNode.parentElement : null
);
const scrollableAncestors = useScrollableAncestors(
activeId ? overNode ?? activeNode : null
);
const scrollableAncestorRects = useClientRects(scrollableAncestors);

const dragOverlay = useDragOverlayMeasuring({
disabled: activeId == null,
forceRecompute: willRecomputeLayouts,
measure: measuring?.dragOverlay?.measure,
});

// Use the rect of the drag overlay if it is mounted
const draggingNode = dragOverlay.nodeRef.current ?? activeNode;
const draggingNodeRect = dragOverlay.rect ?? activeNodeRect;
const initialActiveNodeRectRef = useRef<ClientRect | null>(null);
const initialActiveNodeRect = initialActiveNodeRectRef.current;

// The delta between the previous and new position of the draggable node
// is only relevant when there is no drag overlay
Expand All @@ -241,6 +239,18 @@ export const DndContext = memo(function DndContext({
? getRectDelta(activeNodeRect, initialActiveNodeRect)
: defaultCoordinates;

// Get the window rect of the dragging node
const windowRect = useWindowRect(
draggingNode ? draggingNode.ownerDocument.defaultView : null
);

// Get scrollable ancestors of the dragging node
const scrollableAncestors = useScrollableAncestors(
activeId ? overNode ?? draggingNode : null
);
const scrollableAncestorRects = useClientRects(scrollableAncestors as any);

// Apply modifiers
const modifiedTranslate = applyModifiers(modifiers, {
transform: {
x: translate.x - nodeRectDelta.x,
Expand All @@ -250,7 +260,7 @@ export const DndContext = memo(function DndContext({
},
activatorEvent,
active,
activeNodeRect: activeNodeClientRect,
activeNodeRect,
containerNodeRect,
draggingNodeRect,
over: sensorContext.current.over,
Expand All @@ -268,13 +278,10 @@ export const DndContext = memo(function DndContext({

const scrollAdjustedTranslate = add(modifiedTranslate, scrollAdjustment);

const translatedRect = draggingNodeRect
const collisionRect = draggingNodeRect
? getAdjustedRect(draggingNodeRect, modifiedTranslate)
: null;

const collisionRect = translatedRect
? getAdjustedRect(translatedRect, scrollAdjustment)
: null;
const overId =
active && collisionRect
? collisionDetection({
Expand Down Expand Up @@ -393,14 +400,15 @@ export const DndContext = memo(function DndContext({
if (event) {
setMonitorState({type, event});
}
});

if (event) {
const {onDragCancel, onDragEnd} = latestProps.current;
const handler = type === Action.DragEnd ? onDragEnd : onDragCancel;
if (event) {
const {onDragCancel, onDragEnd} = latestProps.current;
const handler =
type === Action.DragEnd ? onDragEnd : onDragCancel;

handler?.(event);
}
handler?.(event);
}
});
};
}
},
Expand Down Expand Up @@ -539,35 +547,35 @@ export const DndContext = memo(function DndContext({
collisionRect,
droppableRects,
draggableNodes,
draggingNode,
draggingNodeRect,
droppableContainers,
over,
scrollableAncestors,
scrollAdjustedTranslate: scrollAdjustedTranslate,
translatedRect,
};

activeRects.current = {
initial: draggingNodeRect,
translated: translatedRect,
translated: collisionRect,
};
}, [
active,
activeNode,
collisionRect,
draggableNodes,
draggingNode,
draggingNodeRect,
droppableRects,
droppableContainers,
over,
scrollableAncestors,
scrollAdjustedTranslate,
translatedRect,
]);

useAutoScroller({
...getAutoScrollerOptions(),
draggingRect: translatedRect,
draggingRect: collisionRect,
pointerCoordinates,
scrollableAncestors,
scrollableAncestorRects,
Expand All @@ -578,7 +586,6 @@ export const DndContext = memo(function DndContext({
active,
activeNode,
activeNodeRect,
activeNodeClientRect,
activatorEvent,
activators,
ariaDescribedById: {
Expand All @@ -602,7 +609,6 @@ export const DndContext = memo(function DndContext({
}, [
active,
activeNode,
activeNodeClientRect,
activeNodeRect,
activatorEvent,
activators,
Expand Down
39 changes: 21 additions & 18 deletions packages/core/src/components/DragOverlay/DragOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {getRelativeTransformOrigin} from '../../utilities';
import {applyModifiers, Modifiers} from '../../modifiers';
import {ActiveDraggableContext} from '../DndContext';
import {useDndContext} from '../../hooks';
import type {ViewRect} from '../../types';
import {useDropAnimation, DropAnimation} from './hooks';
import type {ClientRect} from '../../types';
import {useDropAnimation, defaultDropAnimation, DropAnimation} from './hooks';

type TransitionGetter = (
activatorEvent: Event | null
Expand All @@ -30,12 +30,6 @@ const defaultTransition: TransitionGetter = (activatorEvent) => {
return isKeyboardActivator ? 'transform 250ms ease' : undefined;
};

export const defaultDropAnimation: DropAnimation = {
duration: 250,
easing: 'ease',
dragSourceOpacity: 0,
};

export const DragOverlay = React.memo(
({
adjustScale = false,
Expand All @@ -51,7 +45,6 @@ export const DragOverlay = React.memo(
const {
active,
activeNodeRect,
activeNodeClientRect,
containerNodeRect,
draggableNodes,
activatorEvent,
Expand All @@ -65,7 +58,7 @@ export const DragOverlay = React.memo(
const modifiedTransform = applyModifiers(modifiers, {
activatorEvent,
active,
activeNodeRect: activeNodeClientRect,
activeNodeRect,
containerNodeRect,
draggingNodeRect: dragOverlay.rect,
over,
Expand All @@ -84,31 +77,41 @@ export const DragOverlay = React.memo(
scaleY: 1,
};

const initialNodeRect = useLazyMemo<ViewRect | null>(
const initialRect = useLazyMemo<ClientRect | null>(
(previousValue) => {
if (isDragging) {
return previousValue ?? activeNodeRect;
if (previousValue) {
return previousValue;
}

if (!activeNodeRect) {
return null;
}

return {
...activeNodeRect,
};
}

return null;
},
[isDragging, activeNodeRect]
);
const style: React.CSSProperties | undefined = initialNodeRect
const style: React.CSSProperties | undefined = initialRect
? {
position: 'fixed',
width: initialNodeRect.width,
height: initialNodeRect.height,
top: initialNodeRect.top,
left: initialNodeRect.left,
width: initialRect.width,
height: initialRect.height,
top: initialRect.top,
left: initialRect.left,
zIndex,
transform: CSS.Transform.toString(finalTransform),
touchAction: 'none',
transformOrigin:
adjustScale && activatorEvent
? getRelativeTransformOrigin(
activatorEvent as MouseEvent | KeyboardEvent | TouchEvent,
initialNodeRect as any
initialRect
)
: undefined,
transition:
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/components/DragOverlay/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {useDropAnimation} from './useDropAnimation';
export {useDropAnimation, defaultDropAnimation} from './useDropAnimation';
export type {DropAnimation} from './useDropAnimation';
Loading

0 comments on commit 9719ab8

Please sign in to comment.