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

Commit

Permalink
Merge 9be428b into 17c4158
Browse files Browse the repository at this point in the history
  • Loading branch information
thgreasi committed Nov 26, 2017
2 parents 17c4158 + 9be428b commit e0867a9
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 21 deletions.
11 changes: 11 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# ui.item.sortable API documentation

This refers to the additional properties that are exposed through the `ui` parameter in the provided callback hooks. eg:
```js
$scope.sortableOptions = {
update: function(e, ui) {
if (ui.item.sortable.model == "can't be moved") {
ui.item.sortable.cancel();
}
}
};
```

## Properties

**Note:**
Expand Down
36 changes: 27 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,15 @@ 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
#### ui-floating

**ui-floating** (default: undefined)
Description: Enables a workaround for smooth horizontal sorting.
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.

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).
Expand Down Expand Up @@ -118,14 +126,24 @@ $scope.sortableOptions = {
```


**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.
#### ui-model-items

**ui-model-items** (default: `> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]`)
Description: Defines which elements should be considered as part of your model.
Type: [CSS selector](http://api.jquery.com/Types/#Selector)/[String](http://api.jquery.com/Types/#String)

This is the model related counterpart option of [jQuery's items option](http://api.jqueryui.com/sortable/#option-items).

#### ui-preserve-size

**ui-preserve-size** (default: undefined)
Description: Set's the size of the sorting helper to the size of the original element before the sorting.
Type: [Boolean](http://api.jquery.com/Types/#Boolean)/`undefined`

This is useful for elements that their size is dependent to other page characteristics.
A representative example of such cases are `<table>` `<tr>`s and `<td>`s.

#### Attributes For Event Handling
### Attributes For Event Handling

To handle events with html bindings just define any expression to listed event attributes.
If you defined an attribute for this events and defined callback function in sortableOptions at the same time, the attribute based callback will be called first.
Expand Down Expand Up @@ -161,7 +179,7 @@ $scope.sortableOptions = {
</ul>
```

#### Canceling
### Canceling

Inside the `update` callback, you can check the item that is dragged and cancel the sorting.

Expand Down
55 changes: 44 additions & 11 deletions src/sortable.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ angular.module('ui.sortable', [])
return null;
}

function setItemChildrenWidth(item) {
item.children().each(function() {
var $el = angular.element(this);

// Preserve the with of the element
$el.width($el.width());
});
}

function dummyHelper(e, item) {
return item;
}

function patchSortableOption(key, value) {
if (callbacks[key]) {
if( key === 'stop' ){
Expand All @@ -85,7 +98,8 @@ angular.module('ui.sortable', [])
return value;
}

function patchUISortableOptions(newVal, oldVal, sortableWidgetInstance) {
function patchUISortableOptions(newOpts, oldOpts, sortableWidgetInstance) {

function addDummyOptionKey(value, key) {
if (!(key in opts)) {
// add the key in the opts object so that
Expand All @@ -100,11 +114,11 @@ angular.module('ui.sortable', [])
// update some options of the sortable
var optsDiff = null;

if (oldVal) {
if (oldOpts) {
// reset deleted options to default
var defaultOptions;
angular.forEach(oldVal, function(oldValue, key) {
if (!newVal || !(key in newVal)) {
angular.forEach(oldOpts, function(oldValue, key) {
if (!newOpts || !(key in newOpts)) {
if (key in directiveOpts) {
if (key === 'ui-floating') {
opts[key] = 'auto';
Expand All @@ -129,16 +143,33 @@ angular.module('ui.sortable', [])
});
}

newOpts = angular.extend({}, newOpts);
// update changed options
angular.forEach(newVal, function(value, key) {
// if it's a custom option of the directive,
// handle it approprietly
// handle the custom option of the directive first
angular.forEach(newOpts, function(value, key) {
if (key in directiveOpts) {
if (key === 'ui-floating' && (value === false || value === true) && sortableWidgetInstance) {
sortableWidgetInstance.floating = value;
}

if (key === 'ui-preserve-size' && (value === false || value === true)) {
var userProvidedHelper = opts.helper;
newOpts.helper = function(e, item) {
if (opts['ui-preserve-size'] === true) {
setItemChildrenWidth(item);
}
return (userProvidedHelper || dummyHelper).apply(this, arguments);
};
}

opts[key] = patchSortableOption(key, value);
}
});

// handle the normal option of the directive
angular.forEach(newOpts, function(value, key) {
if (key in directiveOpts) {
// the custom option of the directive are already handled
return;
}

Expand Down Expand Up @@ -197,7 +228,7 @@ angular.module('ui.sortable', [])
}

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

Expand Down Expand Up @@ -228,7 +259,8 @@ angular.module('ui.sortable', [])
// directive specific options
var directiveOpts = {
'ui-floating': undefined,
'ui-model-items': uiSortableConfig.items
'ui-model-items': uiSortableConfig.items,
'ui-preserve-size': undefined
};

var callbacks = {
Expand Down Expand Up @@ -480,6 +512,7 @@ angular.module('ui.sortable', [])
return function (e, item) {
var oldItemSortable = item.sortable;
var index = getItemIndex(item);

item.sortable = {
model: ngModel.$modelValue[index],
index: index,
Expand All @@ -504,12 +537,12 @@ angular.module('ui.sortable', [])
return inner;
};

scope.$watchCollection('uiSortable', function(newVal, oldVal) {
scope.$watchCollection('uiSortable', function(newOpts, oldOpts) {
// ensure that the jquery-ui-sortable widget instance
// is still bound to the directive's element
var sortableWidgetInstance = getSortableWidgetInstance(element);
if (!!sortableWidgetInstance) {
var optsDiff = patchUISortableOptions(newVal, oldVal, sortableWidgetInstance);
var optsDiff = patchUISortableOptions(newOpts, oldOpts, sortableWidgetInstance);

if (optsDiff) {
element.sortable('option', optsDiff);
Expand Down
100 changes: 99 additions & 1 deletion test/sortable.e2e.directiveoptions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ describe('uiSortable', function() {
beforeEach(module('ui.sortable'));
beforeEach(module('ui.sortable.testHelper'));

var EXTRA_DY_PERCENTAGE, listContent, beforeLiElement, afterLiElement;
var EXTRA_DY_PERCENTAGE, listContent, beforeLiElement, afterLiElement, beforeTrElement, afterTrElement;

beforeEach(inject(function (sortableTestHelper) {
EXTRA_DY_PERCENTAGE = sortableTestHelper.EXTRA_DY_PERCENTAGE;
listContent = sortableTestHelper.listContent;
beforeLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeLiElement;
afterLiElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterLiElement;
beforeTrElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.beforeTrElement;
afterTrElement = sortableTestHelper.extraElements && sortableTestHelper.extraElements.afterTrElement;
}));

tests.description = 'Custom directive options related';
Expand All @@ -32,6 +34,7 @@ describe('uiSortable', function() {

if (!useExtraElements) {
beforeLiElement = afterLiElement = '';
beforeTrElement = afterTrElement = '';
}
}));

Expand Down Expand Up @@ -393,6 +396,101 @@ describe('uiSortable', function() {
});
});

it('should work when the "ui-preserve-size" option is used', function() {
inject(function($compile, $rootScope) {
var width = '200px';
var element;
element = $compile(''.concat(
'<table>',
'<tbody><tr><td style="width: ' + width + ';"></td></tr></tbody>',
'<tbody ui-sortable="opts" ng-model="items">',
beforeTrElement,
'<tr ng-repeat="item in items" id="s-{{$index}}">',
'<td class="sortable-item">{{ item }}</td>',
'</tr>',
afterTrElement,
'</tbody>',
'</table>'
))($rootScope);

var itemsSelector = '.sortable-item';
$rootScope.$apply(function() {
$rootScope.opts = {
'ui-preserve-size': true,
stop: function(e, ui) {
expect(ui.item.children().css('width')).toEqual(width);
}
};
$rootScope.items = ['One', 'Two', 'Three'];
});

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

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

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

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

it('should work when the "ui-preserve-size" & helper options are used', function() {
inject(function($compile, $rootScope) {
var width = '200px';
var element;
element = $compile(''.concat(
'<table>',
'<tbody><tr><td style="width: ' + width + ';"></td></tr></tbody>',
'<tbody ui-sortable="opts" ng-model="items">',
beforeTrElement,
'<tr ng-repeat="item in items" id="s-{{$index}}">',
'<td class="sortable-item">{{ item }}</td>',
'</tr>',
afterTrElement,
'</tbody>',
'</table>'
))($rootScope);

var itemsSelector = '.sortable-item';
$rootScope.$apply(function() {
$rootScope.opts = {
'ui-preserve-size': true,
helper: function (e, item) {
return item.clone();
},
stop: function(e, ui) {
expect(ui.item.children().css('width')).toEqual(width);
}
};
$rootScope.items = ['One', 'Two', 'Three'];
});

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

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

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

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

}

[0, 1].forEach(function(useExtraElements){
Expand Down
2 changes: 2 additions & 0 deletions test/sortable.test-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ angular.module('ui.sortable.testHelper', [])
extraElements: {
beforeLiElement: '<li>extra element</li>',
afterLiElement: '<li>extra element</li>',
beforeTrElement: '<tr><td>extra element</td></tr>',
afterTrElement: '<tr><td>extra element</td></tr>',
beforeDivElement: '<div>extra element</div>',
afterDivElement: '<div>extra element</div>'
}
Expand Down

0 comments on commit e0867a9

Please sign in to comment.