-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cb38e79
commit ce9f997
Showing
8 changed files
with
246 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
|
||
## Rules for finding the best Droppable: | ||
|
||
### 1. Find lists on the cross axis | ||
|
||
Find the list(s) that are closest on the cross axis | ||
|
||
Conditions | ||
1. The list must have one corner with the size (height: vertical) of the source list | ||
2. The list must be visible to the user | ||
|
||
If more than one list is as close on the cross axis, then: | ||
|
||
### 2. Find the closest corner | ||
|
||
Based on the draggable items current center position, we need to find the list that | ||
has the closest corner point. That is the closest list. | ||
|
||
We do not need to consider the conditions in step 1 as they have already been applied | ||
|
||
## Rules for finding the best location within a Droppable | ||
|
||
### If moving on the main axis | ||
Move into the first / last position depending on if you are leaving the front / back of the Droppable | ||
|
||
nice | ||
|
||
### If moving on the cross axis | ||
|
||
#### Moving to empty list | ||
|
||
Move to the top (vertical list) / left (horizontal list) of the list | ||
|
||
#### Moving to populated list | ||
|
||
1. Find the draggable with the closest center position | ||
|
||
If there is more than one with the closest - choose the the one closest to the top left corner of the page | ||
|
||
2. Move below the item if the Draggables current center position is less than the destination. Otherwise, move above | ||
Below = go below | ||
Above = go above | ||
Equal = go above |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// @flow | ||
import memoizeOne from 'memoize-one'; | ||
import { distance } from './position'; | ||
import type { | ||
Axis, | ||
Position, | ||
DimensionFragment, | ||
DraggableId, | ||
DroppableId, | ||
DraggableDimension, | ||
DroppableDimension, | ||
DraggableDimensionMap, | ||
DroppableDimensionMap, | ||
} from '../types'; | ||
|
||
type DroppableCornerMap = {| | ||
[id: DroppableId]: Position[], | ||
|} | ||
|
||
type GetBestDroppableArgs = {| | ||
draggableId: DraggableId, | ||
center: Position, | ||
isMovingForward: boolean, | ||
plane: 'main-axis' | 'cross-axis', | ||
// the droppable the draggable is currently in | ||
droppableId: DroppableId, | ||
droppables: DroppableDimensionMap, | ||
draggables: DraggableDimensionMap, | ||
|} | ||
|
||
const sortOnCrossAxis = memoizeOne( | ||
(droppables: DroppableDimensionMap, axis: Axis): DroppableDimension[] => | ||
Object.keys(droppables) | ||
.map((key: DroppableId): DroppableDimension => droppables[key]) | ||
.sort((a: DroppableDimension, b: DroppableDimension) => ( | ||
a.page.withMargin[axis.crossAxisStart] - b.page.withMargin[axis.crossAxisStart] | ||
) | ||
) | ||
); | ||
|
||
type IsWithResultFn = (number) => boolean; | ||
|
||
const isWithin = (lowerBound: number, upperBound: number): IsWithResultFn => | ||
(value: number): boolean => value <= upperBound && value >= lowerBound; | ||
|
||
export default ({ | ||
isMovingForward, | ||
draggableId, | ||
center, | ||
droppableId, | ||
droppables, | ||
draggables, | ||
}: GetBestDroppableArgs): ?DroppableId => { | ||
const draggable: DraggableDimension = draggables[draggableId]; | ||
const source: DroppableDimension = droppables[droppableId]; | ||
const axis: Axis = source.axis; | ||
|
||
const sorted: DroppableDimension[] = sortOnCrossAxis(droppables, axis); | ||
|
||
const candidates: DroppableDimension[] = | ||
// 1. Remove the source droppable from the list | ||
sorted.filter((droppable: DroppableDimension): boolean => droppable !== source) | ||
// 2. Get only droppables that are on the desired side | ||
.filter((droppable: DroppableDimension): boolean => { | ||
if (isMovingForward) { | ||
// is the droppable in front of the source on the cross axis? | ||
return source.page.withMargin[axis.crossAxisEnd] <= | ||
droppable.page.withMargin[axis.crossAxisStart]; | ||
} | ||
// is the droppable behind the source on the cross axis? | ||
return droppable.page.withMargin[axis.crossAxisEnd] <= | ||
source.page.withMargin[axis.crossAxisStart]; | ||
}) | ||
// 3. is there any overlap on the main axis? | ||
.filter((droppable: DroppableDimension): boolean => { | ||
const sourceFragment: DimensionFragment = source.page.withMargin; | ||
const destinationFragment: DimensionFragment = droppable.page.withMargin; | ||
|
||
const isBetweenSourceBounds = isWithin( | ||
sourceFragment[axis.start], | ||
sourceFragment[axis.end] | ||
); | ||
const isBetweenDestBounds = isWithin( | ||
destinationFragment[axis.start], | ||
destinationFragment[axis.end] | ||
); | ||
|
||
return isBetweenSourceBounds(destinationFragment[axis.start]) || | ||
isBetweenSourceBounds(destinationFragment[axis.end]) || | ||
isBetweenDestBounds(sourceFragment[axis.start]) || | ||
isBetweenDestBounds(sourceFragment[axis.end]); | ||
}) | ||
// 4. Find the droppables that have the same cross axis value as the first item | ||
.filter((droppable: DroppableDimension, index: number, array: DroppableDimension[]): boolean => | ||
droppable.page.withMargin[axis.crossAxisStart] === | ||
array[0].page.withMargin[axis.crossAxisStart] | ||
); | ||
|
||
// no possible candidates | ||
if (!candidates.length) { | ||
return null; | ||
} | ||
|
||
// only one result - all done! | ||
if (candidates.length === 1) { | ||
return candidates[0].id; | ||
} | ||
|
||
// At this point we have a number of candidates that | ||
// all have the same axis.crossAxisStart value. | ||
// Now need to consider the main axis as a tiebreaker | ||
|
||
// 1. Get the distance to all of the corner points | ||
// 2. Find the closest corner to current center | ||
// 3. in the event of a tie: choose the corner that is closest to {x: 0, y: 0} | ||
const items: DroppableDimension[] = | ||
candidates.map((droppable: DroppableDimension): DroppableCornerMap => { | ||
const fragment: DimensionFragment = droppable.page.withMargin; | ||
const first: Position = { | ||
x: fragment[axis.crossAxisStart], | ||
y: fragment[axis.start], | ||
}; | ||
const second: Position = { | ||
x: 2, | ||
y: 3, | ||
}; | ||
return { | ||
[droppable.id]: [first, second], | ||
}; | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// @flow | ||
|
||
// finds best location for a draggable moving between droppables | ||
|
||
// will return the offset for the draggable to move, and the impact of the drag |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters