- PVSM.RU - https://www.pvsm.ru -
Привет! В преддверии нового года хочу поделится своей радостью — выходом Sortable v1.0 [1]. Ровно год назад я представил на ваш суд мой маленький инструмент для сортировки списка при помощи drag’n’drop. Всё это время я скрупулезно собирал обратную связь, добавлял новые возможности и правил мелкие баги. Под катом я расскажу о новых возможностях, интеграции с AngularJS [2], Meteor [3] и других нюансах.
Sortable [4] — это минималистичный инструмент, для организации сортировки внутри списка и между списками. Библиотека не зависит от jQuery или других решений, использует Native Drag’n’Drop API, работает как на desktop, так и touch устройствах. Имеет простое API, легко интегрируется в проект и является отличной заменой jQueryUI / Sortable [5] ;]
С самого начала библиотека имела возможность перемещения между группами, достаточно было задать им одинаковое имя и готово:
// foo и bar — ссылки на HTMLElement
Sortable.create(foo, { group: 'shared', });
Sortable.create(bar, { group: 'shared' });
http://jsbin.com/yexine/1/edit [6]

Со временем, стало очевидно, что этого недостаточно. Например, нельзя было сделать так, чтобы один список только отдавал, а другой только принимал элементы. Помимо этого, ключевым недостатком было отсутствие возможности как-то организовать взаимодействие нескольких групп. Нужно было выработать решение, которое бы позволило реализовать возникшие задачи и оставить возможность расширения на будущее, при этом не потерять текущий интерфейс.
Теперь можно задать опцию `group` как объект со следующими свойствами:
Как это работает проще объяснить этом на примере:
Чтобы показать сразу все возможности, я решу эту задачу двумя способами.
| Общая группа | Несколько групп |
|---|---|
| http://jsbin.com/yexine/2/edit [7] | http://jsbin.com/yexine/8/edit [8] |
|
|
Тут особо расписывать нечего, анимация сделана очень просто, при помощи CSS3 transition, включить её можно задав опцию `animation` в `ms`. Увы, у неё есть недостатки, которые пока не решены, но надеюсь в будущем найдется способ, как «дешево» их исправить.
http://jsbin.com/yexine/4/edit [9]

Совсем недавно возникла задача: сделать прокрутку окна при достижении одного из краев. По идее, это должно было работать по умолчанию, т.к. используется Native Drag’n’Drop API, но на деле браузер крайне не охотно прокручивал окно. Так же оставалась проблема, если список находится в overflow. Поэтому подумав, получилось сделать умную прокрутку, которая сначала прокручивала список, если он в overflow и/или окно если мы достигли края браузера. Для более тонкой настройки введены три дополнительные опции:
Примеры:
Да, именно так, это может показаться странным, но при помощи этого параметра можно отключить то, ради чего и создан данный инструмент ;] Например это можно использовать для имитации draggable и droppable: http://jsbin.com/xizeh/3/edit?html,js,output [12]
Так как эта библиотека появилась в результате исследования возможностей Drag’n’Drop API, то банальные методы получить порядок или изменить его просто не закладывались. Заглянув в API jQueryUI, обнаружил, что у них можно только получить порядок элементов, но изменить его нельзя, это не порядок ;] Чтобы решить все эти проблемы, было добавлено свойство `store`, которое принимает объект из двух параметров `get` и `set` для получения и сохранения сортировки, а так же два метода `toArray` и `sort`.
Например, сохранение порядка через `localStorage` будет выглядеть следующим образом:
Sortable.create(users, {
store: {
// Получение сортировки (вызывается при инициализации)
get: function (sortable) {
var order = localStorage.getItem(sortable.options.group);
return order ? order.split('|') : [];
},
// Сохранение сортировки (вызывается каждый раз при её изменении)
set: function (sortable) {
var order = sortable.toArray();
localStorage.setItem(sortable.options.group, order.join('|'));
}
}
});
http://jsbin.com/yexine/7/edit [13] (измените порядок и обновите страницу)
Допустим вам нужно сделать сортируемый список с возможностью редактирования и удаления элемента. Раньше вам бы понадобилась самостоятельно навесить нужные обработчики. Теперь, решить подобную задачу можно средствами самой библиотеки, без дополнительных инструментов: http://jsbin.com/yexine/6/edit?html,js,output [14]
Angular всё больше завоевывает рынок и чтобы облегчить людям использование Sortable, было решено сделать директиву, для быстрой интеграции в проект. Посмотрев на аналоги, я увидел странность, все как один делают так:
<ul ui-sortable="sortableOptions" ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
Зачем? Ведь это попросту copy-paste, да и если быть совсем честным, костыль. На мой взгляд, логичной и правильной будет следующая запись, без `ng-model`:
<ul ng-sortable="sortableOptions">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
Интересующие нас данные уже содержит `ng-repeat`, чтобы их получить нам понадобиться функция `$parse` и небольшая хитрость. Хитрость заключается в том, что данные из `ng-repeat` можно получить только найдя спец. комментарий оставленный самим ангуляром:
<ul ng-sortable="{ animation: 150 }">
<!-- ngRepeat: item in items -->
<!-- end ngRepeat: item in items -->
</ul>
Теперь мы можем создать метод для работы с данными связанными с `ng-repeat`:
/**
* Получить объект для работы с данными в ng-repeat
* @param {HTMLElement} el
* @returns {object}
*/
function getNgRepeat(el) {
// Получаем текущий `scope` связанный в элементом
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+ins+([^s|]+)/);
// Конвертируем названия переменных в `expression` для получения их значений из `scope`
var itemExpr = $parse(ngRepeat[1]);
var itemsExpr = $parse(ngRepeat[2]);
return {
// Получить модель элемента списка
item: function (el) {
return itemExpr(angular.element(el).scope());
},
// Получить массив связанный с `ng-repeat`
items: function () {
return itemsExpr(scope);
}
};
}
Пример работы: http://jsbin.com/fumote/1/edit [15]
Полный код директивы: https://github.com/RubaXa/Sortable/blob/master/ng-sortable.js [16]
Это совсем новая возможность, которая появилось благодаря Dan Dascalescu [17], так что если вы используете meteor, то библиотека добавлена в атмосферу [18], а Dan добавил подробный мануал [19] по использованию и пример [20]. Если что, ставьте задачу с меткой meteor на него, он будет рад помочь ;]
В завершении хочу сказать спасибо всем тем, кто принимал участие в тестирование и развитии библиотеки, хоть она немного и «потолстела», но это все тот же легкий и гибкий интрумент. Спасибо за внимание.
Примеры [1]   |  Код и документация [22]   |  Мой github [23]   |  @ibnRubaXa [24]
Автор: RubaXa
Источник [25]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/77803
Ссылки в тексте:
[1] Sortable v1.0: http://rubaxa.github.io/Sortable/
[2] AngularJS: http://angularjs.org
[3] Meteor: https://www.meteor.com/
[4] Sortable: https://github.com/RubaXa/Sortable#features
[5] jQueryUI / Sortable: http://jqueryui.com/sortable/
[6] http://jsbin.com/yexine/1/edit: http://jsbin.com/yexine/1/edit
[7] http://jsbin.com/yexine/2/edit: http://jsbin.com/yexine/2/edit
[8] http://jsbin.com/yexine/8/edit: http://jsbin.com/yexine/8/edit
[9] http://jsbin.com/yexine/4/edit: http://jsbin.com/yexine/4/edit
[10] http://jsbin.com/boqugumiqi/1/edit?html,js,output: http://jsbin.com/boqugumiqi/1/edit?html,js,output
[11] http://jsbin.com/kohamakiwi/1/edit?html,js,output: http://jsbin.com/kohamakiwi/1/edit?html,js,output
[12] http://jsbin.com/xizeh/3/edit?html,js,output: http://jsbin.com/xizeh/3/edit?html,js,output
[13] http://jsbin.com/yexine/7/edit: http://jsbin.com/yexine/7/edit
[14] http://jsbin.com/yexine/6/edit?html,js,output: http://jsbin.com/yexine/6/edit?html,js,output
[15] http://jsbin.com/fumote/1/edit: http://jsbin.com/fumote/1/edit
[16] https://github.com/RubaXa/Sortable/blob/master/ng-sortable.js: https://github.com/RubaXa/Sortable/blob/master/ng-sortable.js#L5
[17] Dan Dascalescu: https://github.com/dandv
[18] атмосферу: https://atmospherejs.com/rubaxa/sortable
[19] мануал: https://github.com/RubaXa/Sortable/tree/master/meteor
[20] пример: https://github.com/RubaXa/Sortable/tree/master/meteor/example
[21] Ваш вариант: https://github.com/RubaXa/Sortable/issues/new
[22] Код и документация: https://github.com/RubaXa/Sortable
[23] Мой github: https://github.com/rubaxa/
[24] @ibnRubaXa: https://twitter.com/ibnRubaXa
[25] Источник: http://habrahabr.ru/post/246373/
Нажмите здесь для печати.