From 429dc043abc681a7d9d309462a81c7ab71519fc4 Mon Sep 17 00:00:00 2001 From: ChiefORZ Date: Mon, 22 Jun 2015 17:04:13 +0200 Subject: [PATCH 1/4] fix click event for mobile devices and old browser added forcePolyfill option. forcePolyfill is made to make cross-browser testing more easy. forcePolyfill provides a reliable, consistent cross-browser Solution for Sortable. forcePolyfill gives us the possibility to change the way "dragged items" lok like. --- README.md | 19 +++++++++++++++++ Sortable.js | 59 +++++++++++++++++++++++++++++++++------------------- package.json | 2 +- 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index cc767bb..411cd53 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,13 @@ var sortable = new Sortable(el, { ghostClass: "sortable-ghost", // Class name for the drop placeholder dataIdAttr: 'data-id', + /* + ignore the HTML5 DnD behaviour and force the fallback to kick in + - provide a more reliable cross-browser solution + - grants the ability to display a different "dragged" element + */ + forcePolyfill: false, + scroll: true, // or HTMLElement scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling. scrollSpeed: 10, // px @@ -253,6 +260,18 @@ Sortable.create(list, { --- +#### `forcePolyfill` option +If set to `true`, the polyfill - or 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 polyfill always generates a copy of that DOM Element in the Document's Body. This behaviour can be exploited to give us more control over the look of this 'dragged' Element. + +Demo: http://jsbin.com/zejimolava/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 ba23977..76a5a70 100644 --- a/Sortable.js +++ b/Sortable.js @@ -176,7 +176,8 @@ dropBubble: false, dragoverBubble: false, dataIdAttr: 'data-id', - delay: 0 + delay: 0, + forcePolyfill: false }; @@ -323,8 +324,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 +344,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 +362,7 @@ this._onDragStart(tapEvt, 'touch'); } - else if (!supportDraggable) { + else if (!supportDraggable || this.options.forcePolyfill) { this._onDragStart(tapEvt, true); } else { @@ -422,6 +429,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, @@ -438,20 +451,8 @@ } }, - - _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; @@ -466,12 +467,28 @@ _css(ghostEl, 'position', 'fixed'); _css(ghostEl, 'zIndex', '100000'); - rootEl.appendChild(ghostEl); + document.body.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 +510,9 @@ } _on(document, 'drop', this); + setTimeout(this._dragStarted, 0); } - - setTimeout(this._dragStarted, 0); + }, _onDragOver: function (/**Event*/evt) { diff --git a/package.json b/package.json index 344c30b..f92bbad 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": "*", From 5d6747a9c7a5effb3b17ce661798f88c82bbd0ca Mon Sep 17 00:00:00 2001 From: ChiefORZ Date: Mon, 22 Jun 2015 19:57:15 +0200 Subject: [PATCH 2/4] react handleMove/onMove event modified the private _onMove prototype, so it calls events on it's source Element like it's sibling function _dispatchEvent added the handleMove, which listens to the onMove events to the react mixin --- Sortable.js | 20 +++++++++++--------- react-sortable-mixin.js | 5 +++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Sortable.js b/Sortable.js index 76a5a70..e48916a 100644 --- a/Sortable.js +++ b/Sortable.js @@ -1021,17 +1021,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/react-sortable-mixin.js b/react-sortable-mixin.js index c40c15f..0736c8f 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; From 2f06d97b3627b6350a1d91b943a29f9b8ff0659a Mon Sep 17 00:00:00 2001 From: ChiefORZ Date: Tue, 23 Jun 2015 00:19:39 +0200 Subject: [PATCH 3/4] missed some stuff wasn't merging all necessary stuff. now it should be fully functional --- Sortable.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Sortable.js b/Sortable.js index e48916a..be7d423 100644 --- a/Sortable.js +++ b/Sortable.js @@ -45,6 +45,8 @@ tapEvt, touchEvt, + moved, + /** @const */ RSPACE = /\s+/g, @@ -442,6 +444,8 @@ touchEvt = touch; + moved = true; + _css(ghostEl, 'webkitTransform', translate3d); _css(ghostEl, 'mozTransform', translate3d); _css(ghostEl, 'msTransform', translate3d); @@ -682,9 +686,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) { @@ -742,6 +747,8 @@ tapEvt = touchEvt = + moved = + lastEl = lastCSS = From d56e4d41dbe52168de5d8d2bff010a126b62af1c Mon Sep 17 00:00:00 2001 From: ChiefORZ Date: Thu, 25 Jun 2015 12:33:03 +0200 Subject: [PATCH 4/4] forceFallback changes removed the default behaviour to append the cloned Element to the body. now the cloned Element gets added in the same parent, with the addition of the class defined in options.fallbackClass. added the possibility to change the fallback class. added the possibility to decide wheter the fallback should be cloned into the same parent or to the document's body. --- README.md | 17 +++++++---------- Sortable.js | 11 ++++++++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 411cd53..2128b33 100644 --- a/README.md +++ b/README.md @@ -65,12 +65,9 @@ var sortable = new Sortable(el, { ghostClass: "sortable-ghost", // Class name for the drop placeholder dataIdAttr: 'data-id', - /* - ignore the HTML5 DnD behaviour and force the fallback to kick in - - provide a more reliable cross-browser solution - - grants the ability to display a different "dragged" element - */ - forcePolyfill: false, + 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. @@ -260,13 +257,13 @@ Sortable.create(list, { --- -#### `forcePolyfill` option -If set to `true`, the polyfill - or Fallback for non HTML5 Browser - will be used, even if we are using an HTML5 Browser. +#### `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 polyfill always generates a copy of that DOM Element in the Document's Body. This behaviour can be exploited to give us more control over the look of this 'dragged' Element. +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/zejimolava/edit?html,css,js,output +Demo: http://jsbin.com/xinuyenabi/edit?html,css,js,output --- diff --git a/Sortable.js b/Sortable.js index be7d423..fd5c1c0 100644 --- a/Sortable.js +++ b/Sortable.js @@ -179,7 +179,9 @@ dragoverBubble: false, dataIdAttr: 'data-id', delay: 0, - forcePolyfill: false + forceFallback: false, + fallbackClass: 'sortable-fallback', + fallbackOnBody: false }; @@ -364,7 +366,7 @@ this._onDragStart(tapEvt, 'touch'); } - else if (!supportDraggable || this.options.forcePolyfill) { + else if (!supportDraggable || this.options.forceFallback) { this._onDragStart(tapEvt, true); } else { @@ -463,6 +465,9 @@ 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); @@ -471,7 +476,7 @@ _css(ghostEl, 'position', 'fixed'); _css(ghostEl, 'zIndex', '100000'); - document.body.appendChild(ghostEl); + this.options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl); // Fixing dimensions. ghostRect = ghostEl.getBoundingClientRect();