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

Commit 6a7579f

Browse files
committed
Merge pull request #258 from thgreasi/floating-src
feat(sortable): add workaround for horizontal lists
2 parents 5168154 + 11e0af4 commit 6a7579f

File tree

4 files changed

+244
-2
lines changed

4 files changed

+244
-2
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,41 @@ myAppModule.controller('MyController', function($scope) {
6969
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).
7070
The suggested pattern is to use callbacks for emmiting events and altering the scope (inside the 'Angular world').
7171

72+
#### Floating
73+
74+
To have a smooth horizontal-list reordering, jquery.ui.sortable needs to detect the orientation of the list.
75+
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).
76+
There is also a [known issue](bugs.jqueryui.com/ticket/7498) about initially empty horizontal lists.
77+
78+
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/).
79+
80+
```html
81+
<ul ui-sortable="{ 'ui-floating': true }" ng-model="items">
82+
<li ng-repeat="item in items">{{ item }}</li>
83+
</ul>
84+
```
85+
86+
**OR**
87+
88+
```js
89+
$scope.sortableOptions = {
90+
'ui-floating': true
91+
};
92+
```
93+
```html
94+
<ul ui-sortable="sortableOptions" ng-model="items">
95+
<li ng-repeat="item in items">{{ item }}</li>
96+
</ul>
97+
```
98+
99+
100+
**ui-floating** (default: undefined)
101+
Type: [Boolean](http://api.jquery.com/Types/#Boolean)/[String](http://api.jquery.com/Types/#String)/`undefined`
102+
* **undefined**: Relies on jquery.ui to detect the list's orientation.
103+
* **false**: Forces jquery.ui.sortable to detect the list as vertical.
104+
* **true**: Forces jquery.ui.sortable to detect the list as horizontal.
105+
* **"auto"**: Detects on each drag `start` if the element is floating or not.
106+
72107
#### Canceling
73108

74109
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://
138173
- [Filtering](http://codepen.io/thgreasi/pen/mzGbq) ([details](https://github.com/angular-ui/ui-sortable/issues/113))
139174
- [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))
140175
- [Cloning](http://codepen.io/thgreasi/pen/qmvhG) ([details](https://github.com/angular-ui/ui-sortable/issues/139))
176+
- [Horizontal List](http://codepen.io/thgreasi/pen/wsfjD)
141177
- [Tree with dynamic template](http://codepen.io/thgreasi/pen/uyHFC)
142178
- Canceling
143179
- [Connected Lists With Max Size](http://codepen.io/thgreasi/pen/IdvFc)

src/sortable.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,18 @@ angular.module('ui.sortable', [])
2828
return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed());
2929
}
3030

31+
// thanks jquery-ui
32+
function isFloating (item) {
33+
return (/left|right/).test(item.css('float')) || (/inline|table-cell/).test(item.css('display'));
34+
}
35+
3136
var opts = {};
3237

38+
// directive specific options
39+
var directiveOpts = {
40+
'ui-floating': undefined
41+
};
42+
3343
var callbacks = {
3444
receive: null,
3545
remove:null,
@@ -42,7 +52,7 @@ angular.module('ui.sortable', [])
4252
helper: null
4353
};
4454

45-
angular.extend(opts, uiSortableConfig, scope.$eval(attrs.uiSortable));
55+
angular.extend(opts, directiveOpts, uiSortableConfig, scope.$eval(attrs.uiSortable));
4656

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

6777
callbacks.start = function(e, ui) {
78+
if (opts['ui-floating'] === 'auto') {
79+
// since the drag has started, the element will be
80+
// absolutely positioned, so we check its siblings
81+
var siblings = ui.item.siblings();
82+
angular.element(e.target).data('ui-sortable').floating = isFloating(siblings);
83+
}
84+
6885
// Save the starting position of dragged item
6986
ui.item.sortable = {
7087
index: ui.item.index(),
@@ -228,7 +245,18 @@ angular.module('ui.sortable', [])
228245
// is still bound to the directive's element
229246
if (!!element.data('ui-sortable')) {
230247
angular.forEach(newVal, function(value, key) {
231-
if(callbacks[key]) {
248+
// if it's a custom option of the directive,
249+
// handle it approprietly
250+
if (key in directiveOpts) {
251+
if (key === 'ui-floating' && (value === false || value === true)) {
252+
element.data('ui-sortable').floating = value;
253+
}
254+
255+
opts[key] = value;
256+
return;
257+
}
258+
259+
if (callbacks[key]) {
232260
if( key === 'stop' ){
233261
// call apply after stop
234262
value = combineCallbacks(
@@ -240,6 +268,7 @@ angular.module('ui.sortable', [])
240268
value = wrappers[key](value);
241269
}
242270

271+
opts[key] = value;
243272
element.sortable('option', key, value);
244273
});
245274
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
'use strict';
2+
3+
describe('uiSortable', function() {
4+
5+
// Ensure the sortable angular module is loaded
6+
beforeEach(module('ui.sortable'));
7+
beforeEach(module('ui.sortable.testHelper'));
8+
9+
var EXTRA_DY_PERCENTAGE, listContent;
10+
11+
beforeEach(inject(function (sortableTestHelper) {
12+
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
13+
listContent = sortableTestHelper.listContent;
14+
}));
15+
16+
describe('Custom directive options related', function() {
17+
18+
var host;
19+
20+
beforeEach(inject(function() {
21+
host = $('<div id="test-host"></div>');
22+
$('body').append(host);
23+
}));
24+
25+
afterEach(function() {
26+
host.remove();
27+
host = null;
28+
});
29+
30+
it('should work when "ui-floating: false" option is used', function() {
31+
inject(function($compile, $rootScope) {
32+
var element;
33+
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);
34+
$rootScope.$apply(function() {
35+
$rootScope.opts = {
36+
'ui-floating': false
37+
};
38+
$rootScope.items = ['One', 'Two', 'Three'];
39+
});
40+
41+
host.append(element);
42+
43+
var li = element.find(':eq(0)');
44+
var dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
45+
li.simulate('drag', { dy: dy });
46+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
47+
expect($rootScope.items).toEqual(listContent(element));
48+
49+
li = element.find(':eq(1)');
50+
dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
51+
li.simulate('drag', { dy: dy });
52+
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
53+
expect($rootScope.items).toEqual(listContent(element));
54+
55+
li = element.find(':eq(1)');
56+
dy = (1 + EXTRA_DY_PERCENTAGE) * li.outerHeight();
57+
li.simulate('drag', { dy: dy });
58+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
59+
expect($rootScope.items).toEqual(listContent(element));
60+
61+
$(element).remove();
62+
});
63+
});
64+
65+
it('should work when "ui-floating: true" option is used', function() {
66+
inject(function($compile, $rootScope) {
67+
var element;
68+
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);
69+
$rootScope.$apply(function() {
70+
$rootScope.opts = {
71+
'ui-floating': true
72+
};
73+
$rootScope.items = ['One', 'Two', 'Three'];
74+
});
75+
76+
host.append(element).append('<div class="clear"></div>');
77+
78+
var li = element.find(':eq(0)');
79+
var dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
80+
li.simulate('drag', { dx: dx });
81+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
82+
expect($rootScope.items).toEqual(listContent(element));
83+
84+
li = element.find(':eq(1)');
85+
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
86+
li.simulate('drag', { dx: dx });
87+
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
88+
expect($rootScope.items).toEqual(listContent(element));
89+
90+
li = element.find(':eq(1)');
91+
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
92+
li.simulate('drag', { dx: dx, moves: 5 });
93+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
94+
expect($rootScope.items).toEqual(listContent(element));
95+
96+
$(element).remove();
97+
});
98+
});
99+
100+
it('should work when "ui-floating: \'auto\'" option is used and elements are "float"ing', function() {
101+
inject(function($compile, $rootScope) {
102+
var element;
103+
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);
104+
$rootScope.$apply(function() {
105+
$rootScope.opts = {
106+
'ui-floating': 'auto'
107+
};
108+
$rootScope.items = ['One', 'Two', 'Three'];
109+
});
110+
111+
host.append(element).append('<div class="clear"></div>');
112+
113+
var li = element.find(':eq(0)');
114+
var dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
115+
li.simulate('drag', { dx: dx });
116+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
117+
expect($rootScope.items).toEqual(listContent(element));
118+
119+
li = element.find(':eq(1)');
120+
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
121+
li.simulate('drag', { dx: dx });
122+
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
123+
expect($rootScope.items).toEqual(listContent(element));
124+
125+
li = element.find(':eq(1)');
126+
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
127+
li.simulate('drag', { dx: dx, moves: 5 });
128+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
129+
expect($rootScope.items).toEqual(listContent(element));
130+
131+
$(element).remove();
132+
});
133+
});
134+
135+
it('should work when "ui-floating: \'auto\'" option is used and elements are "display: inline-block"', function() {
136+
inject(function($compile, $rootScope) {
137+
var element;
138+
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);
139+
$rootScope.$apply(function() {
140+
$rootScope.opts = {
141+
'ui-floating': 'auto'
142+
};
143+
$rootScope.items = ['One', 'Two', 'Three'];
144+
});
145+
146+
host.append(element);
147+
148+
var li = element.find(':eq(0)');
149+
var dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
150+
li.simulate('drag', { dx: dx });
151+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
152+
expect($rootScope.items).toEqual(listContent(element));
153+
154+
li = element.find(':eq(1)');
155+
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
156+
li.simulate('drag', { dx: dx });
157+
expect($rootScope.items).toEqual(['Two', 'Three', 'One']);
158+
expect($rootScope.items).toEqual(listContent(element));
159+
160+
li = element.find(':eq(1)');
161+
dx = (1 + EXTRA_DY_PERCENTAGE) * li.outerWidth();
162+
li.simulate('drag', { dx: dx, moves: 5 });
163+
expect($rootScope.items).toEqual(['Two', 'One', 'Three']);
164+
expect($rootScope.items).toEqual(listContent(element));
165+
166+
$(element).remove();
167+
});
168+
});
169+
170+
});
171+
172+
});

test/sortable.tests.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
.inline-block {
2+
display: inline-block;
3+
}
4+
5+
.floatleft,
16
.cross-sortable {
27
float: left;
38
}

0 commit comments

Comments
 (0)