Socket
Socket
Sign inDemoInstall

@atlaskit/pragmatic-drag-and-drop

Package Overview
Dependencies
Maintainers
1
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@atlaskit/pragmatic-drag-and-drop - npm Package Compare versions

Comparing version 1.0.2 to 1.1.0

22

CHANGELOG.md
# @atlaskit/pragmatic-drag-and-drop
## 1.1.0
### Minor Changes
- [#82653](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/82653) [`136d8da5542d`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/136d8da5542d) - Adding additional information to `onDrop()` events to expose what the final `dropEffect` was for a drag operation.
```ts
type DropData = {
dropEffect: DataTransfer['dropEffect'];
};
monitorForElements({
onDrop(payload) {
const drop: DropData = payload.drop;
},
});
```
Fixing a bug where `preventUnhandled.start()` would prevent unhandled drag operations forever. It now only prevents unhandled drag operations for the current drag operation. `preventUnhandled.stop()` is now optional, as `preventUnhandled.start()` now tidies up itself. You can still leverage `preventUnhandled.stop()` to stop preventing unhandled drag operations during a drag.
Tightening the `getDropEffect()` function on drop targets slightly so that `"none"` cannot be provided. Using `"none"` as the drop effect would break the expected behaviour for nested drop targets.
## 1.0.2

@@ -4,0 +26,0 @@

2

dist/cjs/ledger/dispatch-consumer-event.js

@@ -115,2 +115,3 @@ "use strict";

var current = _ref5.current,
drop = _ref5.drop,
updatedSourcePayload = _ref5.updatedSourcePayload;

@@ -122,2 +123,3 @@ dragStart.flush();

payload: {
drop: drop,
source: updatedSourcePayload !== null && updatedSourcePayload !== void 0 ? updatedSourcePayload : source,

@@ -124,0 +126,0 @@ location: {

174

dist/cjs/ledger/lifecycle-manager.js

@@ -15,5 +15,7 @@ "use strict";

var _dispatchConsumerEvent = require("./dispatch-consumer-event");
var isActive = false;
var globalState = {
isActive: false
};
function canStart() {
return !isActive;
return !globalState.isActive;
}

@@ -51,3 +53,2 @@ function getNativeSetDragImage(event) {

}
isActive = true;
var initial = getStartLocation({

@@ -58,5 +59,9 @@ event: event,

});
var current = initial;
globalState.isActive = true;
var state = {
current: initial
};
// Setting initial drop effect for the drag
setDropEffect({
setDropEffectOnEvent({
event: event,

@@ -73,3 +78,3 @@ current: initial.dropTargets

var hasChanged = hasHierarchyChanged({
current: current.dropTargets,
current: state.current.dropTargets,
next: next.dropTargets

@@ -81,6 +86,6 @@ });

// Consumers can get the latest data by using `onDrag`
current = next;
state.current = next;
if (hasChanged) {
dispatch.dragUpdate({
current: current
current: state.current
});

@@ -95,3 +100,3 @@ }

source: dragType.payload,
current: current.dropTargets
current: state.current.dropTargets
});

@@ -101,3 +106,3 @@ if (nextDropTargets.length) {

event.preventDefault();
setDropEffect({
setDropEffectOnEvent({
event: event,

@@ -112,18 +117,3 @@ current: nextDropTargets

}
function onDrop(args) {
// When dropping something native, we need to extract the latest
// `.items` from the "drop" event as it is now accessible
if (args.type === 'success' && dragType.type === 'external') {
dispatch.drop({
current: current,
updatedSourcePayload: dragType.getDropPayload(args.event)
});
return;
}
dispatch.drop({
current: current,
updatedSourcePayload: null
});
}
function cancel() {
function cancel(drop) {
// The spec behaviour is that when a drag is cancelled, or when dropping on no drop targets,

@@ -137,10 +127,12 @@ // a "dragleave" event is fired on the active drop target before a "dragend" event.

if (current.dropTargets.length) {
if (state.current.dropTargets.length) {
updateDropTargets({
dropTargets: [],
input: current.input
input: state.current.input
});
}
onDrop({
type: 'cancel'
dispatch.drop({
current: state.current,
drop: drop,
updatedSourcePayload: null
});

@@ -150,3 +142,3 @@ finish();

function finish() {
isActive = false;
globalState.isActive = false;
unbindEvents();

@@ -176,3 +168,3 @@ }

dispatch.drag({
current: current
current: state.current
});

@@ -184,9 +176,2 @@ }

}, {
// This was the only reliable cross browser way I found to detect
// when the user is leaving the `window`.
// 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',

@@ -200,23 +185,51 @@ 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"
/**
* At this point we don't know if a drag is being cancelled,
* or if a drag is leaving the `window`.
*
* Both have:
* 1. "dragleave" (with `relatedTarget: null`)
* 2. "dragend" (a "dragend" can occur when outside the `window`)
*
* **Clearing drop targets**
*
* For either case we are clearing the the drop targets
*
* - cancelling: we clear drop targets in `"dragend"` anyway
* - leaving the `window`: we clear the drop targets (to clear stickiness)
*
* **Leaving the window and finishing the drag**
*
* _internal drags_
*
* - The drag continues when the user is outside the `window`
* and can resume if the user drags back over the `window`,
* or end when the user drops in an external `window`.
* - We will get a `"dragend"`, or we can listen for other
* events to determine the drag is finished when the user re-enters the `window`).
*
* _external drags_
*
* - We conclude the drag operation.
* - We have no idea if the user will drag back over the `window`,
* or if the drag ends elsewhere.
* - We will create a new drag if the user re-enters the `window`.
*
* **Not updating `input`**
*
* 🐛 Bug[Chrome] the final `"dragleave"` has default input values (eg `clientX == 0`)
* Workaround: intentionally not updating `input` in "dragleave"
* 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)
**/
// 🐛 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: current.input,
input: state.current.input,
dropTargets: []
});
// End the drag operation if a native drag is leaving the window
if (dragType.startedFrom === 'external') {
cancel();
cancel({
dropEffect: 'none'
});
}

@@ -227,15 +240,24 @@ }

listener: function listener(event) {
var _event$dataTransfer$d, _event$dataTransfer;
// A "drop" can only happen if the browser allowed the drop
// Opting out of standard browser drop behaviour for the drag
// Accepting drop operation.
// Also: opting out of standard browser drop behaviour for the drag
event.preventDefault();
// applying the latest drop effect to the event
setDropEffect({
setDropEffectOnEvent({
event: event,
current: current.dropTargets
current: state.current.dropTargets
});
onDrop({
type: 'success',
event: event
dispatch.drop({
current: state.current,
drop: {
// At this point the dropEffect has been set on the event
// (if we have set it), so we can read it from there
dropEffect: (_event$dataTransfer$d = (_event$dataTransfer = event.dataTransfer) === null || _event$dataTransfer === void 0 ? void 0 : _event$dataTransfer.dropEffect) !== null && _event$dataTransfer$d !== void 0 ? _event$dataTransfer$d : 'none'
},
// When dropping something native, we need to extract the latest
// `.items` from the "drop" event as it is now accessible
updatedSourcePayload: dragType.type === 'external' ? dragType.getDropPayload(event) : null
});

@@ -248,3 +270,3 @@ finish();

(0, _fixPostDragPointerBug.fixPostDragPointerBug)({
current: current
current: state.current
});

@@ -256,8 +278,20 @@ }

// when the drag is finished.
// "dragend" will fire after "drop"(if there was a successful drop)
// "dragend" will fire after "drop" (if there was a successful drop)
// "dragend" does not fire if the draggable source has been removed during the drag
// or for external drag sources (eg files)
// This "dragend" listener will not fire if there was a successful drop
// as we will have already removed the event listener
type: 'dragend',
listener: function listener() {
cancel();
listener: function listener(event) {
var _event$dataTransfer$d2, _event$dataTransfer2;
// The `dropEffect` will be:
// - "none" if dropped on no drop targets
// - "none" if cancelled locally
// - "none" if cancelled externally
// - "none" if `preventUnhandled` is used (and there is no drop target)
// - [not "none"] if accepted externally
cancel({
dropEffect: (_event$dataTransfer$d2 = (_event$dataTransfer2 = event.dataTransfer) === null || _event$dataTransfer2 === void 0 ? void 0 : _event$dataTransfer2.dropEffect) !== null && _event$dataTransfer$d2 !== void 0 ? _event$dataTransfer$d2 : 'none'
});

@@ -268,3 +302,3 @@ // Applying this fix after `dispatch.drop` so that frameworks have the opportunity

(0, _fixPostDragPointerBug.fixPostDragPointerBug)({
current: current
current: state.current
});

@@ -274,3 +308,7 @@ }

}].concat((0, _toConsumableArray2.default)((0, _detectBrokenDrag.getBindingsForBrokenDrags)({
onDragEnd: cancel
onDragEnd: function onDragEnd() {
return cancel({
dropEffect: 'none'
});
}
}))),

@@ -288,3 +326,3 @@ // Once we have started a managed drag operation it is important that we see / own all drag events

}
function setDropEffect(_ref3) {
function setDropEffectOnEvent(_ref3) {
var _current$;

@@ -291,0 +329,0 @@ var event = _ref3.event,

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {

@@ -7,6 +8,7 @@ value: true

exports.preventUnhandled = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _bindEventListener = require("bind-event-listener");
function cancel(event) {
// if `@atlaskit/pragmatic-drag-and-drop` has already prevented the event
// we don't need to do anything
var _detectBrokenDrag = require("../util/detect-broken-drag");
function acceptDrop(event) {
// if the event is already prevented the event we don't need to do anything
if (event.defaultPrevented) {

@@ -18,3 +20,3 @@ return;

// on the page
// Note: using "none" will not allow a drop to occur, so we are using "move"
// Note: using "none" will not allow a "drop" to occur, so we are using "move"
if (event.dataTransfer) {

@@ -31,22 +33,53 @@ event.dataTransfer.dropEffect = 'move';

*/
function start() {
cleanup();
unbindEvents = (0, _bindEventListener.bindAll)(window, [{
type: 'dragover',
listener: acceptDrop
}, {
type: 'dragenter',
listener: acceptDrop
}, {
type: 'drop',
listener: function listener(event) {
// our lifecycle manager already prevents events, but just being super safe
event.preventDefault();
// not setting dropEffect, as `drop.dropEffect` has already been published to the user
// (lifecycle-manager binds events in the capture phase)
// we don't need to wait for "dragend", and "dragend" might not even happen,
// such as when the draggable has been removed during a drag.
cleanup();
}
}, {
type: 'dragend',
listener: cleanup
}].concat((0, _toConsumableArray2.default)((0, _detectBrokenDrag.getBindingsForBrokenDrags)({
onDragEnd: cleanup
}))),
// being clear that these are added in the bubble phase
{
capture: false
});
}
function cleanup() {
var _unbindEvents;
(_unbindEvents = unbindEvents) === null || _unbindEvents === void 0 || _unbindEvents();
unbindEvents = null;
}
/**
* TODO: for next major, we could look at do the following:
*
* ```diff
* - preventUnhandled.start();
* - preventUnhandled.stop();
* + const stop = preventUnhandled();
* ```
*/
var preventUnhandled = exports.preventUnhandled = {
start: function start() {
var _unbindEvents;
(_unbindEvents = unbindEvents) === null || _unbindEvents === void 0 || _unbindEvents();
unbindEvents = (0, _bindEventListener.bindAll)(window, [{
type: 'dragover',
listener: cancel
}, {
type: 'dragenter',
listener: cancel
}, {
type: 'drop',
listener: cancel
}]);
},
stop: function stop() {
var _unbindEvents2;
(_unbindEvents2 = unbindEvents) === null || _unbindEvents2 === void 0 || _unbindEvents2();
unbindEvents = null;
}
start: start,
stop: cleanup
};

@@ -110,2 +110,3 @@ import rafSchd from 'raf-schd';

current,
drop,
updatedSourcePayload

@@ -118,2 +119,3 @@ }) {

payload: {
drop,
source: updatedSourcePayload !== null && updatedSourcePayload !== void 0 ? updatedSourcePayload : source,

@@ -120,0 +122,0 @@ location: {

@@ -7,5 +7,7 @@ import { bindAll } from 'bind-event-listener';

import { makeDispatch } from './dispatch-consumer-event';
let isActive = false;
const globalState = {
isActive: false
};
function canStart() {
return !isActive;
return !globalState.isActive;
}

@@ -45,3 +47,2 @@ function getNativeSetDragImage(event) {

}
isActive = true;
const initial = getStartLocation({

@@ -52,5 +53,9 @@ event,

});
let current = initial;
globalState.isActive = true;
const state = {
current: initial
};
// Setting initial drop effect for the drag
setDropEffect({
setDropEffectOnEvent({
event,

@@ -67,3 +72,3 @@ current: initial.dropTargets

const hasChanged = hasHierarchyChanged({
current: current.dropTargets,
current: state.current.dropTargets,
next: next.dropTargets

@@ -75,6 +80,6 @@ });

// Consumers can get the latest data by using `onDrag`
current = next;
state.current = next;
if (hasChanged) {
dispatch.dragUpdate({
current
current: state.current
});

@@ -89,3 +94,3 @@ }

source: dragType.payload,
current: current.dropTargets
current: state.current.dropTargets
});

@@ -95,3 +100,3 @@ if (nextDropTargets.length) {

event.preventDefault();
setDropEffect({
setDropEffectOnEvent({
event,

@@ -106,18 +111,3 @@ current: nextDropTargets

}
function onDrop(args) {
// When dropping something native, we need to extract the latest
// `.items` from the "drop" event as it is now accessible
if (args.type === 'success' && dragType.type === 'external') {
dispatch.drop({
current,
updatedSourcePayload: dragType.getDropPayload(args.event)
});
return;
}
dispatch.drop({
current,
updatedSourcePayload: null
});
}
function cancel() {
function cancel(drop) {
// The spec behaviour is that when a drag is cancelled, or when dropping on no drop targets,

@@ -131,10 +121,12 @@ // a "dragleave" event is fired on the active drop target before a "dragend" event.

if (current.dropTargets.length) {
if (state.current.dropTargets.length) {
updateDropTargets({
dropTargets: [],
input: current.input
input: state.current.input
});
}
onDrop({
type: 'cancel'
dispatch.drop({
current: state.current,
drop,
updatedSourcePayload: null
});

@@ -144,3 +136,3 @@ finish();

function finish() {
isActive = false;
globalState.isActive = false;
unbindEvents();

@@ -170,3 +162,3 @@ }

dispatch.drag({
current
current: state.current
});

@@ -178,9 +170,2 @@ }

}, {
// This was the only reliable cross browser way I found to detect
// when the user is leaving the `window`.
// 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',

@@ -194,23 +179,51 @@ 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"
/**
* At this point we don't know if a drag is being cancelled,
* or if a drag is leaving the `window`.
*
* Both have:
* 1. "dragleave" (with `relatedTarget: null`)
* 2. "dragend" (a "dragend" can occur when outside the `window`)
*
* **Clearing drop targets**
*
* For either case we are clearing the the drop targets
*
* - cancelling: we clear drop targets in `"dragend"` anyway
* - leaving the `window`: we clear the drop targets (to clear stickiness)
*
* **Leaving the window and finishing the drag**
*
* _internal drags_
*
* - The drag continues when the user is outside the `window`
* and can resume if the user drags back over the `window`,
* or end when the user drops in an external `window`.
* - We will get a `"dragend"`, or we can listen for other
* events to determine the drag is finished when the user re-enters the `window`).
*
* _external drags_
*
* - We conclude the drag operation.
* - We have no idea if the user will drag back over the `window`,
* or if the drag ends elsewhere.
* - We will create a new drag if the user re-enters the `window`.
*
* **Not updating `input`**
*
* 🐛 Bug[Chrome] the final `"dragleave"` has default input values (eg `clientX == 0`)
* Workaround: intentionally not updating `input` in "dragleave"
* 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)
**/
// 🐛 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: current.input,
input: state.current.input,
dropTargets: []
});
// End the drag operation if a native drag is leaving the window
if (dragType.startedFrom === 'external') {
cancel();
cancel({
dropEffect: 'none'
});
}

@@ -221,15 +234,24 @@ }

listener(event) {
var _event$dataTransfer$d, _event$dataTransfer;
// A "drop" can only happen if the browser allowed the drop
// Opting out of standard browser drop behaviour for the drag
// Accepting drop operation.
// Also: opting out of standard browser drop behaviour for the drag
event.preventDefault();
// applying the latest drop effect to the event
setDropEffect({
setDropEffectOnEvent({
event,
current: current.dropTargets
current: state.current.dropTargets
});
onDrop({
type: 'success',
event
dispatch.drop({
current: state.current,
drop: {
// At this point the dropEffect has been set on the event
// (if we have set it), so we can read it from there
dropEffect: (_event$dataTransfer$d = (_event$dataTransfer = event.dataTransfer) === null || _event$dataTransfer === void 0 ? void 0 : _event$dataTransfer.dropEffect) !== null && _event$dataTransfer$d !== void 0 ? _event$dataTransfer$d : 'none'
},
// When dropping something native, we need to extract the latest
// `.items` from the "drop" event as it is now accessible
updatedSourcePayload: dragType.type === 'external' ? dragType.getDropPayload(event) : null
});

@@ -242,3 +264,3 @@ finish();

fixPostDragPointerBug({
current
current: state.current
});

@@ -250,8 +272,20 @@ }

// when the drag is finished.
// "dragend" will fire after "drop"(if there was a successful drop)
// "dragend" will fire after "drop" (if there was a successful drop)
// "dragend" does not fire if the draggable source has been removed during the drag
// or for external drag sources (eg files)
// This "dragend" listener will not fire if there was a successful drop
// as we will have already removed the event listener
type: 'dragend',
listener() {
cancel();
listener(event) {
var _event$dataTransfer$d2, _event$dataTransfer2;
// The `dropEffect` will be:
// - "none" if dropped on no drop targets
// - "none" if cancelled locally
// - "none" if cancelled externally
// - "none" if `preventUnhandled` is used (and there is no drop target)
// - [not "none"] if accepted externally
cancel({
dropEffect: (_event$dataTransfer$d2 = (_event$dataTransfer2 = event.dataTransfer) === null || _event$dataTransfer2 === void 0 ? void 0 : _event$dataTransfer2.dropEffect) !== null && _event$dataTransfer$d2 !== void 0 ? _event$dataTransfer$d2 : 'none'
});

@@ -262,3 +296,3 @@ // Applying this fix after `dispatch.drop` so that frameworks have the opportunity

fixPostDragPointerBug({
current
current: state.current
});

@@ -268,3 +302,5 @@ }

}, ...getBindingsForBrokenDrags({
onDragEnd: cancel
onDragEnd: () => cancel({
dropEffect: 'none'
})
})],

@@ -282,3 +318,3 @@ // Once we have started a managed drag operation it is important that we see / own all drag events

}
function setDropEffect({
function setDropEffectOnEvent({
event,

@@ -285,0 +321,0 @@ current

import { bindAll } from 'bind-event-listener';
function cancel(event) {
// if `@atlaskit/pragmatic-drag-and-drop` has already prevented the event
// we don't need to do anything
import { getBindingsForBrokenDrags } from '../util/detect-broken-drag';
function acceptDrop(event) {
// if the event is already prevented the event we don't need to do anything
if (event.defaultPrevented) {

@@ -11,3 +11,3 @@ return;

// on the page
// Note: using "none" will not allow a drop to occur, so we are using "move"
// Note: using "none" will not allow a "drop" to occur, so we are using "move"
if (event.dataTransfer) {

@@ -24,22 +24,53 @@ event.dataTransfer.dropEffect = 'move';

*/
function start() {
cleanup();
unbindEvents = bindAll(window, [{
type: 'dragover',
listener: acceptDrop
}, {
type: 'dragenter',
listener: acceptDrop
}, {
type: 'drop',
listener(event) {
// our lifecycle manager already prevents events, but just being super safe
event.preventDefault();
// not setting dropEffect, as `drop.dropEffect` has already been published to the user
// (lifecycle-manager binds events in the capture phase)
// we don't need to wait for "dragend", and "dragend" might not even happen,
// such as when the draggable has been removed during a drag.
cleanup();
}
}, {
type: 'dragend',
listener: cleanup
}, ...getBindingsForBrokenDrags({
onDragEnd: cleanup
})],
// being clear that these are added in the bubble phase
{
capture: false
});
}
function cleanup() {
var _unbindEvents;
(_unbindEvents = unbindEvents) === null || _unbindEvents === void 0 ? void 0 : _unbindEvents();
unbindEvents = null;
}
/**
* TODO: for next major, we could look at do the following:
*
* ```diff
* - preventUnhandled.start();
* - preventUnhandled.stop();
* + const stop = preventUnhandled();
* ```
*/
export const preventUnhandled = {
start() {
var _unbindEvents;
(_unbindEvents = unbindEvents) === null || _unbindEvents === void 0 ? void 0 : _unbindEvents();
unbindEvents = bindAll(window, [{
type: 'dragover',
listener: cancel
}, {
type: 'dragenter',
listener: cancel
}, {
type: 'drop',
listener: cancel
}]);
},
stop() {
var _unbindEvents2;
(_unbindEvents2 = unbindEvents) === null || _unbindEvents2 === void 0 ? void 0 : _unbindEvents2();
unbindEvents = null;
}
start,
stop: cleanup
};

@@ -108,2 +108,3 @@ import rafSchd from 'raf-schd';

var current = _ref5.current,
drop = _ref5.drop,
updatedSourcePayload = _ref5.updatedSourcePayload;

@@ -115,2 +116,3 @@ dragStart.flush();

payload: {
drop: drop,
source: updatedSourcePayload !== null && updatedSourcePayload !== void 0 ? updatedSourcePayload : source,

@@ -117,0 +119,0 @@ location: {

@@ -8,5 +8,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";

import { makeDispatch } from './dispatch-consumer-event';
var isActive = false;
var globalState = {
isActive: false
};
function canStart() {
return !isActive;
return !globalState.isActive;
}

@@ -44,3 +46,2 @@ function getNativeSetDragImage(event) {

}
isActive = true;
var initial = getStartLocation({

@@ -51,5 +52,9 @@ event: event,

});
var current = initial;
globalState.isActive = true;
var state = {
current: initial
};
// Setting initial drop effect for the drag
setDropEffect({
setDropEffectOnEvent({
event: event,

@@ -66,3 +71,3 @@ current: initial.dropTargets

var hasChanged = hasHierarchyChanged({
current: current.dropTargets,
current: state.current.dropTargets,
next: next.dropTargets

@@ -74,6 +79,6 @@ });

// Consumers can get the latest data by using `onDrag`
current = next;
state.current = next;
if (hasChanged) {
dispatch.dragUpdate({
current: current
current: state.current
});

@@ -88,3 +93,3 @@ }

source: dragType.payload,
current: current.dropTargets
current: state.current.dropTargets
});

@@ -94,3 +99,3 @@ if (nextDropTargets.length) {

event.preventDefault();
setDropEffect({
setDropEffectOnEvent({
event: event,

@@ -105,18 +110,3 @@ current: nextDropTargets

}
function onDrop(args) {
// When dropping something native, we need to extract the latest
// `.items` from the "drop" event as it is now accessible
if (args.type === 'success' && dragType.type === 'external') {
dispatch.drop({
current: current,
updatedSourcePayload: dragType.getDropPayload(args.event)
});
return;
}
dispatch.drop({
current: current,
updatedSourcePayload: null
});
}
function cancel() {
function cancel(drop) {
// The spec behaviour is that when a drag is cancelled, or when dropping on no drop targets,

@@ -130,10 +120,12 @@ // a "dragleave" event is fired on the active drop target before a "dragend" event.

if (current.dropTargets.length) {
if (state.current.dropTargets.length) {
updateDropTargets({
dropTargets: [],
input: current.input
input: state.current.input
});
}
onDrop({
type: 'cancel'
dispatch.drop({
current: state.current,
drop: drop,
updatedSourcePayload: null
});

@@ -143,3 +135,3 @@ finish();

function finish() {
isActive = false;
globalState.isActive = false;
unbindEvents();

@@ -169,3 +161,3 @@ }

dispatch.drag({
current: current
current: state.current
});

@@ -177,9 +169,2 @@ }

}, {
// This was the only reliable cross browser way I found to detect
// when the user is leaving the `window`.
// 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',

@@ -193,23 +178,51 @@ 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"
/**
* At this point we don't know if a drag is being cancelled,
* or if a drag is leaving the `window`.
*
* Both have:
* 1. "dragleave" (with `relatedTarget: null`)
* 2. "dragend" (a "dragend" can occur when outside the `window`)
*
* **Clearing drop targets**
*
* For either case we are clearing the the drop targets
*
* - cancelling: we clear drop targets in `"dragend"` anyway
* - leaving the `window`: we clear the drop targets (to clear stickiness)
*
* **Leaving the window and finishing the drag**
*
* _internal drags_
*
* - The drag continues when the user is outside the `window`
* and can resume if the user drags back over the `window`,
* or end when the user drops in an external `window`.
* - We will get a `"dragend"`, or we can listen for other
* events to determine the drag is finished when the user re-enters the `window`).
*
* _external drags_
*
* - We conclude the drag operation.
* - We have no idea if the user will drag back over the `window`,
* or if the drag ends elsewhere.
* - We will create a new drag if the user re-enters the `window`.
*
* **Not updating `input`**
*
* 🐛 Bug[Chrome] the final `"dragleave"` has default input values (eg `clientX == 0`)
* Workaround: intentionally not updating `input` in "dragleave"
* 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)
**/
// 🐛 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: current.input,
input: state.current.input,
dropTargets: []
});
// End the drag operation if a native drag is leaving the window
if (dragType.startedFrom === 'external') {
cancel();
cancel({
dropEffect: 'none'
});
}

@@ -220,15 +233,24 @@ }

listener: function listener(event) {
var _event$dataTransfer$d, _event$dataTransfer;
// A "drop" can only happen if the browser allowed the drop
// Opting out of standard browser drop behaviour for the drag
// Accepting drop operation.
// Also: opting out of standard browser drop behaviour for the drag
event.preventDefault();
// applying the latest drop effect to the event
setDropEffect({
setDropEffectOnEvent({
event: event,
current: current.dropTargets
current: state.current.dropTargets
});
onDrop({
type: 'success',
event: event
dispatch.drop({
current: state.current,
drop: {
// At this point the dropEffect has been set on the event
// (if we have set it), so we can read it from there
dropEffect: (_event$dataTransfer$d = (_event$dataTransfer = event.dataTransfer) === null || _event$dataTransfer === void 0 ? void 0 : _event$dataTransfer.dropEffect) !== null && _event$dataTransfer$d !== void 0 ? _event$dataTransfer$d : 'none'
},
// When dropping something native, we need to extract the latest
// `.items` from the "drop" event as it is now accessible
updatedSourcePayload: dragType.type === 'external' ? dragType.getDropPayload(event) : null
});

@@ -241,3 +263,3 @@ finish();

fixPostDragPointerBug({
current: current
current: state.current
});

@@ -249,8 +271,20 @@ }

// when the drag is finished.
// "dragend" will fire after "drop"(if there was a successful drop)
// "dragend" will fire after "drop" (if there was a successful drop)
// "dragend" does not fire if the draggable source has been removed during the drag
// or for external drag sources (eg files)
// This "dragend" listener will not fire if there was a successful drop
// as we will have already removed the event listener
type: 'dragend',
listener: function listener() {
cancel();
listener: function listener(event) {
var _event$dataTransfer$d2, _event$dataTransfer2;
// The `dropEffect` will be:
// - "none" if dropped on no drop targets
// - "none" if cancelled locally
// - "none" if cancelled externally
// - "none" if `preventUnhandled` is used (and there is no drop target)
// - [not "none"] if accepted externally
cancel({
dropEffect: (_event$dataTransfer$d2 = (_event$dataTransfer2 = event.dataTransfer) === null || _event$dataTransfer2 === void 0 ? void 0 : _event$dataTransfer2.dropEffect) !== null && _event$dataTransfer$d2 !== void 0 ? _event$dataTransfer$d2 : 'none'
});

@@ -261,3 +295,3 @@ // Applying this fix after `dispatch.drop` so that frameworks have the opportunity

fixPostDragPointerBug({
current: current
current: state.current
});

@@ -267,3 +301,7 @@ }

}].concat(_toConsumableArray(getBindingsForBrokenDrags({
onDragEnd: cancel
onDragEnd: function onDragEnd() {
return cancel({
dropEffect: 'none'
});
}
}))),

@@ -281,3 +319,3 @@ // Once we have started a managed drag operation it is important that we see / own all drag events

}
function setDropEffect(_ref3) {
function setDropEffectOnEvent(_ref3) {
var _current$;

@@ -284,0 +322,0 @@ var event = _ref3.event,

@@ -0,5 +1,6 @@

import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import { bindAll } from 'bind-event-listener';
function cancel(event) {
// if `@atlaskit/pragmatic-drag-and-drop` has already prevented the event
// we don't need to do anything
import { getBindingsForBrokenDrags } from '../util/detect-broken-drag';
function acceptDrop(event) {
// if the event is already prevented the event we don't need to do anything
if (event.defaultPrevented) {

@@ -11,3 +12,3 @@ return;

// on the page
// Note: using "none" will not allow a drop to occur, so we are using "move"
// Note: using "none" will not allow a "drop" to occur, so we are using "move"
if (event.dataTransfer) {

@@ -24,22 +25,53 @@ event.dataTransfer.dropEffect = 'move';

*/
function start() {
cleanup();
unbindEvents = bindAll(window, [{
type: 'dragover',
listener: acceptDrop
}, {
type: 'dragenter',
listener: acceptDrop
}, {
type: 'drop',
listener: function listener(event) {
// our lifecycle manager already prevents events, but just being super safe
event.preventDefault();
// not setting dropEffect, as `drop.dropEffect` has already been published to the user
// (lifecycle-manager binds events in the capture phase)
// we don't need to wait for "dragend", and "dragend" might not even happen,
// such as when the draggable has been removed during a drag.
cleanup();
}
}, {
type: 'dragend',
listener: cleanup
}].concat(_toConsumableArray(getBindingsForBrokenDrags({
onDragEnd: cleanup
}))),
// being clear that these are added in the bubble phase
{
capture: false
});
}
function cleanup() {
var _unbindEvents;
(_unbindEvents = unbindEvents) === null || _unbindEvents === void 0 || _unbindEvents();
unbindEvents = null;
}
/**
* TODO: for next major, we could look at do the following:
*
* ```diff
* - preventUnhandled.start();
* - preventUnhandled.stop();
* + const stop = preventUnhandled();
* ```
*/
export var preventUnhandled = {
start: function start() {
var _unbindEvents;
(_unbindEvents = unbindEvents) === null || _unbindEvents === void 0 || _unbindEvents();
unbindEvents = bindAll(window, [{
type: 'dragover',
listener: cancel
}, {
type: 'dragenter',
listener: cancel
}, {
type: 'drop',
listener: cancel
}]);
},
stop: function stop() {
var _unbindEvents2;
(_unbindEvents2 = unbindEvents) === null || _unbindEvents2 === void 0 || _unbindEvents2();
unbindEvents = null;
}
start: start,
stop: cleanup
};

@@ -1,1 +0,1 @@

export type { DropTargetRecord, Position, Input, DragLocation, DragLocationHistory, CleanupFn, AllDragTypes, MonitorArgs, BaseEventPayload, ElementDragType, TextSelectionDragType, ExternalDragType, } from '../internal-types';
export type { DropTargetAllowedDropEffect, DropTargetRecord, Position, Input, DragLocation, DragLocationHistory, DropData, CleanupFn, AllDragTypes, MonitorArgs, BaseEventPayload, ElementDragType, TextSelectionDragType, ExternalDragType, } from '../internal-types';
export type CleanupFn = () => void;
/**
* Drop effects allowed to be passed to `getDropEffect()`.
* Cannot use `"none"` as a `dropEffect` for drop targets as
* it will opt out of accepting a drop for all nested drop targets.
* Please use `canDrop()` to disable dropping for this drop target.
*/
export type DropTargetAllowedDropEffect = Exclude<DataTransfer['dropEffect'], 'none'>;
/**
* Information about a drop target

@@ -21,3 +28,3 @@ */

*/
dropEffect: DataTransfer['dropEffect'];
dropEffect: DropTargetAllowedDropEffect;
/**

@@ -35,3 +42,3 @@ * Whether or not the drop target is active due to _stickiness_

};
export type StartedFrom = 'internal' | 'external';
export type Region = 'internal' | 'external';
export type ElementDragPayload = {

@@ -191,2 +198,5 @@ element: HTMLElement;

};
export type DropData = {
dropEffect: DataTransfer['dropEffect'];
};
export type EventPayloadMap<DragType extends AllDragTypes> = {

@@ -227,3 +237,5 @@ /**

*/
onDrop: BaseEventPayload<DragType>;
onDrop: BaseEventPayload<DragType> & {
drop: DropData;
};
};

@@ -315,5 +327,5 @@ export type AllEvents<DragType extends AllDragTypes> = {

* Optionally provide a _drop effect_ to be applied when
* this drop target is the innermost drop target being dragged over
* this drop target is the innermost drop target being dragged over.
*/
getDropEffect?: (args: DropTargetGetFeedbackArgs<DragType>) => DataTransfer['dropEffect'];
getDropEffect?: (args: DropTargetGetFeedbackArgs<DragType>) => DropTargetAllowedDropEffect;
/**

@@ -320,0 +332,0 @@ * Return `true` if you want your drop target to hold onto

@@ -1,2 +0,2 @@

import { AllDragTypes, DragLocation, EventPayloadMap } from '../internal-types';
import { AllDragTypes, DragLocation, DropData, EventPayloadMap } from '../internal-types';
export declare function makeDispatch<DragType extends AllDragTypes>({ source, initial, dispatchEvent, }: {

@@ -19,4 +19,5 @@ source: DragType['payload'];

}): void;
drop({ current, updatedSourcePayload, }: {
drop({ current, drop, updatedSourcePayload, }: {
current: DragLocation;
drop: DropData;
/** When dragging from an external source, we need to collect the

@@ -23,0 +24,0 @@ drag source information again as it is often only available during

@@ -1,6 +0,6 @@

import { AdapterAPI, AllDragTypes, CleanupFn, EventPayloadMap } from '../internal-types';
import { AdapterAPI, AllDragTypes, CleanupFn, DropTargetAllowedDropEffect, EventPayloadMap } from '../internal-types';
export declare function makeAdapter<DragType extends AllDragTypes>({ typeKey, mount, dispatchEventToSource, defaultDropEffect, }: {
typeKey: DragType['type'];
mount: (api: AdapterAPI<DragType>) => CleanupFn;
defaultDropEffect: DataTransfer['dropEffect'];
defaultDropEffect: DropTargetAllowedDropEffect;
dispatchEventToSource?: <EventName extends keyof EventPayloadMap<DragType>>(args: {

@@ -7,0 +7,0 @@ eventName: EventName;

@@ -1,5 +0,5 @@

import { AllDragTypes, DropTargetAPI } from '../internal-types';
import { AllDragTypes, DropTargetAllowedDropEffect, DropTargetAPI } from '../internal-types';
export declare function makeDropTarget<DragType extends AllDragTypes>({ typeKey, defaultDropEffect, }: {
typeKey: DragType['type'];
defaultDropEffect: DataTransfer['dropEffect'];
defaultDropEffect: DropTargetAllowedDropEffect;
}): DropTargetAPI<DragType>;
/**
* Block drag operations outside of `@atlaskit/pragmatic-drag-and-drop`
*/
declare function start(): void;
declare function cleanup(): void;
/**
* TODO: for next major, we could look at do the following:
*
* ```diff
* - preventUnhandled.start();
* - preventUnhandled.stop();
* + const stop = preventUnhandled();
* ```
*/
export declare const preventUnhandled: {
start(): void;
stop(): void;
start: typeof start;
stop: typeof cleanup;
};
export {};

@@ -1,1 +0,1 @@

export type { DropTargetRecord, Position, Input, DragLocation, DragLocationHistory, CleanupFn, AllDragTypes, MonitorArgs, BaseEventPayload, ElementDragType, TextSelectionDragType, ExternalDragType, } from '../internal-types';
export type { DropTargetAllowedDropEffect, DropTargetRecord, Position, Input, DragLocation, DragLocationHistory, DropData, CleanupFn, AllDragTypes, MonitorArgs, BaseEventPayload, ElementDragType, TextSelectionDragType, ExternalDragType, } from '../internal-types';
export type CleanupFn = () => void;
/**
* Drop effects allowed to be passed to `getDropEffect()`.
* Cannot use `"none"` as a `dropEffect` for drop targets as
* it will opt out of accepting a drop for all nested drop targets.
* Please use `canDrop()` to disable dropping for this drop target.
*/
export type DropTargetAllowedDropEffect = Exclude<DataTransfer['dropEffect'], 'none'>;
/**
* Information about a drop target

@@ -21,3 +28,3 @@ */

*/
dropEffect: DataTransfer['dropEffect'];
dropEffect: DropTargetAllowedDropEffect;
/**

@@ -35,3 +42,3 @@ * Whether or not the drop target is active due to _stickiness_

};
export type StartedFrom = 'internal' | 'external';
export type Region = 'internal' | 'external';
export type ElementDragPayload = {

@@ -191,2 +198,5 @@ element: HTMLElement;

};
export type DropData = {
dropEffect: DataTransfer['dropEffect'];
};
export type EventPayloadMap<DragType extends AllDragTypes> = {

@@ -227,3 +237,5 @@ /**

*/
onDrop: BaseEventPayload<DragType>;
onDrop: BaseEventPayload<DragType> & {
drop: DropData;
};
};

@@ -315,5 +327,5 @@ export type AllEvents<DragType extends AllDragTypes> = {

* Optionally provide a _drop effect_ to be applied when
* this drop target is the innermost drop target being dragged over
* this drop target is the innermost drop target being dragged over.
*/
getDropEffect?: (args: DropTargetGetFeedbackArgs<DragType>) => DataTransfer['dropEffect'];
getDropEffect?: (args: DropTargetGetFeedbackArgs<DragType>) => DropTargetAllowedDropEffect;
/**

@@ -320,0 +332,0 @@ * Return `true` if you want your drop target to hold onto

@@ -1,2 +0,2 @@

import { AllDragTypes, DragLocation, EventPayloadMap } from '../internal-types';
import { AllDragTypes, DragLocation, DropData, EventPayloadMap } from '../internal-types';
export declare function makeDispatch<DragType extends AllDragTypes>({ source, initial, dispatchEvent, }: {

@@ -19,4 +19,5 @@ source: DragType['payload'];

}): void;
drop({ current, updatedSourcePayload, }: {
drop({ current, drop, updatedSourcePayload, }: {
current: DragLocation;
drop: DropData;
/** When dragging from an external source, we need to collect the

@@ -23,0 +24,0 @@ drag source information again as it is often only available during

@@ -1,6 +0,6 @@

import { AdapterAPI, AllDragTypes, CleanupFn, EventPayloadMap } from '../internal-types';
import { AdapterAPI, AllDragTypes, CleanupFn, DropTargetAllowedDropEffect, EventPayloadMap } from '../internal-types';
export declare function makeAdapter<DragType extends AllDragTypes>({ typeKey, mount, dispatchEventToSource, defaultDropEffect, }: {
typeKey: DragType['type'];
mount: (api: AdapterAPI<DragType>) => CleanupFn;
defaultDropEffect: DataTransfer['dropEffect'];
defaultDropEffect: DropTargetAllowedDropEffect;
dispatchEventToSource?: <EventName extends keyof EventPayloadMap<DragType>>(args: {

@@ -7,0 +7,0 @@ eventName: EventName;

@@ -1,5 +0,5 @@

import { AllDragTypes, DropTargetAPI } from '../internal-types';
import { AllDragTypes, DropTargetAllowedDropEffect, DropTargetAPI } from '../internal-types';
export declare function makeDropTarget<DragType extends AllDragTypes>({ typeKey, defaultDropEffect, }: {
typeKey: DragType['type'];
defaultDropEffect: DataTransfer['dropEffect'];
defaultDropEffect: DropTargetAllowedDropEffect;
}): DropTargetAPI<DragType>;
/**
* Block drag operations outside of `@atlaskit/pragmatic-drag-and-drop`
*/
declare function start(): void;
declare function cleanup(): void;
/**
* TODO: for next major, we could look at do the following:
*
* ```diff
* - preventUnhandled.start();
* - preventUnhandled.stop();
* + const stop = preventUnhandled();
* ```
*/
export declare const preventUnhandled: {
start(): void;
stop(): void;
start: typeof start;
stop: typeof cleanup;
};
export {};
{
"name": "@atlaskit/pragmatic-drag-and-drop",
"version": "1.0.2",
"version": "1.1.0",
"description": "Fast drag and drop for any experience on any tech stack",

@@ -5,0 +5,0 @@ "repository": "https://github.com/atlassian/pragmatic-drag-and-drop",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc