diff --git a/README.md b/README.md index c90775f..2f062b9 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ var sortable = new Sortable(el, { ghostClass: "sortable-ghost", // Class name for the drop placeholder dataIdAttr: 'data-id', + forceFallback: false, // ignore the HTML5 DnD behaviour and force the fallback to kick in + fallbackClass: "sortable-fallback" // Class name for the cloned DOM Element when using forceFallback + fallbackOnBody: false // Appends the cloned DOM Element into the Document's Body + scroll: true, // or HTMLElement scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling. scrollSpeed: 10, // px @@ -253,6 +257,18 @@ Sortable.create(list, { --- +#### `forceFallback` option +If set to `true`, the Fallback for non HTML5 Browser will be used, even if we are using an HTML5 Browser. +This gives us the possiblity to test the behaviour for older Browsers even in newer Browser, or make the Drag 'n Drop feel more consistent between Desktop , Mobile and old Browsers. + +On top of that, the Fallback always generates a copy of that DOM Element and appends the class `fallbackClass` definied in the options. This behaviour controls the look of this 'dragged' Element. + +Demo: http://jsbin.com/xinuyenabi/edit?html,css,js,output + + +--- + + #### `scroll` option If set to `true`, the page (or sortable-area) scrolls when coming to an edge. diff --git a/Sortable.js b/Sortable.js index c2fad29..a2477f6 100644 --- a/Sortable.js +++ b/Sortable.js @@ -45,6 +45,8 @@ tapEvt, touchEvt, + moved, + /** @const */ RSPACE = /\s+/g, @@ -176,7 +178,10 @@ dropBubble: false, dragoverBubble: false, dataIdAttr: 'data-id', - delay: 0 + delay: 0, + forceFallback: false, + fallbackClass: 'sortable-fallback', + fallbackOnBody: false }; @@ -323,8 +328,12 @@ _on(ownerDocument, 'touchcancel', _this._onDrop); if (options.delay) { - // If the user moves the pointer before the delay has been reached: + // If the user moves the pointer or let go the click or touch + // before the delay has been reached: // disable the delayed drag + _on(ownerDocument, 'mouseup', _this._disableDelayedDrag); + _on(ownerDocument, 'touchend', _this._disableDelayedDrag); + _on(ownerDocument, 'touchcancel', _this._disableDelayedDrag); _on(ownerDocument, 'mousemove', _this._disableDelayedDrag); _on(ownerDocument, 'touchmove', _this._disableDelayedDrag); @@ -339,7 +348,9 @@ var ownerDocument = this.el.ownerDocument; clearTimeout(this._dragStartTimer); - + _off(ownerDocument, 'mouseup', this._disableDelayedDrag); + _off(ownerDocument, 'touchend', this._disableDelayedDrag); + _off(ownerDocument, 'touchcancel', this._disableDelayedDrag); _off(ownerDocument, 'mousemove', this._disableDelayedDrag); _off(ownerDocument, 'touchmove', this._disableDelayedDrag); }, @@ -355,7 +366,7 @@ this._onDragStart(tapEvt, 'touch'); } - else if (!supportDraggable) { + else if (!supportDraggable || this.options.forceFallback) { this._onDragStart(tapEvt, true); } else { @@ -422,6 +433,12 @@ _onTouchMove: function (/**TouchEvent*/evt) { if (tapEvt) { + // only set the status to dragging, when we are actually dragging + if(!Sortable.active) { + this._dragStarted(); + } + // as well as creating the ghost element on the document body + this._appendGhost(); var touch = evt.touches ? evt.touches[0] : evt, dx = touch.clientX - tapEvt.clientX, dy = touch.clientY - tapEvt.clientY, @@ -429,6 +446,8 @@ touchEvt = touch; + moved = true; + _css(ghostEl, 'webkitTransform', translate3d); _css(ghostEl, 'mozTransform', translate3d); _css(ghostEl, 'msTransform', translate3d); @@ -438,26 +457,17 @@ } }, - - _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { - var dataTransfer = evt.dataTransfer, - options = this.options; - - this._offUpEvents(); - - if (activeGroup.pull == 'clone') { - cloneEl = dragEl.cloneNode(true); - _css(cloneEl, 'display', 'none'); - rootEl.insertBefore(cloneEl, dragEl); - } - - if (useFallback) { + _appendGhost: function() { + if(!ghostEl) { var rect = dragEl.getBoundingClientRect(), css = _css(dragEl), ghostRect; ghostEl = dragEl.cloneNode(true); + _toggleClass(ghostEl, this.options.ghostClass, false); + _toggleClass(ghostEl, this.options.fallbackClass, true); + _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10)); _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10)); _css(ghostEl, 'width', rect.width); @@ -466,12 +476,28 @@ _css(ghostEl, 'position', 'fixed'); _css(ghostEl, 'zIndex', '100000'); - rootEl.appendChild(ghostEl); + this.options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl); // Fixing dimensions. ghostRect = ghostEl.getBoundingClientRect(); _css(ghostEl, 'width', rect.width * 2 - ghostRect.width); _css(ghostEl, 'height', rect.height * 2 - ghostRect.height); + } + }, + + _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { + var dataTransfer = evt.dataTransfer, + options = this.options; + + this._offUpEvents(); + + if (activeGroup.pull == 'clone') { + cloneEl = dragEl.cloneNode(true); + _css(cloneEl, 'display', 'none'); + rootEl.insertBefore(cloneEl, dragEl); + } + + if (useFallback) { if (useFallback === 'touch') { // Bind touch events @@ -493,9 +519,9 @@ } _on(document, 'drop', this); + setTimeout(this._dragStarted, 0); } - - setTimeout(this._dragStarted, 0); + }, _onDragOver: function (/**Event*/evt) { @@ -665,9 +691,10 @@ this._offUpEvents(); if (evt) { - evt.preventDefault(); - !options.dropBubble && evt.stopPropagation(); - + if(moved) { + evt.preventDefault(); + !options.dropBubble && evt.stopPropagation(); + } ghostEl && ghostEl.parentNode.removeChild(ghostEl); if (dragEl) { @@ -725,6 +752,8 @@ tapEvt = touchEvt = + moved = + lastEl = lastCSS = @@ -996,17 +1025,19 @@ onMoveFn = sortable.options.onMove, retVal; - if (onMoveFn) { - evt = document.createEvent('Event'); - evt.initEvent('move', true, true); + evt = document.createEvent('Event'); + evt.initEvent('move', true, true); - evt.to = toEl; - evt.from = fromEl; - evt.dragged = dragEl; - evt.draggedRect = dragRect; - evt.related = targetEl || toEl; - evt.relatedRect = targetRect || toEl.getBoundingClientRect(); + evt.to = toEl; + evt.from = fromEl; + evt.dragged = dragEl; + evt.draggedRect = dragRect; + evt.related = targetEl || toEl; + evt.relatedRect = targetRect || toEl.getBoundingClientRect(); + fromEl.dispatchEvent(evt); + + if (onMoveFn) { retVal = onMoveFn.call(sortable, evt); } diff --git a/package.json b/package.json index 2a1c170..a03ce9b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sortablejs", "exportName": "Sortable", - "version": "1.2.1", + "version": "1.2.2", "devDependencies": { "grunt": "*", "grunt-version": "*", diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 1076d59..47ab533 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -34,7 +34,8 @@ onUpdate: 'handleUpdate', onRemove: 'handleRemove', onSort: 'handleSort', - onFilter: 'handleFilter' + onFilter: 'handleFilter', + onMove: 'handleMove' }; @@ -90,7 +91,7 @@ // Bind callbacks so that "this" refers to the component - 'onStart onEnd onAdd onSort onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { + 'onStart onEnd onAdd onSort onUpdate onRemove onFilter onMove'.split(' ').forEach(function (/** string */name) { copyOptions[name] = function (evt) { if (name === 'onStart') { _nextSibling = evt.item.nextElementSibling;