diff --git a/plugins/AutoScroll/AutoScroll.js b/plugins/AutoScroll/AutoScroll.js index 97cba2e28..3027bb351 100644 --- a/plugins/AutoScroll/AutoScroll.js +++ b/plugins/AutoScroll/AutoScroll.js @@ -27,7 +27,7 @@ let autoScrolls = [], function AutoScrollPlugin() { function AutoScroll() { - this.options = { + this.defaults = { scroll: true, scrollSensitivity: 30, scrollSpeed: 10, @@ -47,7 +47,7 @@ function AutoScrollPlugin() { if (this.sortable.nativeDraggable) { on(document, 'dragover', this._handleAutoScroll); } else { - if (this.sortable.options.supportPointer) { + if (this.options.supportPointer) { on(document, 'pointermove', this._handleFallbackAutoScroll); } else if (originalEvent.touches) { on(document, 'touchmove', this._handleFallbackAutoScroll); @@ -59,7 +59,7 @@ function AutoScrollPlugin() { dragOverCompleted({ originalEvent }) { // For when bubbling is canceled and using fallback (fallback 'touchmove' always reached) - if (!this.sortable.options.dragOverBubble && !originalEvent.rootEl) { + if (!this.options.dragOverBubble && !originalEvent.rootEl) { this._handleAutoScroll(originalEvent); } }, @@ -107,7 +107,7 @@ function AutoScrollPlugin() { // MACOS Safari does not have autoscroll, // Firefox and Chrome are good if (fallback || Edge || IE11OrLess || Safari) { - autoScroll(evt, this.sortable.options, elem, fallback); + autoScroll(evt, this.options, elem, fallback); // Listener for pointer element change let ogElemScroller = getParentAutoScrollElement(elem, true); @@ -127,18 +127,18 @@ function AutoScrollPlugin() { ogElemScroller = newElem; clearAutoScrolls(); } - autoScroll(evt, this.sortable.options, newElem, fallback); + autoScroll(evt, this.options, newElem, fallback); }, 10); lastAutoScrollX = x; lastAutoScrollY = y; } } else { // if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll - if (!this.sortable.options.bubbleScroll || getParentAutoScrollElement(elem, true) === getWindowScrollingElement()) { + if (!this.options.bubbleScroll || getParentAutoScrollElement(elem, true) === getWindowScrollingElement()) { clearAutoScrolls(); return; } - autoScroll(evt, this.sortable.options, getParentAutoScrollElement(elem, false), false); + autoScroll(evt, this.options, getParentAutoScrollElement(elem, false), false); } } }; diff --git a/plugins/MultiDrag/MultiDrag.js b/plugins/MultiDrag/MultiDrag.js index 6d88b9895..988d9a537 100644 --- a/plugins/MultiDrag/MultiDrag.js +++ b/plugins/MultiDrag/MultiDrag.js @@ -45,7 +45,7 @@ function MultiDragPlugin() { on(document, 'keydown', this._checkKeyDown); on(document, 'keyup', this._checkKeyUp); - this.options = { + this.defaults = { selectedClass: 'sortable-selected', multiDragKey: null, setData(dataTransfer, dragEl) { @@ -75,7 +75,7 @@ function MultiDragPlugin() { this.isMultiDrag = ~multiDragElements.indexOf(dragEl); }, - setupClone({ sortable }) { + setupClone({ sortable, cancel }) { if (!this.isMultiDrag) return; for (let i = 0; i < multiDragElements.length; i++) { multiDragClones.push(clone(multiDragElements[i])); @@ -85,27 +85,27 @@ function MultiDragPlugin() { multiDragClones[i].draggable = false; multiDragClones[i].style['will-change'] = ''; - toggleClass(multiDragClones[i], sortable.options.selectedClass, false); - multiDragElements[i] === dragEl && toggleClass(multiDragClones[i], sortable.options.chosenClass, false); + toggleClass(multiDragClones[i], this.options.selectedClass, false); + multiDragElements[i] === dragEl && toggleClass(multiDragClones[i], this.options.chosenClass, false); } sortable._hideClone(); - return true; + cancel(); }, - clone({ sortable, rootEl, dispatchSortableEvent }) { + clone({ sortable, rootEl, dispatchSortableEvent, cancel }) { if (!this.isMultiDrag) return; - if (!sortable.options.removeCloneOnHide) { + if (!this.options.removeCloneOnHide) { if (multiDragElements.length && multiDragSortable === sortable) { insertMultiDragClones(true, rootEl); dispatchSortableEvent('clone'); - return true; + cancel(); } } }, - showClone({ cloneNowShown, rootEl }) { + showClone({ cloneNowShown, rootEl, cancel }) { if (!this.isMultiDrag) return; insertMultiDragClones(false, rootEl); multiDragClones.forEach(clone => { @@ -114,21 +114,21 @@ function MultiDragPlugin() { cloneNowShown(); clonesHidden = false; - return true; + cancel(); }, - hideClone({ sortable, cloneNowHidden }) { + hideClone({ sortable, cloneNowHidden, cancel }) { if (!this.isMultiDrag) return; multiDragClones.forEach(clone => { css(clone, 'display', 'none'); - if (sortable.options.removeCloneOnHide && clone.parentNode) { + if (this.options.removeCloneOnHide && clone.parentNode) { clone.parentNode.removeChild(clone); } }); cloneNowHidden(); clonesHidden = true; - return true; + cancel(); }, dragStartGlobal({ sortable }) { @@ -149,7 +149,7 @@ function MultiDragPlugin() { dragStarted({ sortable }) { if (!this.isMultiDrag) return; - if (sortable.options.sort) { + if (this.options.sort) { // Capture rects, // hide multi drag elements (by positioning them absolute), // set multi drag elements rects to dragRect, @@ -159,7 +159,7 @@ function MultiDragPlugin() { sortable.captureAnimationState(); - if (sortable.options.animation) { + if (this.options.animation) { multiDragElements.forEach(multiDragElement => { if (multiDragElement === dragEl) return; css(multiDragElement, 'position', 'absolute'); @@ -177,26 +177,27 @@ function MultiDragPlugin() { } } - sortable.animateAll(function() { + sortable.animateAll(() => { folding = false; initialFolding = false; - if (sortable.options.animation) { + if (this.options.animation) { multiDragElements.forEach(multiDragElement => { unsetRect(multiDragElement); }); } // Remove all auxiliary multidrag items from el, if sorting enabled - if (sortable.options.sort) { + if (this.options.sort) { removeMultiDragElements(); } }); }, - dragOver({ target, completed }) { + dragOver({ target, completed, cancel }) { if (folding && ~multiDragElements.indexOf(target)) { - return completed(false); + completed(false); + cancel(); } }, @@ -216,12 +217,12 @@ function MultiDragPlugin() { fromSortable.removeAnimationState(multiDragElement); }); folding = false; - insertMultiDragElements(!sortable.options.removeCloneOnHide, rootEl); + insertMultiDragElements(!this.options.removeCloneOnHide, rootEl); } }, dragOverCompleted({ sortable, isOwner, insertion, activeSortable, parentEl, putSortable }) { - let options = sortable.options; + let options = this.options; if (insertion) { // Clones must be hidden before folding animation to capture dragRectAbsolute properly if (isOwner) { @@ -301,7 +302,7 @@ function MultiDragPlugin() { if (!evt) return; - let options = sortable.options, + let options = this.options, children = parentEl.children; // Multi-drag selection @@ -344,7 +345,7 @@ function MultiDragPlugin() { multiDragElements.push(children[i]); dispatchEvent({ - sortable: sortable, + sortable, rootEl, name: 'select', targetEl: children[i], @@ -375,7 +376,7 @@ function MultiDragPlugin() { // Do not "unfold" after around dragEl if reverted if ((parentEl[expando].options.sort || parentEl !== rootEl) && multiDragElements.length > 1) { let dragRect = getRect(dragEl), - multiDragIndex = index(dragEl, ':not(.' + sortable.options.selectedClass + ')'); + multiDragIndex = index(dragEl, ':not(.' + this.options.selectedClass + ')'); if (!initialFolding && options.animation) dragEl.thisAnimationDuration = null; @@ -472,14 +473,14 @@ function MultiDragPlugin() { if (multiDragSortable !== this.sortable) return; // Only deselect if target is not item in this sortable - if (evt && closest(evt.target, this.sortable.options.draggable, this.sortable.el, false)) return; + if (evt && closest(evt.target, this.options.draggable, this.sortable.el, false)) return; // Only deselect if left click if (evt && evt.button !== 0) return; while (multiDragElements.length) { let el = multiDragElements[0]; - toggleClass(el, this.sortable.options.selectedClass, false); + toggleClass(el, this.options.selectedClass, false); multiDragElements.shift(); dispatchEvent({ sortable: this.sortable, @@ -492,13 +493,13 @@ function MultiDragPlugin() { }, _checkKeyDown(evt) { - if (evt.key === this.sortable.options.multiDragKey) { + if (evt.key === this.options.multiDragKey) { this.multiDragKeyDown = true; } }, _checkKeyUp(evt) { - if (evt.key === this.sortable.options.multiDragKey) { + if (evt.key === this.options.multiDragKey) { this.multiDragKeyDown = false; } } @@ -534,7 +535,7 @@ function MultiDragPlugin() { multiDragElements.splice(index, 1); } }, - eventOptions() { + eventProperties() { const oldIndicies = [], newIndicies = []; diff --git a/plugins/OnSpill/OnSpill.js b/plugins/OnSpill/OnSpill.js index 7c36af97e..ebd221936 100644 --- a/plugins/OnSpill/OnSpill.js +++ b/plugins/OnSpill/OnSpill.js @@ -33,7 +33,7 @@ Revert.prototype = { if (putSortable) { putSortable.captureAnimationState(); } - let nextSibling = getChild(this.sortable.el, this.startIndex, this.sortable.options); + let nextSibling = getChild(this.sortable.el, this.startIndex, this.options); if (nextSibling) { this.sortable.el.insertBefore(dragEl, nextSibling); diff --git a/plugins/README.md b/plugins/README.md index 0e81d4d00..c48255a40 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -8,6 +8,8 @@ Sortable plugins are plugins that can be directly mounted to the Sortable class. `el: HTMLElement` — The element that the sortable is being initialized on +`options: Object` — The options object that the user has passed into Sortable (not merged with defaults yet) + ## Static Properties The constructor function passed to `Sortable.mount` may contain several static properties and methods. The following static properties may be defined: @@ -16,10 +18,10 @@ The constructor function passed to `Sortable.mount` may contain several static p The name of the option that the user will use in their sortable's options to enable the plugin. Should start with a lower case and be camel-cased. For example: `'multiDrag'`. This is also the property name that the plugin's instance will be under in a sortable instance (ex. `sortableInstance.multiDrag`). `utils: Object` -Object containing functions that will be added to the `Sortable.utils` default object on the Sortable class. +Object containing functions that will be added to the `Sortable.utils` static object on the Sortable class. `eventOptions(eventName: String): Function` -A function that is called whenever Sortable fires an event. This function should return an object to be combined with the event object that Sortable will emit. The function will be called in the context of the Sortable that is firing the event (ie. the `this` keyword will be the Sortable calling the event). +A function that is called whenever Sortable fires an event. This function should return an object to be combined with the event object that Sortable will emit. The function will be called in the context of the instance of the plugin on the Sortable that is firing the event (ie. the `this` keyword will be the plugin instance). `initializeByDefault: Boolean` Determines whether or not the plugin will always be initialized on every new Sortable instance. If this option is enabled, it does not mean that by default the plugin will be enabled on the Sortable - this must still be done in the options via the plugin's `pluginName`, or it can be enabled by default if your plugin specifies it's pluginName as a default option that is truthy. Since the plugin will already be initialized on every Sortable instance, it can also be enabled dynamically via `sortableInstance.option('pluginName', true)`. @@ -51,11 +53,11 @@ Plugin.optionModifiers = { ``` ## Plugin Options -Plugins may have custom options or override the defaults of certain options. In order to do this, there must be an `options` object on the initialized plugin. This can be set in the plugin's prototype, or during the initialization of the plugin (when the `el` is available). For example: +Plugins may have custom default options or may override the defaults of other options. In order to do this, there must be a `defaults` object on the initialized plugin. This can be set in the plugin's prototype, or during the initialization of the plugin (when the `el` is available). For example: ```js -function myPlugin(el) { - this.options = { +function myPlugin(sortable, el, options) { + this.defaults = { color: el.style.backgroundColor }; } @@ -67,7 +69,7 @@ Sortable.mount(myPlugin); ## Plugin Events ### Context -The events will be fired in the context of their own parent object (ie. context is not changed), however the plugin instance's Sortable instance is available under `this.sortable`. +The events will be fired in the context of their own parent object (ie. context is not changed), however the plugin instance's Sortable instance is available under `this.sortable`. Likewise, the options are available under `this.options`. ### Event List The following table contains details on the events that a plugin may handle in the prototype of the plugin's constructor function. diff --git a/plugins/Swap/Swap.js b/plugins/Swap/Swap.js index c7a026dcf..3f0feb7dc 100644 --- a/plugins/Swap/Swap.js +++ b/plugins/Swap/Swap.js @@ -8,7 +8,7 @@ let lastSwapEl; function SwapPlugin() { function Swap() { - this.options = { + this.defaults = { swapClass: 'sortable-swap-highlight' }; } @@ -17,10 +17,10 @@ function SwapPlugin() { dragStart({ dragEl }) { lastSwapEl = dragEl; }, - dragOverValid({ completed, target, onMove, activeSortable, changed }) { + dragOverValid({ completed, target, onMove, activeSortable, changed, cancel }) { if (!activeSortable.options.swap) return; let el = this.sortable.el, - options = this.sortable.options; + options = this.options; if (target && target !== el) { let prevSwapEl = lastSwapEl; if (onMove(target) !== false) { @@ -36,11 +36,12 @@ function SwapPlugin() { } changed(); - return completed(true); + completed(true); + cancel(); }, drop({ activeSortable, putSortable, dragEl }) { let toSortable = (putSortable || this.sortable); - let options = this.sortable.options; + let options = this.options; lastSwapEl && toggleClass(lastSwapEl, options.swapClass, false); if (lastSwapEl && (options.swap || putSortable && putSortable.options.swap)) { if (dragEl !== lastSwapEl) { @@ -60,7 +61,7 @@ function SwapPlugin() { return Object.assign(Swap, { pluginName: 'swap', - eventOptions() { + eventProperties() { return { swapItem: lastSwapEl }; diff --git a/src/EventDispatcher.js b/src/EventDispatcher.js index db0361b27..e47cb5809 100644 --- a/src/EventDispatcher.js +++ b/src/EventDispatcher.js @@ -8,7 +8,7 @@ export default function dispatchEvent( targetEl, cloneEl, toEl, fromEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, - originalEvent, putSortable, eventOptions + originalEvent, putSortable, extraEventProperties } ) { sortable = (sortable || (rootEl && rootEl[expando])); @@ -42,9 +42,9 @@ export default function dispatchEvent( evt.originalEvent = originalEvent; evt.pullMode = putSortable ? putSortable.lastPutMode : undefined; - let allEventOptions = { ...eventOptions, ...PluginManager.getEventOptions(name, sortable) }; - for (let option in allEventOptions) { - evt[option] = allEventOptions[option]; + let allEventProperties = { ...extraEventProperties, ...PluginManager.getEventProperties(name, sortable) }; + for (let option in allEventProperties) { + evt[option] = allEventProperties[option]; } if (rootEl) { diff --git a/src/PluginManager.js b/src/PluginManager.js index 8d65904dc..623c26f62 100644 --- a/src/PluginManager.js +++ b/src/PluginManager.js @@ -16,6 +16,9 @@ export default { }, pluginEvent(eventName, sortable, evt) { this.eventCanceled = false; + evt.cancel = () => { + this.eventCanceled = true; + }; const eventNameGlobal = eventName + 'Global'; plugins.forEach(plugin => { if (!sortable[plugin.pluginName]) return; @@ -23,7 +26,7 @@ export default { if ( sortable[plugin.pluginName][eventNameGlobal] ) { - this.eventCanceled = !!sortable[plugin.pluginName][eventNameGlobal]({ sortable, ...evt }); + sortable[plugin.pluginName][eventNameGlobal]({ sortable, ...evt }); } // Only fire plugin event if plugin is enabled in this sortable, @@ -32,21 +35,22 @@ export default { sortable.options[plugin.pluginName] && sortable[plugin.pluginName][eventName] ) { - this.eventCanceled = this.eventCanceled || !!sortable[plugin.pluginName][eventName]({ sortable, ...evt }); + sortable[plugin.pluginName][eventName]({ sortable, ...evt }); } }); }, - initializePlugins(sortable, el, defaults) { + initializePlugins(sortable, el, defaults, options) { plugins.forEach(plugin => { const pluginName = plugin.pluginName; if (!sortable.options[pluginName] && !plugin.initializeByDefault) return; - let initialized = new plugin(sortable, el); + let initialized = new plugin(sortable, el, sortable.options); initialized.sortable = sortable; + initialized.options = sortable.options; sortable[pluginName] = initialized; // Add default options from plugin - Object.assign(defaults, initialized.options); + Object.assign(defaults, initialized.defaults); }); for (let option in sortable.options) { @@ -57,14 +61,14 @@ export default { } } }, - getEventOptions(name, sortable) { - let eventOptions = {}; + getEventProperties(name, sortable) { + let eventProperties = {}; plugins.forEach(plugin => { - if (typeof(plugin.eventOptions) !== 'function') return; - Object.assign(eventOptions, plugin.eventOptions.call(sortable, name)); + if (typeof(plugin.eventProperties) !== 'function') return; + Object.assign(eventProperties, plugin.eventProperties.call(sortable[plugin.pluginName], name)); }); - return eventOptions; + return eventProperties; }, modifyOption(sortable, name, value) { let modifiedValue;