@shopify/draggable
Version:
The JavaScript Drag & Drop library your grandparents warned you about.
1,836 lines (1,480 loc) • 134 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("Droppable", [], factory);
else if(typeof exports === 'object')
exports["Droppable"] = factory();
else
root["Droppable"] = factory();
})(window, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 44);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _closest = __webpack_require__(40);
Object.defineProperty(exports, 'closest', {
enumerable: true,
get: function () {
return _interopRequireDefault(_closest).default;
}
});
var _requestNextAnimationFrame = __webpack_require__(38);
Object.defineProperty(exports, 'requestNextAnimationFrame', {
enumerable: true,
get: function () {
return _interopRequireDefault(_requestNextAnimationFrame).default;
}
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _SensorEvent = __webpack_require__(19);
Object.keys(_SensorEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _SensorEvent[key];
}
});
});
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _Sensor = __webpack_require__(22);
var _Sensor2 = _interopRequireDefault(_Sensor);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _Sensor2.default;
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _AbstractEvent = __webpack_require__(42);
var _AbstractEvent2 = _interopRequireDefault(_AbstractEvent);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _AbstractEvent2.default;
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _AbstractPlugin = __webpack_require__(31);
var _AbstractPlugin2 = _interopRequireDefault(_AbstractPlugin);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _AbstractPlugin2.default;
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _Sensor = __webpack_require__(2);
Object.defineProperty(exports, 'Sensor', {
enumerable: true,
get: function () {
return _interopRequireDefault(_Sensor).default;
}
});
var _MouseSensor = __webpack_require__(21);
Object.defineProperty(exports, 'MouseSensor', {
enumerable: true,
get: function () {
return _interopRequireDefault(_MouseSensor).default;
}
});
var _TouchSensor = __webpack_require__(18);
Object.defineProperty(exports, 'TouchSensor', {
enumerable: true,
get: function () {
return _interopRequireDefault(_TouchSensor).default;
}
});
var _DragSensor = __webpack_require__(16);
Object.defineProperty(exports, 'DragSensor', {
enumerable: true,
get: function () {
return _interopRequireDefault(_DragSensor).default;
}
});
var _ForceTouchSensor = __webpack_require__(14);
Object.defineProperty(exports, 'ForceTouchSensor', {
enumerable: true,
get: function () {
return _interopRequireDefault(_ForceTouchSensor).default;
}
});
var _SensorEvent = __webpack_require__(1);
Object.keys(_SensorEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _SensorEvent[key];
}
});
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _Announcement = __webpack_require__(33);
Object.defineProperty(exports, 'Announcement', {
enumerable: true,
get: function () {
return _interopRequireDefault(_Announcement).default;
}
});
Object.defineProperty(exports, 'defaultAnnouncementOptions', {
enumerable: true,
get: function () {
return _Announcement.defaultOptions;
}
});
var _Focusable = __webpack_require__(30);
Object.defineProperty(exports, 'Focusable', {
enumerable: true,
get: function () {
return _interopRequireDefault(_Focusable).default;
}
});
var _Mirror = __webpack_require__(28);
Object.defineProperty(exports, 'Mirror', {
enumerable: true,
get: function () {
return _interopRequireDefault(_Mirror).default;
}
});
Object.defineProperty(exports, 'defaultMirrorOptions', {
enumerable: true,
get: function () {
return _Mirror.defaultOptions;
}
});
var _Scrollable = __webpack_require__(24);
Object.defineProperty(exports, 'Scrollable', {
enumerable: true,
get: function () {
return _interopRequireDefault(_Scrollable).default;
}
});
Object.defineProperty(exports, 'defaultScrollableOptions', {
enumerable: true,
get: function () {
return _Scrollable.defaultOptions;
}
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _DraggableEvent = __webpack_require__(34);
Object.keys(_DraggableEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _DraggableEvent[key];
}
});
});
/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _DragEvent = __webpack_require__(35);
Object.keys(_DragEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _DragEvent[key];
}
});
});
/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _DroppableEvent = __webpack_require__(43);
Object.keys(_DroppableEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _DroppableEvent[key];
}
});
});
/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* The Emitter is a simple emitter class that provides you with `on()`, `off()` and `trigger()` methods
* @class Emitter
* @module Emitter
*/
class Emitter {
constructor() {
this.callbacks = {};
}
/**
* Registers callbacks by event name
* @param {String} type
* @param {...Function} callbacks
*/
on(type, ...callbacks) {
if (!this.callbacks[type]) {
this.callbacks[type] = [];
}
this.callbacks[type].push(...callbacks);
return this;
}
/**
* Unregisters callbacks by event name
* @param {String} type
* @param {Function} callback
*/
off(type, callback) {
if (!this.callbacks[type]) {
return null;
}
const copy = this.callbacks[type].slice(0);
for (let i = 0; i < copy.length; i++) {
if (callback === copy[i]) {
this.callbacks[type].splice(i, 1);
}
}
return this;
}
/**
* Triggers event callbacks by event object
* @param {AbstractEvent} event
*/
trigger(event) {
if (!this.callbacks[event.type]) {
return null;
}
const callbacks = [...this.callbacks[event.type]];
const caughtErrors = [];
for (let i = callbacks.length - 1; i >= 0; i--) {
const callback = callbacks[i];
try {
callback(event);
} catch (error) {
caughtErrors.push(error);
}
}
if (caughtErrors.length) {
/* eslint-disable no-console */
console.error(`Draggable caught errors while triggering '${event.type}'`, caughtErrors);
/* eslint-disable no-console */
}
return this;
}
}
exports.default = Emitter;
/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _Emitter = __webpack_require__(10);
var _Emitter2 = _interopRequireDefault(_Emitter);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _Emitter2.default;
/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.defaultOptions = undefined;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _utils = __webpack_require__(0);
var _Plugins = __webpack_require__(6);
var _Emitter = __webpack_require__(11);
var _Emitter2 = _interopRequireDefault(_Emitter);
var _Sensors = __webpack_require__(5);
var _DraggableEvent = __webpack_require__(7);
var _DragEvent = __webpack_require__(8);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const onDragStart = Symbol('onDragStart');
const onDragMove = Symbol('onDragMove');
const onDragStop = Symbol('onDragStop');
const onDragPressure = Symbol('onDragPressure');
/**
* @const {Object} defaultAnnouncements
* @const {Function} defaultAnnouncements['drag:start']
* @const {Function} defaultAnnouncements['drag:stop']
*/
const defaultAnnouncements = {
'drag:start': event => `Picked up ${event.source.textContent.trim() || event.source.id || 'draggable element'}`,
'drag:stop': event => `Released ${event.source.textContent.trim() || event.source.id || 'draggable element'}`
};
const defaultClasses = {
'container:dragging': 'draggable-container--is-dragging',
'source:dragging': 'draggable-source--is-dragging',
'source:placed': 'draggable-source--placed',
'container:placed': 'draggable-container--placed',
'body:dragging': 'draggable--is-dragging',
'draggable:over': 'draggable--over',
'container:over': 'draggable-container--over',
'source:original': 'draggable--original',
mirror: 'draggable-mirror'
};
const defaultOptions = exports.defaultOptions = {
draggable: '.draggable-source',
handle: null,
delay: 100,
placedTimeout: 800,
plugins: [],
sensors: []
};
/**
* This is the core draggable library that does the heavy lifting
* @class Draggable
* @module Draggable
*/
class Draggable {
/**
* Draggable constructor.
* @constructs Draggable
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Draggable containers
* @param {Object} options - Options for draggable
*/
constructor(containers = [document.body], options = {}) {
/**
* Draggable containers
* @property containers
* @type {HTMLElement[]}
*/
if (containers instanceof NodeList || containers instanceof Array) {
this.containers = [...containers];
} else if (containers instanceof HTMLElement) {
this.containers = [containers];
} else {
throw new Error('Draggable containers are expected to be of type `NodeList`, `HTMLElement[]` or `HTMLElement`');
}
this.options = _extends({}, defaultOptions, options, {
classes: _extends({}, defaultClasses, options.classes || {}),
announcements: _extends({}, defaultAnnouncements, options.announcements || {})
});
/**
* Draggables event emitter
* @property emitter
* @type {Emitter}
*/
this.emitter = new _Emitter2.default();
/**
* Current drag state
* @property dragging
* @type {Boolean}
*/
this.dragging = false;
/**
* Active plugins
* @property plugins
* @type {Plugin[]}
*/
this.plugins = [];
/**
* Active sensors
* @property sensors
* @type {Sensor[]}
*/
this.sensors = [];
this[onDragStart] = this[onDragStart].bind(this);
this[onDragMove] = this[onDragMove].bind(this);
this[onDragStop] = this[onDragStop].bind(this);
this[onDragPressure] = this[onDragPressure].bind(this);
document.addEventListener('drag:start', this[onDragStart], true);
document.addEventListener('drag:move', this[onDragMove], true);
document.addEventListener('drag:stop', this[onDragStop], true);
document.addEventListener('drag:pressure', this[onDragPressure], true);
const defaultPlugins = Object.values(Draggable.Plugins).map(Plugin => Plugin);
const defaultSensors = [_Sensors.MouseSensor, _Sensors.TouchSensor];
this.addPlugin(...[...defaultPlugins, ...this.options.plugins]);
this.addSensor(...[...defaultSensors, ...this.options.sensors]);
const draggableInitializedEvent = new _DraggableEvent.DraggableInitializedEvent({
draggable: this
});
this.on('mirror:created', ({ mirror }) => this.mirror = mirror);
this.on('mirror:destroy', () => this.mirror = null);
this.trigger(draggableInitializedEvent);
}
/**
* Destroys Draggable instance. This removes all internal event listeners and
* deactivates sensors and plugins
*/
/**
* Default plugins draggable uses
* @static
* @property {Object} Plugins
* @property {Announcement} Plugins.Announcement
* @property {Focusable} Plugins.Focusable
* @property {Mirror} Plugins.Mirror
* @property {Scrollable} Plugins.Scrollable
* @type {Object}
*/
destroy() {
document.removeEventListener('drag:start', this[onDragStart], true);
document.removeEventListener('drag:move', this[onDragMove], true);
document.removeEventListener('drag:stop', this[onDragStop], true);
document.removeEventListener('drag:pressure', this[onDragPressure], true);
const draggableDestroyEvent = new _DraggableEvent.DraggableDestroyEvent({
draggable: this
});
this.trigger(draggableDestroyEvent);
this.removePlugin(...this.plugins.map(plugin => plugin.constructor));
this.removeSensor(...this.sensors.map(sensor => sensor.constructor));
}
/**
* Adds plugin to this draggable instance. This will end up calling the attach method of the plugin
* @param {...typeof Plugin} plugins - Plugins that you want attached to draggable
* @return {Draggable}
* @example draggable.addPlugin(CustomA11yPlugin, CustomMirrorPlugin)
*/
addPlugin(...plugins) {
const activePlugins = plugins.map(Plugin => new Plugin(this));
activePlugins.forEach(plugin => plugin.attach());
this.plugins = [...this.plugins, ...activePlugins];
return this;
}
/**
* Removes plugins that are already attached to this draggable instance. This will end up calling
* the detach method of the plugin
* @param {...typeof Plugin} plugins - Plugins that you want detached from draggable
* @return {Draggable}
* @example draggable.removePlugin(MirrorPlugin, CustomMirrorPlugin)
*/
removePlugin(...plugins) {
const removedPlugins = this.plugins.filter(plugin => plugins.includes(plugin.constructor));
removedPlugins.forEach(plugin => plugin.detach());
this.plugins = this.plugins.filter(plugin => !plugins.includes(plugin.constructor));
return this;
}
/**
* Adds sensors to this draggable instance. This will end up calling the attach method of the sensor
* @param {...typeof Sensor} sensors - Sensors that you want attached to draggable
* @return {Draggable}
* @example draggable.addSensor(ForceTouchSensor, CustomSensor)
*/
addSensor(...sensors) {
const activeSensors = sensors.map(Sensor => new Sensor(this.containers, this.options));
activeSensors.forEach(sensor => sensor.attach());
this.sensors = [...this.sensors, ...activeSensors];
return this;
}
/**
* Removes sensors that are already attached to this draggable instance. This will end up calling
* the detach method of the sensor
* @param {...typeof Sensor} sensors - Sensors that you want attached to draggable
* @return {Draggable}
* @example draggable.removeSensor(TouchSensor, DragSensor)
*/
removeSensor(...sensors) {
const removedSensors = this.sensors.filter(sensor => sensors.includes(sensor.constructor));
removedSensors.forEach(sensor => sensor.detach());
this.sensors = this.sensors.filter(sensor => !sensors.includes(sensor.constructor));
return this;
}
/**
* Adds container to this draggable instance
* @param {...HTMLElement} containers - Containers you want to add to draggable
* @return {Draggable}
* @example draggable.addContainer(document.body)
*/
addContainer(...containers) {
this.containers = [...this.containers, ...containers];
this.sensors.forEach(sensor => sensor.addContainer(...containers));
return this;
}
/**
* Removes container from this draggable instance
* @param {...HTMLElement} containers - Containers you want to remove from draggable
* @return {Draggable}
* @example draggable.removeContainer(document.body)
*/
removeContainer(...containers) {
this.containers = this.containers.filter(container => !containers.includes(container));
this.sensors.forEach(sensor => sensor.removeContainer(...containers));
return this;
}
/**
* Adds listener for draggable events
* @param {String} type - Event name
* @param {...Function} callbacks - Event callbacks
* @return {Draggable}
* @example draggable.on('drag:start', (dragEvent) => dragEvent.cancel());
*/
on(type, ...callbacks) {
this.emitter.on(type, ...callbacks);
return this;
}
/**
* Removes listener from draggable
* @param {String} type - Event name
* @param {Function} callback - Event callback
* @return {Draggable}
* @example draggable.off('drag:start', handlerFunction);
*/
off(type, callback) {
this.emitter.off(type, callback);
return this;
}
/**
* Triggers draggable event
* @param {AbstractEvent} event - Event instance
* @return {Draggable}
* @example draggable.trigger(event);
*/
trigger(event) {
this.emitter.trigger(event);
return this;
}
/**
* Returns class name for class identifier
* @param {String} name - Name of class identifier
* @return {String|null}
*/
getClassNameFor(name) {
return this.options.classes[name];
}
/**
* Returns true if this draggable instance is currently dragging
* @return {Boolean}
*/
isDragging() {
return Boolean(this.dragging);
}
/**
* Returns all draggable elements
* @return {HTMLElement[]}
*/
getDraggableElements() {
return this.containers.reduce((current, container) => {
return [...current, ...this.getDraggableElementsForContainer(container)];
}, []);
}
/**
* Returns draggable elements for a given container, excluding the mirror and
* original source element if present
* @param {HTMLElement} container
* @return {HTMLElement[]}
*/
getDraggableElementsForContainer(container) {
const allDraggableElements = container.querySelectorAll(this.options.draggable);
return [...allDraggableElements].filter(childElement => {
return childElement !== this.originalSource && childElement !== this.mirror;
});
}
/**
* Drag start handler
* @private
* @param {Event} event - DOM Drag event
*/
[onDragStart](event) {
const sensorEvent = getSensorEvent(event);
const { target, container } = sensorEvent;
if (!this.containers.includes(container)) {
return;
}
if (this.options.handle && target && !(0, _utils.closest)(target, this.options.handle)) {
sensorEvent.cancel();
return;
}
// Find draggable source element
this.originalSource = (0, _utils.closest)(target, this.options.draggable);
this.sourceContainer = container;
if (!this.originalSource) {
sensorEvent.cancel();
return;
}
if (this.lastPlacedSource && this.lastPlacedContainer) {
clearTimeout(this.placedTimeoutID);
this.lastPlacedSource.classList.remove(this.getClassNameFor('source:placed'));
this.lastPlacedContainer.classList.remove(this.getClassNameFor('container:placed'));
}
this.source = this.originalSource.cloneNode(true);
this.originalSource.parentNode.insertBefore(this.source, this.originalSource);
this.originalSource.style.display = 'none';
const dragEvent = new _DragEvent.DragStartEvent({
source: this.source,
originalSource: this.originalSource,
sourceContainer: container,
sensorEvent
});
this.trigger(dragEvent);
this.dragging = !dragEvent.canceled();
if (dragEvent.canceled()) {
this.source.parentNode.removeChild(this.source);
this.originalSource.style.display = null;
return;
}
this.originalSource.classList.add(this.getClassNameFor('source:original'));
this.source.classList.add(this.getClassNameFor('source:dragging'));
this.sourceContainer.classList.add(this.getClassNameFor('container:dragging'));
document.body.classList.add(this.getClassNameFor('body:dragging'));
applyUserSelect(document.body, 'none');
requestAnimationFrame(() => {
const oldSensorEvent = getSensorEvent(event);
const newSensorEvent = oldSensorEvent.clone({ target: this.source });
this[onDragMove](_extends({}, event, {
detail: newSensorEvent
}));
});
}
/**
* Drag move handler
* @private
* @param {Event} event - DOM Drag event
*/
[onDragMove](event) {
if (!this.dragging) {
return;
}
const sensorEvent = getSensorEvent(event);
const { container } = sensorEvent;
let target = sensorEvent.target;
const dragMoveEvent = new _DragEvent.DragMoveEvent({
source: this.source,
originalSource: this.originalSource,
sourceContainer: container,
sensorEvent
});
this.trigger(dragMoveEvent);
if (dragMoveEvent.canceled()) {
sensorEvent.cancel();
}
target = (0, _utils.closest)(target, this.options.draggable);
const withinCorrectContainer = (0, _utils.closest)(sensorEvent.target, this.containers);
const overContainer = sensorEvent.overContainer || withinCorrectContainer;
const isLeavingContainer = this.currentOverContainer && overContainer !== this.currentOverContainer;
const isLeavingDraggable = this.currentOver && target !== this.currentOver;
const isOverContainer = overContainer && this.currentOverContainer !== overContainer;
const isOverDraggable = withinCorrectContainer && target && this.currentOver !== target;
if (isLeavingDraggable) {
const dragOutEvent = new _DragEvent.DragOutEvent({
source: this.source,
originalSource: this.originalSource,
sourceContainer: container,
sensorEvent,
over: this.currentOver
});
this.currentOver.classList.remove(this.getClassNameFor('draggable:over'));
this.currentOver = null;
this.trigger(dragOutEvent);
}
if (isLeavingContainer) {
const dragOutContainerEvent = new _DragEvent.DragOutContainerEvent({
source: this.source,
originalSource: this.originalSource,
sourceContainer: container,
sensorEvent,
overContainer: this.currentOverContainer
});
this.currentOverContainer.classList.remove(this.getClassNameFor('container:over'));
this.currentOverContainer = null;
this.trigger(dragOutContainerEvent);
}
if (isOverContainer) {
overContainer.classList.add(this.getClassNameFor('container:over'));
const dragOverContainerEvent = new _DragEvent.DragOverContainerEvent({
source: this.source,
originalSource: this.originalSource,
sourceContainer: container,
sensorEvent,
overContainer
});
this.currentOverContainer = overContainer;
this.trigger(dragOverContainerEvent);
}
if (isOverDraggable) {
target.classList.add(this.getClassNameFor('draggable:over'));
const dragOverEvent = new _DragEvent.DragOverEvent({
source: this.source,
originalSource: this.originalSource,
sourceContainer: container,
sensorEvent,
overContainer,
over: target
});
this.currentOver = target;
this.trigger(dragOverEvent);
}
}
/**
* Drag stop handler
* @private
* @param {Event} event - DOM Drag event
*/
[onDragStop](event) {
if (!this.dragging) {
return;
}
this.dragging = false;
const dragStopEvent = new _DragEvent.DragStopEvent({
source: this.source,
originalSource: this.originalSource,
sensorEvent: event.sensorEvent,
sourceContainer: this.sourceContainer
});
this.trigger(dragStopEvent);
this.source.parentNode.insertBefore(this.originalSource, this.source);
this.source.parentNode.removeChild(this.source);
this.originalSource.style.display = '';
this.source.classList.remove(this.getClassNameFor('source:dragging'));
this.originalSource.classList.remove(this.getClassNameFor('source:original'));
this.originalSource.classList.add(this.getClassNameFor('source:placed'));
this.sourceContainer.classList.add(this.getClassNameFor('container:placed'));
this.sourceContainer.classList.remove(this.getClassNameFor('container:dragging'));
document.body.classList.remove(this.getClassNameFor('body:dragging'));
applyUserSelect(document.body, '');
if (this.currentOver) {
this.currentOver.classList.remove(this.getClassNameFor('draggable:over'));
}
if (this.currentOverContainer) {
this.currentOverContainer.classList.remove(this.getClassNameFor('container:over'));
}
this.lastPlacedSource = this.originalSource;
this.lastPlacedContainer = this.sourceContainer;
this.placedTimeoutID = setTimeout(() => {
if (this.lastPlacedSource) {
this.lastPlacedSource.classList.remove(this.getClassNameFor('source:placed'));
}
if (this.lastPlacedContainer) {
this.lastPlacedContainer.classList.remove(this.getClassNameFor('container:placed'));
}
this.lastPlacedSource = null;
this.lastPlacedContainer = null;
}, this.options.placedTimeout);
this.source = null;
this.originalSource = null;
this.currentOverContainer = null;
this.currentOver = null;
this.sourceContainer = null;
}
/**
* Drag pressure handler
* @private
* @param {Event} event - DOM Drag event
*/
[onDragPressure](event) {
if (!this.dragging) {
return;
}
const sensorEvent = getSensorEvent(event);
const source = this.source || (0, _utils.closest)(sensorEvent.originalEvent.target, this.options.draggable);
const dragPressureEvent = new _DragEvent.DragPressureEvent({
sensorEvent,
source,
pressure: sensorEvent.pressure
});
this.trigger(dragPressureEvent);
}
}
exports.default = Draggable;
Draggable.Plugins = { Announcement: _Plugins.Announcement, Focusable: _Plugins.Focusable, Mirror: _Plugins.Mirror, Scrollable: _Plugins.Scrollable };
function getSensorEvent(event) {
return event.detail;
}
function applyUserSelect(element, value) {
element.style.webkitUserSelect = value;
element.style.mozUserSelect = value;
element.style.msUserSelect = value;
element.style.oUserSelect = value;
element.style.userSelect = value;
}
/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _Sensor = __webpack_require__(2);
var _Sensor2 = _interopRequireDefault(_Sensor);
var _SensorEvent = __webpack_require__(1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const onMouseForceWillBegin = Symbol('onMouseForceWillBegin');
const onMouseForceDown = Symbol('onMouseForceDown');
const onMouseDown = Symbol('onMouseDown');
const onMouseForceChange = Symbol('onMouseForceChange');
const onMouseMove = Symbol('onMouseMove');
const onMouseUp = Symbol('onMouseUp');
const onMouseForceGlobalChange = Symbol('onMouseForceGlobalChange');
/**
* This sensor picks up native force touch events and dictates drag operations
* @class ForceTouchSensor
* @module ForceTouchSensor
* @extends Sensor
*/
class ForceTouchSensor extends _Sensor2.default {
/**
* ForceTouchSensor constructor.
* @constructs ForceTouchSensor
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
* @param {Object} options - Options
*/
constructor(containers = [], options = {}) {
super(containers, options);
/**
* Draggable element needs to be remembered to unset the draggable attribute after drag operation has completed
* @property mightDrag
* @type {Boolean}
*/
this.mightDrag = false;
this[onMouseForceWillBegin] = this[onMouseForceWillBegin].bind(this);
this[onMouseForceDown] = this[onMouseForceDown].bind(this);
this[onMouseDown] = this[onMouseDown].bind(this);
this[onMouseForceChange] = this[onMouseForceChange].bind(this);
this[onMouseMove] = this[onMouseMove].bind(this);
this[onMouseUp] = this[onMouseUp].bind(this);
}
/**
* Attaches sensors event listeners to the DOM
*/
attach() {
for (const container of this.containers) {
container.addEventListener('webkitmouseforcewillbegin', this[onMouseForceWillBegin], false);
container.addEventListener('webkitmouseforcedown', this[onMouseForceDown], false);
container.addEventListener('mousedown', this[onMouseDown], true);
container.addEventListener('webkitmouseforcechanged', this[onMouseForceChange], false);
}
document.addEventListener('mousemove', this[onMouseMove]);
document.addEventListener('mouseup', this[onMouseUp]);
}
/**
* Detaches sensors event listeners to the DOM
*/
detach() {
for (const container of this.containers) {
container.removeEventListener('webkitmouseforcewillbegin', this[onMouseForceWillBegin], false);
container.removeEventListener('webkitmouseforcedown', this[onMouseForceDown], false);
container.removeEventListener('mousedown', this[onMouseDown], true);
container.removeEventListener('webkitmouseforcechanged', this[onMouseForceChange], false);
}
document.removeEventListener('mousemove', this[onMouseMove]);
document.removeEventListener('mouseup', this[onMouseUp]);
}
/**
* Mouse force will begin handler
* @private
* @param {Event} event - Mouse force will begin event
*/
[onMouseForceWillBegin](event) {
event.preventDefault();
this.mightDrag = true;
}
/**
* Mouse force down handler
* @private
* @param {Event} event - Mouse force down event
*/
[onMouseForceDown](event) {
if (this.dragging) {
return;
}
const target = document.elementFromPoint(event.clientX, event.clientY);
const container = event.currentTarget;
const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
clientX: event.clientX,
clientY: event.clientY,
target,
container,
originalEvent: event
});
this.trigger(container, dragStartEvent);
this.currentContainer = container;
this.dragging = !dragStartEvent.canceled();
this.mightDrag = false;
}
/**
* Mouse up handler
* @private
* @param {Event} event - Mouse up event
*/
[onMouseUp](event) {
if (!this.dragging) {
return;
}
const dragStopEvent = new _SensorEvent.DragStopSensorEvent({
clientX: event.clientX,
clientY: event.clientY,
target: null,
container: this.currentContainer,
originalEvent: event
});
this.trigger(this.currentContainer, dragStopEvent);
this.currentContainer = null;
this.dragging = false;
this.mightDrag = false;
}
/**
* Mouse down handler
* @private
* @param {Event} event - Mouse down event
*/
[onMouseDown](event) {
if (!this.mightDrag) {
return;
}
// Need workaround for real click
// Cancel potential drag events
event.stopPropagation();
event.stopImmediatePropagation();
event.preventDefault();
}
/**
* Mouse move handler
* @private
* @param {Event} event - Mouse force will begin event
*/
[onMouseMove](event) {
if (!this.dragging) {
return;
}
const target = document.elementFromPoint(event.clientX, event.clientY);
const dragMoveEvent = new _SensorEvent.DragMoveSensorEvent({
clientX: event.clientX,
clientY: event.clientY,
target,
container: this.currentContainer,
originalEvent: event
});
this.trigger(this.currentContainer, dragMoveEvent);
}
/**
* Mouse force change handler
* @private
* @param {Event} event - Mouse force change event
*/
[onMouseForceChange](event) {
if (this.dragging) {
return;
}
const target = event.target;
const container = event.currentTarget;
const dragPressureEvent = new _SensorEvent.DragPressureSensorEvent({
pressure: event.webkitForce,
clientX: event.clientX,
clientY: event.clientY,
target,
container,
originalEvent: event
});
this.trigger(container, dragPressureEvent);
}
/**
* Mouse force global change handler
* @private
* @param {Event} event - Mouse force global change event
*/
[onMouseForceGlobalChange](event) {
if (!this.dragging) {
return;
}
const target = event.target;
const dragPressureEvent = new _SensorEvent.DragPressureSensorEvent({
pressure: event.webkitForce,
clientX: event.clientX,
clientY: event.clientY,
target,
container: this.currentContainer,
originalEvent: event
});
this.trigger(this.currentContainer, dragPressureEvent);
}
}
exports.default = ForceTouchSensor;
/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ForceTouchSensor = __webpack_require__(13);
var _ForceTouchSensor2 = _interopRequireDefault(_ForceTouchSensor);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _ForceTouchSensor2.default;
/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(0);
var _Sensor = __webpack_require__(2);
var _Sensor2 = _interopRequireDefault(_Sensor);
var _SensorEvent = __webpack_require__(1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const onMouseDown = Symbol('onMouseDown');
const onMouseUp = Symbol('onMouseUp');
const onDragStart = Symbol('onDragStart');
const onDragOver = Symbol('onDragOver');
const onDragEnd = Symbol('onDragEnd');
const onDrop = Symbol('onDrop');
const reset = Symbol('reset');
/**
* This sensor picks up native browser drag events and dictates drag operations
* @class DragSensor
* @module DragSensor
* @extends Sensor
*/
class DragSensor extends _Sensor2.default {
/**
* DragSensor constructor.
* @constructs DragSensor
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
* @param {Object} options - Options
*/
constructor(containers = [], options = {}) {
super(containers, options);
/**
* Mouse down timer which will end up setting the draggable attribute, unless canceled
* @property mouseDownTimeout
* @type {Number}
*/
this.mouseDownTimeout = null;
/**
* Draggable element needs to be remembered to unset the draggable attribute after drag operation has completed
* @property draggableElement
* @type {HTMLElement}
*/
this.draggableElement = null;
/**
* Native draggable element could be links or images, their draggable state will be disabled during drag operation
* @property nativeDraggableElement
* @type {HTMLElement}
*/
this.nativeDraggableElement = null;
this[onMouseDown] = this[onMouseDown].bind(this);
this[onMouseUp] = this[onMouseUp].bind(this);
this[onDragStart] = this[onDragStart].bind(this);
this[onDragOver] = this[onDragOver].bind(this);
this[onDragEnd] = this[onDragEnd].bind(this);
this[onDrop] = this[onDrop].bind(this);
}
/**
* Attaches sensors event listeners to the DOM
*/
attach() {
document.addEventListener('mousedown', this[onMouseDown], true);
}
/**
* Detaches sensors event listeners to the DOM
*/
detach() {
document.removeEventListener('mousedown', this[onMouseDown], true);
}
/**
* Drag start handler
* @private
* @param {Event} event - Drag start event
*/
[onDragStart](event) {
// Need for firefox. "text" key is needed for IE
event.dataTransfer.setData('text', '');
event.dataTransfer.effectAllowed = this.options.type;
const target = document.elementFromPoint(event.clientX, event.clientY);
this.currentContainer = (0, _utils.closest)(event.target, this.containers);
if (!this.currentContainer) {
return;
}
const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
clientX: event.clientX,
clientY: event.clientY,
target,
container: this.currentContainer,
originalEvent: event
});
// Workaround
setTimeout(() => {
this.trigger(this.currentContainer, dragStartEvent);
if (dragStartEvent.canceled()) {
this.dragging = false;
} else {
this.dragging = true;
}
}, 0);
}
/**
* Drag over handler
* @private
* @param {Event} event - Drag over event
*/
[onDragOver](event) {
if (!this.dragging) {
return;
}
const target = document.elementFromPoint(event.clientX, event.clientY);
const container = this.currentContainer;
const dragMoveEvent = new _SensorEvent.DragMoveSensorEvent({
clientX: event.clientX,
clientY: event.clientY,
target,
container,
originalEvent: event
});
this.trigger(container, dragMoveEvent);
if (!dragMoveEvent.canceled()) {
event.preventDefault();
event.dataTransfer.dropEffect = this.options.type;
}
}
/**
* Drag end handler
* @private
* @param {Event} event - Drag end event
*/
[onDragEnd](event) {
if (!this.dragging) {
return;
}
document.removeEventListener('mouseup', this[onMouseUp], true);
const target = document.elementFromPoint(event.clientX, event.clientY);
const container = this.currentContainer;
const dragStopEvent = new _SensorEvent.DragStopSensorEvent({
clientX: event.clientX,
clientY: event.clientY,
target,
container,
originalEvent: event
});
this.trigger(container, dragStopEvent);
this.dragging = false;
this[reset]();
}
/**
* Drop handler
* @private
* @param {Event} event - Drop event
*/
[onDrop](event) {
// eslint-disable-line class-methods-use-this
event.preventDefault();
}
/**
* Mouse down handler
* @private
* @param {Event} event - Mouse down event
*/
[onMouseDown](event) {
// Firefox bug for inputs within draggables https://bugzilla.mozilla.org/show_bug.cgi?id=739071
if (event.target && (event.target.form || event.target.contenteditable)) {
return;
}
const nativeDraggableElement = (0, _utils.closest)(event.target, element => element.draggable);
if (nativeDraggableElement) {
nativeDraggableElement.draggable = false;
this.nativeDraggableElement = nativeDraggableElement;
}
document.addEventListener('mouseup', this[onMouseUp], true);
document.addEventListener('dragstart', this[onDragStart], false);
document.addEventListener('dragover', this[onDragOver], false);
document.addEventListener('dragend', this[onDragEnd], false);
document.addEventListener('drop', this[onDrop], false);
const target = (0, _utils.closest)(event.target, this.options.draggable);
if (!target) {
return;
}
this.mouseDownTimeout = setTimeout(() => {
target.draggable = true;
this.draggableElement = target;
}, this.options.delay);
}
/**
* Mouse up handler
* @private
* @param {Event} event - Mouse up event
*/
[onMouseUp]() {
this[reset]();
}
/**
* Mouse up handler
* @private
* @param {Event} event - Mouse up event
*/
[reset]() {
clearTimeout(this.mouseDownTimeout);
document.removeEventListener('mouseup', this[onMouseUp], true);
document.removeEventListener('dragstart', this[onDragStart], false);
document.removeEventListener('dragover', this[onDragOver], false);
document.removeEventListener('dragend', this[onDragEnd], false);
document.removeEventListener('drop', this[onDrop], false);
if (this.nativeDraggableElement) {
this.nativeDraggableElement.draggable = true;
this.nativeDraggableElement = null;
}
if (this.draggableElement) {
this.draggableElement.draggable = false;
this.draggableElement = null;
}
}
}
exports.default = DragSensor;
/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _DragSensor = __webpack_require__(15);
var _DragSensor2 = _interopRequireDefault(_DragSensor);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _DragSensor2.default;
/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(0);
var _Sensor = __webpack_require__(2);
var _Sensor2 = _interopRequireDefault(_Sensor);
var _SensorEvent = __webpack_require__(1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const onTouchStart = Symbol('onTouchStart');
const onTouchHold = Symbol('onTouchHold');
const onTouchEnd = Symbol('onTouchEnd');
const onTouchMove = Symbol('onTouchMove');
/**
* Prevents scrolling when set to true
* @var {Boolean} preventScrolling
*/
let preventScrolling = false;
// WebKit requires cancelable `touchmove` events to be added as early as possible
window.addEventListener('touchmove', event => {
if (!preventScrolling) {
return;
}
// Prevent scrolling
event.preventDefault();
}, { passive: false });
/**
* This sensor picks up native browser touch events and dictates drag operations
* @class TouchSensor
* @module TouchSensor
* @extends Sensor
*/
class TouchSensor extends _Sensor2.default {
/**
* TouchSensor constructor.
* @constructs TouchSensor
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
* @param {Object} options - Options
*/
constructor(containers = [], options = {}) {
super(containers, options);
/**
* Closest scrollable container so accidental scroll can cancel long touch
* @property currentScrollableParent
* @type {HTMLElement}
*/
this.currentScrollableParent = null;
/**
* TimeoutID for long touch
* @property tapTimeout
* @type {Number}
*/
this.tapTimeout = null;
/**
* touchMoved indicates if touch has moved during tapTimeout
* @property touchMoved
* @type {Boolean}
*/
this.touchMoved = false;
this[onTouchStart] = this[onTouchStart].bind(this);
this[onTouchHold] = this[onTouchHold].bind(this);
this[onTouchEnd] = this[onTouchEnd].bind(this);
this[onTouchMove] = this[onTouchMove].bind(this);
}
/**
* Attaches sensors event listeners to the DOM
*/
attach() {
document.addEventListener('touchstart', this[onTouchStart]);
}
/**
* Detaches sensors event listeners to the DOM
*/
detach() {
document.removeEventListener('touchstart', this[onTouchStart]);
}
/**
* Touch start handler
* @private
* @param {Event} event - Touch start event
*/
[onTouchStart](event) {
const container = (0, _utils.closest)(event.target, this.containers);
if (!container) {
return;
}
document.addEventListener('touchmove', this[onTouchMove]);
document.addEventListener('touchend', this[onTouchEnd]);
document.addEventListener('touchcancel', this[onTouchEnd]);
container.addEventListener('contextmenu', onContextMenu);
this.currentContainer = container;
this.tapTimeout = setTimeout(this[onTouchHold](event, container), this.options.delay);
}
/**
* Touch hold handler
* @private
* @param {Event} event - Touch start event
* @param {HTMLElement} container - Container element
*/
[onTouchHold](event, container) {
return () => {
if (this.touchMoved) {
return;
}
const touch = event.touches[0] || event.changedTouches[0];
const target = event.target;
const dragStartEvent = new _SensorEvent.DragStartSensorEvent({
clientX: touch.pageX,
clientY: touch.pageY,
target,
container,
originalEvent: event
});
this.trigger(container, dragStartEvent);
this.dragging = !dragStartEvent.canceled();
preventScrolling = this.dragging;
};
}
/**
* Touch move handler
* @private
* @param {Event} event - Touc