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

chore rebase to v0.12.11 #266

Merged
merged 9 commits into from
Sep 7, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,41 @@ myAppModule.controller('MyController', function($scope) {
When using event callbacks ([start](http://api.jqueryui.com/sortable/#event-start)/[update](http://api.jqueryui.com/sortable/#event-update)/[stop](http://api.jqueryui.com/sortable/#event-stop)...), avoid manipulating DOM elements (especially the one with the ng-repeat attached).
The suggested pattern is to use callbacks for emmiting events and altering the scope (inside the 'Angular world').

#### Floating

To have a smooth horizontal-list reordering, jquery.ui.sortable needs to detect the orientation of the list.
This detection takes place during the initialization of the plugin (and some of the checks include: whether the first item is floating left/right or if 'axis' parameter is 'x', etc).
There is also a [known issue](bugs.jqueryui.com/ticket/7498) about initially empty horizontal lists.

To provide a solution/workaround (till jquery.ui.sortable.refresh() also tests the orientation or a more appropriate method is provided), ui-sortable directive provides a `ui-floating` option as an extra to the [jquery.ui.sortable options](http://api.jqueryui.com/sortable/).

```html
<ul ui-sortable="{ 'ui-floating': true }" ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
```

**OR**

```js
$scope.sortableOptions = {
'ui-floating': true
};
```
```html
<ul ui-sortable="sortableOptions" ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
```


**ui-floating** (default: undefined)
Type: [Boolean](http://api.jquery.com/Types/#Boolean)/[String](http://api.jquery.com/Types/#String)/`undefined`
* **undefined**: Relies on jquery.ui to detect the list's orientation.
* **false**: Forces jquery.ui.sortable to detect the list as vertical.
* **true**: Forces jquery.ui.sortable to detect the list as horizontal.
* **"auto"**: Detects on each drag `start` if the element is floating or not.

#### Canceling

Inside the `update` callback, you can check the item that is dragged and cancel the sorting.
Expand Down Expand Up @@ -138,6 +173,7 @@ For more details about the events check the [jQueryUI API documentation](http://
- [Filtering](http://codepen.io/thgreasi/pen/mzGbq) ([details](https://github.com/angular-ui/ui-sortable/issues/113))
- [Ordering 1](http://codepen.io/thgreasi/pen/iKEHd) & [Ordering 2](http://plnkr.co/edit/XPUzJjdvwE0QWQ6py6mQ?p=preview) ([details](https://github.com/angular-ui/ui-sortable/issues/70))
- [Cloning](http://codepen.io/thgreasi/pen/qmvhG) ([details](https://github.com/angular-ui/ui-sortable/issues/139))
- [Horizontal List](http://codepen.io/thgreasi/pen/wsfjD)
- [Tree with dynamic template](http://codepen.io/thgreasi/pen/uyHFC)
- Canceling
- [Connected Lists With Max Size](http://codepen.io/thgreasi/pen/IdvFc)
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-ui-sortable",
"version": "0.12.10",
"version": "0.12.11",
"description": "This directive allows you to jQueryUI Sortable.",
"author": "https://github.com/angular-ui/ui-sortable/graphs/contributors",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-ui-sortable",
"version": "0.12.10",
"version": "0.12.11",
"description": "This directive allows you to jQueryUI Sortable.",
"author": "https://github.com/angular-ui/ui-sortable/graphs/contributors",
"license": "MIT",
Expand Down
36 changes: 33 additions & 3 deletions src/sortable.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,18 @@ angular.module('ui.sortable', [])
return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed());
}

// thanks jquery-ui
function isFloating (item) {
return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display'));
}

var opts = {};

// directive specific options
var directiveOpts = {
'ui-floating': undefined
};

var callbacks = {
receive: null,
remove:null,
Expand All @@ -42,7 +52,7 @@ angular.module('ui.sortable', [])
helper: null
};

angular.extend(opts, uiSortableConfig, scope.$eval(attrs.uiSortable));
angular.extend(opts, directiveOpts, uiSortableConfig, scope.$eval(attrs.uiSortable));

if (!angular.element.fn || !angular.element.fn.jquery) {
$log.error('ui.sortable: jQuery should be included before AngularJS!');
Expand All @@ -65,6 +75,13 @@ angular.module('ui.sortable', [])
});

callbacks.start = function(e, ui) {
if (opts['ui-floating'] === 'auto') {
// since the drag has started, the element will be
// absolutely positioned, so we check its siblings
var siblings = ui.item.siblings();
angular.element(e.target).data('ui-sortable').floating = isFloating(siblings);
}

// Save the starting position of dragged item
ui.item.sortable = {
index: ui.item.index(),
Expand Down Expand Up @@ -133,7 +150,8 @@ angular.module('ui.sortable', [])
// the start and stop of repeat sections and sortable doesn't
// respect their order (even if we cancel, the order of the
// comments are still messed up).
if (hasSortingHelper(element, ui) && !ui.item.sortable.received) {
if (hasSortingHelper(element, ui) && !ui.item.sortable.received &&
element.sortable( 'option', 'appendTo' ) === 'parent') {
// restore all the savedNodes except .ui-sortable-helper element
// (which is placed last). That way it will be garbage collected.
savedNodes = savedNodes.not(savedNodes.last());
Expand Down Expand Up @@ -228,7 +246,18 @@ angular.module('ui.sortable', [])
// is still bound to the directive's element
if (!!element.data('ui-sortable')) {
angular.forEach(newVal, function(value, key) {
if(callbacks[key]) {
// if it's a custom option of the directive,
// handle it approprietly
if (key in directiveOpts) {
if (key === 'ui-floating' && (value === false || value === true)) {
element.data('ui-sortable').floating = value;
}

opts[key] = value;
return;
}

if (callbacks[key]) {
if( key === 'stop' ){
// call apply after stop
value = combineCallbacks(
Expand All @@ -240,6 +269,7 @@ angular.module('ui.sortable', [])
value = wrappers[key](value);
}

opts[key] = value;
element.sortable('option', key, value);
});
}
Expand Down
172 changes: 172 additions & 0 deletions test/sortable.e2e.directiveoptions.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
'use strict';

describe('uiSortable', function() {

// Ensure the sortable angular module is loaded
beforeEach(module('ui.sortable'));
beforeEach(module('ui.sortable.testHelper'));

var EXTRA_DY_PERCENTAGE, listContent;

beforeEach(inject(function (sortableTestHelper) {
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
listContent = sortableTestHelper.listContent;
}));

describe('Custom directive options related', function() {

var host;

beforeEach(inject(function() {
host = $('<div id="test-host"></div>');
$('body').append(host);
}));

afterEach(function() {
host.remove();
host = null;
});

it('should work when "ui-floating: false" option is used', function() {
inject(function($compile, $rootScope) {
var element;
element = $compile('<ul ui-sortable="opts" ng-model="items"><li ng-repeat="item in items" id="s-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
$rootScope.$apply(function() {
$rootScope.opts = {
'ui-floating': false
};
$rootScope.items = ['One', 'Two', 'Three'];
});

host.append(element);

var li = element.find(':eq(0)');
var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

$(element).remove();
});
});

it('should work when "ui-floating: true" option is used', function() {
inject(function($compile, $rootScope) {
var element;
element = $compile('<ul ui-sortable="opts" ng-model="items"><li class="floatleft" ng-repeat="item in items" id="s-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
$rootScope.$apply(function() {
$rootScope.opts = {
'ui-floating': true
};
$rootScope.items = ['One', 'Two', 'Three'];
});

host.append(element).append('<div class="clear"></div>');

var li = element.find(':eq(0)');
var dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx });
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx, moves: 5 });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

$(element).remove();
});
});

it('should work when "ui-floating: \'auto\'" option is used and elements are "float"ing', function() {
inject(function($compile, $rootScope) {
var element;
element = $compile('<ul ui-sortable="opts" ng-model="items"><li class="floatleft" ng-repeat="item in items" id="s-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
$rootScope.$apply(function() {
$rootScope.opts = {
'ui-floating': 'auto'
};
$rootScope.items = ['One', 'Two', 'Three'];
});

host.append(element).append('<div class="clear"></div>');

var li = element.find(':eq(0)');
var dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx });
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx, moves: 5 });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

$(element).remove();
});
});

it('should work when "ui-floating: \'auto\'" option is used and elements are "display: inline-block"', function() {
inject(function($compile, $rootScope) {
var element;
element = $compile('<ul ui-sortable="opts" ng-model="items"><li class="inline-block" ng-repeat="item in items" id="s-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
$rootScope.$apply(function() {
$rootScope.opts = {
'ui-floating': 'auto'
};
$rootScope.items = ['One', 'Two', 'Three'];
});

host.append(element);

var li = element.find(':eq(0)');
var dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx });
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
expect($rootScope.items).toEqual(listContent(element));

li = element.find(':eq(1)');
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
li.simulate('drag', { dx: dx, moves: 5 });
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

$(element).remove();
});
});

});

});
32 changes: 31 additions & 1 deletion test/sortable.e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,36 @@ describe('uiSortable', function() {
});
});

it('should work when "helper: clone" and "appendTo" options are used together', function() {
inject(function($compile, $rootScope) {
var element;
element = $compile('<ul ui-sortable="opts" ng-model="items"><li ng-repeat="item in items" id="s-{{$index}}" class="sortable-item">{{ item }}</li></ul>')($rootScope);
$rootScope.$apply(function() {
$rootScope.opts = {
helper: 'clone',
appendTo: 'body'
};
$rootScope.items = ['One', 'Two', 'Three'];
});

host.append(element);

var li = element.find(':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(listContent(element));

li = element.find(':eq(2)');
dy = -(1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
li.simulate('drag', { dy: dy });
expect($rootScope.items).toEqual(['One', 'Two', 'Three']);
expect($rootScope.items).toEqual(listContent(element));

$(element).remove();
});
});

it('should work when "helper: clone" and "placeholder" options are used together.', function() {
inject(function($compile, $rootScope) {
var element;
Expand Down Expand Up @@ -648,4 +678,4 @@ describe('uiSortable', function() {

});

});
});
5 changes: 5 additions & 0 deletions test/sortable.tests.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.inline-block {
display: inline-block;
}

.floatleft,
.cross-sortable {
float: left;
}
Expand Down