From 7ebf8ae093ce9c2689424efc44c8c3f6c0068a66 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 14 Jan 2015 15:04:23 +0300 Subject: [PATCH 01/27] #217: + inited commit --- react-sortable-mixin.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 react-sortable-mixin.js diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js new file mode 100644 index 0000000..4f46968 --- /dev/null +++ b/react-sortable-mixin.js @@ -0,0 +1,36 @@ +/** + * @author RubaXa + * @licence MIT + */ + +(function (factory) { + 'use strict'; + + if (typeof define === 'function' && define.amd) { + define(['sortable'], factory); + } + else if (typeof module != 'undefined' && typeof module.exports != 'undefined') { + module.exports = factory(require('Sortable')); + } + else { + /* jshint sub:true */ + window['SortableMixin'] = factory(Sortable); + } +})(function (/** Sortable */Sortable) { + 'use strict'; + + + /** + * Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to + * make reorderable drag-and-drop lists on modern browsers and touch devices. + * + * @mixin + */ + var SortableMixin = { + sortableMixinVersion: '0.0.0' + }; + + + // Export + return SortableMixin; +}); From dc2b05d8f2c8b59d7229a2353afcc57cb1007298 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 14 Jan 2015 23:29:46 +0300 Subject: [PATCH 02/27] #217: + base react mixin without 'linked lists' --- README.md | 34 ++++++++++++++- index.html | 12 +++++- package.json | 6 ++- react-sortable-mixin.js | 94 ++++++++++++++++++++++++++++++++++++++++- st/app.js | 74 ++++++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c109fef..94fc435 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Demo: http://rubaxa.github.io/Sortable/ * Supports drag handles *and selectable text* (better than voidberg's html5sortable) * Smart auto-scrolling * Built using native HTML5 drag and drop API - * Supports [Meteor](meteor/README.md) and [AngularJS](#ng) + * Supports [Meteor](meteor/README.md), [AngularJS](#ng) and [React](#react) * Supports any CSS library, e.g. [Bootstrap](#bs) * Simple API * No jQuery (but there is [support](#jq)) @@ -282,6 +282,38 @@ angular.module('myApp', ['ng-sortable']) --- + +### Support React +Include [react-sortable-mixin.js](react-sortable-mixin.js). +See more [here](react-sortable-mixin.js#L37). + + +```jsx +var SortableList = React.createClass({ + mixins: [SortableMixin], + + getInitialState: function() { + return { + items: ['Mixin', 'Sortable'] + }; + }, + + render: function() { + return
    { + this.state.items.map(function (text) { + return
  • {text}
  • + }) + }
+ } +}); + +React.render(, document.body); +``` + + +--- + + ### Method diff --git a/index.html b/index.html index 15f14c2..c9119f2 100644 --- a/index.html +++ b/index.html @@ -7,8 +7,8 @@ Sortable. No jQuery. - - + + @@ -210,6 +210,14 @@ + + +
+
+
+
+ +
diff --git a/package.json b/package.json index cade1db..da4c293 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,11 @@ "keywords": [ "sortable", "reorder", - "drag" + "drag", + "meteor", + "angular", + "react", + "mixin" ], "author": "Konstantin Lebedev ", "license": "MIT" diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 4f46968..5415a13 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -27,7 +27,99 @@ * @mixin */ var SortableMixin = { - sortableMixinVersion: '0.0.0' + sortableMixinVersion: '0.0.0', + + /** + * @type {Sortable} + * @private + */ + _sortableInstance: null, + + + /** + * Sortable options + * @returns {object} + */ + getDefaultProps: function () { + return { + sortable: { + ref: 'list', + model: 'items', + + animation: 100, + onStart: 'handleStart', + onEnd: 'handleEnd', + onAdd: 'handleAdd', + onUpdate: 'handleUpdate', + onRemove: 'handleRemove', + onSort: 'handleSort', + onFilter: 'handleFilter' + } + }; + }, + + + componentDidMount: function () { + var nextSibling, + sortableProps = this.props.sortable, + sortableOptions = {}, + + callMethod = function (/** string */type, /** Event */evt) { + var method = this[sortableProps[type]]; + method && method.call(this, evt, this._sortableInstance); + }.bind(this); + + + // Pass through unrecognized options + for (var key in sortableProps) { + sortableOptions[key] = sortableProps[key]; + } + + + // Bind callbacks so that "this" refers to the component + 'onEnd onAdd onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { + if (sortableProps[name]) { + sortableOptions[name] = callMethod.bind(this, name); + } + }.bind(this)); + + + sortableOptions.onStart = function (/** Event */evt) { + nextSibling = evt.item.nextSibling; + callMethod('onStart', evt); + }.bind(this); + + + sortableOptions.onSort = function (/** Event */evt) { + evt.from.insertBefore(evt.item, nextSibling || null); + + var modelName = sortableProps.model, + newState = {}, + items = this.state[modelName]; + + if (items) { + items = items.slice(); // clone + items.splice(evt.newIndex, 0, items.splice(evt.oldIndex, 1)[0]); + + newState[modelName] = items; + this.setState(newState); + } + + callMethod('onSort', evt); + }.bind(this); + + + /** @namespace this.refs — http://facebook.github.io/react/docs/more-about-refs.html */ + if (!sortableProps.ref || this.refs[sortableProps.ref]) { + this._sortableInstance = Sortable.create((this.refs[sortableProps.ref] || this).getDOMNode(), sortableOptions); + } + }, + + + componentWillUnmount: function () { + this._sortableInstance.destroy(); + this._sortableInstance = null; + } }; diff --git a/st/app.js b/st/app.js index 109c70b..f6e121d 100644 --- a/st/app.js +++ b/st/app.js @@ -1,5 +1,41 @@ (function () { + 'use strict'; + var byId = function (id) { return document.getElementById(id); }, + + loadScripts = function (desc, callback) { + var deps = [], key, idx = 0; + + for (key in desc) { + deps.push(key); + } + + (function _next() { + var pid, + name = deps[idx], + script = document.createElement('script'); + + script.type = 'text/javascript'; + script.src = desc[deps[idx]]; + + pid = setInterval(function () { + if (window[name]) { + clearTimeout(pid); + + deps[idx++] = window[name]; + + if (deps[idx]) { + _next(); + } else { + callback.apply(null, deps); + } + } + }, 30); + + document.getElementsByTagName('head')[0].appendChild(script); + })() + }, + console = window.console; @@ -159,9 +195,47 @@ $scope.sortableConfig['on' + name] = console.log.bind(console, name); }); }]); + + + + // React + loadScripts({ + 'React': '//fb.me/react-0.12.2.js', + 'SortableMixin': 'react-sortable-mixin.js' + }, function (React, SortableMixin) { + var SortableList = React.createClass({ + mixins: [SortableMixin], + + getInitialState: function() { + return { + items: [ + 'Mixin', + 'Sortable' + ] + }; + }, + + render: function() { + return React.DOM.div(null, + React.DOM.h4({ children: 'React mixin', className: 'layer title title_xl', style: { marginBottom: 0 } }), + React.DOM.div({ style: { width: '30%', marginLeft: '10px', cursor: 'move' }, className: 'block__list_words' }, + React.DOM.ul({ + ref: 'list', + children: this.state.items.map(function (v) { + return React.DOM.li(null, v); + }) + }) + ) + ); + } + }); + + React.render(React.createElement(SortableList, {}), byId('react-box')); + }); })(); + // Background document.addEventListener("DOMContentLoaded", function () { function setNoiseBackground(el, width, height, opacity) { From 5325c8a84224f231578f092ca56382480652cc91 Mon Sep 17 00:00:00 2001 From: Markus Ast Date: Tue, 27 Jan 2015 16:41:11 +0100 Subject: [PATCH 03/27] fix index calculation to skip templates --- Sortable.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sortable.js b/Sortable.js index 60cd0bd..153cf1b 100644 --- a/Sortable.js +++ b/Sortable.js @@ -949,8 +949,10 @@ */ function _index(/**HTMLElement*/el) { var index = 0; - while (el && (el = el.previousElementSibling) && (el.nodeName.toUpperCase() !== 'TEMPLATE')) { - index++; + while (el && (el = el.previousElementSibling)) { + if (el.nodeName.toUpperCase() !== 'TEMPLATE') { + index++; + } } return index; } From f500b679f22b5aadf794a522751d5bb13ba881a0 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 27 Jan 2015 23:47:16 +0300 Subject: [PATCH 04/27] #238: * disabled --- Sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sortable.js b/Sortable.js index 60cd0bd..6395215 100644 --- a/Sortable.js +++ b/Sortable.js @@ -469,7 +469,7 @@ !options.dragoverBubble && evt.stopPropagation(); } - if (!_silent && activeGroup && + if (!_silent && activeGroup && !options.disabled && (isOwner ? canSort || (revert = !rootEl.contains(dragEl)) : activeGroup.pull && groupPut && ( From 705b3ed1aef454a897c433468e046d1ddd9f0cfb Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 27 Jan 2015 23:47:48 +0300 Subject: [PATCH 05/27] #238: * scope after event --- ng-sortable.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ng-sortable.js b/ng-sortable.js index 8d310c4..ecbf23b 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -15,7 +15,7 @@ 'use strict'; angular.module('ng-sortable', []) - .constant('$version', '0.3.4') + .constant('$version', '0.3.5') .directive('ngSortable', ['$parse', function ($parse) { var removed, nextSibling; @@ -101,13 +101,16 @@ onStart: function (/**Event*/evt) { nextSibling = evt.item.nextSibling; options.onStart(source.items()); + scope.$apply(); }, onEnd: function () { options.onEnd(source.items()); + scope.$apply(); }, onAdd: function (/**Event*/evt) { _sync(evt); options.onAdd(source.items(), removed); + scope.$apply(); }, onUpdate: function (/**Event*/evt) { _sync(evt); From 778ee0b202f7d2396f945420896b312c01219725 Mon Sep 17 00:00:00 2001 From: "E.T.Cook" Date: Wed, 28 Jan 2015 00:06:55 -0600 Subject: [PATCH 06/27] Deep copy object to avoid unwanted referencing In relation to https://github.com/RubaXa/Sortable/issues/237 --- ng-sortable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ng-sortable.js b/ng-sortable.js index 8d310c4..3a2162f 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -77,6 +77,7 @@ if (evt.clone) { evt.from.removeChild(evt.clone); + removed = angular.copy(removed); } else { prevItems.splice(oldIndex, 1); From 2843550ac8c57c306edfe9ff6856727cf1a324e3 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 28 Jan 2015 12:58:53 +0300 Subject: [PATCH 07/27] - comments --- .jshintrc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.jshintrc b/.jshintrc index e870dcc..3f67a09 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,9 +1,9 @@ { "strict": true, - "newcap": false, // "Tolerate uncapitalized constructors" + "newcap": false, "node": true, - "expr": true, // - true && call() "Expected an assignment or function call and instead saw an expression." - "supernew": true, // - "Missing '()' invoking a constructor." + "expr": true, + "supernew": true, "laxbreak": true, "white": true, "globals": { From c9051c24e18269ad13ce5ba4ddcf5ac7406f4e0a Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 2 Feb 2015 14:20:04 +0300 Subject: [PATCH 08/27] + support IE9 --- Sortable.js | 37 ++++++++++++++++++++++--------------- Sortable.min.js | 2 +- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Sortable.js b/Sortable.js index 3176f3a..be35fe7 100644 --- a/Sortable.js +++ b/Sortable.js @@ -48,7 +48,9 @@ win = window, document = win.document, parseInt = win.parseInt, - supportIEdnd = !!document.createElement('div').dragDrop, + + supportDraggable = !!('draggable' in document.createElement('div')), + _silent = false, @@ -154,7 +156,6 @@ // Bind events _on(el, 'mousedown', this._onTapStart); _on(el, 'touchstart', this._onTapStart); - supportIEdnd && _on(el, 'selectstart', this._onTapStart); _on(el, 'dragover', this._onDragOver); _on(el, 'dragenter', this._onDragOver); @@ -229,9 +230,6 @@ // Prepare `dragstart` if (target && !dragEl && (target.parentNode === el)) { - // IE 9 Support - (type === 'selectstart') && target.dragDrop(); - tapEvt = evt; rootEl = this.el; @@ -254,7 +252,7 @@ clientY: touch.clientY }; - this._onDragStart(tapEvt, true); + this._onDragStart(tapEvt, 'touch'); evt.preventDefault(); } @@ -267,6 +265,10 @@ _on(document, 'dragover', this); + if (!supportDraggable) { + _on(document, 'mousemove', this); + this._onDragStart(tapEvt, true); + } try { if (document.selection) { @@ -316,10 +318,10 @@ _onTouchMove: function (/**TouchEvent*/evt) { if (tapEvt) { - var touch = evt.touches[0], + var touch = evt.touches ? evt.touches[0] : evt, dx = touch.clientX - tapEvt.clientX, dy = touch.clientY - tapEvt.clientY, - translate3d = 'translate3d(' + dx + 'px,' + dy + 'px,0)'; + translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; touchEvt = touch; @@ -334,7 +336,7 @@ }, - _onDragStart: function (/**Event*/evt, /**boolean*/isTouch) { + _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { var dataTransfer = evt.dataTransfer, options = this.options; @@ -346,7 +348,7 @@ rootEl.insertBefore(cloneEl, dragEl); } - if (isTouch) { + if (useFallback) { var rect = dragEl.getBoundingClientRect(), css = _css(dragEl), ghostRect; @@ -368,10 +370,16 @@ _css(ghostEl, 'width', rect.width * 2 - ghostRect.width); _css(ghostEl, 'height', rect.height * 2 - ghostRect.height); - // Bind touch events - _on(document, 'touchmove', this._onTouchMove); - _on(document, 'touchend', this._onDrop); - _on(document, 'touchcancel', this._onDrop); + if (useFallback === 'touch') { + // Bind touch events + _on(document, 'touchmove', this._onTouchMove); + _on(document, 'touchend', this._onDrop); + _on(document, 'touchcancel', this._onDrop); + } else { + // Old brwoser + _on(document, 'mousemove', this._onTouchMove); + _on(document, 'mouseup', this._onDrop); + } this._loopId = setInterval(this._emulateDragOver, 150); } @@ -774,7 +782,6 @@ _off(el, 'mousedown', this._onTapStart); _off(el, 'touchstart', this._onTapStart); - _off(el, 'selectstart', this._onTapStart); _off(el, 'dragover', this._onDragOver); _off(el, 'dragenter', this._onDragOver); diff --git a/Sortable.min.js b/Sortable.min.js index 56d114b..05f8468 100644 --- a/Sortable.min.js +++ b/Sortable.min.js @@ -1,2 +1,2 @@ /*! Sortable 1.0.1 - MIT | git://github.com/rubaxa/Sortable.git */ -!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),L.forEach(function(d){b[d]=c(this,b[d]||M),f(a,d.substr(2).toLowerCase(),b[d])},this),a[E]=g.name+" "+(g.put.join?g.put.join(" "):"");for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),I&&f(a,"selectstart",this._onTapStart),f(a,"dragover",this._onDragOver),f(a,"dragenter",this._onDragOver),P.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=O.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(O.call(arguments)))}}function d(a,b,c){if(a){c=c||G,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return G.defaultView&&G.defaultView.getComputedStyle?c=G.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){J=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling)&&"TEMPLATE"!==a.nodeName.toUpperCase();)b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D={},E="Sortable"+(new Date).getTime(),F=window,G=F.document,H=F.parseInt,I=!!G.createElement("div").dragDrop,J=!1,K=function(a,b,c,d,e,f){var g=G.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},L="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),M=function(){},N=Math.abs,O=[].slice,P=[];return a.prototype={constructor:a,_dragStarted:function(){h(q,this.options.ghostClass,!0),a.active=this,K(t,"start",q,t,y)},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)){if(h.handle&&(e=d(e,h.handle,i)),e=d(e,h.draggable,i),y=o(e),"function"==typeof l){if(l.call(this,a,e,this))return K(g,"filter",e,i,y),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(K(a,"filter",e,i,y),!0):void 0})))return void a.preventDefault();if(e&&!q&&e.parentNode===i){"selectstart"===b&&e.dragDrop(),B=a,t=this.el,q=e,v=q.nextSibling,A=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(B={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(B,!0),a.preventDefault()),f(G,"mouseup",this._onDrop),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),f(G,"dragover",this);try{G.selection?G.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(C){i(r,"display","none");var a=G.elementFromPoint(C.clientX,C.clientY),b=a,c=this.options.group.name,d=P.length;if(b)do{if((" "+b[E]+" ").indexOf(c)>-1){for(;d--;)P[d]({clientX:C.clientX,clientY:C.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(B){var b=a.touches[0],c=b.clientX-B.clientX,d=b.clientY-B.clientY,e="translate3d("+c+"px,"+d+"px,0)";C=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),this._onDrag(b),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==A.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-H(h.marginTop,10)),i(r,"left",g.left-H(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),f(G,"touchmove",this._onTouchMove),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(G,"drop",this);if(u=d.scroll,u===!0){u=t;do if(u.offsetWidth=i-g)-(e>=g),l=(e>=j-h)-(e>=h);k||l?b=F:u&&(b=u,c=u.getBoundingClientRect(),k=(N(c.right-g)<=e)-(N(c.left-g)<=e),l=(N(c.bottom-h)<=e)-(N(c.top-h)<=e)),(D.vx!==k||D.vy!==l||D.el!==b)&&(D.el=b,D.vx=k,D.vy=l,clearInterval(D.pid),b&&(D.pid=setInterval(function(){b===F?F.scrollTo(F.scrollX+k*f,F.scrollY+l*f):(l&&(b.scrollTop+=l*f),k&&(b.scrollLeft+=k*f))},24)))}},30),_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=A===j,o=h.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),!J&&A&&(n?o||(f=!t.contains(q)):A.pull&&k&&(A.name===j.name||k.indexOf&&~k.indexOf(A.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||v?t.insertBefore(q,s||v):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;u=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(u,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[E]){w!==c&&(w=c,x=i(c));var p,u=c.getBoundingClientRect(),y=u.right-u.left,z=u.bottom-u.top,B=/left|right|inline/.test(x.cssFloat+x.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,F=(B?(a.clientX-u.left)/y:(a.clientY-u.top)/z)>.5,G=c.nextElementSibling;J=!0,setTimeout(l,30),b(n),p=B?c.previousElementSibling===q&&!C||F&&C:G!==q&&!D||F&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(u,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),b.animated=!1},c)}},_offUpEvents:function(){g(G,"mouseup",this._onDrop),g(G,"touchmove",this._onTouchMove),g(G,"touchend",this._onDrop),g(G,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(D.pid),g(G,"drop",this),g(G,"dragover",this),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(z=o(q),K(q.parentNode,"sort",q,t,y,z),K(t,"sort",q,t,y,z),K(q,"add",q,t,y,z),K(t,"remove",q,t,y,z)):(s&&s.parentNode.removeChild(s),q.nextSibling!==v&&(z=o(q),K(t,"update",q,t,y,z),K(t,"sort",q,t,y,z))),a.active&&K(t,"end",q,t,y,z)),t=q=r=v=s=B=C=w=x=A=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b?(this._onDrag(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length;f>e;e++)a=c[e],d(a,this.options.draggable,this.el)&&b.push(a.getAttribute("data-id")||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;L.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"selectstart",this._onTapStart),g(a,"dragover",this._onDragOver),g(a,"dragenter",this._onDragOver),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),P.splice(P.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:K,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file +!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),L.forEach(function(d){b[d]=c(this,b[d]||M),f(a,d.substr(2).toLowerCase(),b[d])},this),a[E]=g.name+" "+(g.put.join?g.put.join(" "):"");for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this._onDragOver),f(a,"dragenter",this._onDragOver),P.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=O.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(O.call(arguments)))}}function d(a,b,c){if(a){c=c||G,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return G.defaultView&&G.defaultView.getComputedStyle?c=G.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){J=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D={},E="Sortable"+(new Date).getTime(),F=window,G=F.document,H=F.parseInt,I=!!("draggable"in G.createElement("div")),J=!1,K=function(a,b,c,d,e,f){var g=G.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},L="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),M=function(){},N=Math.abs,O=[].slice,P=[];return a.prototype={constructor:a,_dragStarted:function(){h(q,this.options.ghostClass,!0),a.active=this,K(t,"start",q,t,y)},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)){if(h.handle&&(e=d(e,h.handle,i)),e=d(e,h.draggable,i),y=o(e),"function"==typeof l){if(l.call(this,a,e,this))return K(g,"filter",e,i,y),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(K(a,"filter",e,i,y),!0):void 0})))return void a.preventDefault();if(e&&!q&&e.parentNode===i){B=a,t=this.el,q=e,v=q.nextSibling,A=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(B={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(B,"touch"),a.preventDefault()),f(G,"mouseup",this._onDrop),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),f(G,"dragover",this),I||(f(G,"mousemove",this),this._onDragStart(B,!0));try{G.selection?G.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(C){i(r,"display","none");var a=G.elementFromPoint(C.clientX,C.clientY),b=a,c=this.options.group.name,d=P.length;if(b)do{if((" "+b[E]+" ").indexOf(c)>-1){for(;d--;)P[d]({clientX:C.clientX,clientY:C.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(B){var b=a.touches?a.touches[0]:a,c=b.clientX-B.clientX,d=b.clientY-B.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";C=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),this._onDrag(b),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==A.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-H(h.marginTop,10)),i(r,"left",g.left-H(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),"touch"===b?(f(G,"touchmove",this._onTouchMove),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop)):(f(G,"mousemove",this._onTouchMove),f(G,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(G,"drop",this);if(u=d.scroll,u===!0){u=t;do if(u.offsetWidth=i-g)-(e>=g),l=(e>=j-h)-(e>=h);k||l?b=F:u&&(b=u,c=u.getBoundingClientRect(),k=(N(c.right-g)<=e)-(N(c.left-g)<=e),l=(N(c.bottom-h)<=e)-(N(c.top-h)<=e)),(D.vx!==k||D.vy!==l||D.el!==b)&&(D.el=b,D.vx=k,D.vy=l,clearInterval(D.pid),b&&(D.pid=setInterval(function(){b===F?F.scrollTo(F.scrollX+k*f,F.scrollY+l*f):(l&&(b.scrollTop+=l*f),k&&(b.scrollLeft+=k*f))},24)))}},30),_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=A===j,o=h.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),!J&&A&&!h.disabled&&(n?o||(f=!t.contains(q)):A.pull&&k&&(A.name===j.name||k.indexOf&&~k.indexOf(A.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||v?t.insertBefore(q,s||v):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;u=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(u,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[E]){w!==c&&(w=c,x=i(c));var p,u=c.getBoundingClientRect(),y=u.right-u.left,z=u.bottom-u.top,B=/left|right|inline/.test(x.cssFloat+x.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,F=(B?(a.clientX-u.left)/y:(a.clientY-u.top)/z)>.5,G=c.nextElementSibling;J=!0,setTimeout(l,30),b(n),p=B?c.previousElementSibling===q&&!C||F&&C:G!==q&&!D||F&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(u,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),b.animated=!1},c)}},_offUpEvents:function(){g(G,"mouseup",this._onDrop),g(G,"touchmove",this._onTouchMove),g(G,"touchend",this._onDrop),g(G,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(D.pid),g(G,"drop",this),g(G,"dragover",this),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(z=o(q),K(q.parentNode,"sort",q,t,y,z),K(t,"sort",q,t,y,z),K(q,"add",q,t,y,z),K(t,"remove",q,t,y,z)):(s&&s.parentNode.removeChild(s),q.nextSibling!==v&&(z=o(q),K(t,"update",q,t,y,z),K(t,"sort",q,t,y,z))),a.active&&K(t,"end",q,t,y,z)),t=q=r=v=s=B=C=w=x=A=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b?(this._onDrag(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length;f>e;e++)a=c[e],d(a,this.options.draggable,this.el)&&b.push(a.getAttribute("data-id")||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;L.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this._onDragOver),g(a,"dragenter",this._onDragOver),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),P.splice(P.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:K,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file From c71b88f0756a3cafe62eec4a93ddf82b8798d120 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Fri, 6 Feb 2015 11:38:35 +0300 Subject: [PATCH 09/27] #251: + 'mousemove' unbind --- Sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sortable.js b/Sortable.js index be35fe7..baee3e5 100644 --- a/Sortable.js +++ b/Sortable.js @@ -266,7 +266,6 @@ _on(document, 'dragover', this); if (!supportDraggable) { - _on(document, 'mousemove', this); this._onDragStart(tapEvt, true); } @@ -604,6 +603,7 @@ // Unbind events _off(document, 'drop', this); _off(document, 'dragover', this); + _off(document, 'mousemove', this._onTouchMove); _off(el, 'dragstart', this._onDragStart); From 7bff4352d60cc1991f245777bfa858e663a8faaa Mon Sep 17 00:00:00 2001 From: RubaXa Date: Fri, 6 Feb 2015 12:24:01 +0300 Subject: [PATCH 10/27] #250: + additional check --- Sortable.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sortable.js b/Sortable.js index 3176f3a..bcc6b13 100644 --- a/Sortable.js +++ b/Sortable.js @@ -171,13 +171,15 @@ _dragStarted: function () { - // Apply effect - _toggleClass(dragEl, this.options.ghostClass, true); + if (rootEl && dragEl) { + // Apply effect + _toggleClass(dragEl, this.options.ghostClass, true); - Sortable.active = this; + Sortable.active = this; - // Drag start event - _dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex); + // Drag start event + _dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex); + } }, From 7bad7a3c1e7a2ee4612109928ecf7f6505d294aa Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sat, 7 Feb 2015 12:12:52 +0300 Subject: [PATCH 11/27] =?UTF-8?q?#317:=20+=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B4=D0=B0=D1=82=D1=83=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 8 --- react-sortable-mixin.js | 153 ++++++++++++++++++++++------------------ st/app.js | 37 ---------- 3 files changed, 85 insertions(+), 113 deletions(-) diff --git a/index.html b/index.html index 6b7a46d..3e83346 100644 --- a/index.html +++ b/index.html @@ -210,14 +210,6 @@
- - -
-
-
-
- -
diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 5415a13..72a215d 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -19,6 +19,46 @@ })(function (/** Sortable */Sortable) { 'use strict'; + var _nextSibling; + + var _activeComponent; + + + var _defaultOptions = { + ref: 'list', + model: 'items', + + animation: 100, + onStart: 'handleStart', + onEnd: 'handleEnd', + onAdd: 'handleAdd', + onUpdate: 'handleUpdate', + onRemove: 'handleRemove', + onSort: 'handleSort', + onFilter: 'handleFilter' + }; + + + function _getModelName(component) { + return component.sortableOptions && component.sortableOptions.model || _defaultOptions.model; + } + + + function _getModelItems(component) { + return component.state[_getModelName(component)].slice(); + } + + + function _extend(dst, src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + + return dst; + } + /** * Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to @@ -27,7 +67,8 @@ * @mixin */ var SortableMixin = { - sortableMixinVersion: '0.0.0', + sortableMixinVersion: '0.1.0', + /** * @type {Sortable} @@ -36,83 +77,59 @@ _sortableInstance: null, - /** - * Sortable options - * @returns {object} - */ - getDefaultProps: function () { - return { - sortable: { - ref: 'list', - model: 'items', - - animation: 100, - onStart: 'handleStart', - onEnd: 'handleEnd', - onAdd: 'handleAdd', - onUpdate: 'handleUpdate', - onRemove: 'handleRemove', - onSort: 'handleSort', - onFilter: 'handleFilter' - } - }; - }, - - componentDidMount: function () { - var nextSibling, - sortableProps = this.props.sortable, - sortableOptions = {}, + var options = _extend(_extend({}, _defaultOptions), this.sortableOptions || {}), + copyOptions = _extend({}, options), - callMethod = function (/** string */type, /** Event */evt) { - var method = this[sortableProps[type]]; + emitEvent = function (/** string */type, /** Event */evt) { + var method = this[options[type]]; method && method.call(this, evt, this._sortableInstance); }.bind(this); - // Pass through unrecognized options - for (var key in sortableProps) { - sortableOptions[key] = sortableProps[key]; - } - - // Bind callbacks so that "this" refers to the component - 'onEnd onAdd onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { - if (sortableProps[name]) { - sortableOptions[name] = callMethod.bind(this, name); - } - }.bind(this)); - - - sortableOptions.onStart = function (/** Event */evt) { - nextSibling = evt.item.nextSibling; - callMethod('onStart', evt); - }.bind(this); - - - sortableOptions.onSort = function (/** Event */evt) { - evt.from.insertBefore(evt.item, nextSibling || null); - - var modelName = sortableProps.model, - newState = {}, - items = this.state[modelName]; - - if (items) { - items = items.slice(); // clone - items.splice(evt.newIndex, 0, items.splice(evt.oldIndex, 1)[0]); - - newState[modelName] = items; - this.setState(newState); - } - - callMethod('onSort', evt); - }.bind(this); + 'onStart onEnd onAdd onSort onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { + copyOptions[name] = function (evt) { + if (name === 'onStart') { + _nextSibling = evt.item.nextElementSibling; + _activeComponent = this; + } + else if (name === 'onAdd' || name === 'onUpdate') { + evt.from.insertBefore(evt.item, _nextSibling); + + var newState = {}, + remoteState = {}, + oldIndex = evt.oldIndex, + newIndex = evt.newIndex, + items = _getModelItems(this), + remoteItems, + item; + + if (name === 'onAdd') { + remoteItems = _getModelItems(_activeComponent); + item = remoteItems.splice(oldIndex, 1)[0]; + items.splice(newIndex, 0, item); + + remoteState[_getModelName(_activeComponent)] = remoteItems; + } + else { + items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]); + } + + newState[_getModelName(this)] = items; + this.setState(newState); + (this !== _activeComponent) && _activeComponent.setState(remoteState); + } + + setTimeout(function () { + emitEvent(name, evt); + }, 0); + }.bind(this); + }, this); /** @namespace this.refs — http://facebook.github.io/react/docs/more-about-refs.html */ - if (!sortableProps.ref || this.refs[sortableProps.ref]) { - this._sortableInstance = Sortable.create((this.refs[sortableProps.ref] || this).getDOMNode(), sortableOptions); - } + this._sortableInstance = Sortable.create((this.refs[options.ref] || this).getDOMNode(), copyOptions); }, diff --git a/st/app.js b/st/app.js index f6e121d..fe50fe9 100644 --- a/st/app.js +++ b/st/app.js @@ -195,43 +195,6 @@ $scope.sortableConfig['on' + name] = console.log.bind(console, name); }); }]); - - - - // React - loadScripts({ - 'React': '//fb.me/react-0.12.2.js', - 'SortableMixin': 'react-sortable-mixin.js' - }, function (React, SortableMixin) { - var SortableList = React.createClass({ - mixins: [SortableMixin], - - getInitialState: function() { - return { - items: [ - 'Mixin', - 'Sortable' - ] - }; - }, - - render: function() { - return React.DOM.div(null, - React.DOM.h4({ children: 'React mixin', className: 'layer title title_xl', style: { marginBottom: 0 } }), - React.DOM.div({ style: { width: '30%', marginLeft: '10px', cursor: 'move' }, className: 'block__list_words' }, - React.DOM.ul({ - ref: 'list', - children: this.state.items.map(function (v) { - return React.DOM.li(null, v); - }) - }) - ) - ); - } - }); - - React.render(React.createElement(SortableList, {}), byId('react-box')); - }); })(); From accec9b001c459901a47de8fe035c0699bf21258 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sat, 7 Feb 2015 12:12:52 +0300 Subject: [PATCH 12/27] #217: + linked lists --- index.html | 8 --- react-sortable-mixin.js | 153 ++++++++++++++++++++++------------------ st/app.js | 37 ---------- 3 files changed, 85 insertions(+), 113 deletions(-) diff --git a/index.html b/index.html index 6b7a46d..3e83346 100644 --- a/index.html +++ b/index.html @@ -210,14 +210,6 @@
- - -
-
-
-
- -
diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 5415a13..72a215d 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -19,6 +19,46 @@ })(function (/** Sortable */Sortable) { 'use strict'; + var _nextSibling; + + var _activeComponent; + + + var _defaultOptions = { + ref: 'list', + model: 'items', + + animation: 100, + onStart: 'handleStart', + onEnd: 'handleEnd', + onAdd: 'handleAdd', + onUpdate: 'handleUpdate', + onRemove: 'handleRemove', + onSort: 'handleSort', + onFilter: 'handleFilter' + }; + + + function _getModelName(component) { + return component.sortableOptions && component.sortableOptions.model || _defaultOptions.model; + } + + + function _getModelItems(component) { + return component.state[_getModelName(component)].slice(); + } + + + function _extend(dst, src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + + return dst; + } + /** * Simple and easy mixin-wrapper for rubaxa/Sortable library, in order to @@ -27,7 +67,8 @@ * @mixin */ var SortableMixin = { - sortableMixinVersion: '0.0.0', + sortableMixinVersion: '0.1.0', + /** * @type {Sortable} @@ -36,83 +77,59 @@ _sortableInstance: null, - /** - * Sortable options - * @returns {object} - */ - getDefaultProps: function () { - return { - sortable: { - ref: 'list', - model: 'items', - - animation: 100, - onStart: 'handleStart', - onEnd: 'handleEnd', - onAdd: 'handleAdd', - onUpdate: 'handleUpdate', - onRemove: 'handleRemove', - onSort: 'handleSort', - onFilter: 'handleFilter' - } - }; - }, - - componentDidMount: function () { - var nextSibling, - sortableProps = this.props.sortable, - sortableOptions = {}, + var options = _extend(_extend({}, _defaultOptions), this.sortableOptions || {}), + copyOptions = _extend({}, options), - callMethod = function (/** string */type, /** Event */evt) { - var method = this[sortableProps[type]]; + emitEvent = function (/** string */type, /** Event */evt) { + var method = this[options[type]]; method && method.call(this, evt, this._sortableInstance); }.bind(this); - // Pass through unrecognized options - for (var key in sortableProps) { - sortableOptions[key] = sortableProps[key]; - } - - // Bind callbacks so that "this" refers to the component - 'onEnd onAdd onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { - if (sortableProps[name]) { - sortableOptions[name] = callMethod.bind(this, name); - } - }.bind(this)); - - - sortableOptions.onStart = function (/** Event */evt) { - nextSibling = evt.item.nextSibling; - callMethod('onStart', evt); - }.bind(this); - - - sortableOptions.onSort = function (/** Event */evt) { - evt.from.insertBefore(evt.item, nextSibling || null); - - var modelName = sortableProps.model, - newState = {}, - items = this.state[modelName]; - - if (items) { - items = items.slice(); // clone - items.splice(evt.newIndex, 0, items.splice(evt.oldIndex, 1)[0]); - - newState[modelName] = items; - this.setState(newState); - } - - callMethod('onSort', evt); - }.bind(this); + 'onStart onEnd onAdd onSort onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) { + copyOptions[name] = function (evt) { + if (name === 'onStart') { + _nextSibling = evt.item.nextElementSibling; + _activeComponent = this; + } + else if (name === 'onAdd' || name === 'onUpdate') { + evt.from.insertBefore(evt.item, _nextSibling); + + var newState = {}, + remoteState = {}, + oldIndex = evt.oldIndex, + newIndex = evt.newIndex, + items = _getModelItems(this), + remoteItems, + item; + + if (name === 'onAdd') { + remoteItems = _getModelItems(_activeComponent); + item = remoteItems.splice(oldIndex, 1)[0]; + items.splice(newIndex, 0, item); + + remoteState[_getModelName(_activeComponent)] = remoteItems; + } + else { + items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]); + } + + newState[_getModelName(this)] = items; + this.setState(newState); + (this !== _activeComponent) && _activeComponent.setState(remoteState); + } + + setTimeout(function () { + emitEvent(name, evt); + }, 0); + }.bind(this); + }, this); /** @namespace this.refs — http://facebook.github.io/react/docs/more-about-refs.html */ - if (!sortableProps.ref || this.refs[sortableProps.ref]) { - this._sortableInstance = Sortable.create((this.refs[sortableProps.ref] || this).getDOMNode(), sortableOptions); - } + this._sortableInstance = Sortable.create((this.refs[options.ref] || this).getDOMNode(), copyOptions); }, diff --git a/st/app.js b/st/app.js index f6e121d..fe50fe9 100644 --- a/st/app.js +++ b/st/app.js @@ -195,43 +195,6 @@ $scope.sortableConfig['on' + name] = console.log.bind(console, name); }); }]); - - - - // React - loadScripts({ - 'React': '//fb.me/react-0.12.2.js', - 'SortableMixin': 'react-sortable-mixin.js' - }, function (React, SortableMixin) { - var SortableList = React.createClass({ - mixins: [SortableMixin], - - getInitialState: function() { - return { - items: [ - 'Mixin', - 'Sortable' - ] - }; - }, - - render: function() { - return React.DOM.div(null, - React.DOM.h4({ children: 'React mixin', className: 'layer title title_xl', style: { marginBottom: 0 } }), - React.DOM.div({ style: { width: '30%', marginLeft: '10px', cursor: 'move' }, className: 'block__list_words' }, - React.DOM.ul({ - ref: 'list', - children: this.state.items.map(function (v) { - return React.DOM.li(null, v); - }) - }) - ) - ); - } - }); - - React.render(React.createElement(SortableList, {}), byId('react-box')); - }); })(); From 5551a9ba3e4c8f77c2c724b03dab54cceb96a1b5 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sat, 7 Feb 2015 13:02:31 +0300 Subject: [PATCH 13/27] #217: + react examples --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++- react-sortable-mixin.js | 1 - 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d6417f..4bbe34f 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,7 @@ angular.module('myApp', ['ng-sortable']) ### Support React Include [react-sortable-mixin.js](react-sortable-mixin.js). -See more [here](react-sortable-mixin.js#L37). +See [more options](react-sortable-mixin.js#L26). ```jsx @@ -298,6 +298,8 @@ var SortableList = React.createClass({ }; }, + onSort: function (/** Event */evt) { /*..*/ }, + render: function() { return
    { this.state.items.map(function (text) { @@ -308,6 +310,58 @@ var SortableList = React.createClass({ }); React.render(, document.body); + + +// +// Groups +// +var AllUsers = React.createClass({ + mixins: [SortableMixin], + + sortableOptions: { + ref: "user", + group: "shared", + model: "users" + }, + + getInitialState: function() { + return { users: ['Abbi', 'Adela', 'Bud', 'Cate', 'Davis', 'Eric']; }; + }, + + render: function() { + return ( +

    Users

    +
      { + this.state.users.map(function (text) { + return
    • {text}
    • + }) + }
    + ); + } +}); + +var ApprovedUsers = React.createClass({ + mixins: [SortableMixin], + sortableOptions: { group: "shared" }, + + getInitialState: function() { + return { items: ['Hal', 'Judy']; }; + }, + + render: function() { + return
      { + this.state.items.map(function (text) { + return
    • {text}
    • + }) + }
    + } +}); + +React.render(
    + +
    + +
    , document.body); ``` diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 72a215d..fb99379 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -23,7 +23,6 @@ var _activeComponent; - var _defaultOptions = { ref: 'list', model: 'items', From fbf4f8b65feed6d6d81da28d6ee15ece93dd1ac2 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sat, 7 Feb 2015 15:52:24 +0300 Subject: [PATCH 14/27] #217: onSort -> handleSort --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4bbe34f..de56363 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,7 @@ var SortableList = React.createClass({ }; }, - onSort: function (/** Event */evt) { /*..*/ }, + handleSort: function (/** Event */evt) { /*..*/ }, render: function() { return
      { From 15d6f07a01f299bc1aa2f4240ae6713414e797ad Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sat, 7 Feb 2015 23:06:01 +0300 Subject: [PATCH 15/27] #254: + check 'effectAllowed' on 'dragover' --- Sortable.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sortable.js b/Sortable.js index bcc6b13..be83ad3 100644 --- a/Sortable.js +++ b/Sortable.js @@ -466,6 +466,10 @@ isOwner = (activeGroup === group), canSort = options.sort; + if (evt.dataTransfer && evt.dataTransfer.effectAllowed !== 'move') { + return; + } + if (evt.preventDefault !== void 0) { evt.preventDefault(); !options.dragoverBubble && evt.stopPropagation(); From bfddd35dd4244f32f70433f9594f205abe31e2b7 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 9 Feb 2015 16:48:41 +0300 Subject: [PATCH 16/27] + choice between 'state' or 'props' --- react-sortable-mixin.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index fb99379..6f25acc 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -44,7 +44,10 @@ function _getModelItems(component) { - return component.state[_getModelName(component)].slice(); + var name = _getModelName(component), + items = component.state && component.state[name] || component.props[name]; + + return items.slice(); } From 21bf07a93fe0ec9a997a6d81b117ce64198e2837 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 9 Feb 2015 17:16:40 +0300 Subject: [PATCH 17/27] #256: * fixed auto-scrolling --- Sortable.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/Sortable.js b/Sortable.js index bcc6b13..4a0e8fa 100644 --- a/Sortable.js +++ b/Sortable.js @@ -156,8 +156,8 @@ _on(el, 'touchstart', this._onTapStart); supportIEdnd && _on(el, 'selectstart', this._onTapStart); - _on(el, 'dragover', this._onDragOver); - _on(el, 'dragenter', this._onDragOver); + _on(el, 'dragover', this); + _on(el, 'dragenter', this); touchDragOverListeners.push(this._onDragOver); @@ -267,9 +267,6 @@ _on(dragEl, 'dragend', this); _on(rootEl, 'dragstart', this._onDragStart); - _on(document, 'dragover', this); - - try { if (document.selection) { document.selection.empty(); @@ -597,8 +594,6 @@ // Unbind events _off(document, 'drop', this); - _off(document, 'dragover', this); - _off(el, 'dragstart', this._onDragStart); this._offUpEvents(); @@ -671,8 +666,9 @@ handleEvent: function (/**Event*/evt) { var type = evt.type; - if (type === 'dragover') { + if (type === 'dragover' || type === 'dragenter') { this._onDrag(evt); + this._onDragOver(evt); _globalDragOver(evt); } else if (type === 'drop' || type === 'dragend') { @@ -778,8 +774,8 @@ _off(el, 'touchstart', this._onTapStart); _off(el, 'selectstart', this._onTapStart); - _off(el, 'dragover', this._onDragOver); - _off(el, 'dragenter', this._onDragOver); + _off(el, 'dragover', this); + _off(el, 'dragenter', this); //remove draggable attributes Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { From 85292d9558472686321ff12d973cdb5c65f823ff Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 9 Feb 2015 18:10:20 +0300 Subject: [PATCH 18/27] #236: + ngSortEvent --- README.md | 8 +++++++- ng-sortable.js | 44 +++++++++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d548c23..132d385 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,13 @@ angular.module('myApp', ['ng-sortable']) $scope.items = ['item 1', 'item 2']; $scope.foo = ['foo 1', '..']; $scope.bar = ['bar 1', '..']; - $scope.barConfig = { group: 'foobar', animation: 150 }; + $scope.barConfig = { + group: 'foobar', + animation: 150, + onSort: function (/** ngSortEvent */evt){ + // @see https://github.com/RubaXa/Sortable/blob/master/ng-sortable.js#L18-L24 + } + }; }]); ``` diff --git a/ng-sortable.js b/ng-sortable.js index d4fe47f..ec4aba6 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -14,6 +14,16 @@ })(function (angular, Sortable) { 'use strict'; + + /** + * @typedef {Object} ngSortEvent + * @property {*} model List item + * @property {Object|Array} models List of items + * @property {number} oldIndex before sort + * @property {number} newIndex after sort + */ + + angular.module('ng-sortable', []) .constant('$version', '0.3.5') .directive('ngSortable', ['$parse', function ($parse) { @@ -58,12 +68,20 @@ ; - 'Start End Add Update Remove Sort'.split(' ').forEach(function (name) { - options['on' + name] = options['on' + name] || function () {}; - }); + function _emitEvent(/**Event*/evt, /*Mixed*/item) { + var name = 'on' + evt.type.charAt(0).toUpperCase() + evt.type.substr(1); + + /* jshint expr:true */ + options[name] && options[name]({ + model: item, + models: source.items(), + oldIndex: evt.oldIndex, + newIndex: evt.newIndex + }); + } - function _sync(evt) { + function _sync(/**Event*/evt) { var oldIndex = evt.oldIndex, newIndex = evt.newIndex, items = source.items(); @@ -101,27 +119,27 @@ }, { onStart: function (/**Event*/evt) { nextSibling = evt.item.nextSibling; - options.onStart(source.items()); + _emitEvent(evt); scope.$apply(); }, - onEnd: function () { - options.onEnd(source.items()); + onEnd: function (/**Event*/evt) { + _emitEvent(evt, removed); scope.$apply(); }, onAdd: function (/**Event*/evt) { _sync(evt); - options.onAdd(source.items(), removed); + _emitEvent(evt, removed); scope.$apply(); }, onUpdate: function (/**Event*/evt) { _sync(evt); - options.onUpdate(source.items(), source.item(evt.item)); + _emitEvent(evt, source.item(evt.item)); }, - onRemove: function () { - options.onRemove(source.items(), removed); + onRemove: function (/**Event*/evt) { + _emitEvent(evt, removed); }, - onSort: function () { - options.onSort(source.items()); + onSort: function (/**Event*/evt) { + _emitEvent(evt, source.item(evt.item)); } })); From 55ae4457ca7bd8de31b0a98d527f8a0cb3204212 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 9 Feb 2015 18:27:02 +0300 Subject: [PATCH 19/27] #267: + support without ng-repeat --- ng-sortable.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ng-sortable.js b/ng-sortable.js index ec4aba6..bef396c 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -39,6 +39,11 @@ ); })[0]; + if (!ngRepeat) { + // Without ng-repeat + return null; + } + // tests: http://jsbin.com/kosubutilo/1/edit?js,output ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*(?:\(.*?,\s*)?([^\s)]+)[\s)]+in\s+([^\s|]+)/); @@ -74,7 +79,7 @@ /* jshint expr:true */ options[name] && options[name]({ model: item, - models: source.items(), + models: source && source.items(), oldIndex: evt.oldIndex, newIndex: evt.newIndex }); @@ -82,6 +87,11 @@ function _sync(/**Event*/evt) { + if (!source) { + // Without ng-repeat + return; + } + var oldIndex = evt.oldIndex, newIndex = evt.newIndex, items = source.items(); @@ -133,13 +143,13 @@ }, onUpdate: function (/**Event*/evt) { _sync(evt); - _emitEvent(evt, source.item(evt.item)); + _emitEvent(evt, source && source.item(evt.item)); }, onRemove: function (/**Event*/evt) { _emitEvent(evt, removed); }, onSort: function (/**Event*/evt) { - _emitEvent(evt, source.item(evt.item)); + _emitEvent(evt, source && source.item(evt.item)); } })); From 64ec81ab6d6032afa52da9f74f82aa8fef3bbce1 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 10 Feb 2015 22:35:41 +0300 Subject: [PATCH 20/27] + IE9 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2056c7..b532cdc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Demo: http://rubaxa.github.io/Sortable/ ## Features - * Supports touch devices and [modern](http://caniuse.com/#search=drag) browsers + * Supports touch devices and [modern](http://caniuse.com/#search=drag) browsers (including IE9) * Can drag from one list to another or within the same list * CSS animation when moving items * Supports drag handles *and selectable text* (better than voidberg's html5sortable) From ab52c138509968f1c85dd2c3d5205a85e54ef797 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 10 Feb 2015 23:20:37 +0300 Subject: [PATCH 21/27] #271: * logic of auto-scrolling --- Sortable.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Sortable.js b/Sortable.js index 19c504a..ac34a5b 100644 --- a/Sortable.js +++ b/Sortable.js @@ -424,20 +424,28 @@ winWidth = window.innerWidth, winHeight = window.innerHeight, - vx = (winWidth - x <= sens) - (x <= sens), - vy = (winHeight - y <= sens) - (y <= sens) + vx, + vy ; - if (vx || vy) { - el = win; - } - else if (scrollEl) { + + if (scrollEl) { el = scrollEl; rect = scrollEl.getBoundingClientRect(); vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); } + + if (!(vx || vy)) { + vx = (winWidth - x <= sens) - (x <= sens); + vy = (winHeight - y <= sens) - (y <= sens); + + /* jshint expr:true */ + (vx || vy) && (el = win); + } + + if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { autoScroll.el = el; autoScroll.vx = vx; From e97970b303af821d517ecc42a5f97c8a5fd77e28 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Feb 2015 18:12:28 +0300 Subject: [PATCH 22/27] #256: uber-autoscroll, support drag between lists --- Sortable.js | 172 +++++++++++++++++++++++++++------------------------- 1 file changed, 90 insertions(+), 82 deletions(-) diff --git a/Sortable.js b/Sortable.js index b50378b..050e742 100644 --- a/Sortable.js +++ b/Sortable.js @@ -28,9 +28,11 @@ ghostEl, cloneEl, rootEl, - scrollEl, nextEl, + scrollEl, + scrollParentEl, + lastEl, lastCSS, @@ -76,7 +78,82 @@ abs = Math.abs, slice = [].slice, - touchDragOverListeners = [] + touchDragOverListeners = [], + + _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) { + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 + if (rootEl && options.scroll) { + var el, + rect, + sens = options.scrollSensitivity, + speed = options.scrollSpeed, + + x = evt.clientX, + y = evt.clientY, + + winWidth = window.innerWidth, + winHeight = window.innerHeight, + + vx, + vy + ; + + // Delect scrollEl + if (scrollParentEl !== rootEl) { + scrollEl = options.scroll; + scrollParentEl = rootEl; + + if (scrollEl === true) { + scrollEl = rootEl; + + do { + if ((scrollEl.offsetWidth < scrollEl.scrollWidth) || + (scrollEl.offsetHeight < scrollEl.scrollHeight) + ) { + break; + } + /* jshint boss:true */ + } while (scrollEl = scrollEl.parentNode); + } + } + + if (scrollEl) { + el = scrollEl; + rect = scrollEl.getBoundingClientRect(); + vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); + vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); + } + + + if (!(vx || vy)) { + vx = (winWidth - x <= sens) - (x <= sens); + vy = (winHeight - y <= sens) - (y <= sens); + + /* jshint expr:true */ + (vx || vy) && (el = win); + } + + + if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { + autoScroll.el = el; + autoScroll.vx = vx; + autoScroll.vy = vy; + + clearInterval(autoScroll.pid); + + if (el) { + autoScroll.pid = setInterval(function () { + if (el === win) { + win.scrollTo(win.scrollX + vx * speed, win.scrollY + vy * speed); + } else { + vy && (el.scrollTop += vy * speed); + vx && (el.scrollLeft += vx * speed); + } + }, 24); + } + } + } + }, 30) ; @@ -141,8 +218,9 @@ }, this); - // Export group name - el[expando] = group.name + ' ' + (group.put.join ? group.put.join(' ') : ''); + // Export options + options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' '; + el[expando] = options; // Bind all private methods @@ -286,12 +364,12 @@ var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY), parent = target, - groupName = this.options.group.name, + groupName = ' ' + this.options.group.name + '', i = touchDragOverListeners.length; if (parent) { do { - if ((' ' + parent[expando] + ' ').indexOf(groupName) > -1) { + if (parent[expando] && parent[expando].groups.indexOf(groupName) > -1) { while (i--) { touchDragOverListeners[i]({ clientX: touchEvt.clientX, @@ -391,83 +469,9 @@ _on(document, 'drop', this); } - scrollEl = options.scroll; - - if (scrollEl === true) { - scrollEl = rootEl; - - do { - if ((scrollEl.offsetWidth < scrollEl.scrollWidth) || - (scrollEl.offsetHeight < scrollEl.scrollHeight) - ) { - break; - } - /* jshint boss:true */ - } while (scrollEl = scrollEl.parentNode); - } - setTimeout(this._dragStarted, 0); }, - _onDrag: _throttle(function (/**Event*/evt) { - // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 - if (rootEl && this.options.scroll) { - var el, - rect, - options = this.options, - sens = options.scrollSensitivity, - speed = options.scrollSpeed, - - x = evt.clientX, - y = evt.clientY, - - winWidth = window.innerWidth, - winHeight = window.innerHeight, - - vx, - vy - ; - - - if (scrollEl) { - el = scrollEl; - rect = scrollEl.getBoundingClientRect(); - vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); - vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); - } - - - if (!(vx || vy)) { - vx = (winWidth - x <= sens) - (x <= sens); - vy = (winHeight - y <= sens) - (y <= sens); - - /* jshint expr:true */ - (vx || vy) && (el = win); - } - - - if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { - autoScroll.el = el; - autoScroll.vx = vx; - autoScroll.vy = vy; - - clearInterval(autoScroll.pid); - - if (el) { - autoScroll.pid = setInterval(function () { - if (el === win) { - win.scrollTo(win.scrollX + vx * speed, win.scrollY + vy * speed); - } else { - vy && (el.scrollTop += vy * speed); - vx && (el.scrollLeft += vx * speed); - } - }, 24); - } - } - } - }, 30), - - _onDragOver: function (/**Event*/evt) { var el = this.el, target, @@ -488,6 +492,8 @@ !options.dragoverBubble && evt.stopPropagation(); } + _autoScroll(evt, options, this.el); + if (!_silent && activeGroup && !options.disabled && (isOwner ? canSort || (revert = !rootEl.contains(dragEl)) @@ -662,13 +668,16 @@ Sortable.active && _dispatchEvent(rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); } - // Set NULL + // Nulling rootEl = dragEl = ghostEl = nextEl = cloneEl = + scrollEl = + scrollParentEl = + tapEvt = touchEvt = @@ -688,7 +697,6 @@ var type = evt.type; if (type === 'dragover' || type === 'dragenter') { - this._onDrag(evt); this._onDragOver(evt); _globalDragOver(evt); } From 4c0f1afd324e8b598782a8fa62bfa5b8a21401ef Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Feb 2015 19:12:00 +0300 Subject: [PATCH 23/27] #256: - _onDrag --- Sortable.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sortable.js b/Sortable.js index 050e742..0e16a7d 100644 --- a/Sortable.js +++ b/Sortable.js @@ -407,7 +407,6 @@ _css(ghostEl, 'msTransform', translate3d); _css(ghostEl, 'transform', translate3d); - this._onDrag(touch); evt.preventDefault(); } }, @@ -492,9 +491,7 @@ !options.dragoverBubble && evt.stopPropagation(); } - _autoScroll(evt, options, this.el); - - if (!_silent && activeGroup && !options.disabled && + if (activeGroup && !options.disabled && (isOwner ? canSort || (revert = !rootEl.contains(dragEl)) : activeGroup.pull && groupPut && ( @@ -504,6 +501,13 @@ ) && (evt.rootEl === void 0 || evt.rootEl === this.el) ) { + // Smart auto-scrolling + _autoScroll(evt, options, this.el); + + if (_silent) { + return; + } + target = _closest(evt.target, options.draggable, el); dragRect = dragEl.getBoundingClientRect(); From 563311f25eddbeb7b0276593f5a7d787504c7026 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Feb 2015 23:16:46 +0300 Subject: [PATCH 24/27] #271: + remove 'transform' (#issuecomment-73954644) --- Sortable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Sortable.js b/Sortable.js index 0e16a7d..5c4672c 100644 --- a/Sortable.js +++ b/Sortable.js @@ -603,6 +603,7 @@ clearTimeout(target.animated); target.animated = setTimeout(function () { _css(target, 'transition', ''); + _css(target, 'transform', ''); target.animated = false; }, ms); } From a89c2d5dc27001de87f967157e321339c1b337fc Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 12 Feb 2015 13:21:30 +0300 Subject: [PATCH 25/27] #217: * modularizing --- react-sortable-mixin.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 6f25acc..2cb2cb4 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -6,12 +6,12 @@ (function (factory) { 'use strict'; - if (typeof define === 'function' && define.amd) { - define(['sortable'], factory); - } - else if (typeof module != 'undefined' && typeof module.exports != 'undefined') { + if (typeof module != 'undefined' && typeof module.exports != 'undefined') { module.exports = factory(require('Sortable')); } + else if (typeof define === 'function' && define.amd) { + define(['sortable'], factory); + } else { /* jshint sub:true */ window['SortableMixin'] = factory(Sortable); From ef76066cc5df2bc41468ec1511b309eabf9b368f Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 12 Feb 2015 18:36:45 +0300 Subject: [PATCH 26/27] * relative path --- ng-sortable.js | 2 +- react-sortable-mixin.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ng-sortable.js b/ng-sortable.js index bef396c..a872561 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -9,7 +9,7 @@ factory(angular, Sortable); } else if (typeof define === 'function' && define.amd) { - define(['angular', 'sortable'], factory); + define(['angular', './Sortable'], factory); } })(function (angular, Sortable) { 'use strict'; diff --git a/react-sortable-mixin.js b/react-sortable-mixin.js index 2cb2cb4..47d1c36 100644 --- a/react-sortable-mixin.js +++ b/react-sortable-mixin.js @@ -7,10 +7,10 @@ 'use strict'; if (typeof module != 'undefined' && typeof module.exports != 'undefined') { - module.exports = factory(require('Sortable')); + module.exports = factory(require('./Sortable')); } else if (typeof define === 'function' && define.amd) { - define(['sortable'], factory); + define(['./Sortable'], factory); } else { /* jshint sub:true */ From 52a49f36ed9faa92b5782e95e894edb56be01343 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 12 Feb 2015 22:34:02 +0300 Subject: [PATCH 27/27] * version-task --- Gruntfile.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0f1db6c..26e1656 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -5,7 +5,16 @@ module.exports = function (grunt) { pkg: grunt.file.readJSON('package.json'), version: { - src: ['<%= pkg.exportName %>.js', '*.json'] + js: { + src: ['<%= pkg.exportName %>.js', '*.json'] + }, + cdn: { + options: { + prefix: '(cdnjs\\.cloudflare\\.com\\/ajax\\/libs\\/Sortable|cdn\\.jsdelivr\\.net\\/sortable)\\/', + replace: '[0-9\\.]+' + }, + src: ['README.md'] + } }, jshint: {