From df1c4d896eaf1ad1a34cf33bda3baa77a9841443 Mon Sep 17 00:00:00 2001 From: jpatte Date: Tue, 22 Oct 2013 17:34:04 +0200 Subject: [PATCH] added support for jQuery sortable's "over" event --- README.md | 10 ++++++++++ build/knockout-sortable.js | 26 ++++++++++++++++++++++++++ build/knockout-sortable.min.js | 5 +++-- src/knockout-sortable.js | 26 ++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d7504b..27e8eb6 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,16 @@ Note: The sortable binding assumes that the child "templates" have a single cont This option can be passed in the binding or configured globally by setting `ko.bindingHandlers.sortable.afterMove`. This callback also receives the `event` and `ui` objects as the second and third arguments. +* **hovering** - specify a function to execute while an item is being dragged over a potential location. This function receives an object for its first argument that contains the following information: + * `arg.item` - the actual item being moved + * `arg.sourceIndex` - the position of the item in the original observableArray + * `arg.sourceParent` - the original observableArray + * `arg.sourceParentNode` - the container node of the original list. Useful if moving items between lists, but within a single array. The value of `this` in the callback will be the target container node. + * `arg.targetIndex` - the targeted position of the item in the destination observableArray + * `arg.targetParent` - the destination observableArray + + This option can be passed in the binding or configured globally by setting `ko.bindingHandlers.sortable.afterMove`. This callback also receives the `event` and `ui` objects as the second and third arguments. Warning: this callback might be called multiple times for the same target location. + * **dragged** - specify a function to execute after a draggable item has been dropped into a sortable. This callback receives the drag item as the first argument, the `event` as the second argument, and the `ui` object as the third argument. If the function returns a value, then it will be used as item that is dropped into the sortable. This can be used as an alternative to the original item including a `clone` function. * **isEnabled** - specify whether the sortable widget should be enabled. If this is an observable, then it will enable/disable the widget when the observable's value changes. This option can be passed in the binding or configured globally by setting `ko.bindingHandlers.sortable.isEnabled`. diff --git a/build/knockout-sortable.js b/build/knockout-sortable.js index 48ea156..a264c84 100644 --- a/build/knockout-sortable.js +++ b/build/knockout-sortable.js @@ -138,6 +138,31 @@ startActual.apply(this, arguments); } }, + over: function(event, ui) { + if(!sortable.hovering) + return; + var sourceParent, targetParent, sourceIndex, targetIndex, arg, + el = ui.item[0], + placeHolderEl = ui.placeholder[0], + item = dataGet(el, ITEMKEY) || dragItem; + + sourceParent = dataGet(el, PARENTKEY); + sourceIndex = dataGet(el, INDEXKEY); + targetParent = dataGet(placeHolderEl.parentNode, LISTKEY); + targetIndex = ko.utils.arrayIndexOf(ui.placeholder.parent().children(), placeHolderEl); + if (sourceParent === targetParent && targetIndex > sourceIndex) + targetIndex--; + + arg = { + item: item, + sourceParent: sourceParent, + sourceParentNode: sourceParent && ui.sender || el.parentNode, + sourceIndex: sourceIndex, + targetParent: targetParent, + targetIndex: targetIndex + }; + sortable.hovering.call(this, arg, event, ui); + }, receive: function(event, ui) { dragItem = dataGet(ui.item[0], DRAGKEY); if (dragItem) { @@ -274,6 +299,7 @@ allowDrop: true, afterMove: null, beforeMove: null, + hovering: null, options: {} }; diff --git a/build/knockout-sortable.min.js b/build/knockout-sortable.min.js index e1b4c11..d389014 100644 --- a/build/knockout-sortable.min.js +++ b/build/knockout-sortable.min.js @@ -1,2 +1,3 @@ -// knockout-sortable 0.8.3 | (c) 2013 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license -!function(a){"function"==typeof define&&define.amd?define(["knockout","jquery","jquery.ui.sortable"],a):a(window.ko,jQuery)}(function(a,b){var c="ko_sortItem",d="ko_sourceIndex",e="ko_sortList",f="ko_parentList",g="ko_dragItem",h=a.utils.unwrapObservable,i=a.utils.domData.get,j=a.utils.domData.set,k=function(b,d){a.utils.arrayForEach(b,function(a){1===a.nodeType&&(j(a,c,d),j(a,f,i(a.parentNode,e)))})},l=function(b,c){var d,e={},f=h(b());return f.data?(e[c]=f.data,e.name=f.template):e[c]=b(),a.utils.arrayForEach(["afterAdd","afterRender","as","beforeRemove","includeDestroyed","templateEngine","templateOptions"],function(b){e[b]=f[b]||a.bindingHandlers.sortable[b]}),"foreach"===c&&(e.afterRender?(d=e.afterRender,e.afterRender=function(a,b){k.call(b,a,b),d.call(b,a,b)}):e.afterRender=k),e},m=function(a,b){var c=h(b);if(c)for(var d=0;a>d;d++)c[d]&&h(c[d]._destroy)&&a++;return a};a.bindingHandlers.sortable={init:function(k,n,o,p,q){var r,s,t=b(k),u=h(n())||{},v=l(n,"foreach"),w={};a.utils.arrayForEach(k.childNodes,function(a){a&&1!==a.nodeType&&a.parentNode.removeChild(a)}),b.extend(!0,w,a.bindingHandlers.sortable),u.options&&w.options&&(a.utils.extend(w.options,u.options),delete u.options),a.utils.extend(w,u),w.connectClass&&(a.isObservable(w.allowDrop)||"function"==typeof w.allowDrop)?a.computed({read:function(){var b=h(w.allowDrop),c="function"==typeof b?b.call(this,v.foreach):b;a.utils.toggleDomNodeCssClass(k,w.connectClass,c)},disposeWhenNodeIsRemoved:k},this):a.utils.toggleDomNodeCssClass(k,w.connectClass,w.allowDrop),a.bindingHandlers.template.init(k,function(){return v},o,p,q),r=w.options.start,s=w.options.update;var x=setTimeout(function(){var l;t.sortable(a.utils.extend(w.options,{start:function(b,c){var e=c.item[0];j(e,d,a.utils.arrayIndexOf(c.item.parent().children(),e)),c.item.find("input:focus").change(),r&&r.apply(this,arguments)},receive:function(a,b){l=i(b.item[0],g),l&&(l.clone&&(l=l.clone()),w.dragged&&(l=w.dragged.call(this,l,a,b)||l))},update:function(g,h){var k,n,o,p,q,r=h.item[0],t=h.item.parent()[0],u=i(r,c)||l;if(l=null,u&&(this===t||b.contains(this,t))){if(k=i(r,f),o=i(r,d),n=i(r.parentNode,e),p=a.utils.arrayIndexOf(h.item.parent().children(),r),v.includeDestroyed||(o=m(o,k),p=m(p,n)),(w.beforeMove||w.afterMove)&&(q={item:u,sourceParent:k,sourceParentNode:k&&h.sender||r.parentNode,sourceIndex:o,targetParent:n,targetIndex:p,cancelDrop:!1}),w.beforeMove&&(w.beforeMove.call(this,q,g,h),q.cancelDrop))return q.sourceParent?b(q.sourceParent===q.targetParent?this:h.sender).sortable("cancel"):b(r).remove(),void 0;p>=0&&(k&&(k.splice(o,1),a.processAllDeferredBindingUpdates&&a.processAllDeferredBindingUpdates()),n.splice(p,0,u)),j(r,c,null),h.item.remove(),a.processAllDeferredBindingUpdates&&a.processAllDeferredBindingUpdates(),w.afterMove&&w.afterMove.call(this,q,g,h)}s&&s.apply(this,arguments)},connectWith:w.connectClass?"."+w.connectClass:!1})),void 0!==w.isEnabled&&a.computed({read:function(){t.sortable(h(w.isEnabled)?"enable":"disable")},disposeWhenNodeIsRemoved:k})},0);return a.utils.domNodeDisposal.addDisposeCallback(k,function(){t.data("sortable")&&t.sortable("destroy"),clearTimeout(x)}),{controlsDescendantBindings:!0}},update:function(b,c,d,f,g){var h=l(c,"foreach");j(b,e,h.foreach),a.bindingHandlers.template.update(b,function(){return h},d,f,g)},connectClass:"ko_container",allowDrop:!0,afterMove:null,beforeMove:null,options:{}},a.bindingHandlers.draggable={init:function(c,d,e,f,i){var k=h(d())||{},m=k.options||{},n=a.utils.extend({},a.bindingHandlers.draggable.options),o=l(d,"data"),p=k.connectClass||a.bindingHandlers.draggable.connectClass,q=void 0!==k.isEnabled?k.isEnabled:a.bindingHandlers.draggable.isEnabled;return k=k.data||k,j(c,g,k),a.utils.extend(n,m),n.connectToSortable=p?"."+p:!1,b(c).draggable(n),void 0!==q&&a.computed({read:function(){b(c).draggable(h(q)?"enable":"disable")},disposeWhenNodeIsRemoved:c}),a.bindingHandlers.template.init(c,function(){return o},e,f,i)},update:function(b,c,d,e,f){var g=l(c,"data");return a.bindingHandlers.template.update(b,function(){return g},d,e,f)},connectClass:a.bindingHandlers.sortable.connectClass,options:{helper:"clone"}}}); \ No newline at end of file +// knockout-sortable 0.8.3 | (c) 2013 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license +(function(n){typeof define=="function"&&define.amd?define(["knockout","jquery","jquery.ui.sortable"],n):n(window.ko,jQuery)})(function(n,t){var f="ko_sortItem",s="ko_sourceIndex",e="ko_sortList",h="ko_parentList",c="ko_dragItem",r=n.utils.unwrapObservable,i=n.utils.domData.get,u=n.utils.domData.set,l=function(t,r){n.utils.arrayForEach(t,function(n){n.nodeType===1&&(u(n,f,r),u(n,h,i(n.parentNode,e)))})},o=function(t,i){var u={},f=r(t()),e;return f.data?(u[i]=f.data,u.name=f.template):u[i]=t(),n.utils.arrayForEach(["afterAdd","afterRender","as","beforeRemove","includeDestroyed","templateEngine","templateOptions"],function(t){u[t]=f[t]||n.bindingHandlers.sortable[t]}),i==="foreach"&&(u.afterRender?(e=u.afterRender,u.afterRender=function(n,t){l.call(t,n,t),e.call(t,n,t)}):u.afterRender=l),u},a=function(n,t){var u=r(t),i;if(u)for(i=0;iv&&c--,y={item:w,sourceParent:u,sourceParentNode:u&&r.sender||l.parentNode,sourceIndex:v,targetParent:a,targetIndex:c},b.hovering.call(this,y,t,r)}},receive:function(n,t){o=i(t.item[0],c),o&&(o.clone&&(o=o.clone()),b.dragged&&(o=b.dragged.call(this,o,n,t)||o))},update:function(r,c){var y,k,w,p,v,l=c.item[0],nt=c.item.parent()[0],d=i(l,f)||o;if(o=null,d&&(this===nt||t.contains(this,nt))){if(y=i(l,h),w=i(l,s),k=i(l.parentNode,e),p=n.utils.arrayIndexOf(c.item.parent().children(),l),g.includeDestroyed||(w=a(w,y),p=a(p,k)),(b.beforeMove||b.afterMove)&&(v={item:d,sourceParent:y,sourceParentNode:y&&c.sender||l.parentNode,sourceIndex:w,targetParent:k,targetIndex:p,cancelDrop:!1}),b.beforeMove&&(b.beforeMove.call(this,v,r,c),v.cancelDrop)){v.sourceParent?t(v.sourceParent===v.targetParent?this:c.sender).sortable("cancel"):t(l).remove();return}p>=0&&(y&&(y.splice(w,1),n.processAllDeferredBindingUpdates&&n.processAllDeferredBindingUpdates()),k.splice(p,0,d)),u(l,f,null),c.item.remove(),n.processAllDeferredBindingUpdates&&n.processAllDeferredBindingUpdates(),b.afterMove&&b.afterMove.call(this,v,r,c)}tt&&tt.apply(this,arguments)},connectWith:b.connectClass?"."+b.connectClass:!1})),b.isEnabled!==undefined&&n.computed({read:function(){k.sortable(r(b.isEnabled)?"enable":"disable")},disposeWhenNodeIsRemoved:l})},0),n.utils.domNodeDisposal.addDisposeCallback(l,function(){k.data("sortable")&&k.sortable("destroy"),clearTimeout(it)}),{controlsDescendantBindings:!0}},update:function(t,i,r,f,s){var h=o(i,"foreach");u(t,e,h.foreach),n.bindingHandlers.template.update(t,function(){return h},r,f,s)},connectClass:"ko_container",allowDrop:!0,afterMove:null,beforeMove:null,hovering:null,options:{}},n.bindingHandlers.draggable={init:function(i,f,e,s,h){var l=r(f())||{},p=l.options||{},a=n.utils.extend({},n.bindingHandlers.draggable.options),w=o(f,"data"),v=l.connectClass||n.bindingHandlers.draggable.connectClass,y=l.isEnabled!==undefined?l.isEnabled:n.bindingHandlers.draggable.isEnabled;return l=l.data||l,u(i,c,l),n.utils.extend(a,p),a.connectToSortable=v?"."+v:!1,t(i).draggable(a),y!==undefined&&n.computed({read:function(){t(i).draggable(r(y)?"enable":"disable")},disposeWhenNodeIsRemoved:i}),n.bindingHandlers.template.init(i,function(){return w},e,s,h)},update:function(t,i,r,u,f){var e=o(i,"data");return n.bindingHandlers.template.update(t,function(){return e},r,u,f)},connectClass:n.bindingHandlers.sortable.connectClass,options:{helper:"clone"}}}); +//@ sourceMappingURL=knockout-sortable.min.js.map \ No newline at end of file diff --git a/src/knockout-sortable.js b/src/knockout-sortable.js index 894ebab..bb8104f 100644 --- a/src/knockout-sortable.js +++ b/src/knockout-sortable.js @@ -137,6 +137,31 @@ startActual.apply(this, arguments); } }, + over: function(event, ui) { + if(!sortable.hovering) + return; + var sourceParent, targetParent, sourceIndex, targetIndex, arg, + el = ui.item[0], + placeHolderEl = ui.placeholder[0], + item = dataGet(el, ITEMKEY) || dragItem; + + sourceParent = dataGet(el, PARENTKEY); + sourceIndex = dataGet(el, INDEXKEY); + targetParent = dataGet(placeHolderEl.parentNode, LISTKEY); + targetIndex = ko.utils.arrayIndexOf(ui.placeholder.parent().children(), placeHolderEl); + if (sourceParent === targetParent && targetIndex > sourceIndex) + targetIndex--; + + arg = { + item: item, + sourceParent: sourceParent, + sourceParentNode: sourceParent && ui.sender || el.parentNode, + sourceIndex: sourceIndex, + targetParent: targetParent, + targetIndex: targetIndex + }; + sortable.hovering.call(this, arg, event, ui); + }, receive: function(event, ui) { dragItem = dataGet(ui.item[0], DRAGKEY); if (dragItem) { @@ -273,6 +298,7 @@ allowDrop: true, afterMove: null, beforeMove: null, + hovering: null, options: {} };