From eae805dc0afa5ac3bc6f41fc5013722c342db583 Mon Sep 17 00:00:00 2001 From: yenoh2 Date: Tue, 29 Nov 2016 15:24:36 +0100 Subject: [PATCH 01/13] Fixed to work with deep descendants modifies the sortable.js file at line 198, in order for the ui-sortable directive to work on other directives that have isolated scopes. --- src/sortable.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/sortable.js b/src/sortable.js index bfed173..607adb7 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -97,7 +97,7 @@ angular.module('ui.sortable', []) } return; } - + if (!defaultOptions) { defaultOptions = angular.element.ui.sortable().options; } @@ -195,6 +195,20 @@ angular.module('ui.sortable', []) break; } } + //If result is still null it means that the draggable (ng-repeat) item isn't a direct child of + //the element containing the ui.sortable directive. This may be required when using the ui.sortable + //directive with other directives that have isolated scopes. This will compare x.element[0] + //with the closest ancestorof element[0] that has the ui-sortable attribute to get the applicable + //element scope. + if (!result) { + for (i = 0; i < elementScopes.length; i++) { + x = elementScopes[i]; + if (x.element[0] === element[0].closest('[ui-sortable]')) { + result = x.scope; + break; + } + } + } return result; } @@ -459,7 +473,7 @@ angular.module('ui.sortable', []) var sortableWidgetInstance = getSortableWidgetInstance(element); if (!!sortableWidgetInstance) { var optsDiff = patchUISortableOptions(newVal, oldVal, sortableWidgetInstance); - + if (optsDiff) { element.sortable('option', optsDiff); } @@ -475,7 +489,7 @@ angular.module('ui.sortable', []) } else { $log.info('ui.sortable: ngModel not provided!', element); } - + // Create sortable element.sortable(opts); } From 37d04d3597898c73481889beb279c5cdd206952b Mon Sep 17 00:00:00 2001 From: yenoh2 Date: Wed, 30 Nov 2016 07:21:25 +0100 Subject: [PATCH 02/13] Correcting linter errors --- src/sortable.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sortable.js b/src/sortable.js index 607adb7..311ca8b 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -188,8 +188,10 @@ angular.module('ui.sortable', []) function getElementScope(elementScopes, element) { var result = null; - for (var i = 0; i < elementScopes.length; i++) { - var x = elementScopes[i]; + var x = null; + var i = null; + for (i = 0; i < elementScopes.length; i++) { + x = elementScopes[i]; if (x.element[0] === element[0]) { result = x.scope; break; From 3c36ecd753a2619c784fef28284093a8a5b52d54 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Wed, 30 Nov 2016 19:36:18 +0200 Subject: [PATCH 03/13] test(directives): add transclusion tests --- test/sortable.e2e.directives.spec.js | 105 ++++++++++++++++++++++++++- test/sortable.test-directives.js | 16 ++++ test/sortable.test-helper.js | 12 +++ 3 files changed, 132 insertions(+), 1 deletion(-) diff --git a/test/sortable.e2e.directives.spec.js b/test/sortable.e2e.directives.spec.js index 23375c4..5a0e863 100644 --- a/test/sortable.e2e.directives.spec.js +++ b/test/sortable.e2e.directives.spec.js @@ -13,11 +13,12 @@ describe('uiSortable', function() { beforeEach(module('ui.sortable.testHelper')); beforeEach(module('ui.sortable.testDirectives')); - var EXTRA_DY_PERCENTAGE, listContent, listInnerContent, beforeLiElement, afterLiElement; + var EXTRA_DY_PERCENTAGE, listContent, listFindContent, listInnerContent, beforeLiElement, afterLiElement; beforeEach(inject(function (sortableTestHelper) { EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE; listContent = sortableTestHelper.listContent; + listFindContent = sortableTestHelper.listFindContent; listInnerContent = sortableTestHelper.listInnerContent; beforeLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeLiElement; afterLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterLiElement; @@ -112,6 +113,108 @@ describe('uiSortable', function() { }); }); + it('should work when the items are inside a transcluded directive', function() { + inject(function($compile, $rootScope) { + var element; + element = $compile(''.concat( + '
', + '', + beforeLiElement, + '
', + '{{ item }}', + '
', + afterLiElement, + '', + '
'))($rootScope); + + $rootScope.$apply(function() { + $rootScope.opts = { + items: '> * .sortable-item' + }; + $rootScope.items = ['One', 'Two', 'Three']; + }); + + host.append(element); + + var li = element.find('.sortable-item:eq(1)'); + var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['One', 'Three', 'Two']); + expect($rootScope.items).toEqual(listFindContent(element)); + + li = element.find('.sortable-item:eq(1)'); + dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['Three', 'One', 'Two']); + expect($rootScope.items).toEqual(listFindContent(element)); + + $(element).remove(); + }); + }); + + it('should properly cancel() when the items are inside a transcluded directive', function() { + inject(function($compile, $rootScope) { + var element; + element = $compile(''.concat( + '
', + '', + beforeLiElement, + '
', + '{{ item }}', + '
', + afterLiElement, + '', + '
'))($rootScope); + + $rootScope.$apply(function() { + $rootScope.opts = { + items: '> * .sortable-item', + update: function(e, ui) { + if (ui.item.sortable.model === 'Two') { + ui.item.sortable.cancel(); + } + } + }; + $rootScope.items = ['One', 'Two', 'Three']; + }); + + host.append(element); + + var li = element.find('.sortable-item:eq(1)'); + var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['One', 'Two', 'Three']); + expect($rootScope.items).toEqual(listFindContent(element)); + // try again + li = element.find('.sortable-item:eq(1)'); + dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['One', 'Two', 'Three']); + expect($rootScope.items).toEqual(listFindContent(element)); + // try again + li = element.find('.sortable-item:eq(1)'); + dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['One', 'Two', 'Three']); + expect($rootScope.items).toEqual(listFindContent(element)); + + li = element.find('.sortable-item:eq(0)'); + dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['Two', 'One', 'Three']); + expect($rootScope.items).toEqual(listFindContent(element)); + + li = element.find('.sortable-item:eq(2)'); + dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight(); + li.simulate('drag', { dy: dy }); + expect($rootScope.items).toEqual(['Two', 'Three', 'One']); + expect($rootScope.items).toEqual(listFindContent(element)); + + + $(element).remove(); + }); + }); + } [0, 1].forEach(function(useExtraElements){ diff --git a/test/sortable.test-directives.js b/test/sortable.test-directives.js index f555ada..9ee801f 100644 --- a/test/sortable.test-directives.js +++ b/test/sortable.test-directives.js @@ -34,5 +34,21 @@ angular.module('ui.sortable.testDirectives', []) } }; } + ).directive('uiSortableTransclusionTestDirective', + function() { + return { + restrict: 'E', + transclude: true, + scope: true, + template: '
' + + '

Transclusion Directive

' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + }; + } ); diff --git a/test/sortable.test-helper.js b/test/sortable.test-helper.js index ec91db9..e59b5b9 100644 --- a/test/sortable.test-helper.js +++ b/test/sortable.test-helper.js @@ -15,6 +15,17 @@ angular.module('ui.sortable.testHelper', []) return []; } + function listFindContent (list, contentSelector) { + if (!contentSelector) { + contentSelector = '.sortable-item'; + } + + if (list && list.length) { + return list.find(contentSelector).map(function(){ return this.innerHTML; }).toArray(); + } + return []; + } + function listInnerContent (list, contentSelector) { if (!contentSelector) { contentSelector = '.itemContent'; @@ -79,6 +90,7 @@ angular.module('ui.sortable.testHelper', []) return { EXTRA_DY_PERCENTAGE: EXTRA_DY_PERCENTAGE, listContent: listContent, + listFindContent: listFindContent, listInnerContent: listInnerContent, simulateElementDrag: simulateElementDrag, hasUndefinedProperties: hasUndefinedProperties, From c3232ebd132d366fb2515931cd18f0cebae77147 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Wed, 30 Nov 2016 22:49:19 +0200 Subject: [PATCH 04/13] feat(sortable): improve the source and droptarget detection --- src/sortable.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sortable.js b/src/sortable.js index 311ca8b..7b2118a 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -282,7 +282,7 @@ angular.module('ui.sortable', []) ui.item.sortable = { model: ngModel.$modelValue[index], index: index, - source: ui.item.parent(), + source: element, sourceModel: ngModel.$modelValue, cancel: function () { ui.item.sortable._isCanceled = true; @@ -335,7 +335,7 @@ angular.module('ui.sortable', []) // the value will be overwritten with the old value if(!ui.item.sortable.received) { ui.item.sortable.dropindex = getItemIndex(ui.item); - var droptarget = ui.item.parent(); + var droptarget = ui.item.closest('[ui-sortable]'); ui.item.sortable.droptarget = droptarget; var droptargetScope = getElementScope(ui.item.sortable._connectedSortables, droptarget); @@ -449,7 +449,7 @@ angular.module('ui.sortable', []) item.sortable = { model: ngModel.$modelValue[index], index: index, - source: item.parent(), + source: element, sourceModel: ngModel.$modelValue, _restore: function () { angular.forEach(item.sortable, function(value, key) { From fd8238f97e0d35f534e03a7e366e854eef0b418e Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Thu, 1 Dec 2016 07:42:49 +0200 Subject: [PATCH 05/13] refactor: change getElementScope to retireve the whole context --- src/sortable.js | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/sortable.js b/src/sortable.js index 7b2118a..6ba70cb 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -186,32 +186,13 @@ angular.module('ui.sortable', []) return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display')); } - function getElementScope(elementScopes, element) { - var result = null; - var x = null; - var i = null; - for (i = 0; i < elementScopes.length; i++) { - x = elementScopes[i]; - if (x.element[0] === element[0]) { - result = x.scope; - break; + function getElementContext(elementScopes, element) { + for (var i = 0; i < elementScopes.length; i++) { + var c = elementScopes[i]; + if (c.element[0] === element[0]) { + return c; } } - //If result is still null it means that the draggable (ng-repeat) item isn't a direct child of - //the element containing the ui.sortable directive. This may be required when using the ui.sortable - //directive with other directives that have isolated scopes. This will compare x.element[0] - //with the closest ancestorof element[0] that has the ui-sortable attribute to get the applicable - //element scope. - if (!result) { - for (i = 0; i < elementScopes.length; i++) { - x = elementScopes[i]; - if (x.element[0] === element[0].closest('[ui-sortable]')) { - result = x.scope; - break; - } - } - } - return result; } function afterStop(e, ui) { @@ -338,8 +319,8 @@ angular.module('ui.sortable', []) var droptarget = ui.item.closest('[ui-sortable]'); ui.item.sortable.droptarget = droptarget; - var droptargetScope = getElementScope(ui.item.sortable._connectedSortables, droptarget); - ui.item.sortable.droptargetModel = droptargetScope.ngModel; + var droptargetContext = getElementContext(ui.item.sortable._connectedSortables, droptarget); + ui.item.sortable.droptargetModel = droptargetContext.scope.ngModel; // Cancel the sort (let ng-repeat do the sort for us) // Don't cancel if this is the received list because it has From 6ce8a9ce206f1e8febd8b59a7edf61f6115226a3 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Fri, 2 Dec 2016 20:30:10 +0200 Subject: [PATCH 06/13] feat(sortable): expose item container in ui.item.sortable --- src/sortable.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sortable.js b/src/sortable.js index 6ba70cb..22e5418 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -264,6 +264,7 @@ angular.module('ui.sortable', []) model: ngModel.$modelValue[index], index: index, source: element, + sourceList: ui.item.parent(), sourceModel: ngModel.$modelValue, cancel: function () { ui.item.sortable._isCanceled = true; @@ -318,6 +319,7 @@ angular.module('ui.sortable', []) ui.item.sortable.dropindex = getItemIndex(ui.item); var droptarget = ui.item.closest('[ui-sortable]'); ui.item.sortable.droptarget = droptarget; + ui.item.sortable.droptargetList = ui.item.parent(); var droptargetContext = getElementContext(ui.item.sortable._connectedSortables, droptarget); ui.item.sortable.droptargetModel = droptargetContext.scope.ngModel; @@ -431,6 +433,7 @@ angular.module('ui.sortable', []) model: ngModel.$modelValue[index], index: index, source: element, + sourceList: item.parent(), sourceModel: ngModel.$modelValue, _restore: function () { angular.forEach(item.sortable, function(value, key) { From 4fd4207372b49e493a18b8a5b015fb09a01974f2 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Fri, 2 Dec 2016 20:38:03 +0200 Subject: [PATCH 07/13] refactor(sortable): add _getElementContext() on ui.item.sortable --- src/sortable.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sortable.js b/src/sortable.js index 22e5418..ada9d0c 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -281,6 +281,9 @@ angular.module('ui.sortable', []) angular.forEach(ui.item.sortable, function(value, key) { ui.item.sortable[key] = undefined; }); + }, + _getElementContext: function (element) { + return getElementContext(this._connectedSortables || [], element); } }; }; @@ -321,7 +324,7 @@ angular.module('ui.sortable', []) ui.item.sortable.droptarget = droptarget; ui.item.sortable.droptargetList = ui.item.parent(); - var droptargetContext = getElementContext(ui.item.sortable._connectedSortables, droptarget); + var droptargetContext = ui.item.sortable._getElementContext(droptarget); ui.item.sortable.droptargetModel = droptargetContext.scope.ngModel; // Cancel the sort (let ng-repeat do the sort for us) From 19c968d70054694542a8924b253ac7ac89a2d94d Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Fri, 2 Dec 2016 21:00:35 +0200 Subject: [PATCH 08/13] feat(sortable): add support for transcluded descendants --- src/sortable.js | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sortable.js b/src/sortable.js index ada9d0c..c22c1bd 100644 --- a/src/sortable.js +++ b/src/sortable.js @@ -282,18 +282,32 @@ angular.module('ui.sortable', []) ui.item.sortable[key] = undefined; }); }, + _connectedSortables: [], _getElementContext: function (element) { - return getElementContext(this._connectedSortables || [], element); + return getElementContext(this._connectedSortables, element); } }; }; callbacks.activate = function(e, ui) { + var isSourceContext = ui.item.sortable.source === element; + var savedNodesOrigin = isSourceContext ? + ui.item.sortable.sourceList : + element; + var elementContext = { + element: element, + scope: scope, + isSourceContext: isSourceContext, + savedNodesOrigin: savedNodesOrigin + }; + // save the directive's scope so that it is accessible from ui.item.sortable + ui.item.sortable._connectedSortables.push(elementContext); + // We need to make a copy of the current element's contents so // we can restore it after sortable has messed it up. // This is inside activate (instead of start) in order to save // both lists when dragging between connected lists. - savedNodes = element.contents(); + savedNodes = savedNodesOrigin.contents(); // If this list has a placeholder (the connected lists won't), // don't inlcude it in saved nodes. @@ -302,16 +316,6 @@ angular.module('ui.sortable', []) var excludes = getPlaceholderExcludesludes(element, placeholder); savedNodes = savedNodes.not(excludes); } - - // save the directive's scope so that it is accessible from ui.item.sortable - var connectedSortables = ui.item.sortable._connectedSortables || []; - - connectedSortables.push({ - element: element, - scope: scope - }); - - ui.item.sortable._connectedSortables = connectedSortables; }; callbacks.update = function(e, ui) { @@ -345,7 +349,8 @@ angular.module('ui.sortable', []) // That way it will be garbage collected. savedNodes = savedNodes.not(sortingHelper); } - savedNodes.appendTo(element); + var elementContext = ui.item.sortable._getElementContext(element); + savedNodes.appendTo(elementContext.savedNodesOrigin); // If this is the target connected list then // it's safe to clear the restored nodes since: @@ -394,7 +399,8 @@ angular.module('ui.sortable', []) // That way it will be garbage collected. savedNodes = savedNodes.not(sortingHelper); } - savedNodes.appendTo(element); + var elementContext = ui.item.sortable._getElementContext(element); + savedNodes.appendTo(elementContext.savedNodesOrigin); } // It's now safe to clear the savedNodes From bec8e9d3db702e02b0c1268e49f262b4a609d74e Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Sun, 4 Dec 2016 14:18:10 +0200 Subject: [PATCH 09/13] test(directives): add transclusion tests w/ connected sortables --- test/sortable.e2e.directives.spec.js | 141 ++++++++++++++++++++++++++- test/sortable.test-helper.js | 15 ++- 2 files changed, 151 insertions(+), 5 deletions(-) diff --git a/test/sortable.e2e.directives.spec.js b/test/sortable.e2e.directives.spec.js index 5a0e863..193af85 100644 --- a/test/sortable.e2e.directives.spec.js +++ b/test/sortable.e2e.directives.spec.js @@ -13,15 +13,18 @@ describe('uiSortable', function() { beforeEach(module('ui.sortable.testHelper')); beforeEach(module('ui.sortable.testDirectives')); - var EXTRA_DY_PERCENTAGE, listContent, listFindContent, listInnerContent, beforeLiElement, afterLiElement; + var EXTRA_DY_PERCENTAGE, listContent, listFindContent, listInnerContent, simulateElementDrag, beforeLiElement, afterLiElement, beforeDivElement, afterDivElement; beforeEach(inject(function (sortableTestHelper) { EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE; listContent = sortableTestHelper.listContent; listFindContent = sortableTestHelper.listFindContent; listInnerContent = sortableTestHelper.listInnerContent; + simulateElementDrag = sortableTestHelper.simulateElementDrag; beforeLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeLiElement; afterLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterLiElement; + beforeDivElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeDivElement; + afterDivElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterDivElement; })); tests.description = 'Inner directives related'; @@ -35,6 +38,7 @@ describe('uiSortable', function() { if (!useExtraElements) { beforeLiElement = afterLiElement = ''; + beforeDivElement = afterDivElement = ''; } })); @@ -215,6 +219,141 @@ describe('uiSortable', function() { }); }); + it('should update model when the items are inside a transcluded directive and sorting between sortables', function() { + inject(function($compile, $rootScope) { + var elementTop, elementBottom; + elementTop = $compile(''.concat( + '
', + '', + beforeDivElement, + '
{{ item }}
', + afterDivElement, + '', + '
'))($rootScope); + elementBottom = $compile(''.concat( + '
', + '', + beforeDivElement, + '
{{ item }}
', + afterDivElement, + '', + '
'))($rootScope); + $rootScope.$apply(function() { + $rootScope.itemsTop = ['Top One', 'Top Two', 'Top Three']; + $rootScope.itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three']; + $rootScope.opts = { + connectWith: '.cross-sortable', + items: '> * .sortable-item' + }; + }); + + host.append(elementTop).append(elementBottom).append('
'); + + var li1 = elementTop.find('.sortable-item:eq(0)'); + var li2 = elementBottom.find('.sortable-item:eq(2)'); + simulateElementDrag(li1, li2, { place: 'above', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Top One', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + + // it seems that it ony likes the last spot + li1 = elementBottom.find('.sortable-item:eq(2)'); + li2 = elementTop.find('.sortable-item:eq(1)'); + simulateElementDrag(li1, li2, { place: 'below', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three', 'Top One']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + + $(elementTop).remove(); + $(elementBottom).remove(); + }); + }); + + it('should properly cancel() when the items are inside a transcluded directive and sorting between sortables', function() { + inject(function($compile, $rootScope) { + var elementTop, elementBottom; + elementTop = $compile(''.concat( + '
', + '', + beforeDivElement, + '
{{ item }}
', + afterDivElement, + '', + '
'))($rootScope); + elementBottom = $compile(''.concat( + '
', + '', + beforeDivElement, + '
{{ item }}
', + afterDivElement, + '', + '
'))($rootScope); + $rootScope.$apply(function() { + $rootScope.itemsTop = ['Top One', 'Top Two', 'Top Three']; + $rootScope.itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three']; + $rootScope.opts = { + connectWith: '.cross-sortable', + items: '> * .sortable-item', + update: function(e, ui) { + if (ui.item.sortable.model && + (typeof ui.item.sortable.model === 'string') && + ui.item.sortable.model.indexOf('Two') >= 0) { + ui.item.sortable.cancel(); + } + } + }; + }); + + host.append(elementTop).append(elementBottom).append('
'); + + var li1 = elementTop.find('.sortable-item:eq(1)'); + var li2 = elementBottom.find('.sortable-item:eq(0)'); + simulateElementDrag(li1, li2, { place: 'below', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + // try again + li1 = elementBottom.find('.sortable-item:eq(1)'); + li2 = elementTop.find('.sortable-item:eq(1)'); + simulateElementDrag(li1, li2, { place: 'above', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + // try again + li1 = elementBottom.find('.sortable-item:eq(1)'); + li2 = elementTop.find('.sortable-item:eq(1)'); + simulateElementDrag(li1, li2, { place: 'above', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top One', 'Top Two', 'Top Three']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + + li1 = elementTop.find('.sortable-item:eq(0)'); + li2 = elementBottom.find('.sortable-item:eq(2)'); + simulateElementDrag(li1, li2, { place: 'above', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Top One', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + + // it seems that it ony likes the last spot + li1 = elementBottom.find('.sortable-item:eq(2)'); + li2 = elementTop.find('.sortable-item:eq(1)'); + simulateElementDrag(li1, li2, { place: 'below', moves: 100 }); + expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three', 'Top One']); + expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']); + expect($rootScope.itemsTop).toEqual(listFindContent(elementTop)); + expect($rootScope.itemsBottom).toEqual(listFindContent(elementBottom)); + + $(elementTop).remove(); + $(elementBottom).remove(); + }); + }); + } [0, 1].forEach(function(useExtraElements){ diff --git a/test/sortable.test-helper.js b/test/sortable.test-helper.js index e59b5b9..38721b4 100644 --- a/test/sortable.test-helper.js +++ b/test/sortable.test-helper.js @@ -46,11 +46,12 @@ angular.module('ui.sortable.testHelper', []) }; if (options === 'above') { - dragOptions.dy -= EXTRA_DY_PERCENTAGE * draggedElement.outerHeight(); + options = { place: 'above' }; } else if (options === 'below') { - dragOptions.dy += EXTRA_DY_PERCENTAGE * draggedElement.outerHeight(); - } else if (typeof options === 'object') { + options = { place: 'below' }; + } + if (typeof options === 'object') { if ('place' in options) { if (options.place === 'above') { dragOptions.dy -= EXTRA_DY_PERCENTAGE * draggedElement.outerHeight(); @@ -73,6 +74,10 @@ angular.module('ui.sortable.testHelper', []) if (isFinite(options.extradx)) { dragOptions.dx += options.extradx; } + + if (isFinite(options.moves) && options.moves > 0) { + dragOptions.moves = options.moves; + } } draggedElement.simulate(dragOptions.action, dragOptions); @@ -96,7 +101,9 @@ angular.module('ui.sortable.testHelper', []) hasUndefinedProperties: hasUndefinedProperties, extraElements: { beforeLiElement: '
  • extra element
  • ', - afterLiElement: '
  • extra element
  • ' + afterLiElement: '
  • extra element
  • ', + beforeDivElement: '
    extra element
    ', + afterDivElement: '
    extra element
    ' } }; }) From 84fa8b2c845e9a826f492db76cbde8c251d6a332 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Sun, 4 Dec 2016 14:18:37 +0200 Subject: [PATCH 10/13] docs(README): add docs for helper callback --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index cf060eb..05f2716 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,10 @@ update: function(event, ui) { **Single sortable** [demo](http://codepen.io/thgreasi/pen/QKmWGj) ``` +create + +/* dragging starts */ +helper start activate @@ -174,6 +178,11 @@ stop **Connected sortables** [demo](http://codepen.io/thgreasi/pen/YGazpJ) ``` +list A: create +list B: create + +/* dragging starts from sortable A to B */ +list A: helper list A: start list B: activate list A: activate From 343f01d563c7a846dc565a836efb641a1924ffd3 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Sun, 4 Dec 2016 15:17:54 +0200 Subject: [PATCH 11/13] docs(README): add docs about transcluded sorting --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 05f2716..f20543e 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,30 @@ list A: stop For more details about the events check the [jQueryUI API documentation](http://api.jqueryui.com/sortable/). +## Integrating with directives doing transclusion +Wrap the transclusion directive element with the ui-sortable directive and set the `items` to target your `ng-repeat`ed elements. Following best practices, it is also highly recommended that you add a `track by` expression to your `ng-repeat`. [Angular Meterial example](http://codepen.io/thgreasi/pen/NbyLVK). + +```js +myAppModule.controller('MyController', function($scope) { + $scope.items = ["One", "Two", "Three"]; + + $scope.sortableOptions = { + items: '.sortable-item' + // It is suggested to use the most specific cssselector you can, + // after analyzing the DOM elements generated by the transclusion directive + // eg: items: '> .transclusionLvl1 > .transclusionLvl2 > .sortable-item' + }; +}); +``` + +```html +
    + +
    {{ item }}
    +
    +
    +``` + ## Examples - [Simple Demo](http://codepen.io/thgreasi/pen/wzmvgw) @@ -233,6 +257,7 @@ For more details about the events check the [jQueryUI API documentation](http:// ## Integrations - [firebase](http://codepen.io/thgreasi/pen/repEZg?editors=0010) - [ui.bootstrap.accordion](http://plnkr.co/edit/TGIeeEbbvJwpJ3WRqo2z?p=preview) +- [Angular Meterial](http://codepen.io/thgreasi/pen/NbyLVK) (thanks yenoh2) - [Asynchronous loading jQuery+jQueryUI with crisbeto/angular-ui-sortable-loader](https://github.com/crisbeto/angular-ui-sortable-loader) ## Reporting Issues From c43e2492929235894741bb2e1b025db498f768ad Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Sun, 4 Dec 2016 15:23:10 +0200 Subject: [PATCH 12/13] chore: increase version number to v0.16.0 --- bower.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index c3b158d..494249a 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-ui-sortable", - "version": "0.15.1", + "version": "0.16.0", "description": "This directive allows you to jQueryUI Sortable.", "author": "https://github.com/angular-ui/ui-sortable/graphs/contributors", "license": "MIT", diff --git a/package.json b/package.json index a52ae4c..d0df1cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-ui-sortable", - "version": "0.15.1", + "version": "0.16.0", "description": "This directive allows you to jQueryUI Sortable.", "author": "https://github.com/angular-ui/ui-sortable/graphs/contributors", "license": "MIT", From 7c3254cb059ce316539e9708e845d1a369a2c805 Mon Sep 17 00:00:00 2001 From: Thodoris Greasidis Date: Sun, 4 Dec 2016 15:55:43 +0200 Subject: [PATCH 13/13] chore(travis): use latest firefox release --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index c6d6631..7900299 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: node_js node_js: - '0.10' +addons: + firefox: "latest" before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start