diff --git a/README.md b/README.md
index a2c445a..7574742 100644
--- a/README.md
+++ b/README.md
@@ -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
+
+```
+
+**OR**
+
+```js
+$scope.sortableOptions = {
+ 'ui-floating': true
+};
+```
+```html
+
+```
+
+
+**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.
@@ -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)
diff --git a/src/sortable.js b/src/sortable.js
index a18f046..616d239 100644
--- a/src/sortable.js
+++ b/src/sortable.js
@@ -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,
@@ -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!');
@@ -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(),
@@ -228,7 +245,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(
@@ -240,6 +268,7 @@ angular.module('ui.sortable', [])
value = wrappers[key](value);
}
+ opts[key] = value;
element.sortable('option', key, value);
});
}
diff --git a/test/sortable.e2e.directiveoptions.spec.js b/test/sortable.e2e.directiveoptions.spec.js
new file mode 100644
index 0000000..bd2461c
--- /dev/null
+++ b/test/sortable.e2e.directiveoptions.spec.js
@@ -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 = $('');
+ $('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('')($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('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ 'ui-floating': true
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element).append('');
+
+ 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('')($rootScope);
+ $rootScope.$apply(function() {
+ $rootScope.opts = {
+ 'ui-floating': 'auto'
+ };
+ $rootScope.items = ['One', 'Two', 'Three'];
+ });
+
+ host.append(element).append('');
+
+ 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('')($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();
+ });
+ });
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/sortable.tests.css b/test/sortable.tests.css
index 0d7bb75..6601ca0 100644
--- a/test/sortable.tests.css
+++ b/test/sortable.tests.css
@@ -1,3 +1,8 @@
+.inline-block {
+ display: inline-block;
+}
+
+.floatleft,
.cross-sortable {
float: left;
}