Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ease droppable orientation restrictions #1907

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
{
"dist/react-beautiful-dnd.js": {
"bundled": 364998,
"minified": 133574,
"gzipped": 39437
"bundled": 365855,
"minified": 133626,
"gzipped": 39508
},
"dist/react-beautiful-dnd.min.js": {
"bundled": 306810,
"minified": 108365,
"gzipped": 31340
"bundled": 307667,
"minified": 108417,
"gzipped": 31402
},
"dist/react-beautiful-dnd.esm.js": {
"bundled": 240910,
"minified": 125371,
"gzipped": 32650,
"bundled": 241737,
"minified": 125403,
"gzipped": 32719,
"treeshaked": {
"rollup": {
"code": 21121,
Expand Down
15 changes: 9 additions & 6 deletions docs/sensors/keyboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,25 @@ Once a drag is started the following keyboard shortcuts can be used:
- **spacebar** <kbd>space</kbd> - drop the `<Draggable />`
- **escape** <kbd>esc</kbd> - cancel the drag

### Moving a draggable within a droppable

The following commands are also available but they depend on the `type` of `<Droppable />` that the `<Draggable />` is _currently_ in:

### Within a vertical list
#### Within a vertical list

- **Up arrow** <kbd>↑</kbd> - move a `<Draggable />` upwards in a `<Droppable />`
- **Down arrow** <kbd>↓</kbd> - move a `<Draggable />` downwards in a `<Droppable />`
- **Right arrow** <kbd>→</kbd> - move a `<Draggable />` to a `<Droppable />` to the _right_ of the current `<Droppable />` (move to new list)
- **Left arrow** <kbd>←</kbd> - move a `<Draggable />` to a `<Droppable />` to the _left_ of the current `<Droppable />` (move to new list)

### Within a horizontal list
#### Within a horizontal list

- **Up arrow** <kbd>↑</kbd> - move a `<Draggable />` to a `<Droppable />` to _above_ the current `<Droppable />` (move to new list)
- **Down arrow** <kbd>↓</kbd> - move a `<Draggable />` to a `<Droppable />` to _below_ the current `<Droppable />` (move to new list)
- **Right arrow** <kbd>→</kbd> - move a `<Draggable />` to the _right_ in the current `<Droppable />`
- **Left arrow** <kbd>←</kbd> - move a `<Draggable />` to the _left_ in the current `<Droppable />`

### Moving a draggable to another droppable

- **Arrows** <kbd>↑</kbd> <kbd>↓</kbd> <kbd>→</kbd> <kbd>←</kbd> - Move draggables to the nearest droppable.
(If the desired destination draggable exists in the same dimension that the draggables are oriented, the draggable must first reach the outermost position before it can be moved to the droppable.)

During a drag the following standard keyboard events have their default behaviour prevented (through `event.preventDefault()`) to avoid a bad experience:

- **tab** <kbd>tab ↹</kbd> - preventing tabbing
Expand Down
52 changes: 29 additions & 23 deletions src/state/move-in-direction/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
DragImpact,
} from '../../types';
import moveToNextPlace from './move-to-next-place';
import moveCrossAxis from './move-cross-axis';
import moveAxis from './move-axis';
import whatIsDraggedOver from '../droppable/what-is-dragged-over';

type Args = {|
Expand Down Expand Up @@ -59,26 +59,32 @@ export default ({ state, type }: Args): ?PublicResult => {
state.current.page.borderBoxCenter;
const { draggables, droppables } = state.dimensions;

return isMovingOnMainAxis
? moveToNextPlace({
isMovingForward,
previousPageBorderBoxCenter,
draggable,
destination: isOver,
draggables,
viewport: state.viewport,
previousClientSelection: state.current.client.selection,
previousImpact: state.impact,
afterCritical: state.afterCritical,
})
: moveCrossAxis({
isMovingForward,
previousPageBorderBoxCenter,
draggable,
isOver,
draggables,
droppables,
viewport: state.viewport,
afterCritical: state.afterCritical,
});
if (isMovingOnMainAxis) {
const moveToNextPlaceResult = moveToNextPlace({
isMovingForward,
previousPageBorderBoxCenter,
draggable,
destination: isOver,
draggables,
viewport: state.viewport,
previousClientSelection: state.current.client.selection,
previousImpact: state.impact,
afterCritical: state.afterCritical,
});
if (moveToNextPlaceResult) {
return moveToNextPlaceResult;
}
}

return moveAxis({
isMovingOnMainAxis,
isMovingForward,
previousPageBorderBoxCenter,
draggable,
isOver,
draggables,
droppables,
viewport: state.viewport,
afterCritical: state.afterCritical,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
} from '../../../types';

type GetBestDroppableArgs = {|
isMovingOnMainAxis?: boolean,
isMovingForward: boolean,
// the current position of the dragging item
pageBorderBoxCenter: Position,
Expand All @@ -33,6 +34,7 @@ const getKnownActive = (droppable: DroppableDimension): Rect => {
};

export default ({
isMovingOnMainAxis,
isMovingForward,
pageBorderBoxCenter,
source,
Expand All @@ -46,7 +48,23 @@ export default ({
}

const axis: Axis = source.axis;
const isBetweenSourceClipped = isWithin(active[axis.start], active[axis.end]);

const movementAxisStart = isMovingOnMainAxis
? axis.start
: axis.crossAxisStart;
const movementAxisEnd = isMovingOnMainAxis ? axis.end : axis.crossAxisEnd;
const transverseAxisStart = isMovingOnMainAxis
? axis.crossAxisStart
: axis.start;
const transverseAxisEnd = isMovingOnMainAxis ? axis.crossAxisEnd : axis.end;
const transverseAxisLine = isMovingOnMainAxis
? axis.crossAxisLine
: axis.line;

const isBetweenSourceClipped = isWithin(
active[transverseAxisStart],
active[transverseAxisEnd],
);
const candidates: DroppableDimension[] = toDroppableList(droppables)
// Remove the source droppable from the list
.filter((droppable: DroppableDimension): boolean => droppable !== source)
Expand All @@ -65,31 +83,31 @@ export default ({

// is the target in front of the source on the cross axis?
if (isMovingForward) {
return active[axis.crossAxisEnd] < activeOfTarget[axis.crossAxisEnd];
return active[movementAxisEnd] < activeOfTarget[movementAxisEnd];
}
// is the target behind the source on the cross axis?
return activeOfTarget[axis.crossAxisStart] < active[axis.crossAxisStart];
return activeOfTarget[movementAxisStart] < active[movementAxisStart];
})
// Must have some overlap on the main axis
.filter((droppable: DroppableDimension): boolean => {
const activeOfTarget: Rect = getKnownActive(droppable);

const isBetweenDestinationClipped = isWithin(
activeOfTarget[axis.start],
activeOfTarget[axis.end],
activeOfTarget[transverseAxisStart],
activeOfTarget[transverseAxisEnd],
);

return (
isBetweenSourceClipped(activeOfTarget[axis.start]) ||
isBetweenSourceClipped(activeOfTarget[axis.end]) ||
isBetweenDestinationClipped(active[axis.start]) ||
isBetweenDestinationClipped(active[axis.end])
isBetweenSourceClipped(activeOfTarget[transverseAxisStart]) ||
isBetweenSourceClipped(activeOfTarget[transverseAxisEnd]) ||
isBetweenDestinationClipped(active[transverseAxisStart]) ||
isBetweenDestinationClipped(active[transverseAxisEnd])
);
})
// Sort on the cross axis
.sort((a: DroppableDimension, b: DroppableDimension) => {
const first: number = getKnownActive(a)[axis.crossAxisStart];
const second: number = getKnownActive(b)[axis.crossAxisStart];
const first: number = getKnownActive(a)[movementAxisStart];
const second: number = getKnownActive(b)[movementAxisStart];

if (isMovingForward) {
return first - second;
Expand All @@ -103,8 +121,8 @@ export default ({
index: number,
array: DroppableDimension[],
): boolean =>
getKnownActive(droppable)[axis.crossAxisStart] ===
getKnownActive(array[0])[axis.crossAxisStart],
getKnownActive(droppable)[movementAxisStart] ===
getKnownActive(array[0])[movementAxisStart],
);

// no possible candidates
Expand All @@ -124,10 +142,10 @@ export default ({
const contains: DroppableDimension[] = candidates.filter(
(droppable: DroppableDimension) => {
const isWithinDroppable = isWithin(
getKnownActive(droppable)[axis.start],
getKnownActive(droppable)[axis.end],
getKnownActive(droppable)[transverseAxisStart],
getKnownActive(droppable)[transverseAxisEnd],
);
return isWithinDroppable(pageBorderBoxCenter[axis.line]);
return isWithinDroppable(pageBorderBoxCenter[transverseAxisLine]);
},
);

Expand All @@ -140,7 +158,8 @@ export default ({
// sort on the main axis and choose the first
return contains.sort(
(a: DroppableDimension, b: DroppableDimension): number =>
getKnownActive(a)[axis.start] - getKnownActive(b)[axis.start],
getKnownActive(a)[transverseAxisStart] -
getKnownActive(b)[transverseAxisStart],
)[0];
}

Expand All @@ -162,7 +181,10 @@ export default ({

// They both have the same distance -
// choose the one that is first on the main axis
return getKnownActive(a)[axis.start] - getKnownActive(b)[axis.start];
return (
getKnownActive(a)[transverseAxisStart] -
getKnownActive(b)[transverseAxisStart]
);
},
)[0];
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
Viewport,
LiftEffect,
} from '../../../types';
import getBestCrossAxisDroppable from './get-best-cross-axis-droppable';
import getBestAxisDroppable from './get-best-axis-droppable';
import getClosestDraggable from './get-closest-draggable';
// import moveToNewDroppable from './move-to-new-droppable';
import getDraggablesInsideDroppable from '../../get-draggables-inside-droppable';
Expand All @@ -19,6 +19,7 @@ import getPageBorderBoxCenter from '../../get-center-from-impact/get-page-border
import moveToNewDroppable from './move-to-new-droppable';

type Args = {|
isMovingOnMainAxis?: boolean,
isMovingForward: boolean,
// the current page center of the dragging item
previousPageBorderBoxCenter: Position,
Expand All @@ -35,6 +36,7 @@ type Args = {|
|};

export default ({
isMovingOnMainAxis,
isMovingForward,
previousPageBorderBoxCenter,
draggable,
Expand All @@ -46,7 +48,8 @@ export default ({
}: Args): ?PublicResult => {
// not considering the container scroll changes as container scrolling cancels a keyboard drag

const destination: ?DroppableDimension = getBestCrossAxisDroppable({
const destination: ?DroppableDimension = getBestAxisDroppable({
isMovingOnMainAxis,
isMovingForward,
pageBorderBoxCenter: previousPageBorderBoxCenter,
source: isOver,
Expand Down
1 change: 1 addition & 0 deletions stories/3-board.stories.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const data = {

storiesOf('board', module)
.add('simple', () => <Board initial={authorQuoteMap} />)
.add('vertically-stacked', () => <Board initial={authorQuoteMap} vertical />)
.add('dragging a clone', () => <Board initial={authorQuoteMap} useClone />)
.add('medium data set', () => <Board initial={data.medium} />)
.add('large data set', () => <Board initial={data.large} />)
Expand Down
10 changes: 8 additions & 2 deletions stories/src/board/board.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const Container = styled.div`
background-color: ${colors.B100};
min-height: 100vh;
/* like display:flex but will allow bleeding over the window width */
min-width: 100vw;
display: inline-flex;
flex-direction: ${({ vertical }) => (vertical ? 'column' : 'row')};
`;

type Props = {|
Expand All @@ -33,6 +33,7 @@ type Props = {|
isCombineEnabled?: boolean,
containerHeight?: string,
useClone?: boolean,
vertical?: boolean,
|};

type State = {|
Expand Down Expand Up @@ -123,6 +124,7 @@ export default class Board extends Component<Props, State> {
useClone,
isCombineEnabled,
withScrollableColumns,
vertical,
} = this.props;

const board = (
Expand All @@ -134,7 +136,11 @@ export default class Board extends Component<Props, State> {
isCombineEnabled={isCombineEnabled}
>
{(provided: DroppableProvided) => (
<Container ref={provided.innerRef} {...provided.droppableProps}>
<Container
ref={provided.innerRef}
vertical={vertical}
{...provided.droppableProps}
>
{ordered.map((key: string, index: number) => (
<Column
key={key}
Expand Down
3 changes: 0 additions & 3 deletions stories/src/primatives/quote-list.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ const Wrapper = styled.div`
const scrollContainerHeight: number = 250;

const DropZone = styled.div`
/* stop the list collapsing when empty */
min-height: ${scrollContainerHeight}px;

/*
not relying on the items for a margin-bottom
as it will collapse when the list is empty
Expand Down
Loading