Skip to content
This repository was archived by the owner on Sep 8, 2020. It is now read-only.

Commit 011293b

Browse files
committed
Merge pull request #200 from thgreasi/v0.12.8dev
chore: merge version v0.12.8 into master
2 parents d4462d5 + 9f4b2aa commit 011293b

7 files changed

+363
-10
lines changed

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-ui-sortable",
3-
"version": "0.12.7",
3+
"version": "0.12.8",
44
"description": "This directive allows you to jQueryUI Sortable.",
55
"author": "https://github.com/angular-ui/ui-sortable/graphs/contributors",
66
"license": "MIT",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-ui-sortable",
3-
"version": "0.12.7",
3+
"version": "0.12.8",
44
"description": "This directive allows you to jQueryUI Sortable.",
55
"author": "https://github.com/angular-ui/ui-sortable/graphs/contributors",
66
"license": "MIT",

src/sortable.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ angular.module('ui.sortable', [])
2323
return first;
2424
}
2525

26-
function hasSortingHelper (element) {
26+
function hasSortingHelper (element, ui) {
2727
var helperOption = element.sortable('option','helper');
28-
return helperOption === 'clone' || typeof helperOption === 'function';
28+
return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed());
2929
}
3030

3131
var opts = {};
@@ -38,6 +38,10 @@ angular.module('ui.sortable', [])
3838
update:null
3939
};
4040

41+
var wrappers = {
42+
helper: null
43+
};
44+
4145
angular.extend(opts, uiSortableConfig, scope.$eval(attrs.uiSortable));
4246

4347
if (!angular.element.fn || !angular.element.fn.jquery) {
@@ -70,7 +74,11 @@ angular.module('ui.sortable', [])
7074
isCanceled: function () {
7175
return ui.item.sortable._isCanceled;
7276
},
73-
_isCanceled: false
77+
isCustomHelperUsed: function () {
78+
return !!ui.item.sortable._isCustomHelperUsed;
79+
},
80+
_isCanceled: false,
81+
_isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed
7482
};
7583
};
7684

@@ -125,7 +133,7 @@ angular.module('ui.sortable', [])
125133
// the start and stop of repeat sections and sortable doesn't
126134
// respect their order (even if we cancel, the order of the
127135
// comments are still messed up).
128-
if (hasSortingHelper(element) && !ui.item.sortable.received) {
136+
if (hasSortingHelper(element, ui) && !ui.item.sortable.received) {
129137
// restore all the savedNodes except .ui-sortable-helper element
130138
// (which is placed last). That way it will be garbage collected.
131139
savedNodes = savedNodes.not(savedNodes.last());
@@ -161,7 +169,7 @@ angular.module('ui.sortable', [])
161169
// if the item was not moved, then restore the elements
162170
// so that the ngRepeat's comment are correct.
163171
if ((!('dropindex' in ui.item.sortable) || ui.item.sortable.isCanceled()) &&
164-
!hasSortingHelper(element)) {
172+
!hasSortingHelper(element, ui)) {
165173
savedNodes.appendTo(element);
166174
}
167175
}
@@ -174,6 +182,14 @@ angular.module('ui.sortable', [])
174182
};
175183

176184
callbacks.remove = function(e, ui) {
185+
// Workaround for a problem observed in nested connected lists.
186+
// There should be an 'update' event before 'remove' when moving
187+
// elements. If the event did not fire, cancel sorting.
188+
if (!('dropindex' in ui.item.sortable)) {
189+
element.sortable('cancel');
190+
ui.item.sortable.cancel();
191+
}
192+
177193
// Remove the item from this list's model and copy data into item,
178194
// so the next list can retrive it
179195
if (!ui.item.sortable.isCanceled()) {
@@ -184,6 +200,17 @@ angular.module('ui.sortable', [])
184200
}
185201
};
186202

203+
wrappers.helper = function (inner) {
204+
if (inner && typeof inner === 'function') {
205+
return function (e, item) {
206+
var innerResult = inner(e, item);
207+
item.sortable._isCustomHelperUsed = item !== innerResult;
208+
return innerResult;
209+
};
210+
}
211+
return inner;
212+
};
213+
187214
scope.$watch(attrs.uiSortable, function(newVal /*, oldVal*/) {
188215
// ensure that the jquery-ui-sortable widget instance
189216
// is still bound to the directive's element
@@ -197,6 +224,8 @@ angular.module('ui.sortable', [])
197224
}
198225
// wrap the callback
199226
value = combineCallbacks(callbacks[key], value);
227+
} else if (wrappers[key]) {
228+
value = wrappers[key](value);
200229
}
201230

202231
element.sortable('option', key, value);

test/sortable.e2e.callbacks.spec.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,45 @@ describe('uiSortable', function() {
6666
});
6767
});
6868

69+
it('should cancel sorting of node "Two" and "helper: function" that returns a list element is used', function() {
70+
inject(function($compile, $rootScope) {
71+
var element;
72+
element = $compile('<ul ui-sortable="opts" ng-model="items"><li ng-repeat="item in items" id="s-{{$index}}">{{ item }}</li></ul>')($rootScope);
73+
$rootScope.$apply(function() {
74+
$rootScope.opts = {
75+
update: function(e, ui) {
76+
if (ui.item.scope().item === 'Two') {
77+
ui.item.sortable.cancel();
78+
}
79+
}
80+
};
81+
$rootScope.items = ['One', 'Two', 'Three'];
82+
});
83+
84+
host.append(element);
85+
86+
var li = element.find(':eq(1)');
87+
var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
88+
li.simulate('drag', { dy: dy });
89+
expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
90+
expect($rootScope.items).toEqual(listContent(element));
91+
92+
li = element.find(':eq(0)');
93+
dy = (2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
94+
li.simulate('drag', { dy: dy });
95+
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
96+
expect($rootScope.items).toEqual(listContent(element));
97+
98+
li = element.find(':eq(2)');
99+
dy = -(2 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
100+
li.simulate('drag', { dy: dy });
101+
expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
102+
expect($rootScope.items).toEqual(listContent(element));
103+
104+
$(element).remove();
105+
});
106+
});
107+
69108
it('should cancel sorting of nodes that contain "Two"', function() {
70109
inject(function($compile, $rootScope) {
71110
var elementTop, elementBottom;

test/sortable.e2e.multi.spec.js

Lines changed: 189 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ describe('uiSortable', function() {
66
beforeEach(module('ui.sortable'));
77
beforeEach(module('ui.sortable.testHelper'));
88

9-
var EXTRA_DY_PERCENTAGE, listContent;
9+
var EXTRA_DY_PERCENTAGE, listContent, listInnerContent;
1010

1111
beforeEach(inject(function (sortableTestHelper) {
1212
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
1313
listContent = sortableTestHelper.listContent;
14+
listInnerContent = sortableTestHelper.listInnerContent;
1415
}));
1516

1617
describe('Multiple sortables related', function() {
@@ -346,6 +347,193 @@ describe('uiSortable', function() {
346347
});
347348
});
348349

350+
it('should work when "helper: function" that returns a list element is used', function() {
351+
inject(function($compile, $rootScope) {
352+
var elementTop, elementBottom;
353+
elementTop = $compile('<ul ui-sortable="opts" class="cross-sortable" ng-model="itemsTop"><li ng-repeat="item in itemsTop" id="s-top-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
354+
elementBottom = $compile('<ul ui-sortable="opts" class="cross-sortable" ng-model="itemsBottom"><li ng-repeat="item in itemsBottom" id="s-bottom-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
355+
$rootScope.$apply(function() {
356+
$rootScope.itemsTop = ['Top One', 'Top Two', 'Top Three'];
357+
$rootScope.itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three'];
358+
$rootScope.opts = {
359+
helper: function (e, item) {
360+
return item;
361+
},
362+
connectWith: '.cross-sortable'
363+
};
364+
});
365+
366+
host.append(elementTop).append(elementBottom);
367+
368+
var li1 = elementTop.find(':eq(0)');
369+
var li2 = elementBottom.find(':eq(0)');
370+
var dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
371+
li1.simulate('drag', { dy: dy });
372+
expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three']);
373+
expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Top One', 'Bottom Two', 'Bottom Three']);
374+
expect($rootScope.itemsTop).toEqual(listContent(elementTop));
375+
expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
376+
377+
li1 = elementBottom.find(':eq(1)');
378+
li2 = elementTop.find(':eq(1)');
379+
dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
380+
li1.simulate('drag', { dy: dy });
381+
expect($rootScope.itemsTop).toEqual(['Top Two', 'Top One', 'Top Three']);
382+
expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
383+
expect($rootScope.itemsTop).toEqual(listContent(elementTop));
384+
expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
385+
386+
$(elementTop).remove();
387+
$(elementBottom).remove();
388+
});
389+
});
390+
391+
it('should work when "placeholder" and "helper: function" that returns a list element are used', function() {
392+
inject(function($compile, $rootScope) {
393+
var elementTop, elementBottom;
394+
elementTop = $compile('<ul ui-sortable="opts" class="cross-sortable" ng-model="itemsTop"><li ng-repeat="item in itemsTop" id="s-top-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
395+
elementBottom = $compile('<ul ui-sortable="opts" class="cross-sortable" ng-model="itemsBottom"><li ng-repeat="item in itemsBottom" id="s-bottom-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
396+
$rootScope.$apply(function() {
397+
$rootScope.itemsTop = ['Top One', 'Top Two', 'Top Three'];
398+
$rootScope.itemsBottom = ['Bottom One', 'Bottom Two', 'Bottom Three'];
399+
$rootScope.opts = {
400+
helper: function (e, item) {
401+
return item;
402+
},
403+
placeholder: 'sortable-item-placeholder',
404+
connectWith: '.cross-sortable'
405+
};
406+
});
407+
408+
host.append(elementTop).append(elementBottom);
409+
410+
var li1 = elementTop.find(':eq(0)');
411+
var li2 = elementBottom.find(':eq(0)');
412+
var dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
413+
li1.simulate('drag', { dy: dy });
414+
expect($rootScope.itemsTop).toEqual(['Top Two', 'Top Three']);
415+
expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Top One', 'Bottom Two', 'Bottom Three']);
416+
expect($rootScope.itemsTop).toEqual(listContent(elementTop));
417+
expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
418+
419+
li1 = elementBottom.find(':eq(1)');
420+
li2 = elementTop.find(':eq(1)');
421+
dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() - (li1.position().top - li2.position().top);
422+
li1.simulate('drag', { dy: dy });
423+
expect($rootScope.itemsTop).toEqual(['Top Two', 'Top One', 'Top Three']);
424+
expect($rootScope.itemsBottom).toEqual(['Bottom One', 'Bottom Two', 'Bottom Three']);
425+
expect($rootScope.itemsTop).toEqual(listContent(elementTop));
426+
expect($rootScope.itemsBottom).toEqual(listContent(elementBottom));
427+
428+
$(elementTop).remove();
429+
$(elementBottom).remove();
430+
});
431+
});
432+
433+
it('should update model when sorting between nested sortables', function() {
434+
inject(function($compile, $rootScope) {
435+
var elementTree, li1, li2, dy;
436+
437+
elementTree = $compile(''.concat(
438+
'<ul ui-sortable="sortableOptions" ng-model="items" class="apps-container outterList" style="float: left;margin-left: 10px;padding-bottom: 10px;">',
439+
'<li ng-repeat="item in items">',
440+
'<div>',
441+
'<span class="itemContent lvl1ItemContent">{{item.text}}</span>',
442+
'<ul ui-sortable="sortableOptions" ng-model="item.items" class="apps-container innerList" style="margin-left: 10px;padding-bottom: 10px;">',
443+
'<li ng-repeat="i in item.items">',
444+
'<span class="itemContent lvl2ItemContent">{{i.text}}</span>',
445+
'</li>',
446+
'</ul>',
447+
'</div>',
448+
'</li>',
449+
'</ul>',
450+
'<div style="clear: both;"></div>'))($rootScope);
451+
452+
$rootScope.$apply(function() {
453+
$rootScope.items = [
454+
{
455+
text: 'Item 1',
456+
items: []
457+
},
458+
{
459+
text: 'Item 2',
460+
items: [
461+
{ text: 'Item 2.1', items: [] },
462+
{ text: 'Item 2.2', items: [] }
463+
]
464+
}
465+
];
466+
467+
$rootScope.sortableOptions = {
468+
connectWith: '.apps-container'
469+
};
470+
});
471+
472+
host.append(elementTree);
473+
474+
// this should drag the item out of the list and
475+
// the item should return back to its original position
476+
li1 = elementTree.find('.innerList:last').find(':last');
477+
li1.simulate('drag', { dx: -200, moves: 30 });
478+
expect($rootScope.items.map(function(x){ return x.text; }))
479+
.toEqual(['Item 1', 'Item 2']);
480+
expect($rootScope.items.map(function(x){ return x.text; }))
481+
.toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));
482+
expect($rootScope.items[0].items.map(function(x){ return x.text; }))
483+
.toEqual([]);
484+
expect($rootScope.items[0].items.map(function(x){ return x.text; }))
485+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(0)'), '.lvl2ItemContent'));
486+
expect($rootScope.items[1].items.map(function(x){ return x.text; }))
487+
.toEqual(['Item 2.1', 'Item 2.2']);
488+
expect($rootScope.items[1].items.map(function(x){ return x.text; }))
489+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(1)'), '.lvl2ItemContent'));
490+
491+
// this should drag the item from the inner list and
492+
// drop it to the outter list
493+
li1 = elementTree.find('.innerList:last').find(':last');
494+
li2 = elementTree.find('> li:last');
495+
dy = EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
496+
li1.simulate('drag', { dy: dy });
497+
expect($rootScope.items.map(function(x){ return x.text; }))
498+
.toEqual(['Item 1', 'Item 2.2', 'Item 2']);
499+
expect($rootScope.items.map(function(x){ return x.text; }))
500+
.toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));
501+
expect($rootScope.items[0].items.map(function(x){ return x.text; }))
502+
.toEqual([]);
503+
expect($rootScope.items[0].items.map(function(x){ return x.text; }))
504+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(0)'), '.lvl2ItemContent'));
505+
expect($rootScope.items[1].items.map(function(x){ return x.text; }))
506+
.toEqual([]);
507+
expect($rootScope.items[1].items.map(function(x){ return x.text; }))
508+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(1)'), '.lvl2ItemContent'));
509+
expect($rootScope.items[2].items.map(function(x){ return x.text; }))
510+
.toEqual(['Item 2.1']);
511+
expect($rootScope.items[2].items.map(function(x){ return x.text; }))
512+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(2)'), '.lvl2ItemContent'));
513+
514+
// this should drag the item from the outter list and
515+
// drop it to the inner list
516+
li1 = elementTree.find('> li:first');
517+
li2 = elementTree.find('.innerList:last').find(':last');
518+
dy = -EXTRA_DY_PERCENTAGE * li1.outerHeight() + (li2.position().top - li1.position().top);
519+
li1.simulate('drag', { dy: dy });
520+
expect($rootScope.items.map(function(x){ return x.text; }))
521+
.toEqual(['Item 2.2', 'Item 2']);
522+
expect($rootScope.items.map(function(x){ return x.text; }))
523+
.toEqual(listInnerContent(elementTree, '.lvl1ItemContent'));
524+
expect($rootScope.items[0].items.map(function(x){ return x.text; }))
525+
.toEqual([]);
526+
expect($rootScope.items[0].items.map(function(x){ return x.text; }))
527+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(0)'), '.lvl2ItemContent'));
528+
expect($rootScope.items[1].items.map(function(x){ return x.text; }))
529+
.toEqual(['Item 1', 'Item 2.1']);
530+
expect($rootScope.items[1].items.map(function(x){ return x.text; }))
531+
.toEqual(listInnerContent(elementTree.find('.innerList:eq(1)'), '.lvl2ItemContent'));
532+
533+
$(elementTree).remove();
534+
});
535+
});
536+
349537
});
350538

351539
});

0 commit comments

Comments
 (0)