Browse Source

+ onSort; * fixed onEnd; + Support AngularJS

pull/126/merge
RubaXa 10 years ago
parent
commit
164f837e40
  1. 42
      README.md
  2. 28
      Sortable.js
  3. 2
      Sortable.min.js
  4. 96
      index.html
  5. 115
      ng-sortable.js
  6. 9
      st/app.css

42
README.md

@ -5,6 +5,7 @@
* Support touch devices and [modern](http://caniuse.com/#search=drag) browsers * Support touch devices and [modern](http://caniuse.com/#search=drag) browsers
* Animation moving items when sorting (css animation) * Animation moving items when sorting (css animation)
* Built using native HTML5 drag and drop API * Built using native HTML5 drag and drop API
* Support [AngularJS](#ng)
* Simple API * Simple API
* Lightweight, 2KB gzipped * Lightweight, 2KB gzipped
* No jQuery * No jQuery
@ -44,14 +45,23 @@ var sortabel = new Sortable(el, {
onStart: function (/**Event*/evt) { /* dragging */ }, onStart: function (/**Event*/evt) { /* dragging */ },
onEnd: function (/**Event*/evt) { /* dragging */ }, onEnd: function (/**Event*/evt) { /* dragging */ },
// Element is added to the list
onAdd: function (/**Event*/evt){ onAdd: function (/**Event*/evt){
var itemEl = evt.item; // dragged HTMLElement var itemEl = evt.item; // dragged HTMLElement
itemEl.from; // previous list
}, },
// Changed sorting in list
onUpdate: function (/**Event*/evt){ onUpdate: function (/**Event*/evt){
var itemEl = evt.item; // dragged HTMLElement var itemEl = evt.item; // dragged HTMLElement
}, },
// Called by any change to the list (add / update / remove)
onSort: function (/**Event*/evt){
var itemEl = evt.item; // dragged HTMLElement
},
// The element is removed from the list
onRemove: function (/**Event*/evt){ onRemove: function (/**Event*/evt){
var itemEl = evt.item; // dragged HTMLElement var itemEl = evt.item; // dragged HTMLElement
}, },
@ -74,6 +84,38 @@ var sortabel = new Sortable(el, {
--- ---
<a name="ng"></a>
### Support AngularJS
Include [ng-sortable.js](ng-sortable.js)
```html
<div ng-app"myApp">
<ul ng-sortable>
<li ng-repeat="item in items">{{item}}</li>
</ul>
<ul ng-sortable="{ group: 'foobar' }">
<li ng-repeat="item in foo">{{item}}</li>
</ul>
<ul ng-sortable="barConfig">
<li ng-repeat="item in bar">{{item}}</li>
</ul>
</div>
```
```js
angular.module('myApp', ['ng-sortable'])
.controller(function () {
this.items = ['item 1', 'item 2'];
this.foo = ['foo 1', '..'];
this.bar = ['bar 1', '..'];
this.barConfig = { group: 'foobar', animation: 150 };
});
```
---
### Method ### Method

28
Sortable.js

@ -44,18 +44,17 @@
, _silent = false , _silent = false
, _createEvent = function (event/**String*/, item/**HTMLElement*/){ , _dispatchEvent = function (rootEl, name, targetEl, fromEl) {
var evt = document.createEvent('Event'); var evt = document.createEvent('Event');
evt.initEvent(event, true, true);
evt.item = item;
return evt;
}
, _dispatchEvent = function (rootEl, name, targetEl) { evt.initEvent(name, true, true);
rootEl.dispatchEvent(_createEvent(name, targetEl || rootEl)); evt.item = targetEl || rootEl;
evt.from = fromEl || rootEl;
rootEl.dispatchEvent(evt);
} }
, _customEvents = 'onAdd onUpdate onRemove onStart onEnd onFilter'.split(' ') , _customEvents = 'onAdd onUpdate onRemove onStart onEnd onFilter onSort'.split(' ')
, noop = function (){} , noop = function (){}
, slice = [].slice , slice = [].slice
@ -485,18 +484,22 @@
_toggleClass(dragEl, this.options.ghostClass, false); _toggleClass(dragEl, this.options.ghostClass, false);
if( !rootEl.contains(dragEl) ){ if( !rootEl.contains(dragEl) ){
// Remove event _dispatchEvent(dragEl, 'sort');
_dispatchEvent(rootEl, 'remove', dragEl); _dispatchEvent(rootEl, 'sort');
// Add event // Add event
_dispatchEvent(dragEl, 'add'); _dispatchEvent(dragEl, 'add', dragEl, rootEl);
// Remove event
_dispatchEvent(rootEl, 'remove', dragEl);
} }
else if( dragEl.nextSibling !== nextEl ){ else if( dragEl.nextSibling !== nextEl ){
// Update event // Update event
_dispatchEvent(dragEl, 'update'); _dispatchEvent(dragEl, 'update');
_dispatchEvent(dragEl, 'sort');
} }
_dispatchEvent(dragEl, 'end'); _dispatchEvent(rootEl, 'end');
} }
// Set NULL // Set NULL
@ -759,7 +762,6 @@
bind: _bind, bind: _bind,
closest: _closest, closest: _closest,
toggleClass: _toggleClass, toggleClass: _toggleClass,
createEvent: _createEvent,
dispatchEvent: _dispatchEvent dispatchEvent: _dispatchEvent
}; };

2
Sortable.min.js vendored

File diff suppressed because one or more lines are too long

96
index.html

@ -6,8 +6,8 @@
<title>Sortable. No jQuery.</title> <title>Sortable. No jQuery.</title>
<meta name="keywords" content="sortable, reorder, list, javascript, html5, drag and drop, dnd, animation, effects, rubaxa"/> <meta name="keywords" content="sortable, reorder, list, javascript, html5, drag and drop, dnd, animation, groups, angular, ng-sortable, effects, rubaxa"/>
<meta name="description" content="Sortable - is a minimalist JavaScript library for modern browsers and touch devices (No jQuery)."/> <meta name="description" content="Sortable - is a minimalist JavaScript library for modern browsers and touch devices (No jQuery). Support AngularJS."/>
<meta name="viewport" content="width=device-width, initial-scale=0.5"/> <meta name="viewport" content="width=device-width, initial-scale=0.5"/>
<link href="//rubaxa.github.io/Ply/ply.css" rel="stylesheet" type="text/css"/> <link href="//rubaxa.github.io/Ply/ply.css" rel="stylesheet" type="text/css"/>
@ -145,6 +145,47 @@
</div> </div>
<a name="ng"></a>
<div id="todos" ng-app="todoApp" class="container" style="margin-top: 100px">
<div style="margin-left: 30px">
<div><div data-force="5" class="layer title title_xl">AngluarJS</div></div>
<div style="width: 30%; margin-top: -8px; margin-left: 10px; float: left;" class="block__list block__list_words">
<div ng-controller="TodoController">
<span style="padding-left: 20px">{{remaining()}} of {{todos.length}} remaining</span>
[ <a href="" ng-click="archive()">archive</a> ]
<ul ng-sortable="{ group: 'todo', animation: 150 }" class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form ng-submit="addTodo()" style="padding-left: 20px">
<input type="text" ng-model="todoText" size="30"
placeholder="add new todo here">
</form>
</div>
</div>
<div style="width: 30%; margin-top: -8px; margin-left: 10px; float: left;" class="block__list block__list_words">
<div ng-controller="TodoControllerNext">
<span style="padding-left: 20px">{{remaining()}} of {{todos.length}} remaining</span>
<ul ng-sortable="sortableConfig" class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
</div>
</div>
<div style="clear: both"></div>
</div>
</div>
<a name="c"></a> <a name="c"></a>
<div class="container" style="margin-top: 100px"> <div class="container" style="margin-top: 100px">
<div style="margin-left: 30px"> <div style="margin-left: 30px">
@ -202,6 +243,8 @@ var editableList = new Sortable(editable, {
<script src="Sortable.js"></script> <script src="Sortable.js"></script>
<script src="//rubaxa.github.io/Ply/Ply.min.js"></script> <script src="//rubaxa.github.io/Ply/Ply.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="ng-sortable.js"></script>
<script> <script>
(function (){ (function (){
@ -302,6 +345,55 @@ var editableList = new Sortable(editable, {
animation: 150 animation: 150
}); });
}); });
angular.module('todoApp', ['ng-sortable'])
.controller('TodoController', ['$scope', function ($scope) {
$scope.todos = [
{text: 'learn angular', done: true},
{text: 'build an angular app', done: false}
];
$scope.addTodo = function () {
$scope.todos.push({text: $scope.todoText, done: false});
$scope.todoText = '';
};
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.archive = function () {
var oldTodos = $scope.todos;
$scope.todos = [];
angular.forEach(oldTodos, function (todo) {
if (!todo.done) $scope.todos.push(todo);
});
};
}])
.controller('TodoControllerNext', ['$scope', function ($scope) {
$scope.todos = [
{text: 'learn Sortable', done: true},
{text: 'use ng-sortable', done: false},
{text: 'Enjoy', done: false}
];
$scope.remaining = function () {
var count = 0;
angular.forEach($scope.todos, function (todo) {
count += todo.done ? 0 : 1;
});
return count;
};
$scope.sortableConfig = { group: 'todo', animation: 150 };
'Start End Add Update Remove Sort'.split(' ').forEach(function (name) {
$scope.sortableConfig['on' + name] = console.log.bind(console, name);
});
}]);
})(); })();

115
ng-sortable.js

@ -0,0 +1,115 @@
/**
* @author RubaXa <trash@rubaxa.org>
* @licence MIT
*/
angular.module('ng-sortable', [])
.constant('$version', '0.1.0')
.directive('ngSortable', ['$parse', '$rootScope', function ($parse, $rootScope) {
var removed;
function getSource(el) {
var scope = angular.element(el).scope();
var ngRepeat = [].filter.call(el.childNodes, function (node) {
return (
(node.nodeType === 8) &&
(node.nodeValue.indexOf("ngRepeat:") !== -1)
);
})[0];
ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*([^\s]+)\s+in\s+([^\s|]+)/);
var item = $parse(ngRepeat[1]);
var items = $parse(ngRepeat[2]);
return {
item: function (el) {
return item(angular.element(el).scope());
},
items: items(scope),
upd: function () {
items.assign(scope, this.items);
}
};
}
return {
restrict: 'AC',
link: function (scope, $el, attrs) {
var el = $el[0];
var options = scope.$eval(attrs.ngSortable) || {};
var _order = [];
var source = getSource(el);
'Start End Add Update Remove Sort'.split(' ').forEach(function (name) {
options['on' + name] = options['on' + name] || function () {};
});
function _sync(evt) {
sortable.toArray().forEach(function (id, i) {
if (_order[i] !== id) {
var idx = _order.indexOf(id);
if (idx === -1) {
var remoteSource = getSource(evt.from);
idx = remoteSource.items.indexOf(remoteSource.item(evt.item));
removed = remoteSource.items.splice(idx, 1)[0];
_order.splice(i, 0, id);
source.items.splice(i, 0, removed);
remoteSource.upd();
evt.from.appendChild(evt.item); // revert element
} else {
_order.splice(i, 0, _order.splice(idx, 1)[0]);
source.items.splice(i, 0, source.items.splice(idx, 1)[0]);
}
}
});
source.upd();
scope.$apply();
}
var sortable = Sortable.create(el, Object.keys(options).reduce(function(opts, name) {
opts[name] = opts[name] || options[name];
return opts;
}, {
onStart: function () {
$rootScope.$broadcast('sortable:start', sortable);
options.onStart();
},
onEnd:function () {
$rootScope.$broadcast('sortable:end', sortable);
options.onEnd();
},
onAdd: function (evt) {
_sync(evt);
options.onAdd(source.items, removed);
},
onUpdate: function (evt) {
_sync(evt);
options.onUpdate(source.items, source.item(evt.item));
},
onRemove: function (evt) {
options.onRemove(source.items, removed);
},
onSort: function () {
options.onSort(source.items);
}
}));
$rootScope.$on('sortable:start', function () {
_order = sortable.toArray();
});
$el.on('$destroy', function () {
el.sortable = null;
sortable.destroy();
});
}
};
}])
;

9
st/app.css

@ -215,3 +215,12 @@ img {
#filter .block__list { #filter .block__list {
padding-bottom: 0; padding-bottom: 0;
} }
#todos input {
padding: 5px;
font-size: 14px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
}

Loading…
Cancel
Save