Sortable v1.0: Нові можливості

Привіт хабр! Напередодні нового року хочу поділитися своєю радістю — виходом Sortable v1.0. Рівно рік тому я представив на ваш суд мій маленький інструмент для сортування списку за допомогою drag'n'drop. Весь цей час я скрупульозно збирав зворотний зв'язок, додавав нові можливості і правил дрібні баги. Під катом я розповім про нові можливості, інтеграції з AngularJS, Meteor та інших нюансах.


Введення

Sortable — це мінімалістичний інструмент, для організації сортування всередині списку і між списками. Бібліотека не залежить від jQuery або інших рішень, використовує Native drag'n'drop API, працює як на столі, так і touch пристроях. Має простий API, легко інтегрується в проект і є відмінною заміною jQueryUI / Sortable ;]

І так, що ж нового приніс цей реліз:
  • Просунуті групи (гнучка настройка переміщень і не тільки)
  • Анімація при переміщенні
  • Розумна прокрутка вікна браузера і списку
  • Вимкнути сортування дозволяє емітувати draggable і droppable)
  • Методи отримання і зміни сортування
  • Можливість фільтрації
  • Підтримка AngularJS і Meteor


Просунуті групи

З самого початку бібліотека мала можливість переміщення між групами, досить було поставити їм однакове ім'я і готово:
// foo bar і - посилання на HTMLElement
Sortable.create(foo, { group: 'shared', });
Sortable.create(bar, { group: 'shared' });
http://jsbin.com/yexine/1/editПриклад роботи (gif)image
З часом, стало очевидно, що цього недостатньо. Наприклад, не можна було зробити так, щоб один список тільки віддавав, а інший тільки брав елементи. Крім цього, ключовим недоліком є відсутність можливості організувати взаємодію декількох груп. Потрібно було виробити рішення, яке б дозволило реалізувати виникли завдання і залишити можливість розширення на майбутнє, при цьому не втратити поточний інтерфейс.

Тепер можна задати опцію `group` як об'єкт з наступними властивостями:
  • name — назва групи;
  • pull — можливість «витягувати» елементи при переміщенні між списками, так само властивість може приймати значення `clone`;
  • put — можливість взяти елемент з іншої групи, або масив дозволених груп.
Як це працює простіше пояснити це на прикладі:
  • У вас є три списки «A», «B» і «C»;
  • Перетягувати потрібно з «A» і «B» «C», між «A» і «B» перенос неможливий;
  • При перетягуванні з «A» на його місці повинен з'являтися «клон».
Щоб показати всі можливості, я вирішу цю задачу двома способами.
Загальна група Кілька груп
http://jsbin.com/yexine/2/edit http://jsbin.com/yexine/8/edit
Sortable.create(listA, {
group: {
name: 'shared',
pull: 'clone',
put: false
}
});

Sortable.create(listB, {
group: {
name: 'shared',
put: false
}
});

Sortable.create(listC, {
group: 'shared'
});
Sortable.create(listA, {
group: {
name: 'A',
pull: 'clone'
}
});

Sortable.create(listB, {
group: 'B'
});

Sortable.create(listC, {
group: {
put: ['A', 'B']
}
});

Анімація

Тут особливо розписувати нічого, анімація зроблена дуже просто, за допомогою CSS3 transition, включити її можна задавши опцію `animation` в `ms`. На жаль, у неї є недоліки, які поки не вирішені, але сподіваюся, в майбутньому знайдеться спосіб, як «дешево» їх виправити.
http://jsbin.com/yexine/4/editПриклад роботи (gif)

Розумна прокрутка вікна та списку

Зовсім недавно виникла завдання: зробити прокручування вікна при досягненні одного з країв. По ідеї, це повинно було працювати за замовчуванням, оскільки використовується Native drag'n'drop API, але на ділі браузер вкрай не охоче прокручував вікно. Так само залишалася проблема, якщо список знаходиться в overflow. Тож подумавши, вийшло зробити розумну прокручування, яка спочатку прокручувала список, якщо він в overflow та/або вікно якщо ми досягли краю браузера. Для більш тонкої настройки введені три додаткові опції:
  • scroll — включити авто-прокрутку;
  • scrollSensitivity — на скільки потрібно наблизиться до краю для активації прокрутки;
  • scrollSpeed — швидкість прокрутки `px`;
Приклади:

Вимкнути сортування

Так, саме так, це може здатися дивним, але за допомогою цього параметра можна вимкнути те, заради чого й створено цей інструмент ;] Наприклад це можна використовувати для імітації draggable і droppable: http://jsbin.com/xizeh/3/edit?html,js,output

Методи отримання і зміни сортування

Так як ця бібліотека з'явилася в результаті дослідження можливостей 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 (змінити порядок і оновіть сторінку)

Можливість фільтрації

Припустимо вам потрібно зробити сортовані список з можливістю редагування та видалення елемента. Раніше вам би знадобилася самостійно навісити потрібні обробники. Тепер, вирішити подібну задачу можна засобами самої бібліотеки, без додаткових інструментів: http://jsbin.com/yexine/6/edit?html,js,output

Підтримка AngularJS

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) {
// Отримуємо поточний `сфера` пов'язаний елементом
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|]+)/);

// Конвертуємо назви змінних в `expression` для отримання їх значень з `сфера`
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
Повний код директиви: https://github.com/RubaXa/Sortable/краплі/master/ng-sortable.js

Інтеграція з Meteor

Це зовсім нова можливість, яка з'явилася завдяки Dan Dascalescu, так що якщо ви використовуєте meteor, то бібліотека додано в атмосферу, а Dan додав докладний мануал використання приклад. Якщо що, ставте завдання з міткою meteor на нього, він буде радий допомогти ;]

На завершення хочу сказати спасибі всім тим, хто брав участь в тестування і розвитку бібліотеки, хоч вона трохи і «погладшала», але це все той же легкий і гнучкий интрумент. Дякую за увагу.

Плани на майбутнє

  • Покриття тестами (поки не до кінця зрозуміло, як покрити drag'n'drop, але ідеї є)
  • Покращена анімація
  • Система розширень (наприклад вкладені списки або об'єднання двох елементів в один)
  • Обмеження по осях (на жаль, можливо, доведеться відмовитися від drag'n'drop API)
  • Ваш варіант ;]


               Приклади     |    Код і документація     |    Мій github     |    @ibnRubaXa

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.