@atlaskit/drag-and-drop
Advanced tools
Comparing version 0.13.0 to 0.14.0
# @atlaskit/drag-and-drop | ||
## 0.14.0 | ||
### Minor Changes | ||
- [`eab6d26451d`](https://bitbucket.org/atlassian/atlassian-frontend/commits/eab6d26451d) - Improving the resilience of our workaround for a [Browser bug](https://bugs.chromium.org/p/chromium/issues/detail?id=410328) where after a drag finishes, an unrelated element can be entered into. | ||
- [`ba7ea570aee`](https://bitbucket.org/atlassian/atlassian-frontend/commits/ba7ea570aee) - > Both of these changes should not impact most consumers as they are targeted at edge cases. | ||
- **Fix**: We no longer extract user input (eg `clientX`) from native `"dragleave"` events due to a [Bug with Chrome we discovered](https://bugs.chromium.org/p/chromium/issues/detail?id=1429937). Due to this bug, it was possible for `location.current.input` to be incorrectly set in `onDropTargetChange` and `onDrop` when a user was cancelling a drag or dropping or no drop targets. | ||
- **Fix**: `location.previous.dropTargets` _should_ always point to the `location.current.dropTargets` value from the previous event (exception: `onGenerateDragPreview` and `onDragStart` have the same `location.previous` and `location.current` values). Previously, the `location.previous.dropTargets` value did not match the last events `location.current.dropTargets` value in `onDrop`. `onDrop()` would incorrectly use the `location.current` and `location.previous` values from the last event rather than creating a new `location.current` entry. Now, `onDrop()`, `location.previous.dropTargets` points to the `location.current.dropTargets` from the last event (same as all other events) and `location.current.dropTargets` points to what the previous drop target was as well (no change) | ||
## 0.13.0 | ||
@@ -4,0 +15,0 @@ |
@@ -38,8 +38,26 @@ "use strict"; | ||
var source = _ref.source, | ||
initial = _ref.initial, | ||
dispatchEvent = _ref.dispatchEvent; | ||
var previous = { | ||
dropTargets: [] | ||
}; | ||
function safeDispatch(args) { | ||
dispatchEvent(args); | ||
previous = { | ||
dropTargets: args.payload.location.current.dropTargets | ||
}; | ||
} | ||
var dispatch = { | ||
start: function start(_ref2) { | ||
var location = _ref2.location, | ||
nativeSetDragImage = _ref2.nativeSetDragImage; | ||
dispatchEvent({ | ||
var nativeSetDragImage = _ref2.nativeSetDragImage; | ||
// Ensuring that both `onGenerateDragPreview` and `onDragStart` get the same location. | ||
// We do this so that `previous` is`[]` in `onDragStart` (which is logical) | ||
var location = { | ||
current: initial, | ||
previous: previous, | ||
initial: initial | ||
}; | ||
// a `onGenerateDragPreview` does _not_ add another entry for `previous` | ||
// onDragPreview | ||
safeDispatch({ | ||
eventName: 'onGenerateDragPreview', | ||
@@ -53,3 +71,3 @@ payload: { | ||
dragStart.schedule(function () { | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDragStart', | ||
@@ -64,10 +82,14 @@ payload: { | ||
dragUpdate: function dragUpdate(_ref3) { | ||
var location = _ref3.location; | ||
var current = _ref3.current; | ||
dragStart.flush(); | ||
scheduleOnDrag.cancel(); | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDropTargetChange', | ||
payload: { | ||
source: source, | ||
location: location | ||
location: { | ||
initial: initial, | ||
previous: previous, | ||
current: current | ||
} | ||
} | ||
@@ -77,6 +99,11 @@ }); | ||
drag: function drag(_ref4) { | ||
var location = _ref4.location; | ||
var current = _ref4.current; | ||
scheduleOnDrag(function () { | ||
dragStart.flush(); | ||
dispatchEvent({ | ||
var location = { | ||
initial: initial, | ||
previous: previous, | ||
current: current | ||
}; | ||
safeDispatch({ | ||
eventName: 'onDrag', | ||
@@ -91,11 +118,15 @@ payload: { | ||
drop: function drop(_ref5) { | ||
var location = _ref5.location, | ||
var current = _ref5.current, | ||
updatedSourcePayload = _ref5.updatedExternalPayload; | ||
dragStart.flush(); | ||
scheduleOnDrag.cancel(); | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDrop', | ||
payload: { | ||
source: updatedSourcePayload !== null && updatedSourcePayload !== void 0 ? updatedSourcePayload : source, | ||
location: location | ||
location: { | ||
current: current, | ||
previous: previous, | ||
initial: initial | ||
} | ||
} | ||
@@ -102,0 +133,0 @@ }); |
@@ -53,28 +53,12 @@ "use strict"; | ||
}); | ||
var state = { | ||
initial: initial, | ||
current: initial, | ||
// no 'previous' when we first start dragging | ||
previous: { | ||
dropTargets: [] | ||
} | ||
}; | ||
function updateState(_ref3) { | ||
var next = _ref3.next; | ||
var newState = { | ||
initial: initial, | ||
previous: { | ||
dropTargets: state.current.dropTargets | ||
}, | ||
current: next | ||
}; | ||
state = newState; | ||
} | ||
var current = initial; | ||
// Setting initial drop effect for the drag | ||
setDropEffect({ | ||
event: event, | ||
current: state.initial.dropTargets | ||
current: initial.dropTargets | ||
}); | ||
var dispatch = (0, _dispatchConsumerEvent.makeDispatch)({ | ||
source: dragInterface.payload, | ||
dispatchEvent: dispatchEvent | ||
dispatchEvent: dispatchEvent, | ||
initial: initial | ||
}); | ||
@@ -84,3 +68,3 @@ function updateDropTargets(next) { | ||
var hasChanged = hasHierarchyChanged({ | ||
current: state.current.dropTargets, | ||
current: current.dropTargets, | ||
next: next.dropTargets | ||
@@ -92,8 +76,6 @@ }); | ||
// Consumers can get the latest data by using `onDrag` | ||
updateState({ | ||
next: next | ||
}); | ||
current = next; | ||
if (hasChanged) { | ||
dispatch.dragUpdate({ | ||
location: state | ||
current: current | ||
}); | ||
@@ -108,3 +90,3 @@ } | ||
source: dragInterface.payload, | ||
current: state.current.dropTargets | ||
current: current.dropTargets | ||
}); | ||
@@ -124,25 +106,25 @@ if (nextDropTargets.length) { | ||
} | ||
function onDrop(_ref3) { | ||
var updatedExternalPayload = _ref3.updatedExternalPayload; | ||
dispatch.drop({ | ||
current: current, | ||
updatedExternalPayload: updatedExternalPayload | ||
}); | ||
} | ||
function cancel() { | ||
// The spec behaviour is that when a drop is cancelled, | ||
// a 'dragleave' event is fired on the active drop target | ||
// before a `dragend` event. | ||
// We are replicating that behaviour here | ||
// The spec behaviour is that when a drag is cancelled, or when dropping on no drop targets, | ||
// a "dragleave" event is fired on the active drop target before a "dragend" event. | ||
// We are replicating that behaviour in `cancel` if there are any active drop targets to | ||
// ensure consistent behaviour. | ||
// | ||
// Note: When cancelling, or dropping on no drop targets, a "dragleave" event | ||
// will have already cleared the dropTargets to `[]` (as that particular "dragleave" has a `relatedTarget` of `null`) | ||
// If there are any active drop targets we will create | ||
// and update event to leave them | ||
if (state.current.dropTargets.length) { | ||
updateState({ | ||
next: { | ||
// clear the drop targets | ||
dropTargets: [], | ||
// keep the same input | ||
input: state.current.input | ||
} | ||
if (current.dropTargets.length) { | ||
updateDropTargets({ | ||
dropTargets: [], | ||
input: current.input | ||
}); | ||
dispatch.dragUpdate({ | ||
location: state | ||
}); | ||
} | ||
dispatch.drop({ | ||
location: state, | ||
onDrop({ | ||
updatedExternalPayload: null | ||
@@ -176,4 +158,5 @@ }); | ||
// 2. let consumers know a move has occurred | ||
// This will include the latest 'input' values | ||
dispatch.drag({ | ||
location: state | ||
current: current | ||
}); | ||
@@ -187,5 +170,7 @@ } | ||
// when the user is leaving the `window`. | ||
// When we leave the `window` we want to clear any active drop targets, | ||
// Internal drags: when we leave the `window` we want to clear any active drop targets, | ||
// but the drag is not yet over. The user could drag back into the window. | ||
// We only need to do this because of stickiness | ||
// External drags: when we leave the `window` the drag operation is over, | ||
// we will start another drag operation | ||
type: 'dragleave', | ||
@@ -198,4 +183,17 @@ listener: function listener(event) { | ||
} | ||
// When a drag is ending without a drop target (or when the drag is cancelled), | ||
// All browsers fire: | ||
// 1. "drag" | ||
// 2. "dragleave" | ||
// These events have `event.relatedTarget == null` so this code path is also hit in those cases. | ||
// This is all good! We would be clearing the dropTargets in `cancel()` after the "dragend" | ||
// 🐛 Bug workaround: intentionally not updating `input` in "dragleave" | ||
// In Chrome, this final "dragleave" has default input values (eg clientX == 0) | ||
// rather than the users current input values | ||
// | ||
// - [Conversation](https://twitter.com/alexandereardon/status/1642697633864241152) | ||
// - [Bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1429937) | ||
updateDropTargets({ | ||
input: (0, _getInput.getInput)(event), | ||
input: current.input, | ||
dropTargets: [] | ||
@@ -211,8 +209,4 @@ }); | ||
var _dragInterface$getDro; | ||
// this can only happen if the browser allowed the drop | ||
// A "drop" can only happen if the browser allowed the drop | ||
if (dragInterface.startedFrom === 'external') { | ||
dragInterface.key; | ||
} | ||
// Opting out of standard browser drop behaviour for the drag | ||
@@ -224,6 +218,5 @@ event.preventDefault(); | ||
event: event, | ||
current: state.current.dropTargets | ||
current: current.dropTargets | ||
}); | ||
dispatch.drop({ | ||
location: state, | ||
onDrop({ | ||
updatedExternalPayload: dragInterface.startedFrom === 'external' ? ((_dragInterface$getDro = dragInterface.getDropPayload) === null || _dragInterface$getDro === void 0 ? void 0 : _dragInterface$getDro.call(dragInterface, event)) || null : null | ||
@@ -236,3 +229,5 @@ }); | ||
if (dragInterface.startedFrom === 'internal') { | ||
(0, _fixPostDragPointerBug.fixPostDragPointerBug)(state); | ||
(0, _fixPostDragPointerBug.fixPostDragPointerBug)({ | ||
current: current | ||
}); | ||
} | ||
@@ -253,3 +248,5 @@ } | ||
if (dragInterface.startedFrom === 'internal') { | ||
(0, _fixPostDragPointerBug.fixPostDragPointerBug)(state); | ||
(0, _fixPostDragPointerBug.fixPostDragPointerBug)({ | ||
current: current | ||
}); | ||
} | ||
@@ -309,3 +306,2 @@ } | ||
dispatch.start({ | ||
location: state, | ||
nativeSetDragImage: getNativeSetDragImage(event) | ||
@@ -312,0 +308,0 @@ }); |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -8,5 +7,3 @@ value: true | ||
exports.fixPostDragPointerBug = fixPostDragPointerBug; | ||
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); | ||
var _bindEventListener = require("bind-event-listener"); | ||
var _combine = require("./combine"); | ||
/** Set a `style` property on a `HTMLElement` | ||
@@ -18,10 +15,12 @@ * | ||
var property = _ref.property, | ||
rule = _ref.rule; | ||
var original = el.style[property]; | ||
el.style[property] = rule; | ||
rule = _ref.rule, | ||
_ref$priority = _ref.priority, | ||
priority = _ref$priority === void 0 ? '' : _ref$priority; | ||
var originalValue = el.style.getPropertyValue(property); | ||
var originalPriority = el.style.getPropertyPriority(property); | ||
el.style.setProperty(property, rule, priority); | ||
return function cleanup() { | ||
el.style[property] = original; | ||
el.style.setProperty(property, originalValue, originalPriority); | ||
}; | ||
} | ||
function noop() {} | ||
@@ -40,26 +39,33 @@ /** | ||
*/ | ||
function fixUnderPointer(location) { | ||
var underUsersPointer = document.elementFromPoint(location.current.input.clientX, location.current.input.clientY); | ||
function fixUnderPointer(_ref2) { | ||
var current = _ref2.current; | ||
var underUsersPointer = document.elementFromPoint(current.input.clientX, current.input.clientY); | ||
if (!(underUsersPointer instanceof HTMLElement)) { | ||
return noop; | ||
return null; | ||
} | ||
// Disabling pointer events on all first level children | ||
// Equivalent of '> * { pointer-events: none; }', except we don't need | ||
// to insert / remove a style rule | ||
var unsetChildren = Array.from(underUsersPointer.children).map(function (child) { | ||
if (!(child instanceof HTMLElement)) { | ||
return noop; | ||
} | ||
return setStyle(child, { | ||
property: 'pointerEvents', | ||
rule: 'none' | ||
}); | ||
// Debug note: change from 'pointer-events: none' to 'background: green' | ||
// to get a better sense of what is being achieved | ||
return setStyle(underUsersPointer, { | ||
property: 'pointer-events', | ||
rule: 'auto', | ||
priority: 'important' | ||
}); | ||
return _combine.combine.apply(void 0, [setStyle(underUsersPointer, { | ||
property: 'pointerEvents', | ||
rule: 'auto' | ||
})].concat((0, _toConsumableArray2.default)(unsetChildren))); | ||
} | ||
function blockPointerEventsOnEverything() { | ||
var _element$sheet; | ||
var element = document.createElement('style'); | ||
// Adding a data attribute so to make it super clear to consumers | ||
// (and to our tests) what this temporary style tag is for | ||
element.setAttribute('pdnd-post-drag-fix', 'true'); | ||
document.head.appendChild(element); | ||
// Debug note: change from 'pointer-events: none' to 'background: red' | ||
// to get a better sense of what is being achieved | ||
(_element$sheet = element.sheet) === null || _element$sheet === void 0 ? void 0 : _element$sheet.insertRule('* { pointer-events: none !important; }'); | ||
return function cleanup() { | ||
document.head.removeChild(element); | ||
}; | ||
} | ||
/** 🔥🤮 Fix (Chrome, Safari and Firefox) bug where the element under where the user started dragging | ||
@@ -72,13 +78,7 @@ * (on the viewport) is entered into by the browser after a drag finishes ("drop" or "dragend") | ||
* | ||
* Conceptually this is what we are doing (but without adding any new style rules to the page) | ||
* ```css | ||
* body { pointer-events: none; } | ||
* elementUnderPointer { pointer-events: auto; } | ||
* elementUnderPointer > * { pointer-events: none; } | ||
* ``` | ||
* | ||
* - [Visual explanation of bug](https://twitter.com/alexandereardon/status/1633614212873465856) | ||
* - [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=410328) | ||
*/ | ||
function fixPostDragPointerBug(location) { | ||
function fixPostDragPointerBug(_ref3) { | ||
var current = _ref3.current; | ||
// Queuing a microtask to give any opportunity for frameworks to update their UI in a microtask | ||
@@ -89,16 +89,12 @@ // Note: react@18 does standard state updates in a microtask | ||
queueMicrotask(function () { | ||
// we need to get `atDestination` before turning off pointer events on the body, | ||
// or `document.elementFromPoint` will just return the `html` element (`document.documentElement`) | ||
// We are allowing pointer events on the element under the users cursor, | ||
// but _not_ on children of that element | ||
var unsetUnderPointer = fixUnderPointer(location); | ||
var unsetBodyStyles = setStyle(document.body, { | ||
property: 'pointerEvents', | ||
rule: 'none' | ||
// Applying fix to element under the pointer before disabling pointer events everywhere, | ||
// Otherwise `element.elementFromPoint` would return `<html>` (document.documentElement) | ||
var undoUnderPointer = fixUnderPointer({ | ||
current: current | ||
}); | ||
var undoGlobalBlock = blockPointerEventsOnEverything(); | ||
function cleanup() { | ||
unbindEvents(); | ||
unsetBodyStyles(); | ||
unsetUnderPointer(); | ||
undoUnderPointer === null || undoUnderPointer === void 0 ? void 0 : undoUnderPointer(); | ||
undoGlobalBlock(); | ||
} | ||
@@ -105,0 +101,0 @@ var unbindEvents = (0, _bindEventListener.bindAll)(window, [{ |
{ | ||
"name": "@atlaskit/drag-and-drop", | ||
"version": "0.13.0", | ||
"version": "0.14.0", | ||
"sideEffects": false | ||
} |
@@ -29,10 +29,28 @@ import rafSchd from 'raf-schd'; | ||
source, | ||
initial, | ||
dispatchEvent | ||
}) { | ||
let previous = { | ||
dropTargets: [] | ||
}; | ||
function safeDispatch(args) { | ||
dispatchEvent(args); | ||
previous = { | ||
dropTargets: args.payload.location.current.dropTargets | ||
}; | ||
} | ||
const dispatch = { | ||
start({ | ||
location, | ||
nativeSetDragImage | ||
}) { | ||
dispatchEvent({ | ||
// Ensuring that both `onGenerateDragPreview` and `onDragStart` get the same location. | ||
// We do this so that `previous` is`[]` in `onDragStart` (which is logical) | ||
const location = { | ||
current: initial, | ||
previous, | ||
initial | ||
}; | ||
// a `onGenerateDragPreview` does _not_ add another entry for `previous` | ||
// onDragPreview | ||
safeDispatch({ | ||
eventName: 'onGenerateDragPreview', | ||
@@ -46,3 +64,3 @@ payload: { | ||
dragStart.schedule(() => { | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDragStart', | ||
@@ -57,11 +75,15 @@ payload: { | ||
dragUpdate({ | ||
location | ||
current | ||
}) { | ||
dragStart.flush(); | ||
scheduleOnDrag.cancel(); | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDropTargetChange', | ||
payload: { | ||
source, | ||
location | ||
location: { | ||
initial, | ||
previous, | ||
current | ||
} | ||
} | ||
@@ -71,7 +93,12 @@ }); | ||
drag({ | ||
location | ||
current | ||
}) { | ||
scheduleOnDrag(() => { | ||
dragStart.flush(); | ||
dispatchEvent({ | ||
const location = { | ||
initial, | ||
previous, | ||
current | ||
}; | ||
safeDispatch({ | ||
eventName: 'onDrag', | ||
@@ -86,3 +113,3 @@ payload: { | ||
drop({ | ||
location, | ||
current, | ||
updatedExternalPayload: updatedSourcePayload | ||
@@ -92,7 +119,11 @@ }) { | ||
scheduleOnDrag.cancel(); | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDrop', | ||
payload: { | ||
source: updatedSourcePayload !== null && updatedSourcePayload !== void 0 ? updatedSourcePayload : source, | ||
location | ||
location: { | ||
current, | ||
previous, | ||
initial | ||
} | ||
} | ||
@@ -99,0 +130,0 @@ }); |
@@ -49,29 +49,12 @@ import { bindAll } from 'bind-event-listener'; | ||
}); | ||
let state = { | ||
initial, | ||
current: initial, | ||
// no 'previous' when we first start dragging | ||
previous: { | ||
dropTargets: [] | ||
} | ||
}; | ||
function updateState({ | ||
next | ||
}) { | ||
const newState = { | ||
initial, | ||
previous: { | ||
dropTargets: state.current.dropTargets | ||
}, | ||
current: next | ||
}; | ||
state = newState; | ||
} | ||
let current = initial; | ||
// Setting initial drop effect for the drag | ||
setDropEffect({ | ||
event, | ||
current: state.initial.dropTargets | ||
current: initial.dropTargets | ||
}); | ||
const dispatch = makeDispatch({ | ||
source: dragInterface.payload, | ||
dispatchEvent | ||
dispatchEvent, | ||
initial | ||
}); | ||
@@ -81,3 +64,3 @@ function updateDropTargets(next) { | ||
const hasChanged = hasHierarchyChanged({ | ||
current: state.current.dropTargets, | ||
current: current.dropTargets, | ||
next: next.dropTargets | ||
@@ -89,8 +72,6 @@ }); | ||
// Consumers can get the latest data by using `onDrag` | ||
updateState({ | ||
next | ||
}); | ||
current = next; | ||
if (hasChanged) { | ||
dispatch.dragUpdate({ | ||
location: state | ||
current | ||
}); | ||
@@ -105,3 +86,3 @@ } | ||
source: dragInterface.payload, | ||
current: state.current.dropTargets | ||
current: current.dropTargets | ||
}); | ||
@@ -121,25 +102,26 @@ if (nextDropTargets.length) { | ||
} | ||
function onDrop({ | ||
updatedExternalPayload | ||
}) { | ||
dispatch.drop({ | ||
current, | ||
updatedExternalPayload | ||
}); | ||
} | ||
function cancel() { | ||
// The spec behaviour is that when a drop is cancelled, | ||
// a 'dragleave' event is fired on the active drop target | ||
// before a `dragend` event. | ||
// We are replicating that behaviour here | ||
// The spec behaviour is that when a drag is cancelled, or when dropping on no drop targets, | ||
// a "dragleave" event is fired on the active drop target before a "dragend" event. | ||
// We are replicating that behaviour in `cancel` if there are any active drop targets to | ||
// ensure consistent behaviour. | ||
// | ||
// Note: When cancelling, or dropping on no drop targets, a "dragleave" event | ||
// will have already cleared the dropTargets to `[]` (as that particular "dragleave" has a `relatedTarget` of `null`) | ||
// If there are any active drop targets we will create | ||
// and update event to leave them | ||
if (state.current.dropTargets.length) { | ||
updateState({ | ||
next: { | ||
// clear the drop targets | ||
dropTargets: [], | ||
// keep the same input | ||
input: state.current.input | ||
} | ||
if (current.dropTargets.length) { | ||
updateDropTargets({ | ||
dropTargets: [], | ||
input: current.input | ||
}); | ||
dispatch.dragUpdate({ | ||
location: state | ||
}); | ||
} | ||
dispatch.drop({ | ||
location: state, | ||
onDrop({ | ||
updatedExternalPayload: null | ||
@@ -173,4 +155,5 @@ }); | ||
// 2. let consumers know a move has occurred | ||
// This will include the latest 'input' values | ||
dispatch.drag({ | ||
location: state | ||
current | ||
}); | ||
@@ -184,5 +167,7 @@ } | ||
// when the user is leaving the `window`. | ||
// When we leave the `window` we want to clear any active drop targets, | ||
// Internal drags: when we leave the `window` we want to clear any active drop targets, | ||
// but the drag is not yet over. The user could drag back into the window. | ||
// We only need to do this because of stickiness | ||
// External drags: when we leave the `window` the drag operation is over, | ||
// we will start another drag operation | ||
type: 'dragleave', | ||
@@ -195,4 +180,17 @@ listener(event) { | ||
} | ||
// When a drag is ending without a drop target (or when the drag is cancelled), | ||
// All browsers fire: | ||
// 1. "drag" | ||
// 2. "dragleave" | ||
// These events have `event.relatedTarget == null` so this code path is also hit in those cases. | ||
// This is all good! We would be clearing the dropTargets in `cancel()` after the "dragend" | ||
// 🐛 Bug workaround: intentionally not updating `input` in "dragleave" | ||
// In Chrome, this final "dragleave" has default input values (eg clientX == 0) | ||
// rather than the users current input values | ||
// | ||
// - [Conversation](https://twitter.com/alexandereardon/status/1642697633864241152) | ||
// - [Bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1429937) | ||
updateDropTargets({ | ||
input: getInput(event), | ||
input: current.input, | ||
dropTargets: [] | ||
@@ -208,8 +206,4 @@ }); | ||
var _dragInterface$getDro; | ||
// this can only happen if the browser allowed the drop | ||
// A "drop" can only happen if the browser allowed the drop | ||
if (dragInterface.startedFrom === 'external') { | ||
dragInterface.key; | ||
} | ||
// Opting out of standard browser drop behaviour for the drag | ||
@@ -221,6 +215,5 @@ event.preventDefault(); | ||
event, | ||
current: state.current.dropTargets | ||
current: current.dropTargets | ||
}); | ||
dispatch.drop({ | ||
location: state, | ||
onDrop({ | ||
updatedExternalPayload: dragInterface.startedFrom === 'external' ? ((_dragInterface$getDro = dragInterface.getDropPayload) === null || _dragInterface$getDro === void 0 ? void 0 : _dragInterface$getDro.call(dragInterface, event)) || null : null | ||
@@ -233,3 +226,5 @@ }); | ||
if (dragInterface.startedFrom === 'internal') { | ||
fixPostDragPointerBug(state); | ||
fixPostDragPointerBug({ | ||
current | ||
}); | ||
} | ||
@@ -250,3 +245,5 @@ } | ||
if (dragInterface.startedFrom === 'internal') { | ||
fixPostDragPointerBug(state); | ||
fixPostDragPointerBug({ | ||
current | ||
}); | ||
} | ||
@@ -306,3 +303,2 @@ } | ||
dispatch.start({ | ||
location: state, | ||
nativeSetDragImage: getNativeSetDragImage(event) | ||
@@ -309,0 +305,0 @@ }); |
import { bindAll } from 'bind-event-listener'; | ||
import { combine } from './combine'; | ||
/** Set a `style` property on a `HTMLElement` | ||
@@ -9,11 +8,12 @@ * | ||
property, | ||
rule | ||
rule, | ||
priority = '' | ||
}) { | ||
const original = el.style[property]; | ||
el.style[property] = rule; | ||
const originalValue = el.style.getPropertyValue(property); | ||
const originalPriority = el.style.getPropertyPriority(property); | ||
el.style.setProperty(property, rule, priority); | ||
return function cleanup() { | ||
el.style[property] = original; | ||
el.style.setProperty(property, originalValue, originalPriority); | ||
}; | ||
} | ||
function noop() {} | ||
@@ -32,26 +32,34 @@ /** | ||
*/ | ||
function fixUnderPointer(location) { | ||
const underUsersPointer = document.elementFromPoint(location.current.input.clientX, location.current.input.clientY); | ||
function fixUnderPointer({ | ||
current | ||
}) { | ||
const underUsersPointer = document.elementFromPoint(current.input.clientX, current.input.clientY); | ||
if (!(underUsersPointer instanceof HTMLElement)) { | ||
return noop; | ||
return null; | ||
} | ||
// Disabling pointer events on all first level children | ||
// Equivalent of '> * { pointer-events: none; }', except we don't need | ||
// to insert / remove a style rule | ||
const unsetChildren = Array.from(underUsersPointer.children).map(child => { | ||
if (!(child instanceof HTMLElement)) { | ||
return noop; | ||
} | ||
return setStyle(child, { | ||
property: 'pointerEvents', | ||
rule: 'none' | ||
}); | ||
// Debug note: change from 'pointer-events: none' to 'background: green' | ||
// to get a better sense of what is being achieved | ||
return setStyle(underUsersPointer, { | ||
property: 'pointer-events', | ||
rule: 'auto', | ||
priority: 'important' | ||
}); | ||
return combine(setStyle(underUsersPointer, { | ||
property: 'pointerEvents', | ||
rule: 'auto' | ||
}), ...unsetChildren); | ||
} | ||
function blockPointerEventsOnEverything() { | ||
var _element$sheet; | ||
const element = document.createElement('style'); | ||
// Adding a data attribute so to make it super clear to consumers | ||
// (and to our tests) what this temporary style tag is for | ||
element.setAttribute('pdnd-post-drag-fix', 'true'); | ||
document.head.appendChild(element); | ||
// Debug note: change from 'pointer-events: none' to 'background: red' | ||
// to get a better sense of what is being achieved | ||
(_element$sheet = element.sheet) === null || _element$sheet === void 0 ? void 0 : _element$sheet.insertRule('* { pointer-events: none !important; }'); | ||
return function cleanup() { | ||
document.head.removeChild(element); | ||
}; | ||
} | ||
/** 🔥🤮 Fix (Chrome, Safari and Firefox) bug where the element under where the user started dragging | ||
@@ -64,13 +72,8 @@ * (on the viewport) is entered into by the browser after a drag finishes ("drop" or "dragend") | ||
* | ||
* Conceptually this is what we are doing (but without adding any new style rules to the page) | ||
* ```css | ||
* body { pointer-events: none; } | ||
* elementUnderPointer { pointer-events: auto; } | ||
* elementUnderPointer > * { pointer-events: none; } | ||
* ``` | ||
* | ||
* - [Visual explanation of bug](https://twitter.com/alexandereardon/status/1633614212873465856) | ||
* - [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=410328) | ||
*/ | ||
export function fixPostDragPointerBug(location) { | ||
export function fixPostDragPointerBug({ | ||
current | ||
}) { | ||
// Queuing a microtask to give any opportunity for frameworks to update their UI in a microtask | ||
@@ -81,16 +84,12 @@ // Note: react@18 does standard state updates in a microtask | ||
queueMicrotask(() => { | ||
// we need to get `atDestination` before turning off pointer events on the body, | ||
// or `document.elementFromPoint` will just return the `html` element (`document.documentElement`) | ||
// We are allowing pointer events on the element under the users cursor, | ||
// but _not_ on children of that element | ||
const unsetUnderPointer = fixUnderPointer(location); | ||
const unsetBodyStyles = setStyle(document.body, { | ||
property: 'pointerEvents', | ||
rule: 'none' | ||
// Applying fix to element under the pointer before disabling pointer events everywhere, | ||
// Otherwise `element.elementFromPoint` would return `<html>` (document.documentElement) | ||
const undoUnderPointer = fixUnderPointer({ | ||
current | ||
}); | ||
const undoGlobalBlock = blockPointerEventsOnEverything(); | ||
function cleanup() { | ||
unbindEvents(); | ||
unsetBodyStyles(); | ||
unsetUnderPointer(); | ||
undoUnderPointer === null || undoUnderPointer === void 0 ? void 0 : undoUnderPointer(); | ||
undoGlobalBlock(); | ||
} | ||
@@ -97,0 +96,0 @@ const unbindEvents = bindAll(window, [{ |
{ | ||
"name": "@atlaskit/drag-and-drop", | ||
"version": "0.13.0", | ||
"version": "0.14.0", | ||
"sideEffects": false | ||
} |
@@ -31,8 +31,26 @@ import rafSchd from 'raf-schd'; | ||
var source = _ref.source, | ||
initial = _ref.initial, | ||
dispatchEvent = _ref.dispatchEvent; | ||
var previous = { | ||
dropTargets: [] | ||
}; | ||
function safeDispatch(args) { | ||
dispatchEvent(args); | ||
previous = { | ||
dropTargets: args.payload.location.current.dropTargets | ||
}; | ||
} | ||
var dispatch = { | ||
start: function start(_ref2) { | ||
var location = _ref2.location, | ||
nativeSetDragImage = _ref2.nativeSetDragImage; | ||
dispatchEvent({ | ||
var nativeSetDragImage = _ref2.nativeSetDragImage; | ||
// Ensuring that both `onGenerateDragPreview` and `onDragStart` get the same location. | ||
// We do this so that `previous` is`[]` in `onDragStart` (which is logical) | ||
var location = { | ||
current: initial, | ||
previous: previous, | ||
initial: initial | ||
}; | ||
// a `onGenerateDragPreview` does _not_ add another entry for `previous` | ||
// onDragPreview | ||
safeDispatch({ | ||
eventName: 'onGenerateDragPreview', | ||
@@ -46,3 +64,3 @@ payload: { | ||
dragStart.schedule(function () { | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDragStart', | ||
@@ -57,10 +75,14 @@ payload: { | ||
dragUpdate: function dragUpdate(_ref3) { | ||
var location = _ref3.location; | ||
var current = _ref3.current; | ||
dragStart.flush(); | ||
scheduleOnDrag.cancel(); | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDropTargetChange', | ||
payload: { | ||
source: source, | ||
location: location | ||
location: { | ||
initial: initial, | ||
previous: previous, | ||
current: current | ||
} | ||
} | ||
@@ -70,6 +92,11 @@ }); | ||
drag: function drag(_ref4) { | ||
var location = _ref4.location; | ||
var current = _ref4.current; | ||
scheduleOnDrag(function () { | ||
dragStart.flush(); | ||
dispatchEvent({ | ||
var location = { | ||
initial: initial, | ||
previous: previous, | ||
current: current | ||
}; | ||
safeDispatch({ | ||
eventName: 'onDrag', | ||
@@ -84,11 +111,15 @@ payload: { | ||
drop: function drop(_ref5) { | ||
var location = _ref5.location, | ||
var current = _ref5.current, | ||
updatedSourcePayload = _ref5.updatedExternalPayload; | ||
dragStart.flush(); | ||
scheduleOnDrag.cancel(); | ||
dispatchEvent({ | ||
safeDispatch({ | ||
eventName: 'onDrop', | ||
payload: { | ||
source: updatedSourcePayload !== null && updatedSourcePayload !== void 0 ? updatedSourcePayload : source, | ||
location: location | ||
location: { | ||
current: current, | ||
previous: previous, | ||
initial: initial | ||
} | ||
} | ||
@@ -95,0 +126,0 @@ }); |
@@ -47,28 +47,12 @@ import { bindAll } from 'bind-event-listener'; | ||
}); | ||
var state = { | ||
initial: initial, | ||
current: initial, | ||
// no 'previous' when we first start dragging | ||
previous: { | ||
dropTargets: [] | ||
} | ||
}; | ||
function updateState(_ref3) { | ||
var next = _ref3.next; | ||
var newState = { | ||
initial: initial, | ||
previous: { | ||
dropTargets: state.current.dropTargets | ||
}, | ||
current: next | ||
}; | ||
state = newState; | ||
} | ||
var current = initial; | ||
// Setting initial drop effect for the drag | ||
setDropEffect({ | ||
event: event, | ||
current: state.initial.dropTargets | ||
current: initial.dropTargets | ||
}); | ||
var dispatch = makeDispatch({ | ||
source: dragInterface.payload, | ||
dispatchEvent: dispatchEvent | ||
dispatchEvent: dispatchEvent, | ||
initial: initial | ||
}); | ||
@@ -78,3 +62,3 @@ function updateDropTargets(next) { | ||
var hasChanged = hasHierarchyChanged({ | ||
current: state.current.dropTargets, | ||
current: current.dropTargets, | ||
next: next.dropTargets | ||
@@ -86,8 +70,6 @@ }); | ||
// Consumers can get the latest data by using `onDrag` | ||
updateState({ | ||
next: next | ||
}); | ||
current = next; | ||
if (hasChanged) { | ||
dispatch.dragUpdate({ | ||
location: state | ||
current: current | ||
}); | ||
@@ -102,3 +84,3 @@ } | ||
source: dragInterface.payload, | ||
current: state.current.dropTargets | ||
current: current.dropTargets | ||
}); | ||
@@ -118,25 +100,25 @@ if (nextDropTargets.length) { | ||
} | ||
function onDrop(_ref3) { | ||
var updatedExternalPayload = _ref3.updatedExternalPayload; | ||
dispatch.drop({ | ||
current: current, | ||
updatedExternalPayload: updatedExternalPayload | ||
}); | ||
} | ||
function cancel() { | ||
// The spec behaviour is that when a drop is cancelled, | ||
// a 'dragleave' event is fired on the active drop target | ||
// before a `dragend` event. | ||
// We are replicating that behaviour here | ||
// The spec behaviour is that when a drag is cancelled, or when dropping on no drop targets, | ||
// a "dragleave" event is fired on the active drop target before a "dragend" event. | ||
// We are replicating that behaviour in `cancel` if there are any active drop targets to | ||
// ensure consistent behaviour. | ||
// | ||
// Note: When cancelling, or dropping on no drop targets, a "dragleave" event | ||
// will have already cleared the dropTargets to `[]` (as that particular "dragleave" has a `relatedTarget` of `null`) | ||
// If there are any active drop targets we will create | ||
// and update event to leave them | ||
if (state.current.dropTargets.length) { | ||
updateState({ | ||
next: { | ||
// clear the drop targets | ||
dropTargets: [], | ||
// keep the same input | ||
input: state.current.input | ||
} | ||
if (current.dropTargets.length) { | ||
updateDropTargets({ | ||
dropTargets: [], | ||
input: current.input | ||
}); | ||
dispatch.dragUpdate({ | ||
location: state | ||
}); | ||
} | ||
dispatch.drop({ | ||
location: state, | ||
onDrop({ | ||
updatedExternalPayload: null | ||
@@ -170,4 +152,5 @@ }); | ||
// 2. let consumers know a move has occurred | ||
// This will include the latest 'input' values | ||
dispatch.drag({ | ||
location: state | ||
current: current | ||
}); | ||
@@ -181,5 +164,7 @@ } | ||
// when the user is leaving the `window`. | ||
// When we leave the `window` we want to clear any active drop targets, | ||
// Internal drags: when we leave the `window` we want to clear any active drop targets, | ||
// but the drag is not yet over. The user could drag back into the window. | ||
// We only need to do this because of stickiness | ||
// External drags: when we leave the `window` the drag operation is over, | ||
// we will start another drag operation | ||
type: 'dragleave', | ||
@@ -192,4 +177,17 @@ listener: function listener(event) { | ||
} | ||
// When a drag is ending without a drop target (or when the drag is cancelled), | ||
// All browsers fire: | ||
// 1. "drag" | ||
// 2. "dragleave" | ||
// These events have `event.relatedTarget == null` so this code path is also hit in those cases. | ||
// This is all good! We would be clearing the dropTargets in `cancel()` after the "dragend" | ||
// 🐛 Bug workaround: intentionally not updating `input` in "dragleave" | ||
// In Chrome, this final "dragleave" has default input values (eg clientX == 0) | ||
// rather than the users current input values | ||
// | ||
// - [Conversation](https://twitter.com/alexandereardon/status/1642697633864241152) | ||
// - [Bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1429937) | ||
updateDropTargets({ | ||
input: getInput(event), | ||
input: current.input, | ||
dropTargets: [] | ||
@@ -205,8 +203,4 @@ }); | ||
var _dragInterface$getDro; | ||
// this can only happen if the browser allowed the drop | ||
// A "drop" can only happen if the browser allowed the drop | ||
if (dragInterface.startedFrom === 'external') { | ||
dragInterface.key; | ||
} | ||
// Opting out of standard browser drop behaviour for the drag | ||
@@ -218,6 +212,5 @@ event.preventDefault(); | ||
event: event, | ||
current: state.current.dropTargets | ||
current: current.dropTargets | ||
}); | ||
dispatch.drop({ | ||
location: state, | ||
onDrop({ | ||
updatedExternalPayload: dragInterface.startedFrom === 'external' ? ((_dragInterface$getDro = dragInterface.getDropPayload) === null || _dragInterface$getDro === void 0 ? void 0 : _dragInterface$getDro.call(dragInterface, event)) || null : null | ||
@@ -230,3 +223,5 @@ }); | ||
if (dragInterface.startedFrom === 'internal') { | ||
fixPostDragPointerBug(state); | ||
fixPostDragPointerBug({ | ||
current: current | ||
}); | ||
} | ||
@@ -247,3 +242,5 @@ } | ||
if (dragInterface.startedFrom === 'internal') { | ||
fixPostDragPointerBug(state); | ||
fixPostDragPointerBug({ | ||
current: current | ||
}); | ||
} | ||
@@ -303,3 +300,2 @@ } | ||
dispatch.start({ | ||
location: state, | ||
nativeSetDragImage: getNativeSetDragImage(event) | ||
@@ -306,0 +302,0 @@ }); |
@@ -1,4 +0,2 @@ | ||
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; | ||
import { bindAll } from 'bind-event-listener'; | ||
import { combine } from './combine'; | ||
/** Set a `style` property on a `HTMLElement` | ||
@@ -10,10 +8,12 @@ * | ||
var property = _ref.property, | ||
rule = _ref.rule; | ||
var original = el.style[property]; | ||
el.style[property] = rule; | ||
rule = _ref.rule, | ||
_ref$priority = _ref.priority, | ||
priority = _ref$priority === void 0 ? '' : _ref$priority; | ||
var originalValue = el.style.getPropertyValue(property); | ||
var originalPriority = el.style.getPropertyPriority(property); | ||
el.style.setProperty(property, rule, priority); | ||
return function cleanup() { | ||
el.style[property] = original; | ||
el.style.setProperty(property, originalValue, originalPriority); | ||
}; | ||
} | ||
function noop() {} | ||
@@ -32,26 +32,33 @@ /** | ||
*/ | ||
function fixUnderPointer(location) { | ||
var underUsersPointer = document.elementFromPoint(location.current.input.clientX, location.current.input.clientY); | ||
function fixUnderPointer(_ref2) { | ||
var current = _ref2.current; | ||
var underUsersPointer = document.elementFromPoint(current.input.clientX, current.input.clientY); | ||
if (!(underUsersPointer instanceof HTMLElement)) { | ||
return noop; | ||
return null; | ||
} | ||
// Disabling pointer events on all first level children | ||
// Equivalent of '> * { pointer-events: none; }', except we don't need | ||
// to insert / remove a style rule | ||
var unsetChildren = Array.from(underUsersPointer.children).map(function (child) { | ||
if (!(child instanceof HTMLElement)) { | ||
return noop; | ||
} | ||
return setStyle(child, { | ||
property: 'pointerEvents', | ||
rule: 'none' | ||
}); | ||
// Debug note: change from 'pointer-events: none' to 'background: green' | ||
// to get a better sense of what is being achieved | ||
return setStyle(underUsersPointer, { | ||
property: 'pointer-events', | ||
rule: 'auto', | ||
priority: 'important' | ||
}); | ||
return combine.apply(void 0, [setStyle(underUsersPointer, { | ||
property: 'pointerEvents', | ||
rule: 'auto' | ||
})].concat(_toConsumableArray(unsetChildren))); | ||
} | ||
function blockPointerEventsOnEverything() { | ||
var _element$sheet; | ||
var element = document.createElement('style'); | ||
// Adding a data attribute so to make it super clear to consumers | ||
// (and to our tests) what this temporary style tag is for | ||
element.setAttribute('pdnd-post-drag-fix', 'true'); | ||
document.head.appendChild(element); | ||
// Debug note: change from 'pointer-events: none' to 'background: red' | ||
// to get a better sense of what is being achieved | ||
(_element$sheet = element.sheet) === null || _element$sheet === void 0 ? void 0 : _element$sheet.insertRule('* { pointer-events: none !important; }'); | ||
return function cleanup() { | ||
document.head.removeChild(element); | ||
}; | ||
} | ||
/** 🔥🤮 Fix (Chrome, Safari and Firefox) bug where the element under where the user started dragging | ||
@@ -64,13 +71,7 @@ * (on the viewport) is entered into by the browser after a drag finishes ("drop" or "dragend") | ||
* | ||
* Conceptually this is what we are doing (but without adding any new style rules to the page) | ||
* ```css | ||
* body { pointer-events: none; } | ||
* elementUnderPointer { pointer-events: auto; } | ||
* elementUnderPointer > * { pointer-events: none; } | ||
* ``` | ||
* | ||
* - [Visual explanation of bug](https://twitter.com/alexandereardon/status/1633614212873465856) | ||
* - [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=410328) | ||
*/ | ||
export function fixPostDragPointerBug(location) { | ||
export function fixPostDragPointerBug(_ref3) { | ||
var current = _ref3.current; | ||
// Queuing a microtask to give any opportunity for frameworks to update their UI in a microtask | ||
@@ -81,16 +82,12 @@ // Note: react@18 does standard state updates in a microtask | ||
queueMicrotask(function () { | ||
// we need to get `atDestination` before turning off pointer events on the body, | ||
// or `document.elementFromPoint` will just return the `html` element (`document.documentElement`) | ||
// We are allowing pointer events on the element under the users cursor, | ||
// but _not_ on children of that element | ||
var unsetUnderPointer = fixUnderPointer(location); | ||
var unsetBodyStyles = setStyle(document.body, { | ||
property: 'pointerEvents', | ||
rule: 'none' | ||
// Applying fix to element under the pointer before disabling pointer events everywhere, | ||
// Otherwise `element.elementFromPoint` would return `<html>` (document.documentElement) | ||
var undoUnderPointer = fixUnderPointer({ | ||
current: current | ||
}); | ||
var undoGlobalBlock = blockPointerEventsOnEverything(); | ||
function cleanup() { | ||
unbindEvents(); | ||
unsetBodyStyles(); | ||
unsetUnderPointer(); | ||
undoUnderPointer === null || undoUnderPointer === void 0 ? void 0 : undoUnderPointer(); | ||
undoGlobalBlock(); | ||
} | ||
@@ -97,0 +94,0 @@ var unbindEvents = bindAll(window, [{ |
{ | ||
"name": "@atlaskit/drag-and-drop", | ||
"version": "0.13.0", | ||
"version": "0.14.0", | ||
"sideEffects": false | ||
} |
@@ -101,2 +101,3 @@ export declare type CleanupFn = () => void; | ||
* Where the user was previously. | ||
* `previous` points to what `current` was in the last dispatched event | ||
* | ||
@@ -106,2 +107,7 @@ * `previous` is particularly useful for `onDropTargetChange` | ||
* as you can know what the delta of the change | ||
* | ||
* Exception: `onGenerateDragPreview` and `onDragStart` will have the | ||
* same `current` and `previous` values. This is done so that the data | ||
* received in `onDragStart` feels logical | ||
* (`location.previous` should be `[]` in `onDragStart`) | ||
*/ | ||
@@ -108,0 +114,0 @@ previous: Pick<DragLocation, 'dropTargets'>; |
@@ -1,4 +0,5 @@ | ||
import { AllDragTypes, DragLocationHistory, EventPayloadMap } from '../internal-types'; | ||
export declare function makeDispatch<DragType extends AllDragTypes>({ source, dispatchEvent, }: { | ||
import { AllDragTypes, DragLocation, EventPayloadMap } from '../internal-types'; | ||
export declare function makeDispatch<DragType extends AllDragTypes>({ source, initial, dispatchEvent, }: { | ||
source: DragType['payload']; | ||
initial: DragLocation; | ||
dispatchEvent: <EventName extends keyof EventPayloadMap<DragType>>(args: { | ||
@@ -9,14 +10,13 @@ eventName: EventName; | ||
}): { | ||
start({ location, nativeSetDragImage, }: { | ||
location: DragLocationHistory; | ||
start({ nativeSetDragImage, }: { | ||
nativeSetDragImage: DataTransfer['setDragImage'] | null; | ||
}): void; | ||
dragUpdate({ location }: { | ||
location: DragLocationHistory; | ||
dragUpdate({ current }: { | ||
current: DragLocation; | ||
}): void; | ||
drag({ location }: { | ||
location: DragLocationHistory; | ||
drag({ current }: { | ||
current: DragLocation; | ||
}): void; | ||
drop({ location, updatedExternalPayload: updatedSourcePayload, }: { | ||
location: DragLocationHistory; | ||
drop({ current, updatedExternalPayload: updatedSourcePayload, }: { | ||
current: DragLocation; | ||
/** When dragging from an external source, we need to collect the | ||
@@ -23,0 +23,0 @@ drag source information again as it is often only available during |
@@ -1,2 +0,2 @@ | ||
import { DragLocationHistory } from '../internal-types'; | ||
import type { DragLocation } from '../internal-types'; | ||
/** 🔥🤮 Fix (Chrome, Safari and Firefox) bug where the element under where the user started dragging | ||
@@ -9,12 +9,7 @@ * (on the viewport) is entered into by the browser after a drag finishes ("drop" or "dragend") | ||
* | ||
* Conceptually this is what we are doing (but without adding any new style rules to the page) | ||
* ```css | ||
* body { pointer-events: none; } | ||
* elementUnderPointer { pointer-events: auto; } | ||
* elementUnderPointer > * { pointer-events: none; } | ||
* ``` | ||
* | ||
* - [Visual explanation of bug](https://twitter.com/alexandereardon/status/1633614212873465856) | ||
* - [Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=410328) | ||
*/ | ||
export declare function fixPostDragPointerBug(location: DragLocationHistory): void; | ||
export declare function fixPostDragPointerBug({ current }: { | ||
current: DragLocation; | ||
}): void; |
{ | ||
"name": "@atlaskit/drag-and-drop", | ||
"version": "0.13.0", | ||
"version": "0.14.0", | ||
"description": "The core Atlassian drag and drop framework, optimized for performance.", | ||
@@ -5,0 +5,0 @@ "repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror", |
247310
5846