Skip to content
This repository was archived by the owner on Aug 18, 2025. It is now read-only.

Commit 5f43206

Browse files
Merge pull request #1657 from atlassian/distance-from-draggable-origin
Fix the distance calculation to support varied lists
2 parents 0388fec + 73aecc8 commit 5f43206

File tree

2 files changed

+66
-39
lines changed

2 files changed

+66
-39
lines changed

src/state/get-droppable-over.js

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type {
99
} from '../types';
1010
import { toDroppableList } from './dimension-structures';
1111
import isPositionInFrame from './visibility/is-position-in-frame';
12-
import { distance } from './position';
12+
import { distance, patch } from './position';
1313
import isWithin from './is-within';
1414

1515
// https://stackoverflow.com/questions/306316/determine-if-two-rectangles-overlap-each-other
@@ -33,17 +33,40 @@ type WithDistance = {|
3333
distance: number,
3434
id: DroppableId,
3535
|};
36-
function getFurthestAway(
37-
home: DroppableDimension,
38-
droppables: DroppableDimension[],
39-
): ?DroppableId {
40-
const center: Position = home.page.borderBox.center;
41-
42-
const sorted: WithDistance[] = droppables
43-
.map((item: DroppableDimension): WithDistance => {
36+
37+
type GetFurthestArgs = {|
38+
pageBorderBox: Rect,
39+
draggable: DraggableDimension,
40+
candidates: DroppableDimension[],
41+
|};
42+
43+
function getFurthestAway({
44+
pageBorderBox,
45+
draggable,
46+
candidates,
47+
}: GetFurthestArgs): ?DroppableId {
48+
// We are not comparing the center of the home list with the target list as it would
49+
// give preference to giant lists
50+
51+
// We are measuring the distance from where the draggable started
52+
// to where it is *hitting* the candidate
53+
// Note: The hit point might technically not be in the bounds of the candidate
54+
55+
const startCenter: Position = draggable.page.borderBox.center;
56+
const sorted: WithDistance[] = candidates
57+
.map((candidate: DroppableDimension): WithDistance => {
58+
const axis: Axis = candidate.axis;
59+
const target: Position = patch(
60+
candidate.axis.line,
61+
// use the current center of the dragging item on the main axis
62+
pageBorderBox.center[axis.line],
63+
// use the center of the list on the cross axis
64+
candidate.page.borderBox.center[axis.crossAxisLine],
65+
);
66+
4467
return {
45-
id: item.descriptor.id,
46-
distance: distance(center, item.page.borderBox.center),
68+
id: candidate.descriptor.id,
69+
distance: distance(startCenter, target),
4770
};
4871
})
4972
// largest value will be first
@@ -127,6 +150,9 @@ export default function getDroppableOver({
127150
// Multiple options returned
128151
// Should only occur with really large items
129152
// Going to use fallback: distance from home
130-
const home: DroppableDimension = droppables[draggable.descriptor.droppableId];
131-
return getFurthestAway(home, candidates);
153+
return getFurthestAway({
154+
pageBorderBox,
155+
draggable,
156+
candidates,
157+
});
132158
}

test/unit/state/get-droppable-over/preferencing.spec.js

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import getDroppableOver from '../../../../src/state/get-droppable-over';
1414
import { toDroppableMap } from '../../../../src/state/dimension-structures';
1515
import { afterCrossAxisPoint } from '../../../util/after-point';
1616

17-
const droppableLarge: DroppableDimension = getDroppableDimension({
17+
const droppableOrigin: DroppableDimension = getDroppableDimension({
1818
descriptor: {
1919
id: 'large',
2020
type: 'standard',
@@ -28,7 +28,7 @@ const droppableLarge: DroppableDimension = getDroppableDimension({
2828
},
2929
});
3030

31-
const droppableSmall: DroppableDimension = getDroppableDimension({
31+
const droppableFirst: DroppableDimension = getDroppableDimension({
3232
descriptor: {
3333
id: 'small',
3434
type: 'standard',
@@ -42,7 +42,7 @@ const droppableSmall: DroppableDimension = getDroppableDimension({
4242
},
4343
});
4444

45-
const droppableSecondary: DroppableDimension = getDroppableDimension({
45+
const droppableSecond: DroppableDimension = getDroppableDimension({
4646
descriptor: {
4747
id: 'secondary',
4848
type: 'standard',
@@ -52,20 +52,21 @@ const droppableSecondary: DroppableDimension = getDroppableDimension({
5252
top: 1000,
5353
left: 1200,
5454
right: 1300,
55-
bottom: 1100,
55+
// This is really tall to test the distance calculation against varied lists
56+
bottom: 8000,
5657
},
5758
});
5859

59-
const droppableTertiary: DroppableDimension = getDroppableDimension({
60+
const droppableThird: DroppableDimension = getDroppableDimension({
6061
descriptor: {
6162
id: 'tertiary',
6263
type: 'standard',
6364
mode: 'standard',
6465
},
6566
borderBox: {
6667
top: 1000,
67-
left: 1100,
68-
right: 1200,
68+
left: 1400,
69+
right: 1500,
6970
bottom: 1100,
7071
},
7172
});
@@ -74,10 +75,10 @@ const draggable: DraggableDimension = getDraggableDimension({
7475
descriptor: {
7576
id: 'my draggable',
7677
index: 0,
77-
type: droppableLarge.descriptor.type,
78-
droppableId: droppableLarge.descriptor.id,
78+
type: droppableOrigin.descriptor.type,
79+
droppableId: droppableOrigin.descriptor.id,
7980
},
80-
borderBox: droppableLarge.client.borderBox,
81+
borderBox: droppableOrigin.client.borderBox,
8182
});
8283

8384
/**
@@ -86,28 +87,28 @@ const draggable: DraggableDimension = getDraggableDimension({
8687
*/
8788
it('should prefer the furthest away droppable when multiple lists are hit', () => {
8889
const offset = getOffsetForCrossAxisEndEdge({
89-
crossAxisEndEdgeOn: droppableTertiary.page.borderBox.center,
90+
crossAxisEndEdgeOn: droppableThird.page.borderBox.center,
9091
dragging: draggable.page.borderBox,
91-
axis: droppableTertiary.axis,
92+
axis: droppableThird.axis,
9293
});
9394

9495
const pageBorderBox: Rect = offsetRectByPosition(
9596
draggable.page.borderBox,
96-
afterCrossAxisPoint(droppableTertiary.axis, offset),
97+
afterCrossAxisPoint(droppableThird.axis, offset),
9798
);
9899

99100
const result = getDroppableOver({
100101
pageBorderBox,
101102
draggable,
102103
droppables: toDroppableMap([
103-
droppableLarge,
104-
droppableSmall,
105-
droppableSecondary,
106-
droppableTertiary,
104+
droppableOrigin,
105+
droppableFirst,
106+
droppableSecond,
107+
droppableThird,
107108
]),
108109
});
109110

110-
expect(result).toEqual(droppableTertiary.descriptor.id);
111+
expect(result).toEqual(droppableThird.descriptor.id);
111112
});
112113

113114
/**
@@ -116,26 +117,26 @@ it('should prefer the furthest away droppable when multiple lists are hit', () =
116117
*/
117118
it('should prefer the second furthest away droppable when multiple lists are hit', () => {
118119
const offset = getOffsetForCrossAxisEndEdge({
119-
crossAxisEndEdgeOn: droppableSecondary.page.borderBox.center,
120+
crossAxisEndEdgeOn: droppableSecond.page.borderBox.center,
120121
dragging: draggable.page.borderBox,
121-
axis: droppableSecondary.axis,
122+
axis: droppableSecond.axis,
122123
});
123124

124125
const pageBorderBox: Rect = offsetRectByPosition(
125126
draggable.page.borderBox,
126-
afterCrossAxisPoint(droppableSecondary.axis, offset),
127+
afterCrossAxisPoint(droppableSecond.axis, offset),
127128
);
128129

129130
const result = getDroppableOver({
130131
pageBorderBox,
131132
draggable,
132133
droppables: toDroppableMap([
133-
droppableLarge,
134-
droppableSmall,
135-
droppableSecondary,
136-
droppableTertiary,
134+
droppableOrigin,
135+
droppableFirst,
136+
droppableSecond,
137+
droppableThird,
137138
]),
138139
});
139140

140-
expect(result).toEqual(droppableSecondary.descriptor.id);
141+
expect(result).toEqual(droppableSecond.descriptor.id);
141142
});

0 commit comments

Comments
 (0)