react-beautiful-dnd
Advanced tools
Comparing version 2.1.1 to 2.2.0
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.lift = exports.dropAnimationFinished = exports.cancel = exports.drop = exports.completeDrop = exports.clean = exports.moveForward = exports.moveBackward = exports.moveByWindowScroll = exports.move = exports.updateDroppableDimensionScroll = exports.publishDroppableDimension = exports.publishDraggableDimension = exports.requestDimensions = undefined; | ||
exports.lift = exports.dropAnimationFinished = exports.cancel = exports.drop = exports.completeDrop = exports.clean = exports.crossAxisMoveBackward = exports.crossAxisMoveForward = exports.moveForward = exports.moveBackward = exports.moveByWindowScroll = exports.move = exports.updateDroppableDimensionIsEnabled = exports.updateDroppableDimensionScroll = exports.publishDroppableDimension = exports.publishDraggableDimension = exports.requestDimensions = undefined; | ||
@@ -13,5 +13,5 @@ var _noImpact = require('./no-impact'); | ||
var _getNewHomeClientOffset = require('./get-new-home-client-offset'); | ||
var _getNewHomeClientCenter = require('./get-new-home-client-center'); | ||
var _getNewHomeClientOffset2 = _interopRequireDefault(_getNewHomeClientOffset); | ||
var _getNewHomeClientCenter2 = _interopRequireDefault(_getNewHomeClientCenter); | ||
@@ -24,10 +24,12 @@ var _position = require('./position'); | ||
var getScrollDiff = function getScrollDiff(initial, current, droppable) { | ||
var getScrollDiff = function getScrollDiff(_ref) { | ||
var initial = _ref.initial, | ||
current = _ref.current, | ||
droppable = _ref.droppable; | ||
var windowScrollDiff = (0, _position.subtract)(initial.windowScroll, current.windowScroll); | ||
var droppableScrollDiff = (0, _position.subtract)(droppable.scroll.initial, droppable.scroll.current); | ||
return { | ||
window: windowScrollDiff, | ||
droppable: droppableScrollDiff | ||
}; | ||
var droppableScrollDiff = droppable ? (0, _position.subtract)(droppable.scroll.initial, droppable.scroll.current) : origin; | ||
return (0, _position.add)(windowScrollDiff, droppableScrollDiff); | ||
}; | ||
@@ -48,3 +50,3 @@ | ||
var completeLift = function completeLift(id, type, client, page, windowScroll) { | ||
var completeLift = function completeLift(id, type, client, page, windowScroll, isScrollAllowed) { | ||
return { | ||
@@ -57,3 +59,4 @@ type: 'COMPLETE_LIFT', | ||
page: page, | ||
windowScroll: windowScroll | ||
windowScroll: windowScroll, | ||
isScrollAllowed: isScrollAllowed | ||
} | ||
@@ -87,2 +90,12 @@ }; | ||
var updateDroppableDimensionIsEnabled = exports.updateDroppableDimensionIsEnabled = function updateDroppableDimensionIsEnabled(id, isEnabled) { | ||
return { | ||
type: 'UPDATE_DROPPABLE_DIMENSION_IS_ENABLED', | ||
payload: { | ||
id: id, | ||
isEnabled: isEnabled | ||
} | ||
}; | ||
}; | ||
var move = exports.move = function move(id, client, page, windowScroll) { | ||
@@ -124,2 +137,16 @@ return { | ||
var crossAxisMoveForward = exports.crossAxisMoveForward = function crossAxisMoveForward(id) { | ||
return { | ||
type: 'CROSS_AXIS_MOVE_FORWARD', | ||
payload: id | ||
}; | ||
}; | ||
var crossAxisMoveBackward = exports.crossAxisMoveBackward = function crossAxisMoveBackward(id) { | ||
return { | ||
type: 'CROSS_AXIS_MOVE_BACKWARD', | ||
payload: id | ||
}; | ||
}; | ||
var clean = exports.clean = function clean() { | ||
@@ -132,7 +159,7 @@ return { | ||
var animateDrop = function animateDrop(_ref) { | ||
var trigger = _ref.trigger, | ||
newHomeOffset = _ref.newHomeOffset, | ||
impact = _ref.impact, | ||
result = _ref.result; | ||
var animateDrop = function animateDrop(_ref2) { | ||
var trigger = _ref2.trigger, | ||
newHomeOffset = _ref2.newHomeOffset, | ||
impact = _ref2.impact, | ||
result = _ref2.result; | ||
return { | ||
@@ -183,4 +210,4 @@ type: 'DROP_ANIMATE', | ||
var sourceDroppable = state.dimension.droppable[initial.source.droppableId]; | ||
var destinationDroppable = impact.destination ? state.dimension.droppable[impact.destination.droppableId] : null; | ||
var droppable = impact.destination ? state.dimension.droppable[impact.destination.droppableId] : null; | ||
var draggable = state.dimension.draggable[current.id]; | ||
@@ -194,14 +221,13 @@ var result = { | ||
var scrollDiff = getScrollDiff(initial, current, sourceDroppable); | ||
var newHomeOffset = (0, _getNewHomeClientOffset2.default)({ | ||
var newCenter = (0, _getNewHomeClientCenter2.default)({ | ||
movement: impact.movement, | ||
clientOffset: current.client.offset, | ||
pageOffset: current.page.offset, | ||
droppableScrollDiff: scrollDiff.droppable, | ||
windowScrollDiff: scrollDiff.window, | ||
draggable: draggable, | ||
draggables: state.dimension.draggable, | ||
axis: destinationDroppable ? destinationDroppable.axis : null | ||
destination: droppable | ||
}); | ||
var clientOffset = (0, _position.subtract)(newCenter, draggable.client.withMargin.center); | ||
var scrollDiff = getScrollDiff({ initial: initial, current: current, droppable: droppable }); | ||
var newHomeOffset = (0, _position.add)(clientOffset, scrollDiff); | ||
var isAnimationRequired = !(0, _position.isEqual)(current.client.offset, newHomeOffset); | ||
@@ -259,7 +285,7 @@ | ||
var scrollDiff = getScrollDiff(initial, current, droppable); | ||
var scrollDiff = getScrollDiff({ initial: initial, current: current, droppable: droppable }); | ||
dispatch(animateDrop({ | ||
trigger: 'CANCEL', | ||
newHomeOffset: (0, _position.add)(scrollDiff.droppable, scrollDiff.window), | ||
newHomeOffset: scrollDiff, | ||
impact: _noImpact2.default, | ||
@@ -291,3 +317,3 @@ result: result | ||
var lift = exports.lift = function lift(id, type, client, page, windowScroll) { | ||
var lift = exports.lift = function lift(id, type, client, page, windowScroll, isScrollAllowed) { | ||
return function (dispatch, getState) { | ||
@@ -323,3 +349,3 @@ (function () { | ||
} | ||
dispatch(completeLift(id, type, client, page, windowScroll)); | ||
dispatch(completeLift(id, type, client, page, windowScroll, isScrollAllowed)); | ||
}); | ||
@@ -326,0 +352,0 @@ }); |
@@ -9,5 +9,9 @@ 'use strict'; | ||
line: 'y', | ||
crossLine: 'x', | ||
start: 'top', | ||
end: 'bottom', | ||
size: 'height' | ||
size: 'height', | ||
crossAxisStart: 'left', | ||
crossAxisEnd: 'right', | ||
crossAxisSize: 'width' | ||
}; | ||
@@ -18,5 +22,9 @@ | ||
line: 'x', | ||
crossLine: 'y', | ||
start: 'left', | ||
end: 'right', | ||
size: 'width' | ||
size: 'width', | ||
crossAxisStart: 'top', | ||
crossAxisEnd: 'bottom', | ||
crossAxisSize: 'height' | ||
}; |
@@ -6,9 +6,15 @@ 'use strict'; | ||
}); | ||
exports.getDroppableDimension = exports.getDraggableDimension = exports.noMargin = undefined; | ||
exports.getDroppableDimension = exports.getDraggableDimension = exports.noSpacing = undefined; | ||
var _axis = require('./axis'); | ||
var _getClientRect = require('./get-client-rect'); | ||
var _getClientRect2 = _interopRequireDefault(_getClientRect); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var origin = { x: 0, y: 0 }; | ||
var noMargin = exports.noMargin = { | ||
var noSpacing = exports.noSpacing = { | ||
top: 0, | ||
@@ -24,32 +30,24 @@ right: 0, | ||
bottom = clientRect.bottom, | ||
left = clientRect.left, | ||
width = clientRect.width, | ||
height = clientRect.height; | ||
left = clientRect.left; | ||
return { | ||
return (0, _getClientRect2.default)({ | ||
top: top + point.y, | ||
left: left + point.x, | ||
bottom: bottom + point.y, | ||
right: right + point.x, | ||
height: height, | ||
width: width | ||
}; | ||
right: right + point.x | ||
}); | ||
}; | ||
var getWithMargin = function getWithMargin(clientRect, margin) { | ||
var getWithSpacing = function getWithSpacing(clientRect, spacing) { | ||
var top = clientRect.top, | ||
right = clientRect.right, | ||
bottom = clientRect.bottom, | ||
left = clientRect.left, | ||
height = clientRect.height, | ||
width = clientRect.width; | ||
left = clientRect.left; | ||
return { | ||
top: top + margin.top, | ||
left: left + margin.left, | ||
bottom: bottom + margin.bottom, | ||
right: right + margin.right, | ||
height: height + margin.top + margin.bottom, | ||
width: width + margin.left + margin.right | ||
}; | ||
return (0, _getClientRect2.default)({ | ||
top: top + spacing.top, | ||
left: left + spacing.left, | ||
bottom: bottom + spacing.bottom, | ||
right: right + spacing.right | ||
}); | ||
}; | ||
@@ -59,12 +57,20 @@ | ||
var point = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : origin; | ||
return { | ||
var rect = (0, _getClientRect2.default)({ | ||
top: initial.top + point.y, | ||
left: initial.left + point.x, | ||
bottom: initial.bottom + point.y, | ||
right: initial.right + point.x, | ||
width: initial.width, | ||
height: initial.height, | ||
right: initial.right + point.x | ||
}); | ||
return { | ||
top: rect.top, | ||
right: rect.right, | ||
bottom: rect.bottom, | ||
left: rect.left, | ||
width: rect.width, | ||
height: rect.height, | ||
center: { | ||
x: (initial.right + point.x + (initial.left + point.x)) / 2, | ||
y: (initial.bottom + point.y + (initial.top + point.y)) / 2 | ||
x: (rect.right + rect.left) / 2, | ||
y: (rect.bottom + rect.top) / 2 | ||
} | ||
@@ -79,3 +85,3 @@ }; | ||
_ref$margin = _ref.margin, | ||
margin = _ref$margin === undefined ? noMargin : _ref$margin, | ||
margin = _ref$margin === undefined ? noSpacing : _ref$margin, | ||
_ref$windowScroll = _ref.windowScroll, | ||
@@ -85,3 +91,2 @@ windowScroll = _ref$windowScroll === undefined ? origin : _ref$windowScroll; | ||
var withScroll = getWithPosition(clientRect, windowScroll); | ||
var withScrollAndMargin = getWithMargin(withScroll, margin); | ||
@@ -94,3 +99,3 @@ var dimension = { | ||
withoutMargin: getFragment(clientRect), | ||
withMargin: getFragment(getWithMargin(clientRect, margin)) | ||
withMargin: getFragment(getWithSpacing(clientRect, margin)) | ||
}, | ||
@@ -100,3 +105,3 @@ | ||
withoutMargin: getFragment(withScroll), | ||
withMargin: getFragment(withScrollAndMargin) | ||
withMargin: getFragment(getWithSpacing(withScroll, margin)) | ||
} | ||
@@ -108,2 +113,11 @@ }; | ||
var add = function add(spacing1, spacing2) { | ||
return { | ||
top: spacing1.top + spacing2.top, | ||
left: spacing1.left + spacing2.left, | ||
right: spacing1.right + spacing2.right, | ||
bottom: spacing1.bottom + spacing2.bottom | ||
}; | ||
}; | ||
var getDroppableDimension = exports.getDroppableDimension = function getDroppableDimension(_ref2) { | ||
@@ -115,13 +129,18 @@ var id = _ref2.id, | ||
_ref2$margin = _ref2.margin, | ||
margin = _ref2$margin === undefined ? noMargin : _ref2$margin, | ||
margin = _ref2$margin === undefined ? noSpacing : _ref2$margin, | ||
_ref2$padding = _ref2.padding, | ||
padding = _ref2$padding === undefined ? noSpacing : _ref2$padding, | ||
_ref2$windowScroll = _ref2.windowScroll, | ||
windowScroll = _ref2$windowScroll === undefined ? origin : _ref2$windowScroll, | ||
_ref2$scroll = _ref2.scroll, | ||
scroll = _ref2$scroll === undefined ? origin : _ref2$scroll; | ||
scroll = _ref2$scroll === undefined ? origin : _ref2$scroll, | ||
_ref2$isEnabled = _ref2.isEnabled, | ||
isEnabled = _ref2$isEnabled === undefined ? true : _ref2$isEnabled; | ||
var withMargin = getWithSpacing(clientRect, margin); | ||
var withWindowScroll = getWithPosition(clientRect, windowScroll); | ||
var withWindowScrollAndMargin = getWithMargin(withWindowScroll, margin); | ||
var dimension = { | ||
id: id, | ||
isEnabled: isEnabled, | ||
axis: direction === 'vertical' ? _axis.vertical : _axis.horizontal, | ||
@@ -133,5 +152,11 @@ scroll: { | ||
}, | ||
client: { | ||
withoutMargin: getFragment(clientRect), | ||
withMargin: getFragment(withMargin), | ||
withMarginAndPadding: getFragment(getWithSpacing(withMargin, padding)) | ||
}, | ||
page: { | ||
withoutMargin: getFragment(withWindowScroll), | ||
withMargin: getFragment(withWindowScrollAndMargin) | ||
withMargin: getFragment(getWithSpacing(withWindowScroll, margin)), | ||
withMarginAndPadding: getFragment(getWithSpacing(withWindowScroll, add(margin, padding))) | ||
} | ||
@@ -138,0 +163,0 @@ }; |
@@ -23,9 +23,19 @@ 'use strict'; | ||
exports.default = function (_ref) { | ||
var page = _ref.page, | ||
withinDroppable = _ref.withinDroppable, | ||
draggableId = _ref.draggableId, | ||
draggables = _ref.draggables, | ||
droppables = _ref.droppables; | ||
var getDroppablesScrollDiff = function getDroppablesScrollDiff(_ref) { | ||
var sourceDroppable = _ref.sourceDroppable, | ||
destinationDroppable = _ref.destinationDroppable, | ||
line = _ref.line; | ||
var sourceScrollDiff = sourceDroppable.scroll.initial[line] - sourceDroppable.scroll.current[line]; | ||
var destinationScrollDiff = destinationDroppable.scroll.initial[line] - destinationDroppable.scroll.current[line]; | ||
return destinationScrollDiff - sourceScrollDiff; | ||
}; | ||
exports.default = function (_ref2) { | ||
var page = _ref2.page, | ||
withinDroppable = _ref2.withinDroppable, | ||
draggableId = _ref2.draggableId, | ||
draggables = _ref2.draggables, | ||
droppables = _ref2.droppables; | ||
var droppableId = (0, _getDroppableOver2.default)(page, droppables); | ||
@@ -37,12 +47,22 @@ | ||
var droppable = droppables[droppableId]; | ||
var axis = droppable.axis; | ||
if (!droppable.isEnabled) { | ||
return { | ||
movement: _noImpact.noMovement, | ||
direction: axis.direction, | ||
destination: null | ||
}; | ||
} | ||
var insideDroppable = (0, _getDraggablesInsideDroppable2.default)(droppable, draggables); | ||
var newCenter = withinDroppable.center; | ||
var draggingDimension = draggables[draggableId]; | ||
var droppableDimension = droppables[droppableId]; | ||
var isWithinHomeDroppable = draggingDimension.droppableId === droppableId; | ||
var insideDroppable = (0, _getDraggablesInsideDroppable2.default)(droppableDimension, draggables); | ||
var axis = droppableDimension.axis; | ||
var draggableCenter = draggingDimension.page.withoutMargin.center; | ||
var isBeyondStartPosition = newCenter[axis.line] - draggableCenter[axis.line] > 0; | ||
var shouldDisplaceItemsForward = isWithinHomeDroppable ? isBeyondStartPosition : false; | ||
@@ -56,2 +76,11 @@ var moved = insideDroppable.filter(function (dimension) { | ||
if (!isWithinHomeDroppable) { | ||
var scrollDiff = getDroppablesScrollDiff({ | ||
sourceDroppable: droppables[draggingDimension.droppableId], | ||
destinationDroppable: droppable, | ||
line: axis.line | ||
}); | ||
return newCenter[axis.line] - scrollDiff < fragment[axis.end]; | ||
} | ||
if (isBeyondStartPosition) { | ||
@@ -74,4 +103,15 @@ if (fragment.center[axis.line] < draggableCenter[axis.line]) { | ||
var ordered = function () { | ||
if (!isWithinHomeDroppable) { | ||
return moved; | ||
} | ||
return isBeyondStartPosition ? moved.reverse() : moved; | ||
}(); | ||
var startIndex = insideDroppable.indexOf(draggingDimension); | ||
var index = function () { | ||
if (!isWithinHomeDroppable) { | ||
return insideDroppable.length - moved.length; | ||
} | ||
if (!moved.length) { | ||
@@ -94,4 +134,4 @@ return startIndex; | ||
amount: amount, | ||
draggables: moved, | ||
isBeyondStartPosition: isBeyondStartPosition | ||
draggables: ordered, | ||
isBeyondStartPosition: shouldDisplaceItemsForward | ||
}; | ||
@@ -98,0 +138,0 @@ |
@@ -17,10 +17,10 @@ 'use strict'; | ||
exports.default = (0, _memoizeOne2.default)(function (droppableDimension, draggableDimensions) { | ||
return (0, _keys2.default)(draggableDimensions).map(function (key) { | ||
return draggableDimensions[key]; | ||
}).filter(function (dimension) { | ||
return dimension.droppableId === droppableDimension.id; | ||
exports.default = (0, _memoizeOne2.default)(function (droppable, draggables) { | ||
return (0, _keys2.default)(draggables).map(function (id) { | ||
return draggables[id]; | ||
}).filter(function (draggable) { | ||
return droppable.id === draggable.droppableId; | ||
}).sort(function (a, b) { | ||
return a.page.withoutMargin.center[droppableDimension.axis.line] - b.page.withoutMargin.center[droppableDimension.axis.line]; | ||
return a.page.withoutMargin.center[droppable.axis.line] - b.page.withoutMargin.center[droppable.axis.line]; | ||
}); | ||
}); |
@@ -11,14 +11,14 @@ 'use strict'; | ||
var _isInsideDroppable = require('./is-inside-droppable'); | ||
var _isWithinVisibleBoundsOfDroppable = require('./is-within-visible-bounds-of-droppable'); | ||
var _isInsideDroppable2 = _interopRequireDefault(_isInsideDroppable); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = function (target, droppables) { | ||
var maybeId = (0, _keys2.default)(droppables).find(function (key) { | ||
return (0, _isInsideDroppable2.default)(target, droppables[key]); | ||
var maybe = (0, _keys2.default)(droppables).map(function (id) { | ||
return droppables[id]; | ||
}).find(function (droppable) { | ||
return (0, _isWithinVisibleBoundsOfDroppable.isPointWithin)(droppable)(target); | ||
}); | ||
return maybeId || null; | ||
return maybe ? maybe.id : null; | ||
}; |
@@ -10,3 +10,3 @@ 'use strict'; | ||
var noMovement = { | ||
var noMovement = exports.noMovement = { | ||
draggables: [], | ||
@@ -13,0 +13,0 @@ amount: origin, |
@@ -6,2 +6,14 @@ 'use strict'; | ||
}); | ||
exports.closest = exports.distance = exports.patch = exports.absolute = exports.negate = exports.isEqual = exports.subtract = exports.add = undefined; | ||
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); | ||
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); | ||
var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); | ||
var _defineProperty3 = _interopRequireDefault(_defineProperty2); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var add = exports.add = function add(point1, point2) { | ||
@@ -32,7 +44,24 @@ return { | ||
var patch = exports.patch = function patch(line, value) { | ||
var absolute = exports.absolute = function absolute(point) { | ||
return { | ||
x: line === 'x' ? value : 0, | ||
y: line === 'y' ? value : 0 | ||
x: Math.abs(point.x), | ||
y: Math.abs(point.y) | ||
}; | ||
}; | ||
var patch = exports.patch = function patch(line, value) { | ||
var _ref; | ||
var otherValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | ||
return _ref = {}, (0, _defineProperty3.default)(_ref, line, value), (0, _defineProperty3.default)(_ref, line === 'x' ? 'y' : 'x', otherValue), _ref; | ||
}; | ||
var distance = exports.distance = function distance(point1, point2) { | ||
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2)); | ||
}; | ||
var closest = exports.closest = function closest(target, points) { | ||
return Math.min.apply(Math, (0, _toConsumableArray3.default)(points.map(function (point) { | ||
return distance(target, point); | ||
}))); | ||
}; |
@@ -7,2 +7,6 @@ 'use strict'; | ||
var _keys = require('babel-runtime/core-js/object/keys'); | ||
var _keys2 = _interopRequireDefault(_keys); | ||
var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); | ||
@@ -12,5 +16,5 @@ | ||
var _extends5 = require('babel-runtime/helpers/extends'); | ||
var _extends6 = require('babel-runtime/helpers/extends'); | ||
var _extends6 = _interopRequireDefault(_extends5); | ||
var _extends7 = _interopRequireDefault(_extends6); | ||
@@ -21,2 +25,6 @@ var _memoizeOne = require('memoize-one'); | ||
var _getInitialImpact = require('./get-initial-impact'); | ||
var _getInitialImpact2 = _interopRequireDefault(_getInitialImpact); | ||
var _position = require('./position'); | ||
@@ -28,9 +36,9 @@ | ||
var _jumpToNextIndex = require('./jump-to-next-index'); | ||
var _moveToNextIndex = require('./move-to-next-index/'); | ||
var _jumpToNextIndex2 = _interopRequireDefault(_jumpToNextIndex); | ||
var _moveToNextIndex2 = _interopRequireDefault(_moveToNextIndex); | ||
var _getDroppableOver = require('./get-droppable-over'); | ||
var _moveCrossAxis = require('./move-cross-axis/'); | ||
var _getDroppableOver2 = _interopRequireDefault(_getDroppableOver); | ||
var _moveCrossAxis2 = _interopRequireDefault(_moveCrossAxis); | ||
@@ -116,2 +124,3 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
type: previous.type, | ||
isScrollAllowed: previous.isScrollAllowed, | ||
client: client, | ||
@@ -138,3 +147,3 @@ page: page, | ||
return (0, _extends6.default)({}, state, { | ||
return (0, _extends7.default)({}, state, { | ||
drag: drag | ||
@@ -189,7 +198,7 @@ }); | ||
return (0, _extends6.default)({}, state, { | ||
return (0, _extends7.default)({}, state, { | ||
dimension: { | ||
request: state.dimension.request, | ||
droppable: state.dimension.droppable, | ||
draggable: (0, _extends6.default)({}, state.dimension.draggable, (0, _defineProperty3.default)({}, dimension.id, dimension)) | ||
draggable: (0, _extends7.default)({}, state.dimension.draggable, (0, _defineProperty3.default)({}, dimension.id, dimension)) | ||
} | ||
@@ -212,7 +221,7 @@ }); | ||
return (0, _extends6.default)({}, state, { | ||
return (0, _extends7.default)({}, state, { | ||
dimension: { | ||
request: state.dimension.request, | ||
draggable: state.dimension.draggable, | ||
droppable: (0, _extends6.default)({}, state.dimension.droppable, (0, _defineProperty3.default)({}, _dimension.id, _dimension)) | ||
droppable: (0, _extends7.default)({}, state.dimension.droppable, (0, _defineProperty3.default)({}, _dimension.id, _dimension)) | ||
} | ||
@@ -233,23 +242,26 @@ }); | ||
page = _action$payload.page, | ||
_windowScroll = _action$payload.windowScroll; | ||
_windowScroll = _action$payload.windowScroll, | ||
isScrollAllowed = _action$payload.isScrollAllowed; | ||
var withinDroppable = { | ||
center: page.center | ||
}; | ||
var draggables = state.dimension.draggable; | ||
var draggable = state.dimension.draggable[id]; | ||
var droppable = state.dimension.droppable[draggable.droppableId]; | ||
var _impact = (0, _getDragImpact2.default)({ | ||
page: page.selection, | ||
withinDroppable: withinDroppable, | ||
draggableId: id, | ||
draggables: state.dimension.draggable, | ||
droppables: state.dimension.droppable | ||
var _impact = (0, _getInitialImpact2.default)({ | ||
draggable: draggable, | ||
droppable: droppable, | ||
draggables: draggables | ||
}); | ||
var source = _impact.destination; | ||
if (!source) { | ||
console.error('lifting a draggable that is not inside a droppable'); | ||
if (!_impact || !_impact.destination) { | ||
console.error('invalid lift state'); | ||
return clean(); | ||
} | ||
var source = _impact.destination; | ||
var withinDroppable = { | ||
center: page.center | ||
}; | ||
var initial = { | ||
@@ -278,6 +290,7 @@ source: source, | ||
windowScroll: _windowScroll, | ||
isScrollAllowed: isScrollAllowed, | ||
shouldAnimate: false | ||
}; | ||
return (0, _extends6.default)({}, state, { | ||
return (0, _extends7.default)({}, state, { | ||
phase: 'DRAGGING', | ||
@@ -303,2 +316,6 @@ drag: { | ||
if (!state.drag.current.isScrollAllowed) { | ||
return clean(); | ||
} | ||
var _action$payload2 = action.payload, | ||
@@ -316,3 +333,3 @@ _id = _action$payload2.id, | ||
var _dimension2 = (0, _extends6.default)({}, target, { | ||
var _dimension2 = (0, _extends7.default)({}, target, { | ||
scroll: { | ||
@@ -324,7 +341,7 @@ initial: target.scroll.initial, | ||
var withUpdatedDimension = (0, _extends6.default)({}, state, { | ||
var withUpdatedDimension = (0, _extends7.default)({}, state, { | ||
dimension: { | ||
request: state.dimension.request, | ||
draggable: state.dimension.draggable, | ||
droppable: (0, _extends6.default)({}, state.dimension.droppable, (0, _defineProperty3.default)({}, _id, _dimension2)) | ||
droppable: (0, _extends7.default)({}, state.dimension.droppable, (0, _defineProperty3.default)({}, _id, _dimension2)) | ||
} | ||
@@ -345,8 +362,40 @@ }); | ||
if (action.type === 'MOVE') { | ||
if (action.type === 'UPDATE_DROPPABLE_DIMENSION_IS_ENABLED') { | ||
if (!(0, _keys2.default)(state.dimension.droppable).length) { | ||
return state; | ||
} | ||
var _action$payload3 = action.payload, | ||
_client2 = _action$payload3.client, | ||
_page2 = _action$payload3.page, | ||
_windowScroll2 = _action$payload3.windowScroll; | ||
_id2 = _action$payload3.id, | ||
isEnabled = _action$payload3.isEnabled; | ||
var _target = state.dimension.droppable[_id2]; | ||
if (!_target) { | ||
console.error('cannot update enabled flag on droppable that does not have a dimension'); | ||
return clean(); | ||
} | ||
if (_target.isEnabled === isEnabled) { | ||
console.warn('trying to set droppable isEnabled to ' + isEnabled + ' but it is already ' + isEnabled); | ||
return state; | ||
} | ||
var updatedDroppableDimension = (0, _extends7.default)({}, _target, { | ||
isEnabled: isEnabled | ||
}); | ||
return (0, _extends7.default)({}, state, { | ||
dimension: (0, _extends7.default)({}, state.dimension, { | ||
droppable: (0, _extends7.default)({}, state.dimension.droppable, (0, _defineProperty3.default)({}, _id2, updatedDroppableDimension)) | ||
}) | ||
}); | ||
} | ||
if (action.type === 'MOVE') { | ||
var _action$payload4 = action.payload, | ||
_client2 = _action$payload4.client, | ||
_page2 = _action$payload4.page, | ||
_windowScroll2 = _action$payload4.windowScroll; | ||
return move({ | ||
@@ -403,8 +452,15 @@ state: state, | ||
var result = (0, _jumpToNextIndex2.default)({ | ||
if (!existing.impact.destination) { | ||
console.error('cannot move if there is no previous destination'); | ||
return clean(); | ||
} | ||
var _droppable = state.dimension.droppable[existing.impact.destination.droppableId]; | ||
var result = (0, _moveToNextIndex2.default)({ | ||
isMovingForward: isMovingForward, | ||
draggableId: existing.current.id, | ||
impact: existing.impact, | ||
draggables: state.dimension.draggable, | ||
droppables: state.dimension.droppable | ||
droppable: _droppable, | ||
draggables: state.dimension.draggable | ||
}); | ||
@@ -416,20 +472,59 @@ | ||
var _diff = result.diff; | ||
var _impact2 = result.impact; | ||
var _page4 = result.pageCenter; | ||
var _client4 = (0, _position.subtract)(_page4, existing.current.windowScroll); | ||
var _page4 = (0, _position.add)(existing.current.page.selection, _diff); | ||
var _client4 = (0, _position.add)(existing.current.client.selection, _diff); | ||
return move({ | ||
state: state, | ||
impact: _impact2, | ||
clientSelection: _client4, | ||
pageSelection: _page4, | ||
shouldAnimate: true | ||
}); | ||
} | ||
var droppableId = (0, _getDroppableOver2.default)(_page4, state.dimension.droppable); | ||
if (action.type === 'CROSS_AXIS_MOVE_FORWARD' || action.type === 'CROSS_AXIS_MOVE_BACKWARD') { | ||
if (state.phase !== 'DRAGGING') { | ||
console.error('cannot move cross axis when not dragging'); | ||
return clean(); | ||
} | ||
if (!droppableId) { | ||
console.info('currently not supporting moving a draggable outside the visibility bounds of a droppable'); | ||
if (!state.drag) { | ||
console.error('cannot move cross axis if there is no drag information'); | ||
return clean(); | ||
} | ||
if (!state.drag.impact.destination) { | ||
console.error('cannot move cross axis if not in a droppable'); | ||
return clean(); | ||
} | ||
var _current2 = state.drag.current; | ||
var draggableId = _current2.id; | ||
var center = _current2.page.center; | ||
var droppableId = state.drag.impact.destination.droppableId; | ||
var home = state.drag.initial.source; | ||
var _result = (0, _moveCrossAxis2.default)({ | ||
isMovingForward: action.type === 'CROSS_AXIS_MOVE_FORWARD', | ||
pageCenter: center, | ||
draggableId: draggableId, | ||
droppableId: droppableId, | ||
home: home, | ||
draggables: state.dimension.draggable, | ||
droppables: state.dimension.droppable | ||
}); | ||
if (!_result) { | ||
return state; | ||
} | ||
var _page5 = _result.pageCenter; | ||
var _client5 = (0, _position.subtract)(_page5, _current2.windowScroll); | ||
return move({ | ||
state: state, | ||
impact: _impact2, | ||
clientSelection: _client4, | ||
pageSelection: _page4, | ||
clientSelection: _client5, | ||
pageSelection: _page5, | ||
impact: _result.impact, | ||
shouldAnimate: true | ||
@@ -440,7 +535,7 @@ }); | ||
if (action.type === 'DROP_ANIMATE') { | ||
var _action$payload4 = action.payload, | ||
trigger = _action$payload4.trigger, | ||
newHomeOffset = _action$payload4.newHomeOffset, | ||
_impact3 = _action$payload4.impact, | ||
_result = _action$payload4.result; | ||
var _action$payload5 = action.payload, | ||
trigger = _action$payload5.trigger, | ||
newHomeOffset = _action$payload5.newHomeOffset, | ||
_impact3 = _action$payload5.impact, | ||
_result2 = _action$payload5.result; | ||
@@ -461,3 +556,3 @@ | ||
newHomeOffset: newHomeOffset, | ||
result: _result, | ||
result: _result2, | ||
impact: _impact3 | ||
@@ -478,3 +573,3 @@ }; | ||
if (action.type === 'DROP_COMPLETE') { | ||
var _result2 = action.payload; | ||
var _result3 = action.payload; | ||
@@ -486,3 +581,3 @@ return { | ||
pending: null, | ||
result: _result2 | ||
result: _result3 | ||
}, | ||
@@ -489,0 +584,0 @@ dimension: noDimensions |
@@ -6,2 +6,6 @@ 'use strict'; | ||
}); | ||
exports.draggingDraggableSelector = exports.dragSelector = exports.pendingDropSelector = exports.phaseSelector = undefined; | ||
var _reselect = require('reselect'); | ||
var phaseSelector = exports.phaseSelector = function phaseSelector(state) { | ||
@@ -20,2 +24,34 @@ return state.phase; | ||
return state.drag; | ||
}; | ||
}; | ||
var draggableMapSelector = function draggableMapSelector(state) { | ||
return state.dimension.draggable; | ||
}; | ||
var draggingDraggableSelector = exports.draggingDraggableSelector = (0, _reselect.createSelector)([phaseSelector, dragSelector, pendingDropSelector, draggableMapSelector], function (phase, drag, pending, draggables) { | ||
if (phase === 'DRAGGING') { | ||
if (!drag) { | ||
console.error('cannot get placeholder dimensions as there is an invalid drag state'); | ||
return null; | ||
} | ||
var draggable = draggables[drag.current.id]; | ||
return draggable; | ||
} | ||
if (phase === 'DROP_ANIMATING') { | ||
if (!pending) { | ||
console.error('cannot get placeholder dimensions as there is an invalid drag state'); | ||
return null; | ||
} | ||
if (!pending.result.destination) { | ||
return null; | ||
} | ||
var _draggable = draggables[pending.result.draggableId]; | ||
return _draggable; | ||
} | ||
return null; | ||
}); |
@@ -91,2 +91,6 @@ 'use strict'; | ||
_this.ifDragging(_this.props.callbacks.onMoveBackward); | ||
}), _this.scheduleCrossAxisMoveForward = (0, _rafSchd2.default)(function () { | ||
_this.ifDragging(_this.props.callbacks.onCrossAxisMoveForward); | ||
}), _this.scheduleCrossAxisMoveBackward = (0, _rafSchd2.default)(function () { | ||
_this.ifDragging(_this.props.callbacks.onCrossAxisMoveBackward); | ||
}), _this.scheduleWindowScrollMove = (0, _rafSchd2.default)(function () { | ||
@@ -211,2 +215,12 @@ _this.ifDragging(_this.props.callbacks.onWindowScroll); | ||
_this.startPendingMouseDrag(point); | ||
}, _this.executeBasedOnDirection = function (fns) { | ||
if (!_this.props.direction) { | ||
console.error('cannot move based on direction when none is provided'); | ||
_this.stopDragging(function () { | ||
return _this.props.callbacks.onCancel(); | ||
}); | ||
return; | ||
} | ||
_this.props.direction === 'vertical' ? fns.vertical() : fns.horizontal(); | ||
}, _this.onWindowKeydown = function (event) { | ||
@@ -272,13 +286,17 @@ var isMouseDragPending = Boolean(_this.state.pending); | ||
if (_this.props.direction === 'vertical') { | ||
if (event.keyCode === keyCodes.arrowDown) { | ||
event.preventDefault(); | ||
_this.scheduleMoveForward(); | ||
} | ||
if (event.keyCode === keyCodes.arrowDown) { | ||
event.preventDefault(); | ||
_this.executeBasedOnDirection({ | ||
vertical: _this.scheduleMoveForward, | ||
horizontal: _this.scheduleCrossAxisMoveForward | ||
}); | ||
return; | ||
} | ||
if (event.keyCode === keyCodes.arrowUp) { | ||
event.preventDefault(); | ||
_this.scheduleMoveBackward(); | ||
} | ||
if (event.keyCode === keyCodes.arrowUp) { | ||
event.preventDefault(); | ||
_this.executeBasedOnDirection({ | ||
vertical: _this.scheduleMoveBackward, | ||
horizontal: _this.scheduleCrossAxisMoveBackward | ||
}); | ||
return; | ||
@@ -289,3 +307,6 @@ } | ||
event.preventDefault(); | ||
_this.scheduleMoveForward(); | ||
_this.executeBasedOnDirection({ | ||
vertical: _this.scheduleCrossAxisMoveForward, | ||
horizontal: _this.scheduleMoveForward | ||
}); | ||
return; | ||
@@ -296,3 +317,6 @@ } | ||
event.preventDefault(); | ||
_this.scheduleMoveBackward(); | ||
_this.executeBasedOnDirection({ | ||
vertical: _this.scheduleCrossAxisMoveBackward, | ||
horizontal: _this.scheduleMoveBackward | ||
}); | ||
} | ||
@@ -299,0 +323,0 @@ }, _this.onKeyDown = function (event) { |
@@ -172,4 +172,6 @@ 'use strict'; | ||
move: _actionCreators.move, | ||
moveForward: _actionCreators.moveForward, | ||
moveBackward: _actionCreators.moveBackward, | ||
moveForward: _actionCreators.moveForward, | ||
crossAxisMoveForward: _actionCreators.crossAxisMoveForward, | ||
crossAxisMoveBackward: _actionCreators.crossAxisMoveBackward, | ||
moveByWindowScroll: _actionCreators.moveByWindowScroll, | ||
@@ -176,0 +178,0 @@ drop: _actionCreators.drop, |
@@ -70,3 +70,3 @@ 'use strict'; | ||
var _placeholder = require('./placeholder'); | ||
var _placeholder = require('../placeholder'); | ||
@@ -127,3 +127,5 @@ var _placeholder2 = _interopRequireDefault(_placeholder); | ||
lift(draggableId, type, client, page, windowScroll); | ||
var isScrollAllowed = true; | ||
lift(draggableId, type, client, page, windowScroll, isScrollAllowed); | ||
}; | ||
@@ -152,3 +154,5 @@ | ||
lift(draggableId, type, client, page, windowScroll); | ||
var isScrollAllowed = false; | ||
lift(draggableId, type, client, page, windowScroll, isScrollAllowed); | ||
}; | ||
@@ -184,2 +188,12 @@ | ||
_this.onCrossAxisMoveForward = function () { | ||
_this.throwIfCannotDrag(); | ||
_this.props.crossAxisMoveForward(_this.props.draggableId); | ||
}; | ||
_this.onCrossAxisMoveBackward = function () { | ||
_this.throwIfCannotDrag(); | ||
_this.props.crossAxisMoveBackward(_this.props.draggableId); | ||
}; | ||
_this.onWindowScroll = function () { | ||
@@ -286,3 +300,3 @@ _this.throwIfCannotDrag(); | ||
_this.callbacks = { | ||
var callbacks = { | ||
onLift: _this.onLift, | ||
@@ -295,4 +309,8 @@ onMove: _this.onMove, | ||
onMoveForward: _this.onMoveForward, | ||
onCrossAxisMoveForward: _this.onCrossAxisMoveForward, | ||
onCrossAxisMoveBackward: _this.onCrossAxisMoveBackward, | ||
onWindowScroll: _this.onWindowScroll | ||
}; | ||
_this.callbacks = callbacks; | ||
return _this; | ||
@@ -299,0 +317,0 @@ } |
@@ -55,5 +55,6 @@ 'use strict'; | ||
publish: _actionCreators.publishDroppableDimension, | ||
updateScroll: _actionCreators.updateDroppableDimensionScroll | ||
updateScroll: _actionCreators.updateDroppableDimensionScroll, | ||
updateIsEnabled: _actionCreators.updateDroppableDimensionIsEnabled | ||
}; | ||
exports.default = (0, _reactRedux.connect)(makeMapStateToProps, mapDispatchToProps, null, { storeKey: _contextKeys.storeKey })(_droppableDimensionPublisher2.default); |
@@ -1,1 +0,3 @@ | ||
'use strict'; | ||
'use strict'; | ||
var _actionCreators = require('../../state/action-creators'); |
@@ -45,2 +45,6 @@ 'use strict'; | ||
var _getClientRect = require('../../state/get-client-rect'); | ||
var _getClientRect2 = _interopRequireDefault(_getClientRect); | ||
var _dimension = require('../../state/dimension'); | ||
@@ -85,2 +89,3 @@ | ||
direction = _this$props.direction, | ||
isDropDisabled = _this$props.isDropDisabled, | ||
targetRef = _this$props.targetRef; | ||
@@ -90,2 +95,3 @@ | ||
var scroll = _this.getScrollOffset(); | ||
var style = window.getComputedStyle(targetRef); | ||
@@ -99,10 +105,43 @@ | ||
}; | ||
var padding = { | ||
top: parseInt(style.paddingTop, 10), | ||
right: parseInt(style.paddingRight, 10), | ||
bottom: parseInt(style.paddingBottom, 10), | ||
left: parseInt(style.paddingLeft, 10) | ||
}; | ||
var clientRect = function () { | ||
var current = targetRef.getBoundingClientRect(); | ||
if (!_this.closestScrollable) { | ||
return current; | ||
} | ||
if (_this.closestScrollable === targetRef) { | ||
return current; | ||
} | ||
var top = current.top + scroll.y; | ||
var bottom = current.bottom + scroll.y; | ||
var left = current.left + scroll.x; | ||
var right = current.right + scroll.x; | ||
var parent = _this.closestScrollable.getBoundingClientRect(); | ||
return (0, _getClientRect2.default)({ | ||
top: Math.max(top, parent.top), | ||
left: Math.max(left, parent.left), | ||
right: Math.min(right, parent.right), | ||
bottom: Math.min(bottom, parent.bottom) | ||
}); | ||
}(); | ||
var dimension = (0, _dimension.getDroppableDimension)({ | ||
id: droppableId, | ||
direction: direction, | ||
clientRect: targetRef.getBoundingClientRect(), | ||
clientRect: clientRect, | ||
margin: margin, | ||
padding: padding, | ||
windowScroll: (0, _getWindowScrollPosition2.default)(), | ||
scroll: _this.getScrollOffset() | ||
scroll: scroll, | ||
isEnabled: !isDropDisabled | ||
}); | ||
@@ -153,12 +192,8 @@ | ||
value: function componentWillReceiveProps(nextProps) { | ||
if (nextProps.targetRef !== this.props.targetRef) { | ||
if (this.isWatchingScroll) { | ||
console.warn('changing targetRef while watching scroll!'); | ||
this.unwatchScroll(); | ||
} | ||
} | ||
var shouldPublish = !this.props.shouldPublish && nextProps.shouldPublish; | ||
var shouldStartPublishing = !this.props.shouldPublish && nextProps.shouldPublish; | ||
var alreadyPublishing = this.props.shouldPublish && nextProps.shouldPublish; | ||
var stopPublishing = this.props.shouldPublish && !nextProps.shouldPublish; | ||
if (!nextProps.shouldPublish) { | ||
if (stopPublishing) { | ||
this.unwatchScroll(); | ||
@@ -168,6 +203,21 @@ return; | ||
if (!shouldPublish) { | ||
if (alreadyPublishing) { | ||
if (nextProps.targetRef !== this.props.targetRef) { | ||
if (this.isWatchingScroll) { | ||
console.warn('changing targetRef while watching scroll!'); | ||
this.unwatchScroll(); | ||
} | ||
} | ||
if (nextProps.isDropDisabled !== this.props.isDropDisabled) { | ||
this.props.updateIsEnabled(this.props.droppableId, !nextProps.isDropDisabled); | ||
} | ||
return; | ||
} | ||
if (!shouldStartPublishing) { | ||
return; | ||
} | ||
this.closestScrollable = (0, _getClosestScrollable2.default)(this.props.targetRef); | ||
@@ -174,0 +224,0 @@ this.props.publish(this.getDimension()); |
@@ -41,11 +41,40 @@ 'use strict'; | ||
var getMapProps = (0, _memoizeOne2.default)(function (isDraggingOver) { | ||
var memoizedPlaceholder = (0, _memoizeOne2.default)(function (width, height) { | ||
return { | ||
isDraggingOver: isDraggingOver | ||
width: width, height: height | ||
}; | ||
}); | ||
return (0, _reselect.createSelector)([_selectors.phaseSelector, _selectors.dragSelector, _selectors.pendingDropSelector, idSelector, isDropDisabledSelector], function (phase, drag, pending, id, isDropDisabled) { | ||
var getPlaceholder = (0, _memoizeOne2.default)(function (id, source, destination, draggable) { | ||
if (!destination) { | ||
return null; | ||
} | ||
if (destination.droppableId !== id) { | ||
return null; | ||
} | ||
if (source.droppableId === destination.droppableId) { | ||
return null; | ||
} | ||
if (!draggable) { | ||
return null; | ||
} | ||
var placeholder = memoizedPlaceholder(draggable.page.withMargin.width, draggable.page.withMargin.height); | ||
return placeholder; | ||
}); | ||
var getMapProps = (0, _memoizeOne2.default)(function (isDraggingOver, placeholder) { | ||
return { | ||
isDraggingOver: isDraggingOver, | ||
placeholder: placeholder | ||
}; | ||
}); | ||
return (0, _reselect.createSelector)([_selectors.phaseSelector, _selectors.dragSelector, _selectors.draggingDraggableSelector, _selectors.pendingDropSelector, idSelector, isDropDisabledSelector], function (phase, drag, draggable, pending, id, isDropDisabled) { | ||
if (isDropDisabled) { | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
} | ||
@@ -56,7 +85,9 @@ | ||
console.error('cannot determine dragging over as there is not drag'); | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
} | ||
var isDraggingOver = getIsDraggingOver(id, drag.impact.destination); | ||
return getMapProps(isDraggingOver); | ||
var placeholder = getPlaceholder(id, drag.initial.source, drag.impact.destination, draggable); | ||
return getMapProps(isDraggingOver, placeholder); | ||
} | ||
@@ -67,10 +98,11 @@ | ||
console.error('cannot determine dragging over as there is no pending result'); | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
} | ||
var _isDraggingOver = getIsDraggingOver(id, pending.impact.destination); | ||
return getMapProps(_isDraggingOver); | ||
var _placeholder = getPlaceholder(id, pending.result.source, pending.result.destination, draggable); | ||
return getMapProps(_isDraggingOver, _placeholder); | ||
} | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
}); | ||
@@ -77,0 +109,0 @@ }; |
@@ -43,2 +43,6 @@ 'use strict'; | ||
var _placeholder = require('../placeholder/'); | ||
var _placeholder2 = _interopRequireDefault(_placeholder); | ||
var _contextKeys = require('../context-keys'); | ||
@@ -86,9 +90,30 @@ | ||
}, { | ||
key: 'getPlaceholder', | ||
value: function getPlaceholder() { | ||
if (!this.props.placeholder) { | ||
return null; | ||
} | ||
return _react2.default.createElement(_placeholder2.default, { | ||
height: this.props.placeholder.height, | ||
width: this.props.placeholder.width | ||
}); | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _props = this.props, | ||
children = _props.children, | ||
direction = _props.direction, | ||
droppableId = _props.droppableId, | ||
isDraggingOver = _props.isDraggingOver, | ||
isDropDisabled = _props.isDropDisabled, | ||
type = _props.type; | ||
var provided = { | ||
innerRef: this.setRef | ||
innerRef: this.setRef, | ||
placeholder: this.getPlaceholder() | ||
}; | ||
var snapshot = { | ||
isDraggingOver: this.props.isDraggingOver | ||
isDraggingOver: isDraggingOver | ||
}; | ||
@@ -99,8 +124,9 @@ | ||
{ | ||
droppableId: this.props.droppableId, | ||
direction: this.props.direction, | ||
type: this.props.type, | ||
droppableId: droppableId, | ||
direction: direction, | ||
isDropDisabled: isDropDisabled, | ||
type: type, | ||
targetRef: this.state.ref | ||
}, | ||
this.props.children(provided, snapshot) | ||
children(provided, snapshot) | ||
); | ||
@@ -107,0 +133,0 @@ } |
@@ -6,10 +6,12 @@ 'use strict'; | ||
}); | ||
exports.default = undefined; | ||
var _placeholder = require('./placeholder'); | ||
var _placeholder2 = _interopRequireDefault(_placeholder); | ||
Object.defineProperty(exports, 'default', { | ||
enumerable: true, | ||
get: function get() { | ||
return _interopRequireDefault(_placeholder).default; | ||
} | ||
}); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = _placeholder2.default; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
{ | ||
"name": "react-beautiful-dnd", | ||
"version": "2.1.1", | ||
"version": "2.2.0", | ||
"description": "Beautiful, accessible drag and drop for lists with React.js", | ||
@@ -73,3 +73,3 @@ "author": "Alex Reardon <areardon@atlassian.com>", | ||
"peerDependencies": { | ||
"react": "15.6.1" | ||
"react": ">=15.4.0" | ||
}, | ||
@@ -76,0 +76,0 @@ "license": "Apache-2.0", |
@@ -37,6 +37,6 @@ # react-beautiful-dnd | ||
- Mouse 🐭 and **keyboard 🎹** dragging | ||
- Independent nested lists (list can be a child of another list, but you cannot drag items from the parent list into a child list) | ||
- Flexible height items (the draggable items can have different heights) | ||
- Custom drag handle (you can drag a whole item by just a part of it) | ||
- The vertical list can be a scroll container (without a scrollable parent) or be the child of a scroll container (that also does not have a scrollable parent) | ||
- Independent nested lists - a list can be a child of another list, but you cannot drag items from the parent list into a child list | ||
- Flexible item sizes - the draggable items can have different heights (vertical) or widths (horizontal)) | ||
- Custom drag handle - you can drag a whole item by just a part of it | ||
- A droppable list can be a scroll container (without a scrollable parent) or be the child of a scroll container (that also does not have a scrollable parent) | ||
- Server side rendering compatible | ||
@@ -147,2 +147,3 @@ | ||
))} | ||
{provided.placeholder} | ||
</div> | ||
@@ -395,3 +396,3 @@ )} | ||
**Block updates during a drag** | ||
#### Block updates during a drag | ||
@@ -410,9 +411,4 @@ It is **highly** recommended that while a user is dragging that you block any state updates that might impact the amount of `Draggable`s and `Droppable`s, or their dimensions. Please listen to `onDragStart` and block updates to the `Draggable`s and `Droppable`s until you receive at `onDragEnd`. | ||
#### Add a cursor style and block selection | ||
**`onDragStart` and `onDragEnd` pairing** | ||
We try very hard to ensure that each `onDragStart` event is paired with a single `onDragEnd` event. However, there maybe a rouge situation where this is not the case. If that occurs - it is a bug. Currently there is no mechanism to tell the library to cancel a current drag externally. | ||
**Style** | ||
During a drag it is recommended that you add two styles to the body: | ||
@@ -427,5 +423,20 @@ | ||
#### Force focus after a transition between lists | ||
When an item is moved from one list to a different list it looses browser focus if it had it. This is because `React` creates a new node in this situation. It will not loose focus if transitioned within the same list. The dragging item will always have had browser focus if it is dragging with a keyboard. It is highly recommended that you give the item (which is now in a different list) focus again. You can see an example of how to do this in our stories. Here is an example of how you could do it: | ||
- `onDragEnd`: move the item into the new list and record the id fo the item that has moved | ||
- When rendering the reordered list pass down a prop which will tell the newly moved item to obtain focus | ||
- In the `componentDidMount` lifecycle call back check if the item needs to gain focus based on its props (such as an `autoFocus` prop) | ||
- If focus is required - call `.focus` on the node. You can obtain the node by using `ReactDOM.findDOMNode` or monkey patching the `provided.innerRef` callback. | ||
### Other `hooks` information | ||
**`onDragStart` and `onDragEnd` pairing** | ||
We try very hard to ensure that each `onDragStart` event is paired with a single `onDragEnd` event. However, there maybe a rouge situation where this is not the case. If that occurs - it is a bug. Currently there is no mechanism to tell the library to cancel a current drag externally. | ||
**Dynamic hooks** | ||
Your *hook* functions will only be captured *once at start up*. Please do not change the function after that. If there is a valid use case for this then dynamic hooks could be supported. However, at this time it is not. | ||
Your *hook* functions will only be captured *once at start up*. Please do not change the function after that. This behaviour will be changed soon to allow dynamic hooks. | ||
@@ -445,3 +456,4 @@ ## `Droppable` | ||
> | ||
I am a droppable! | ||
<h2>I am a droppable!</h2> | ||
{provided.placeholder} | ||
</div> | ||
@@ -477,10 +489,19 @@ )} | ||
innerRef: (?HTMLElement) => void, | ||
placeholder: ?ReactElement, | ||
|} | ||
``` | ||
In order for the droppable to function correctly, **you must** bind the `provided.innerRef` to the highest possible DOM node in the `ReactElement`. We do this in order to avoid needing to use `ReactDOM` to look up your DOM node. | ||
- `provided.innerRef`: In order for the droppable to function correctly, **you must** bind the `provided.innerRef` to the highest possible DOM node in the `ReactElement`. We do this in order to avoid needing to use `ReactDOM` to look up your DOM node. | ||
- `provided.placeholder`: This is used to create space in the `Droppable` as needed during a drag. This space is needed when a user is dragging over a list that is not the home list. Please be sure to put the placeholder inside of the component that you have provided the ref for. We need to increase the side of the `Droppable` itself. This is different from `Draggable` where the `placeholder` needs to be a *silbing* to the draggable node. | ||
```js | ||
<Droppable droppableId="droppable-1"> | ||
{(provided, snapshot) => <div ref={provided.innerRef}>Good to go</div>} | ||
{(provided, snapshot) => ( | ||
<div ref={provided.innerRef}> | ||
Good to go | ||
{provided.placeholder} | ||
</div> | ||
)} | ||
</Droppable>; | ||
@@ -507,2 +528,4 @@ ``` | ||
I am a droppable! | ||
{provided.placeholder} | ||
</div> | ||
@@ -692,3 +715,3 @@ )} | ||
- `provided.placeholder (?ReactElement)` The `Draggable` element has `position: fixed` applied to it while it is dragging. The role of the `placeholder` is to sit in the place that the `Draggable` was during a drag. It is needed to stop the `Droppable` list from collapsing when you drag. It is advised to render it as a sibling to the `Draggable` node. When the library moves to `React` 16 the `placeholder` will be removed from api. | ||
- `provided.placeholder (?ReactElement)` The `Draggable` element has `position: fixed` applied to it while it is dragging. The role of the `placeholder` is to sit in the place that the `Draggable` was during a drag. It is needed to stop the `Droppable` list from collapsing when you drag. It is advised to render it as a sibling to the `Draggable` node. This is unlike `Droppable` where the `placeholder` needs to be *within* the `Droppable` node. When the library moves to `React` 16 the `placeholder` will be removed from api. | ||
@@ -868,4 +891,9 @@ ```js | ||
innerRef: (?HTMLElement) => void, | ||
placeholder: ?ReactElement, | ||
|} | ||
type DraggableStateSnapshot = {| | ||
isDraggingOver: boolean, | ||
|} | ||
// Draggable | ||
@@ -878,2 +906,7 @@ type DraggableProvided = {| | ||
|} | ||
type DraggableStateSnapshot = {| | ||
isDragging: boolean, | ||
|} | ||
type DraggableStyle = DraggingStyle | NotDraggingStyle | ||
@@ -880,0 +913,0 @@ type DraggingStyle = {| |
@@ -19,3 +19,3 @@ // @flow | ||
import noImpact from './no-impact'; | ||
import getNewHomeClientOffset from './get-new-home-client-offset'; | ||
import getNewHomeClientCenter from './get-new-home-client-center'; | ||
import { add, subtract, isEqual } from './position'; | ||
@@ -25,12 +25,13 @@ | ||
type ScrollDiffResult = {| | ||
droppable: Position, | ||
window: Position, | ||
type ScrollDiffArgs = {| | ||
initial: InitialDrag, | ||
current: CurrentDrag, | ||
droppable: ?DroppableDimension | ||
|} | ||
const getScrollDiff = ( | ||
initial: InitialDrag, | ||
current: CurrentDrag, | ||
droppable: DroppableDimension | ||
): ScrollDiffResult => { | ||
const getScrollDiff = ({ | ||
initial, | ||
current, | ||
droppable, | ||
}: ScrollDiffArgs): Position => { | ||
const windowScrollDiff: Position = subtract( | ||
@@ -40,11 +41,9 @@ initial.windowScroll, | ||
); | ||
const droppableScrollDiff: Position = subtract( | ||
const droppableScrollDiff: Position = droppable ? subtract( | ||
droppable.scroll.initial, | ||
droppable.scroll.current | ||
); | ||
) : origin; | ||
return { | ||
window: windowScrollDiff, | ||
droppable: droppableScrollDiff, | ||
}; | ||
return add(windowScrollDiff, droppableScrollDiff); | ||
}; | ||
@@ -78,2 +77,3 @@ | ||
windowScroll: Position, | ||
isScrollAllowed: boolean, | ||
|} | ||
@@ -87,2 +87,3 @@ |} | ||
windowScroll: Position, | ||
isScrollAllowed: boolean, | ||
): CompleteLiftAction => ({ | ||
@@ -96,2 +97,3 @@ type: 'COMPLETE_LIFT', | ||
windowScroll, | ||
isScrollAllowed, | ||
}, | ||
@@ -139,2 +141,19 @@ }); | ||
export type UpdateDroppableDimensionIsEnabledAction = {| | ||
type: 'UPDATE_DROPPABLE_DIMENSION_IS_ENABLED', | ||
payload: { | ||
id: DroppableId, | ||
isEnabled: boolean, | ||
} | ||
|} | ||
export const updateDroppableDimensionIsEnabled = | ||
(id: DroppableId, isEnabled: boolean): UpdateDroppableDimensionIsEnabledAction => ({ | ||
type: 'UPDATE_DROPPABLE_DIMENSION_IS_ENABLED', | ||
payload: { | ||
id, | ||
isEnabled, | ||
}, | ||
}); | ||
export type MoveAction = {| | ||
@@ -200,2 +219,22 @@ type: 'MOVE', | ||
export type CrossAxisMoveForwardAction = {| | ||
type: 'CROSS_AXIS_MOVE_FORWARD', | ||
payload: DraggableId | ||
|} | ||
export const crossAxisMoveForward = (id: DraggableId): CrossAxisMoveForwardAction => ({ | ||
type: 'CROSS_AXIS_MOVE_FORWARD', | ||
payload: id, | ||
}); | ||
export type CrossAxisMoveBackwardAction = {| | ||
type: 'CROSS_AXIS_MOVE_BACKWARD', | ||
payload: DraggableId | ||
|} | ||
export const crossAxisMoveBackward = (id: DraggableId): CrossAxisMoveBackwardAction => ({ | ||
type: 'CROSS_AXIS_MOVE_BACKWARD', | ||
payload: id, | ||
}); | ||
type CleanAction = { | ||
@@ -280,7 +319,6 @@ type: 'CLEAN', | ||
const { impact, initial, current } = state.drag; | ||
const sourceDroppable: DroppableDimension = | ||
state.dimension.droppable[initial.source.droppableId]; | ||
const destinationDroppable: ?DroppableDimension = impact.destination ? | ||
const droppable: ?DroppableDimension = impact.destination ? | ||
state.dimension.droppable[impact.destination.droppableId] : | ||
null; | ||
const draggable: DraggableDimension = state.dimension.draggable[current.id]; | ||
@@ -294,18 +332,13 @@ const result: DropResult = { | ||
const scrollDiff = getScrollDiff( | ||
initial, | ||
current, | ||
sourceDroppable, | ||
); | ||
const newHomeOffset: Position = getNewHomeClientOffset({ | ||
const newCenter: Position = getNewHomeClientCenter({ | ||
movement: impact.movement, | ||
clientOffset: current.client.offset, | ||
pageOffset: current.page.offset, | ||
droppableScrollDiff: scrollDiff.droppable, | ||
windowScrollDiff: scrollDiff.window, | ||
draggable, | ||
draggables: state.dimension.draggable, | ||
axis: destinationDroppable ? destinationDroppable.axis : null, | ||
destination: droppable, | ||
}); | ||
const clientOffset: Position = subtract(newCenter, draggable.client.withMargin.center); | ||
const scrollDiff: Position = getScrollDiff({ initial, current, droppable }); | ||
const newHomeOffset: Position = add(clientOffset, scrollDiff); | ||
// Do not animate if you do not need to. | ||
@@ -366,7 +399,7 @@ // This will be the case if either you are dragging with a | ||
const scrollDiff = getScrollDiff(initial, current, droppable); | ||
const scrollDiff: Position = getScrollDiff({ initial, current, droppable }); | ||
dispatch(animateDrop({ | ||
trigger: 'CANCEL', | ||
newHomeOffset: add(scrollDiff.droppable, scrollDiff.window), | ||
newHomeOffset: scrollDiff, | ||
impact: noImpact, | ||
@@ -404,2 +437,3 @@ result, | ||
windowScroll: Position, | ||
isScrollAllowed: boolean, | ||
|} | ||
@@ -414,2 +448,3 @@ |} | ||
windowScroll: Position, | ||
isScrollAllowed: boolean, | ||
) => (dispatch: Dispatch, getState: Function) => { | ||
@@ -452,3 +487,3 @@ (() => { | ||
} | ||
dispatch(completeLift(id, type, client, page, windowScroll)); | ||
dispatch(completeLift(id, type, client, page, windowScroll, isScrollAllowed)); | ||
}); | ||
@@ -466,4 +501,6 @@ }); | ||
MoveForwardAction | | ||
CrossAxisMoveForwardAction | | ||
CrossAxisMoveBackwardAction | | ||
DropAnimateAction | | ||
DropCompleteAction | | ||
CleanAction; |
@@ -7,5 +7,9 @@ // @flow | ||
line: 'y', | ||
crossLine: 'x', | ||
start: 'top', | ||
end: 'bottom', | ||
size: 'height', | ||
crossAxisStart: 'left', | ||
crossAxisEnd: 'right', | ||
crossAxisSize: 'width', | ||
}; | ||
@@ -16,5 +20,9 @@ | ||
line: 'x', | ||
crossLine: 'y', | ||
start: 'left', | ||
end: 'right', | ||
size: 'width', | ||
crossAxisStart: 'top', | ||
crossAxisEnd: 'bottom', | ||
crossAxisSize: 'height', | ||
}; |
// @flow | ||
import { vertical, horizontal } from './axis'; | ||
import getClientRect from './get-client-rect'; | ||
import type { | ||
@@ -11,23 +12,9 @@ DroppableId, | ||
DimensionFragment, | ||
Spacing, | ||
ClientRect, | ||
} from '../types'; | ||
export type ClientRect = {| | ||
top: number, | ||
right: number, | ||
bottom: number, | ||
left: number, | ||
width: number, | ||
height: number, | ||
|} | ||
export type Margin = {| | ||
top: number, | ||
right: number, | ||
bottom: number, | ||
left: number, | ||
|} | ||
const origin: Position = { x: 0, y: 0 }; | ||
export const noMargin: Margin = { | ||
export const noSpacing: Spacing = { | ||
top: 0, | ||
@@ -40,4 +27,4 @@ right: 0, | ||
const getWithPosition = (clientRect: ClientRect, point: Position): ClientRect => { | ||
const { top, right, bottom, left, width, height } = clientRect; | ||
return { | ||
const { top, right, bottom, left } = clientRect; | ||
return getClientRect({ | ||
top: top + point.y, | ||
@@ -47,17 +34,13 @@ left: left + point.x, | ||
right: right + point.x, | ||
height, | ||
width, | ||
}; | ||
}); | ||
}; | ||
const getWithMargin = (clientRect: ClientRect, margin: Margin): ClientRect => { | ||
const { top, right, bottom, left, height, width } = clientRect; | ||
return { | ||
top: top + margin.top, | ||
left: left + margin.left, | ||
bottom: bottom + margin.bottom, | ||
right: right + margin.right, | ||
height: height + margin.top + margin.bottom, | ||
width: width + margin.left + margin.right, | ||
}; | ||
const getWithSpacing = (clientRect: ClientRect, spacing: Spacing): ClientRect => { | ||
const { top, right, bottom, left } = clientRect; | ||
return getClientRect({ | ||
top: top + spacing.top, | ||
left: left + spacing.left, | ||
bottom: bottom + spacing.bottom, | ||
right: right + spacing.right, | ||
}); | ||
}; | ||
@@ -68,15 +51,24 @@ | ||
point?: Position = origin, | ||
): DimensionFragment => ({ | ||
top: initial.top + point.y, | ||
left: initial.left + point.x, | ||
bottom: initial.bottom + point.y, | ||
right: initial.right + point.x, | ||
width: initial.width, | ||
height: initial.height, | ||
center: { | ||
x: ((initial.right + point.x) + (initial.left + point.x)) / 2, | ||
y: ((initial.bottom + point.y) + (initial.top + point.y)) / 2, | ||
}, | ||
}); | ||
): DimensionFragment => { | ||
const rect: ClientRect = getClientRect({ | ||
top: initial.top + point.y, | ||
left: initial.left + point.x, | ||
bottom: initial.bottom + point.y, | ||
right: initial.right + point.x, | ||
}); | ||
return { | ||
top: rect.top, | ||
right: rect.right, | ||
bottom: rect.bottom, | ||
left: rect.left, | ||
width: rect.width, | ||
height: rect.height, | ||
center: { | ||
x: (rect.right + rect.left) / 2, | ||
y: (rect.bottom + rect.top) / 2, | ||
}, | ||
}; | ||
}; | ||
type GetDraggableArgs = {| | ||
@@ -86,3 +78,3 @@ id: DraggableId, | ||
clientRect: ClientRect, | ||
margin?: Margin, | ||
margin?: Spacing, | ||
windowScroll?: Position, | ||
@@ -95,7 +87,6 @@ |}; | ||
clientRect, | ||
margin = noMargin, | ||
margin = noSpacing, | ||
windowScroll = origin, | ||
}: GetDraggableArgs): DraggableDimension => { | ||
const withScroll = getWithPosition(clientRect, windowScroll); | ||
const withScrollAndMargin = getWithMargin(withScroll, margin); | ||
@@ -108,3 +99,3 @@ const dimension: DraggableDimension = { | ||
withoutMargin: getFragment(clientRect), | ||
withMargin: getFragment(getWithMargin(clientRect, margin)), | ||
withMargin: getFragment(getWithSpacing(clientRect, margin)), | ||
}, | ||
@@ -114,3 +105,3 @@ // with scroll | ||
withoutMargin: getFragment(withScroll), | ||
withMargin: getFragment(withScrollAndMargin), | ||
withMargin: getFragment(getWithSpacing(withScroll, margin)), | ||
}, | ||
@@ -126,7 +117,18 @@ }; | ||
direction?: Direction, | ||
margin?: Margin, | ||
margin?: Spacing, | ||
padding?: Spacing, | ||
windowScroll?: Position, | ||
scroll?: Position, | ||
scroll ?: Position, | ||
// Whether or not the droppable is currently enabled (can change at during a drag) | ||
// defaults to true | ||
isEnabled?: boolean, | ||
|} | ||
const add = (spacing1: Spacing, spacing2: Spacing): Spacing => ({ | ||
top: spacing1.top + spacing2.top, | ||
left: spacing1.left + spacing2.left, | ||
right: spacing1.right + spacing2.right, | ||
bottom: spacing1.bottom + spacing2.bottom, | ||
}); | ||
export const getDroppableDimension = ({ | ||
@@ -136,11 +138,14 @@ id, | ||
direction = 'vertical', | ||
margin = noMargin, | ||
margin = noSpacing, | ||
padding = noSpacing, | ||
windowScroll = origin, | ||
scroll = origin, | ||
isEnabled = true, | ||
}: GetDroppableArgs): DroppableDimension => { | ||
const withMargin = getWithSpacing(clientRect, margin); | ||
const withWindowScroll = getWithPosition(clientRect, windowScroll); | ||
const withWindowScrollAndMargin = getWithMargin(withWindowScroll, margin); | ||
const dimension: DroppableDimension = { | ||
id, | ||
isEnabled, | ||
axis: direction === 'vertical' ? vertical : horizontal, | ||
@@ -152,5 +157,11 @@ scroll: { | ||
}, | ||
client: { | ||
withoutMargin: getFragment(clientRect), | ||
withMargin: getFragment(withMargin), | ||
withMarginAndPadding: getFragment(getWithSpacing(withMargin, padding)), | ||
}, | ||
page: { | ||
withoutMargin: getFragment(withWindowScroll), | ||
withMargin: getFragment(withWindowScrollAndMargin), | ||
withMargin: getFragment(getWithSpacing(withWindowScroll, margin)), | ||
withMarginAndPadding: getFragment(getWithSpacing(withWindowScroll, add(margin, padding))), | ||
}, | ||
@@ -157,0 +168,0 @@ }; |
@@ -18,4 +18,22 @@ // @flow | ||
import getDraggablesInsideDroppable from './get-draggables-inside-droppable'; | ||
import noImpact from './no-impact'; | ||
import noImpact, { noMovement } from './no-impact'; | ||
// Calculates the net scroll diff along the main axis | ||
// between two droppables with internal scrolling | ||
const getDroppablesScrollDiff = ({ | ||
sourceDroppable, | ||
destinationDroppable, | ||
line, | ||
}: { | ||
sourceDroppable: DroppableDimension, | ||
destinationDroppable: DroppableDimension, | ||
line: 'x' | 'y', | ||
}): number => { | ||
const sourceScrollDiff = sourceDroppable.scroll.initial[line] - | ||
sourceDroppable.scroll.current[line]; | ||
const destinationScrollDiff = destinationDroppable.scroll.initial[line] - | ||
destinationDroppable.scroll.current[line]; | ||
return destinationScrollDiff - sourceScrollDiff; | ||
}; | ||
// It is the responsibility of this function | ||
@@ -29,3 +47,5 @@ // to return the impact of a drag | ||
withinDroppable: WithinDroppable, | ||
// item being dragged | ||
draggableId: DraggableId, | ||
// all dimensions in system | ||
draggables: DraggableDimensionMap, | ||
@@ -51,12 +71,21 @@ droppables: DroppableDimensionMap | ||
const newCenter = withinDroppable.center; | ||
const draggingDimension: DraggableDimension = draggables[draggableId]; | ||
const droppableDimension: DroppableDimension = droppables[droppableId]; | ||
const droppable: DroppableDimension = droppables[droppableId]; | ||
const axis: Axis = droppable.axis; | ||
if (!droppable.isEnabled) { | ||
return { | ||
movement: noMovement, | ||
direction: axis.direction, | ||
destination: null, | ||
}; | ||
} | ||
const insideDroppable: DraggableDimension[] = getDraggablesInsideDroppable( | ||
droppableDimension, | ||
droppable, | ||
draggables, | ||
); | ||
const axis: Axis = droppableDimension.axis; | ||
const newCenter: Position = withinDroppable.center; | ||
const draggingDimension: DraggableDimension = draggables[draggableId]; | ||
const isWithinHomeDroppable = draggingDimension.droppableId === droppableId; | ||
@@ -66,2 +95,3 @@ // not considering margin so that items move based on visible edges | ||
const isBeyondStartPosition: boolean = newCenter[axis.line] - draggableCenter[axis.line] > 0; | ||
const shouldDisplaceItemsForward = isWithinHomeDroppable ? isBeyondStartPosition : false; | ||
@@ -77,2 +107,13 @@ const moved: DraggableId[] = insideDroppable | ||
// If we're over a new droppable items will be displaced | ||
// if they sit ahead of the dragging item | ||
if (!isWithinHomeDroppable) { | ||
const scrollDiff = getDroppablesScrollDiff({ | ||
sourceDroppable: droppables[draggingDimension.droppableId], | ||
destinationDroppable: droppable, | ||
line: axis.line, | ||
}); | ||
return (newCenter[axis.line] - scrollDiff) < fragment[axis.end]; | ||
} | ||
if (isBeyondStartPosition) { | ||
@@ -98,4 +139,16 @@ // 1. item needs to start ahead of the moving item | ||
// Need to ensure that we always order by the closest impacted item | ||
const ordered: DraggableId[] = (() => { | ||
if (!isWithinHomeDroppable) { | ||
return moved; | ||
} | ||
return isBeyondStartPosition ? moved.reverse() : moved; | ||
})(); | ||
const startIndex = insideDroppable.indexOf(draggingDimension); | ||
const index: number = (() => { | ||
if (!isWithinHomeDroppable) { | ||
return insideDroppable.length - moved.length; | ||
} | ||
if (!moved.length) { | ||
@@ -121,4 +174,4 @@ return startIndex; | ||
amount, | ||
draggables: moved, | ||
isBeyondStartPosition, | ||
draggables: ordered, | ||
isBeyondStartPosition: shouldDisplaceItemsForward, | ||
}; | ||
@@ -125,0 +178,0 @@ |
// @flow | ||
import memoizeOne from 'memoize-one'; | ||
import type { | ||
DraggableId, | ||
DraggableDimension, | ||
DroppableDimension, | ||
DraggableDimensionMap, | ||
DraggableId, | ||
} from '../types'; | ||
export default memoizeOne( | ||
(droppableDimension: DroppableDimension, | ||
draggableDimensions: DraggableDimensionMap, | ||
(droppable: DroppableDimension, | ||
draggables: DraggableDimensionMap, | ||
): DraggableDimension[] => | ||
Object.keys(draggableDimensions) | ||
.map((key: DraggableId): DraggableDimension => draggableDimensions[key]) | ||
.filter((dimension: DraggableDimension): boolean => | ||
dimension.droppableId === droppableDimension.id | ||
) | ||
Object.keys(draggables) | ||
.map((id: DraggableId): DraggableDimension => draggables[id]) | ||
.filter((draggable: DraggableDimension): boolean => ( | ||
droppable.id === draggable.droppableId | ||
)) | ||
// Dimensions are not guarenteed to be ordered in the same order as keys | ||
// So we need to sort them so they are in the correct order | ||
.sort((a: DraggableDimension, b: DraggableDimension): number => | ||
a.page.withoutMargin.center[droppableDimension.axis.line] - | ||
b.page.withoutMargin.center[droppableDimension.axis.line] | ||
) | ||
); | ||
.sort((a: DraggableDimension, b: DraggableDimension): number => ( | ||
a.page.withoutMargin.center[droppable.axis.line] - | ||
b.page.withoutMargin.center[droppable.axis.line] | ||
)) | ||
); |
// @flow | ||
import type { DroppableId, Position, DroppableDimensionMap } from '../types'; | ||
import isInsideDroppable from './is-inside-droppable'; | ||
import { isPointWithin } from './is-within-visible-bounds-of-droppable'; | ||
import type { | ||
DroppableId, | ||
Position, | ||
DroppableDimensionMap, | ||
DroppableDimension, | ||
} from '../types'; | ||
@@ -9,6 +14,10 @@ export default ( | ||
): ?DroppableId => { | ||
const maybeId: ?DroppableId = Object.keys(droppables) | ||
.find(key => isInsideDroppable(target, droppables[key])); | ||
const maybe: ?DroppableDimension = | ||
Object.keys(droppables) | ||
.map((id: DroppableId): DroppableDimension => droppables[id]) | ||
.find((droppable: DroppableDimension): boolean => ( | ||
isPointWithin(droppable)(target) | ||
)); | ||
return maybeId || null; | ||
return maybe ? maybe.id : null; | ||
}; |
@@ -6,3 +6,3 @@ // @flow | ||
const noMovement: DragMovement = { | ||
export const noMovement: DragMovement = { | ||
draggables: [], | ||
@@ -9,0 +9,0 @@ amount: origin, |
@@ -23,6 +23,32 @@ // @flow | ||
export const patch = (line: 'x' | 'y', value: number): Position => ({ | ||
x: line === 'x' ? value : 0, | ||
y: line === 'y' ? value : 0, | ||
export const absolute = (point: Position): Position => ({ | ||
x: Math.abs(point.x), | ||
y: Math.abs(point.y), | ||
}); | ||
// Allows you to build a position from values. | ||
// Really useful when working with the Axis type | ||
// patch('x', 5) = { x: 5, y: 0 } | ||
// patch('y', 5, 1) = { x: 1, y: 5 } | ||
export const patch = ( | ||
line: 'x' | 'y', | ||
value: number, | ||
otherValue?: number = 0 | ||
): Position => ({ | ||
// set the value of 'x', or 'y' | ||
[line]: value, | ||
// set the value of the other line | ||
[line === 'x' ? 'y' : 'x']: otherValue, | ||
}); | ||
// Returns the distance between two points | ||
// https://www.mathsisfun.com/algebra/distance-2-points.html | ||
export const distance = (point1: Position, point2: Position): number => | ||
Math.sqrt( | ||
Math.pow((point2.x - point1.x), 2) + | ||
Math.pow((point2.y - point1.y), 2) | ||
); | ||
// When given a list of points, it finds the smallest distance to any point | ||
export const closest = (target: Position, points: Position[]): number => | ||
Math.min(...points.map((point: Position) => distance(target, point))); |
@@ -8,3 +8,5 @@ // @flow | ||
DroppableDimension, | ||
DraggableDimensionMap, | ||
DroppableId, | ||
DraggableId, | ||
DimensionState, | ||
@@ -23,7 +25,9 @@ DragImpact, | ||
} from '../types'; | ||
import getInitialImpact from './get-initial-impact'; | ||
import { add, subtract, negate } from './position'; | ||
import getDragImpact from './get-drag-impact'; | ||
import jumpToNextIndex from './jump-to-next-index'; | ||
import type { JumpToNextResult } from './jump-to-next-index'; | ||
import getDroppableOver from './get-droppable-over'; | ||
import moveToNextIndex from './move-to-next-index/'; | ||
import type { Result as MoveToNextResult } from './move-to-next-index/move-to-next-index-types'; | ||
import type { Result as MoveCrossAxisResult } from './move-cross-axis/move-cross-axis-types'; | ||
import moveCrossAxis from './move-cross-axis/'; | ||
@@ -117,2 +121,3 @@ const noDimensions: DimensionState = { | ||
type: previous.type, | ||
isScrollAllowed: previous.isScrollAllowed, | ||
client, | ||
@@ -232,24 +237,24 @@ page, | ||
const { id, type, client, page, windowScroll } = action.payload; | ||
const { id, type, client, page, windowScroll, isScrollAllowed } = action.payload; | ||
const draggables: DraggableDimensionMap = state.dimension.draggable; | ||
const draggable: DraggableDimension = state.dimension.draggable[id]; | ||
const droppable: DroppableDimension = state.dimension.droppable[draggable.droppableId]; | ||
// no scroll diff yet so withinDroppable is just the center position | ||
const withinDroppable: WithinDroppable = { | ||
center: page.center, | ||
}; | ||
const impact: DragImpact = getDragImpact({ | ||
page: page.selection, | ||
withinDroppable, | ||
draggableId: id, | ||
draggables: state.dimension.draggable, | ||
droppables: state.dimension.droppable, | ||
const impact: ?DragImpact = getInitialImpact({ | ||
draggable, | ||
droppable, | ||
draggables, | ||
}); | ||
const source: ?DraggableLocation = impact.destination; | ||
if (!source) { | ||
console.error('lifting a draggable that is not inside a droppable'); | ||
if (!impact || !impact.destination) { | ||
console.error('invalid lift state'); | ||
return clean(); | ||
} | ||
const source: DraggableLocation = impact.destination; | ||
const withinDroppable: WithinDroppable = { | ||
center: page.center, | ||
}; | ||
const initial: InitialDrag = { | ||
@@ -278,2 +283,3 @@ source, | ||
windowScroll, | ||
isScrollAllowed, | ||
shouldAnimate: false, | ||
@@ -304,2 +310,10 @@ }; | ||
// Currently not supporting container scrolling while dragging with a keyboard | ||
// We do not store whether we are dragging with a keyboard in the state but this flag | ||
// does this trick. Ideally this check would not exist. | ||
// Kill the drag instantly | ||
if (!state.drag.current.isScrollAllowed) { | ||
return clean(); | ||
} | ||
const { id, offset } = action.payload; | ||
@@ -346,2 +360,37 @@ | ||
if (action.type === 'UPDATE_DROPPABLE_DIMENSION_IS_ENABLED') { | ||
if (!Object.keys(state.dimension.droppable).length) { | ||
return state; | ||
} | ||
const { id, isEnabled } = action.payload; | ||
const target = state.dimension.droppable[id]; | ||
if (!target) { | ||
console.error('cannot update enabled flag on droppable that does not have a dimension'); | ||
return clean(); | ||
} | ||
if (target.isEnabled === isEnabled) { | ||
console.warn(`trying to set droppable isEnabled to ${isEnabled} but it is already ${isEnabled}`); | ||
return state; | ||
} | ||
const updatedDroppableDimension = { | ||
...target, | ||
isEnabled, | ||
}; | ||
return { | ||
...state, | ||
dimension: { | ||
...state.dimension, | ||
droppable: { | ||
...state.dimension.droppable, | ||
[id]: updatedDroppableDimension, | ||
}, | ||
}, | ||
}; | ||
} | ||
if (action.type === 'MOVE') { | ||
@@ -406,8 +455,17 @@ const { client, page, windowScroll } = action.payload; | ||
const result: ?JumpToNextResult = jumpToNextIndex({ | ||
if (!existing.impact.destination) { | ||
console.error('cannot move if there is no previous destination'); | ||
return clean(); | ||
} | ||
const droppable: DroppableDimension = state.dimension.droppable[ | ||
existing.impact.destination.droppableId | ||
]; | ||
const result: ?MoveToNextResult = moveToNextIndex({ | ||
isMovingForward, | ||
draggableId: existing.current.id, | ||
impact: existing.impact, | ||
droppable, | ||
draggables: state.dimension.draggable, | ||
droppables: state.dimension.droppable, | ||
}); | ||
@@ -420,24 +478,59 @@ | ||
const diff: Position = result.diff; | ||
const impact: DragImpact = result.impact; | ||
const page: Position = result.pageCenter; | ||
const client: Position = subtract(page, existing.current.windowScroll); | ||
const page: Position = add(existing.current.page.selection, diff); | ||
const client: Position = add(existing.current.client.selection, diff); | ||
return move({ | ||
state, | ||
impact, | ||
clientSelection: client, | ||
pageSelection: page, | ||
shouldAnimate: true, | ||
}); | ||
} | ||
// current limitation: cannot go beyond visible border of list | ||
const droppableId: ?DroppableId = getDroppableOver( | ||
page, state.dimension.droppable, | ||
); | ||
if (action.type === 'CROSS_AXIS_MOVE_FORWARD' || action.type === 'CROSS_AXIS_MOVE_BACKWARD') { | ||
if (state.phase !== 'DRAGGING') { | ||
console.error('cannot move cross axis when not dragging'); | ||
return clean(); | ||
} | ||
if (!droppableId) { | ||
// eslint-disable-next-line no-console | ||
console.info('currently not supporting moving a draggable outside the visibility bounds of a droppable'); | ||
if (!state.drag) { | ||
console.error('cannot move cross axis if there is no drag information'); | ||
return clean(); | ||
} | ||
if (!state.drag.impact.destination) { | ||
console.error('cannot move cross axis if not in a droppable'); | ||
return clean(); | ||
} | ||
const current: CurrentDrag = state.drag.current; | ||
const draggableId: DraggableId = current.id; | ||
const center: Position = current.page.center; | ||
const droppableId: DroppableId = state.drag.impact.destination.droppableId; | ||
const home: DraggableLocation = state.drag.initial.source; | ||
const result: ?MoveCrossAxisResult = moveCrossAxis({ | ||
isMovingForward: action.type === 'CROSS_AXIS_MOVE_FORWARD', | ||
pageCenter: center, | ||
draggableId, | ||
droppableId, | ||
home, | ||
draggables: state.dimension.draggable, | ||
droppables: state.dimension.droppable, | ||
}); | ||
if (!result) { | ||
return state; | ||
} | ||
const page: Position = result.pageCenter; | ||
const client: Position = subtract(page, current.windowScroll); | ||
return move({ | ||
state, | ||
impact, | ||
clientSelection: client, | ||
pageSelection: page, | ||
impact: result.impact, | ||
shouldAnimate: true, | ||
@@ -444,0 +537,0 @@ }); |
// @flow | ||
import { createSelector } from 'reselect'; | ||
import type { | ||
PendingDrop, | ||
DragState, | ||
DraggableDimension, | ||
DraggableDimensionMap, | ||
Phase, | ||
@@ -19,1 +22,41 @@ State, | ||
export const dragSelector = (state: State): ?DragState => state.drag; | ||
const draggableMapSelector = (state: State): DraggableDimensionMap => state.dimension.draggable; | ||
export const draggingDraggableSelector = createSelector([ | ||
phaseSelector, | ||
dragSelector, | ||
pendingDropSelector, | ||
draggableMapSelector, | ||
], (phase: Phase, | ||
drag: ?DragState, | ||
pending: ?PendingDrop, | ||
draggables: DraggableDimensionMap | ||
): ?DraggableDimension => { | ||
if (phase === 'DRAGGING') { | ||
if (!drag) { | ||
console.error('cannot get placeholder dimensions as there is an invalid drag state'); | ||
return null; | ||
} | ||
const draggable: DraggableDimension = draggables[drag.current.id]; | ||
return draggable; | ||
} | ||
if (phase === 'DROP_ANIMATING') { | ||
if (!pending) { | ||
console.error('cannot get placeholder dimensions as there is an invalid drag state'); | ||
return null; | ||
} | ||
if (!pending.result.destination) { | ||
return null; | ||
} | ||
const draggable: DraggableDimension = draggables[pending.result.draggableId]; | ||
return draggable; | ||
} | ||
return null; | ||
} | ||
); |
@@ -16,2 +16,18 @@ // @flow | ||
export type Spacing = {| | ||
top: number, | ||
right: number, | ||
bottom: number, | ||
left: number, | ||
|} | ||
export type ClientRect = {| | ||
top: number, | ||
right: number, | ||
bottom: number, | ||
left: number, | ||
width: number, | ||
height: number, | ||
|} | ||
export type Direction = 'horizontal' | 'vertical'; | ||
@@ -22,5 +38,9 @@ | ||
line: 'y', | ||
crossLine: 'x', | ||
start: 'top', | ||
end: 'bottom', | ||
size: 'height', | ||
crossAxisStart: 'left', | ||
crossAxisEnd: 'right', | ||
crossAxisSize: 'width', | ||
|} | ||
@@ -31,5 +51,9 @@ | ||
line: 'x', | ||
crossLine: 'y', | ||
start: 'left', | ||
end: 'right', | ||
size: 'width', | ||
crossAxisStart: 'top', | ||
crossAxisEnd: 'bottom', | ||
crossAxisSize: 'height', | ||
|} | ||
@@ -40,8 +64,3 @@ | ||
export type DimensionFragment = {| | ||
top: number, | ||
left: number, | ||
bottom: number, | ||
right: number, | ||
width: number, | ||
height: number, | ||
...ClientRect, | ||
center: Position, | ||
@@ -53,10 +72,12 @@ |} | ||
droppableId: DroppableId, | ||
page: {| | ||
// relative to the viewport when the drag started | ||
client: {| | ||
withMargin: DimensionFragment, | ||
withoutMargin: DimensionFragment, | ||
|}, | ||
client: {| | ||
// relative to the whole page | ||
page: {| | ||
withMargin: DimensionFragment, | ||
withoutMargin: DimensionFragment, | ||
|} | ||
|}, | ||
|} | ||
@@ -67,2 +88,3 @@ | ||
axis: Axis, | ||
isEnabled: boolean, | ||
scroll: {| | ||
@@ -72,6 +94,16 @@ initial: Position, | ||
|}, | ||
// relative to the current viewport | ||
client: {| | ||
withMargin: DimensionFragment, | ||
withoutMargin: DimensionFragment, | ||
// the area in which content presses up against | ||
withMarginAndPadding: DimensionFragment, | ||
|}, | ||
// relative to the whole page | ||
page: {| | ||
withMargin: DimensionFragment, | ||
withoutMargin: DimensionFragment, | ||
|} | ||
// the area in which content presses up against | ||
withMarginAndPadding: DimensionFragment, | ||
|}, | ||
|} | ||
@@ -87,2 +119,4 @@ export type DraggableLocation = {| | ||
export type DragMovement = {| | ||
// The draggables that need to move in response to a drag. | ||
// Ordered by closest draggable to the *current* location of the dragging item | ||
draggables: DraggableId[], | ||
@@ -98,3 +132,3 @@ amount: Position, | ||
direction: ?Direction, | ||
destination: ?DraggableLocation | ||
destination: ?DraggableLocation, | ||
|} | ||
@@ -113,5 +147,5 @@ | ||
source: DraggableLocation, | ||
// viewport | ||
// relative to the viewport when the drag started | ||
client: InitialDragLocation, | ||
// viewport + window scroll | ||
// viewport + window scroll (position relative to 0, 0) | ||
page: InitialDragLocation, | ||
@@ -138,2 +172,4 @@ // Storing scroll directly to support movement during a window scroll. | ||
type: TypeId, | ||
// whether scrolling is allowed - otherwise a scroll will cancel the drag | ||
isScrollAllowed: boolean, | ||
// viewport | ||
@@ -148,2 +184,3 @@ client: CurrentDragLocation, | ||
withinDroppable: WithinDroppable, | ||
// whether or not movements should be animated | ||
shouldAnimate: boolean, | ||
@@ -165,3 +202,3 @@ |} | ||
// may not have any destination (drag to nowhere) | ||
destination: ?DraggableLocation | ||
destination: ?DraggableLocation, | ||
|} | ||
@@ -168,0 +205,0 @@ |
@@ -12,2 +12,4 @@ import type { Position, Direction } from '../../types'; | ||
onMoveBackward: () => void, | ||
onCrossAxisMoveForward: () => void, | ||
onCrossAxisMoveBackward: () => void, | ||
onDrop: () => void, | ||
@@ -14,0 +16,0 @@ onCancel: () => void, |
@@ -5,3 +5,3 @@ // @flow | ||
import memoizeOne from 'memoize-one'; | ||
import rafScheduler from 'raf-schd'; | ||
import rafSchedule from 'raf-schd'; | ||
// Using keyCode's for consistent event pattern matching between | ||
@@ -33,2 +33,7 @@ // React synthetic events as well as raw browser events. | ||
type ExecuteBasedOnDirection = {| | ||
vertical: () => void, | ||
horizontal: () => void, | ||
|} | ||
export default class DragHandle extends Component { | ||
@@ -65,15 +70,23 @@ /* eslint-disable react/sort-comp */ | ||
// scheduled functions | ||
scheduleMove = rafScheduler((point: Position) => { | ||
scheduleMove = rafSchedule((point: Position) => { | ||
this.ifDragging(() => this.memoizedMove(point.x, point.y)); | ||
}); | ||
scheduleMoveForward = rafScheduler(() => { | ||
scheduleMoveForward = rafSchedule(() => { | ||
this.ifDragging(this.props.callbacks.onMoveForward); | ||
}) | ||
scheduleMoveBackward = rafScheduler(() => { | ||
scheduleMoveBackward = rafSchedule(() => { | ||
this.ifDragging(this.props.callbacks.onMoveBackward); | ||
}); | ||
scheduleWindowScrollMove = rafScheduler(() => { | ||
scheduleCrossAxisMoveForward = rafSchedule(() => { | ||
this.ifDragging(this.props.callbacks.onCrossAxisMoveForward); | ||
}) | ||
scheduleCrossAxisMoveBackward = rafSchedule(() => { | ||
this.ifDragging(this.props.callbacks.onCrossAxisMoveBackward); | ||
}); | ||
scheduleWindowScrollMove = rafSchedule(() => { | ||
this.ifDragging(this.props.callbacks.onWindowScroll); | ||
@@ -243,2 +256,13 @@ }); | ||
executeBasedOnDirection = (fns: ExecuteBasedOnDirection) => { | ||
if (!this.props.direction) { | ||
console.error('cannot move based on direction when none is provided'); | ||
this.stopDragging(() => this.props.callbacks.onCancel()); | ||
return; | ||
} | ||
// eslint-disable-next-line no-unused-expressions | ||
this.props.direction === 'vertical' ? fns.vertical() : fns.horizontal(); | ||
} | ||
// window keyboard events are bound during a keyboard drag | ||
@@ -305,20 +329,26 @@ // or after the user presses the mouse down | ||
if (this.props.direction === 'vertical') { | ||
if (event.keyCode === keyCodes.arrowDown) { | ||
event.preventDefault(); | ||
this.scheduleMoveForward(); | ||
} | ||
if (event.keyCode === keyCodes.arrowDown) { | ||
event.preventDefault(); | ||
this.executeBasedOnDirection({ | ||
vertical: this.scheduleMoveForward, | ||
horizontal: this.scheduleCrossAxisMoveForward, | ||
}); | ||
return; | ||
} | ||
if (event.keyCode === keyCodes.arrowUp) { | ||
event.preventDefault(); | ||
this.scheduleMoveBackward(); | ||
} | ||
if (event.keyCode === keyCodes.arrowUp) { | ||
event.preventDefault(); | ||
this.executeBasedOnDirection({ | ||
vertical: this.scheduleMoveBackward, | ||
horizontal: this.scheduleCrossAxisMoveBackward, | ||
}); | ||
return; | ||
} | ||
// horizontal dragging | ||
if (event.keyCode === keyCodes.arrowRight) { | ||
event.preventDefault(); | ||
this.scheduleMoveForward(); | ||
this.executeBasedOnDirection({ | ||
vertical: this.scheduleCrossAxisMoveForward, | ||
horizontal: this.scheduleMoveForward, | ||
}); | ||
return; | ||
@@ -329,3 +359,6 @@ } | ||
event.preventDefault(); | ||
this.scheduleMoveBackward(); | ||
this.executeBasedOnDirection({ | ||
vertical: this.scheduleCrossAxisMoveBackward, | ||
horizontal: this.scheduleMoveBackward, | ||
}); | ||
} | ||
@@ -332,0 +365,0 @@ } |
@@ -6,5 +6,3 @@ // @flow | ||
import { getDraggableDimension } from '../../state/dimension'; | ||
// eslint-disable-next-line no-duplicate-imports | ||
import type { Margin } from '../../state/dimension'; | ||
import type { DraggableDimension } from '../../types'; | ||
import type { DraggableDimension, Spacing } from '../../types'; | ||
import type { Props } from './draggable-dimension-publisher-types'; | ||
@@ -27,3 +25,3 @@ | ||
const margin: Margin = { | ||
const margin: Spacing = { | ||
top: parseInt(style.marginTop, 10), | ||
@@ -30,0 +28,0 @@ right: parseInt(style.marginRight, 10), |
@@ -18,2 +18,4 @@ // @flow | ||
moveBackward as moveBackwardAction, | ||
crossAxisMoveForward as crossAxisMoveForwardAction, | ||
crossAxisMoveBackward as crossAxisMoveBackwardAction, | ||
drop as dropAction, | ||
@@ -232,4 +234,6 @@ cancel as cancelAction, | ||
move: moveAction, | ||
moveForward: moveForwardAction, | ||
moveBackward: moveBackwardAction, | ||
moveForward: moveForwardAction, | ||
crossAxisMoveForward: crossAxisMoveForwardAction, | ||
crossAxisMoveBackward: crossAxisMoveBackwardAction, | ||
moveByWindowScroll: moveByWindowScrollAction, | ||
@@ -236,0 +240,0 @@ drop: dropAction, |
@@ -22,2 +22,4 @@ // @flow | ||
moveBackward, | ||
crossAxisMoveForward, | ||
crossAxisMoveBackward, | ||
drop, | ||
@@ -101,2 +103,4 @@ cancel, | ||
moveBackward: PropType<typeof moveBackward, Function>, | ||
crossAxisMoveForward: PropType<typeof crossAxisMoveForward, Function>, | ||
crossAxisMoveBackward: PropType<typeof crossAxisMoveBackward, Function>, | ||
drop: PropType<typeof drop, Function>, | ||
@@ -103,0 +107,0 @@ cancel: PropType<typeof cancel, Function>, |
@@ -23,3 +23,3 @@ // @flow | ||
import getCenterPosition from '../get-center-position'; | ||
import Placeholder from './placeholder'; | ||
import Placeholder from '../placeholder'; | ||
import { droppableIdKey } from '../context-keys'; | ||
@@ -73,3 +73,3 @@ import { add } from '../../state/position'; | ||
this.callbacks = { | ||
const callbacks: DragHandleCallbacks = { | ||
onLift: this.onLift, | ||
@@ -82,4 +82,8 @@ onMove: this.onMove, | ||
onMoveForward: this.onMoveForward, | ||
onCrossAxisMoveForward: this.onCrossAxisMoveForward, | ||
onCrossAxisMoveBackward: this.onCrossAxisMoveBackward, | ||
onWindowScroll: this.onWindowScroll, | ||
}; | ||
this.callbacks = callbacks; | ||
} | ||
@@ -123,3 +127,6 @@ | ||
lift(draggableId, type, client, page, windowScroll); | ||
// Allowing scrolling with a mouse when lifting with a mouse | ||
const isScrollAllowed = true; | ||
lift(draggableId, type, client, page, windowScroll, isScrollAllowed); | ||
} | ||
@@ -145,4 +152,6 @@ | ||
}; | ||
// not allowing scrolling with a mouse when lifting with a keyboard | ||
const isScrollAllowed = false; | ||
lift(draggableId, type, client, page, windowScroll); | ||
lift(draggableId, type, client, page, windowScroll, isScrollAllowed); | ||
} | ||
@@ -176,2 +185,12 @@ | ||
onCrossAxisMoveForward = () => { | ||
this.throwIfCannotDrag(); | ||
this.props.crossAxisMoveForward(this.props.draggableId); | ||
} | ||
onCrossAxisMoveBackward = () => { | ||
this.throwIfCannotDrag(); | ||
this.props.crossAxisMoveBackward(this.props.draggableId); | ||
} | ||
onWindowScroll = () => { | ||
@@ -178,0 +197,0 @@ this.throwIfCannotDrag(); |
@@ -17,2 +17,3 @@ // @flow | ||
updateDroppableDimensionScroll, | ||
updateDroppableDimensionIsEnabled, | ||
} from '../../state/action-creators'; | ||
@@ -47,2 +48,3 @@ | ||
updateScroll: updateDroppableDimensionScroll, | ||
updateIsEnabled: updateDroppableDimensionIsEnabled, | ||
}; | ||
@@ -49,0 +51,0 @@ |
// @flow | ||
import type { PropType } from 'babel-plugin-react-flow-props-to-prop-types'; | ||
import { | ||
publishDroppableDimension, | ||
updateDroppableDimensionIsEnabled, | ||
updateDroppableDimensionScroll, | ||
} from '../../state/action-creators'; | ||
import type { | ||
DroppableDimension, | ||
DroppableId, | ||
@@ -8,3 +13,2 @@ TypeId, | ||
HTMLElement, | ||
Position, | ||
Direction, | ||
@@ -18,4 +22,5 @@ } from '../../types'; | ||
export type DispatchProps = {| | ||
publish: (dimension: DroppableDimension) => mixed, | ||
updateScroll: (id: DroppableId, offset: Position) => mixed, | ||
publish: PropType<typeof publishDroppableDimension, Function>, | ||
updateIsEnabled: PropType<typeof updateDroppableDimensionIsEnabled, Function>, | ||
updateScroll: PropType<typeof updateDroppableDimensionScroll, Function>, | ||
|} | ||
@@ -26,2 +31,3 @@ | ||
direction: Direction, | ||
isDropDisabled: boolean, | ||
type: TypeId, | ||
@@ -32,3 +38,7 @@ targetRef: ?HTMLElement, | ||
export type Props = MapProps & DispatchProps & OwnProps; | ||
export type Props = { | ||
...MapProps, | ||
...DispatchProps, | ||
...OwnProps | ||
} | ||
@@ -35,0 +45,0 @@ // Having issues getting the correct reselect type |
@@ -7,7 +7,13 @@ // @flow | ||
import getWindowScrollPosition from '../get-window-scroll-position'; | ||
import getClientRect from '../../state/get-client-rect'; | ||
import { getDroppableDimension } from '../../state/dimension'; | ||
import getClosestScrollable from '../get-closest-scrollable'; | ||
// eslint-disable-next-line no-duplicate-imports | ||
import type { Margin } from '../../state/dimension'; | ||
import type { DroppableDimension, Position, HTMLElement } from '../../types'; | ||
import type { | ||
DroppableDimension, | ||
Position, | ||
HTMLElement, | ||
ClientRect, | ||
Spacing, | ||
} from '../../types'; | ||
import type { Props } from './droppable-dimension-publisher-types'; | ||
@@ -38,8 +44,11 @@ | ||
getDimension = (): DroppableDimension => { | ||
const { droppableId, direction, targetRef } = this.props; | ||
const { droppableId, direction, isDropDisabled, targetRef } = this.props; | ||
invariant(targetRef, 'DimensionPublisher cannot calculate a dimension when not attached to the DOM'); | ||
const scroll: Position = this.getScrollOffset(); | ||
const style = window.getComputedStyle(targetRef); | ||
const margin: Margin = { | ||
// keeping it simple and always using the margin of the droppable | ||
const margin: Spacing = { | ||
top: parseInt(style.marginTop, 10), | ||
@@ -50,10 +59,48 @@ right: parseInt(style.marginRight, 10), | ||
}; | ||
const padding: Spacing = { | ||
top: parseInt(style.paddingTop, 10), | ||
right: parseInt(style.paddingRight, 10), | ||
bottom: parseInt(style.paddingBottom, 10), | ||
left: parseInt(style.paddingLeft, 10), | ||
}; | ||
const clientRect: ClientRect = (() => { | ||
const current: ClientRect = targetRef.getBoundingClientRect(); | ||
if (!this.closestScrollable) { | ||
return current; | ||
} | ||
if (this.closestScrollable === targetRef) { | ||
return current; | ||
} | ||
// We need to trim the dimension by the visible area of the scroll container | ||
// Adjust the current dimension with the parents scroll | ||
const top = current.top + scroll.y; | ||
const bottom = current.bottom + scroll.y; | ||
const left = current.left + scroll.x; | ||
const right = current.right + scroll.x; | ||
// Trim the dimension by the size of the parent | ||
const parent: ClientRect = this.closestScrollable.getBoundingClientRect(); | ||
return getClientRect({ | ||
top: Math.max(top, parent.top), | ||
left: Math.max(left, parent.left), | ||
right: Math.min(right, parent.right), | ||
bottom: Math.min(bottom, parent.bottom), | ||
}); | ||
})(); | ||
const dimension: DroppableDimension = getDroppableDimension({ | ||
id: droppableId, | ||
direction, | ||
clientRect: targetRef.getBoundingClientRect(), | ||
clientRect, | ||
margin, | ||
padding, | ||
windowScroll: getWindowScrollPosition(), | ||
scroll: this.getScrollOffset(), | ||
scroll, | ||
isEnabled: !isDropDisabled, | ||
}); | ||
@@ -111,17 +158,12 @@ | ||
// TODO: componentDidUpdate? | ||
componentWillReceiveProps(nextProps: Props) { | ||
if (nextProps.targetRef !== this.props.targetRef) { | ||
if (this.isWatchingScroll) { | ||
console.warn('changing targetRef while watching scroll!'); | ||
this.unwatchScroll(); | ||
} | ||
} | ||
// Because the dimension publisher wraps children - it might render even when its props do | ||
// not change. We need to ensure that it does not publish when it should not. | ||
const shouldPublish = !this.props.shouldPublish && nextProps.shouldPublish; | ||
const shouldStartPublishing = !this.props.shouldPublish && nextProps.shouldPublish; | ||
const alreadyPublishing = this.props.shouldPublish && nextProps.shouldPublish; | ||
const stopPublishing = this.props.shouldPublish && !nextProps.shouldPublish; | ||
// should no longer watch for scrolling | ||
if (!nextProps.shouldPublish) { | ||
if (stopPublishing) { | ||
this.unwatchScroll(); | ||
@@ -131,6 +173,26 @@ return; | ||
if (!shouldPublish) { | ||
if (alreadyPublishing) { | ||
// if ref changes and watching scroll - unwatch the scroll | ||
if (nextProps.targetRef !== this.props.targetRef) { | ||
if (this.isWatchingScroll) { | ||
console.warn('changing targetRef while watching scroll!'); | ||
this.unwatchScroll(); | ||
} | ||
} | ||
// publish any changes to the disabled flag | ||
if (nextProps.isDropDisabled !== this.props.isDropDisabled) { | ||
this.props.updateIsEnabled(this.props.droppableId, !nextProps.isDropDisabled); | ||
} | ||
return; | ||
} | ||
// This will be the default when nothing is happening | ||
if (!shouldStartPublishing) { | ||
return; | ||
} | ||
// Need to start publishing | ||
// discovering the closest scrollable for a drag | ||
@@ -137,0 +199,0 @@ this.closestScrollable = getClosestScrollable(this.props.targetRef); |
@@ -6,3 +6,8 @@ // @flow | ||
import { storeKey } from '../context-keys'; | ||
import { dragSelector, pendingDropSelector, phaseSelector } from '../../state/selectors'; | ||
import { | ||
dragSelector, | ||
pendingDropSelector, | ||
phaseSelector, | ||
draggingDraggableSelector, | ||
} from '../../state/selectors'; | ||
import Droppable from './droppable'; | ||
@@ -16,2 +21,3 @@ import type { | ||
DraggableLocation, | ||
DraggableDimension, | ||
} from '../../types'; | ||
@@ -22,2 +28,3 @@ import type { | ||
Selector, | ||
Placeholder, | ||
} from './droppable-types'; | ||
@@ -40,9 +47,49 @@ | ||
const getMapProps = memoizeOne((isDraggingOver: boolean): MapProps => ({ | ||
isDraggingOver, | ||
const memoizedPlaceholder = memoizeOne((width: number, height: number): Placeholder => ({ | ||
width, height, | ||
})); | ||
const getPlaceholder = memoizeOne( | ||
(id: DroppableId, | ||
source: DraggableLocation, | ||
destination: ?DraggableLocation, | ||
draggable: ?DraggableDimension | ||
): ?Placeholder => { | ||
if (!destination) { | ||
return null; | ||
} | ||
// no placeholder needed for this droppable | ||
if (destination.droppableId !== id) { | ||
return null; | ||
} | ||
// no placeholder needed when dragging over the source list | ||
if (source.droppableId === destination.droppableId) { | ||
return null; | ||
} | ||
if (!draggable) { | ||
return null; | ||
} | ||
const placeholder: Placeholder = memoizedPlaceholder( | ||
draggable.page.withMargin.width, | ||
draggable.page.withMargin.height, | ||
); | ||
return placeholder; | ||
} | ||
); | ||
const getMapProps = memoizeOne( | ||
(isDraggingOver: boolean, placeholder: ?Placeholder): MapProps => ({ | ||
isDraggingOver, | ||
placeholder, | ||
}) | ||
); | ||
return createSelector( | ||
[phaseSelector, | ||
dragSelector, | ||
draggingDraggableSelector, | ||
pendingDropSelector, | ||
@@ -54,2 +101,3 @@ idSelector, | ||
drag: ?DragState, | ||
draggable: ?DraggableDimension, | ||
pending: ?PendingDrop, | ||
@@ -60,3 +108,3 @@ id: DroppableId, | ||
if (isDropDisabled) { | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
} | ||
@@ -67,7 +115,14 @@ | ||
console.error('cannot determine dragging over as there is not drag'); | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
} | ||
const isDraggingOver = getIsDraggingOver(id, drag.impact.destination); | ||
return getMapProps(isDraggingOver); | ||
const placeholder: ?Placeholder = getPlaceholder( | ||
id, | ||
drag.initial.source, | ||
drag.impact.destination, | ||
draggable | ||
); | ||
return getMapProps(isDraggingOver, placeholder); | ||
} | ||
@@ -78,10 +133,16 @@ | ||
console.error('cannot determine dragging over as there is no pending result'); | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
} | ||
const isDraggingOver = getIsDraggingOver(id, pending.impact.destination); | ||
return getMapProps(isDraggingOver); | ||
const placeholder: ?Placeholder = getPlaceholder( | ||
id, | ||
pending.result.source, | ||
pending.result.destination, | ||
draggable | ||
); | ||
return getMapProps(isDraggingOver, placeholder); | ||
} | ||
return getMapProps(false); | ||
return getMapProps(false, null); | ||
}, | ||
@@ -88,0 +149,0 @@ ); |
@@ -14,4 +14,10 @@ // @flow | ||
export type Placeholder = {| | ||
height: number, | ||
width: number, | ||
|} | ||
export type Provided = {| | ||
innerRef: (?HTMLElement) => void, | ||
placeholder: ?ReactElement, | ||
|} | ||
@@ -25,2 +31,6 @@ | ||
isDraggingOver: boolean, | ||
// placeholder is used to hold space when | ||
// not the user is dragging over a list that | ||
// is not the source list | ||
placeholder: ?Placeholder, | ||
|} | ||
@@ -27,0 +37,0 @@ |
@@ -6,2 +6,3 @@ import React, { Component } from 'react'; | ||
import DroppableDimensionPublisher from '../droppable-dimension-publisher/'; | ||
import Placeholder from '../placeholder/'; | ||
import { droppableIdKey } from '../context-keys'; | ||
@@ -64,8 +65,30 @@ | ||
getPlaceholder() { | ||
if (!this.props.placeholder) { | ||
return null; | ||
} | ||
return ( | ||
<Placeholder | ||
height={this.props.placeholder.height} | ||
width={this.props.placeholder.width} | ||
/> | ||
); | ||
} | ||
render() { | ||
const { | ||
children, | ||
direction, | ||
droppableId, | ||
isDraggingOver, | ||
isDropDisabled, | ||
type, | ||
} = this.props; | ||
const provided: Provided = { | ||
innerRef: this.setRef, | ||
placeholder: this.getPlaceholder(), | ||
}; | ||
const snapshot: StateSnapshot = { | ||
isDraggingOver: this.props.isDraggingOver, | ||
isDraggingOver, | ||
}; | ||
@@ -75,8 +98,9 @@ | ||
<DroppableDimensionPublisher | ||
droppableId={this.props.droppableId} | ||
direction={this.props.direction} | ||
type={this.props.type} | ||
droppableId={droppableId} | ||
direction={direction} | ||
isDropDisabled={isDropDisabled} | ||
type={type} | ||
targetRef={this.state.ref} | ||
> | ||
{this.props.children(provided, snapshot)} | ||
{children(provided, snapshot)} | ||
</DroppableDimensionPublisher> | ||
@@ -83,0 +107,0 @@ ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
589780
212
10677
995