提交 06ec8157 编写于 作者: O owen-m1

WIP

上级 854fed77
......@@ -6,6 +6,7 @@
"supernew": true,
"laxbreak": true,
"esversion": 9,
"-W138": true,
"white": true,
"globals": {
"define": true,
......
......@@ -365,6 +365,7 @@ for (var i = 0; i < nestedSortables.length; i++) {
<div style="padding: 0" class="col-12">
<pre class="prettyprint">new Sortable(multiDragDemo, {
multiDrag: true, // Enable multi-drag
stackGhost: true, // Stack the items under the cursor while dragging
selectedClass: 'selected', // The class applied to the selected items
animation: 150
});</pre>
......
......@@ -24,7 +24,8 @@ let multiDragElements = [],
dragStarted = false,
dragEl,
clonesFromRect,
clonesHidden;
clonesHidden,
cursorContainer; // Holds elements while creating drag image
function MultiDragPlugin() {
function MultiDrag(sortable) {
......@@ -58,7 +59,8 @@ function MultiDragPlugin() {
data = dragEl.textContent;
}
dataTransfer.setData('Text', data);
}
},
stackGhost: false
};
}
......@@ -131,7 +133,7 @@ function MultiDragPlugin() {
return true;
},
dragStartGlobal({ sortable }) {
dragStartGlobal({ originalEvent, setGhost }) {
if (!this.isMultiDrag && multiDragSortable) {
multiDragSortable.multiDrag._deselectMultiDrag();
}
......@@ -144,11 +146,41 @@ function MultiDragPlugin() {
multiDragElements = multiDragElements.sort(function(a, b) {
return a.sortableIndex - b.sortableIndex;
});
if (this.isMultiDrag && this.sortable.options.stackGhost && multiDragElements.length > 1) {
cursorContainer = document.createElement('div');
css(cursorContainer, 'position', 'relative');
css(cursorContainer, 'left', -window.innerWidth*2);
css(cursorContainer, 'margin', 0);
css(cursorContainer, 'padding', 0);
this.sortable.el.appendChild(cursorContainer);
let culX = 0, culY = 0;
for (let i in multiDragElements) {
const item = multiDragElements[i].cloneNode(true);
const { width, height } = getRect(multiDragElements[i]);
cursorContainer.appendChild(item);
css(item, 'position', 'absolute');
css(item, 'left', culX + 'px');
css(item, 'top', culY + 'px');
culX += 10;
culY += 10;
css(item, 'box-sizing', 'border-box');
css(item, 'width', width);
css(item, 'height', height);
}
setGhost(cursorContainer, false);
}
dragStarted = true;
},
dragStarted({ sortable }) {
dragStarted({ originalEvent, sortable }) {
if (!this.isMultiDrag) return;
if (cursorContainer && originalEvent.dataTransfer) {
cursorContainer.parentNode.removeChild(cursorContainer);
cursorContainer = null;
}
if (sortable.options.sort) {
// Capture rects,
// hide multi drag elements (by positioning them absolute),
......@@ -304,6 +336,12 @@ function MultiDragPlugin() {
let options = sortable.options,
children = parentEl.children;
// Remove custom ghost image
if (cursorContainer) {
cursorContainer.parentNode.removeChild(cursorContainer);
cursorContainer = null;
}
// Multi-drag selection
if (!dragStarted) {
if (options.multiDragKey && !this.multiDragKeyDown) {
......
......@@ -27,6 +27,7 @@ new Sortable(el, {
multiDrag: false, // Enable the plugin
selectedClass: "sortable-selected", // Class name for selected item
multiDragKey: null, // Key that must be down for items to be selected
stackGhost: false, // Stack the items under the cursor while dragging
// Called when an item is selected
onSelect: function(/**Event*/evt) {
......@@ -73,6 +74,13 @@ Sortable.create(list, {
---
#### `stackGhost` option
When set to `true`, the ghost image under the cursor will be the items that are being dragged stacked on top of eachother.
Defaults to `false`. This option may not work on some browsers if using native drag & drop.
---
### Event Properties
- items:`HTMLElement[]` — Array of selected items, or empty
- clones:`HTMLElement[]` — Array of clones, or empty
......
......@@ -72,24 +72,24 @@ The events will be fired in the context of their own parent object (ie. context
### Event List
The following table contains details on the events that a plugin may handle in the prototype of the plugin's constructor function.
| Event Name | Description | Cancelable? | Cancel Behaviour | Event Type | Custom Event Object Properties |
|---------------------------|------------------------------------------------------------------------------------------------------------------|-------------|----------------------------------------------------|------------|-------------------------------------------------------------------------|
| filter | Fired when the element is filtered, and dragging is therefore canceled | No | - | Normal | None |
| delayStart | Fired when the delay starts, even if there is no delay | Yes | Cancels sorting | Normal | None |
| delayEnded | Fired when the delay ends, even if there is no delay | Yes | Cancels sorting | Normal | None |
| setupClone | Fired when Sortable clones the dragged element | Yes | Cancels normal clone setup | Normal | None |
| dragStart | Fired when the dragging is first started | Yes | Cancels sorting | Normal | None |
| clone | Fired when the clone is inserted into the DOM (if `removeCloneOnHide: false`). Tick after dragStart. | Yes | Cancels normal clone insertion & hiding | Normal | None |
| dragStarted | Fired tick after dragStart | No | - | Normal | None |
| dragOver | Fired when the user drags over a sortable | Yes | Cancels normal dragover behaviour | DragOver | None |
| dragOverValid | Fired when the user drags over a sortable that the dragged item can be inserted into | Yes | Cancels normal valid dragover behaviour | DragOver | None |
| revert | Fired when the dragged item is reverted to it's original position when entering it's `sort:false` root | Yes | Cancels normal reverting, but is still completed() | DragOver | None |
| dragOverCompleted | Fired when dragOver is completed (ie. bubbling is disabled). To check if inserted, use `inserted` even property. | No | - | DragOver | `insertion: Boolean` — Whether or not the dragged element was inserted |
| dragOverAnimationCapture | Fired right before the animation state is captured in dragOver | No | - | DragOver | None |
| dragOverAnimationComplete | Fired after the animation is completed after a dragOver insertion | No | - | DragOver | None |
| drop | Fired on drop | Yes | Cancels normal drop behavior | Normal | None |
| nulling | Fired when the plugin should preform cleanups, once all drop events have fired | No | - | Normal | None |
| destroy | Fired when Sortable is destroyed | No | - | Normal | None |
| Event Name | Description | Cancelable? | Cancel Behaviour | Event Type | Custom Event Object Properties |
|---------------------------|------------------------------------------------------------------------------------------------------------------|-------------|----------------------------------------------------|----------------|-------------------------------------------------------------------------|
| filter | Fired when the element is filtered, and dragging is therefore canceled | No | - | Normal | None |
| delayStart | Fired when the delay starts, even if there is no delay | Yes | Cancels sorting | Normal | None |
| delayEnded | Fired when the delay ends, even if there is no delay | Yes | Cancels sorting | Normal | None |
| setupClone | Fired when Sortable clones the dragged element | Yes | Cancels normal clone setup | Normal | None |
| dragStart | Fired when the dragging is first started | Yes | Cancels sorting | DragStartEvent | None |
| clone | Fired when the clone is inserted into the DOM (if `removeCloneOnHide: false`). Tick after dragStart. | Yes | Cancels normal clone insertion & hiding | Normal | None |
| dragStarted | Fired tick after dragStart | No | - | Normal | None |
| dragOver | Fired when the user drags over a sortable | Yes | Cancels normal dragover behaviour | DragOverEvent | None |
| dragOverValid | Fired when the user drags over a sortable that the dragged item can be inserted into | Yes | Cancels normal valid dragover behaviour | DragOverEvent | None |
| revert | Fired when the dragged item is reverted to it's original position when entering it's `sort:false` root | Yes | Cancels normal reverting, but is still completed() | DragOverEvent | None |
| dragOverCompleted | Fired when dragOver is completed (ie. bubbling is disabled). To check if inserted, use `inserted` even property. | No | - | DragOverEvent | `insertion: Boolean` — Whether or not the dragged element was inserted |
| dragOverAnimationCapture | Fired right before the animation state is captured in dragOver | No | - | DragOverEvent | None |
| dragOverAnimationComplete | Fired after the animation is completed after a dragOver insertion | No | - | DragOverEvent | None |
| drop | Fired on drop | Yes | Cancels normal drop behavior | Normal | None |
| nulling | Fired when the plugin should preform cleanups, once all drop events have fired | No | - | Normal | None |
| destroy | Fired when Sortable is destroyed | No | - | Normal | None |
### Global Events
Normally, an event will only be fired in a plugin if the plugin is enabled on the Sortable from which the event is being fired. However, it sometimes may be desirable for a plugin to listen in on an event from Sortables in which it is not enabled on. This is possible with global events. For an event to be global, simply add the suffix 'Global' to the event's name (casing matters) (eg. `dragStartGlobal`).
......@@ -145,8 +145,16 @@ An object with the following properties is passed as an argument to each plugin
`dispatchSortableEvent(eventName: String)` — Function that can be used to emit an event on the current sortable while sorting, with all usual event properties set (eg. indexes, rootEl, cloneEl, originalEvent, etc.).
### DragStartEvent Object
This event is passed to the `dragStart` event, **and includes all methods & properties of the normal event object**.
### Methods:
`setGhost(image: HTMLElement, keepOffset: Boolean)` — Sets the image/element that appears under the cursor while dragging. If `keepOffset` is set to `false`, the top-left of the ghost will positioned at the mouse cursor, as opposed to the position on the dragged element where the user started the drag.
### DragOverEvent Object
This event is passed to dragover events, and extends the normal event object.
This event is passed to dragover events, **and includes all methods & properties of the normal event object**.
#### Properties:
......
......@@ -143,7 +143,7 @@ let dragEl,
CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',
// This will not pass for IE9, because IE9 DnD only works on anchors
supportDraggable = !ChromeForAndroid && ('draggable' in document.createElement('div')),
supportDraggable = !ChromeForAndroid && !IOS && ('draggable' in document.createElement('div')),
supportCssPointerEvents = (function() {
// false when <= IE11
......@@ -698,7 +698,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
}
},
_dragStarted: function (fallback, evt) {
_dragStarted: function (fallback, evt, ghostImg, keepGhostOffset) {
let _this = this;
awaitingDragStarted = false;
if (rootEl && dragEl) {
......@@ -715,7 +715,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
Sortable.active = this;
fallback && this._appendGhost();
fallback && this._appendGhost(ghostImg, !keepGhostOffset ? evt.clientX : null, !keepGhostOffset ? evt.clientY : null);
// Drag start event
_dispatchEvent({
......@@ -813,14 +813,27 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
}
},
_appendGhost: function () {
/**
* Append the ghost element
* @param {[HTMLElement]} image Optional element to be used as the ghost
* @param {[Number]} xPos X position of the ghost, defaults to position of dragged element
* @param {[Number]} yPos Y position of the ghost, defaults to position of dragged element
*/
_appendGhost: function (image, xPos, yPos) {
// Bug if using scale(): https://stackoverflow.com/questions/2637058
// Not being adjusted for
if (!ghostEl) {
let container = this.options.fallbackOnBody ? document.body : rootEl,
rect = getRect(dragEl, true, PositionGhostAbsolutely, true, container),
rect,
options = this.options;
if (!image || xPos == undefined || yPos == undefined) {
rect = getRect(dragEl, true, PositionGhostAbsolutely, true, container);
}
(xPos == undefined) && (xPos = rect.left);
(yPos == undefined) && (yPos = rect.top);
// Position absolutely
if (PositionGhostAbsolutely) {
// Get relatively positioned parent
......@@ -837,8 +850,8 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) {
if (ghostRelativeParent === document) ghostRelativeParent = getWindowScrollingElement();
rect.top += ghostRelativeParent.scrollTop;
rect.left += ghostRelativeParent.scrollLeft;
yPos += ghostRelativeParent.scrollTop;
xPos += ghostRelativeParent.scrollLeft;
} else {
ghostRelativeParent = getWindowScrollingElement();
}
......@@ -846,7 +859,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
}
ghostEl = dragEl.cloneNode(true);
ghostEl = image || dragEl.cloneNode(true);
toggleClass(ghostEl, options.ghostClass, false);
toggleClass(ghostEl, options.fallbackClass, true);
......@@ -855,13 +868,16 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
css(ghostEl, 'transition', '');
css(ghostEl, 'transform', '');
css(ghostEl, 'box-sizing', 'border-box');
css(ghostEl, 'margin', 0);
css(ghostEl, 'top', rect.top);
css(ghostEl, 'left', rect.left);
css(ghostEl, 'width', rect.width);
css(ghostEl, 'height', rect.height);
css(ghostEl, 'opacity', '0.8');
if (!image) {
css(ghostEl, 'box-sizing', 'border-box');
css(ghostEl, 'margin', 0);
css(ghostEl, 'width', rect.width);
css(ghostEl, 'height', rect.height);
css(ghostEl, 'opacity', '0.8');
}
css(ghostEl, 'top', yPos);
css(ghostEl, 'left', xPos);
css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed'));
css(ghostEl, 'zIndex', '100000');
css(ghostEl, 'pointerEvents', 'none');
......@@ -876,8 +892,24 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
let _this = this;
let dataTransfer = evt.dataTransfer;
let options = _this.options;
pluginEvent('dragStart', this, { evt });
let ghostImg, keepGhostOffset = true;
pluginEvent('dragStart', this, {
evt,
setGhost(img, keepOffset = true) {
const dragRect = getRect(dragEl);
if (_this.nativeDraggable) {
evt.dataTransfer &&
evt.dataTransfer.setDragImage &&
evt.dataTransfer.setDragImage(img,
keepOffset ? evt.clientX - dragRect.left : 0,
keepOffset ? evt.clientY - dragRect.top : 0
);
} else {
ghostImg = img;
keepGhostOffset = keepOffset;
}
}
});
if (Sortable.eventCanceled) {
this._onDrop();
return;
......@@ -938,8 +970,7 @@ Sortable.prototype = /** @lends Sortable.prototype */ {
}
awaitingDragStarted = true;
_this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt));
_this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt, ghostImg, keepGhostOffset));
on(document, 'selectstart', _this);
moved = true;
......
......@@ -15,7 +15,16 @@ var example1 = document.getElementById('example1'),
// Example 1 - Simple list
new Sortable(example1, {
animation: 150,
ghostClass: 'blue-background-class'
ghostClass: 'blue-background-class',
setData(dataTransfer, dragEl) {
let holder = document.createElement('div');
example1.appendChild(holder);
holder.appendChild(dragEl.cloneNode(true));
dataTransfer.setDragImage(holder, 10, 10);
setTimeout(() => {
example1.removeChild(holder);
}, 100);
}
});
......@@ -208,8 +217,9 @@ for (var i = 0; i < nestedSortables.length; i++) {
// MultiDrag demo
new Sortable(multiDragDemo, {
multiDrag: true,
selectedClass: 'selected',
multiDrag: true, // Enable multi-drag
stackGhost: true, // Stack the items under the cursor while dragging
selectedClass: 'selected', // The class applied to the selected items
animation: 150
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册