Browse Source

v0.5.0: filter/onFilter and closest (#72)

pull/101/head
RubaXa 10 years ago
parent
commit
fe2184241f
  1. 50
      README.md
  2. 92
      Sortable.js
  3. 4
      Sortable.min.js
  4. 2
      bower.json
  5. 2
      component.json
  6. 226
      index.html
  7. 2
      package.json
  8. 211
      st/app.css

50
README.md

@ -33,27 +33,27 @@ new Sortable(el, {
group: "name",
store: null, // @see Store
handle: ".my-handle", // Restricts sort start click/touch to the specified element
filter: ".ignor-elements", // Selectors that do not lead to dragging (String or Function)
draggable: ".item", // Specifies which items inside the element should be sortable
ghostClass: "sortable-ghost",
onStart: function (/**Event*/evt) { // dragging
var itemEl = evt.item;
},
onEnd: function (/**Event*/evt) { // dragging
var itemEl = evt.item;
},
onStart: function (/**Event*/evt) { /* dragging */ },
onEnd: function (/**Event*/evt) { /* dragging */ },
onAdd: function (/**Event*/evt){
var itemEl = evt.item;
var itemEl = evt.item; // dragged HTMLElement
},
onUpdate: function (/**Event*/evt){
var itemEl = evt.item; // the current dragged HTMLElement
var itemEl = evt.item; // dragged HTMLElement
},
onRemove: function (/**Event*/evt){
var itemEl = evt.item;
var itemEl = evt.item; // dragged HTMLElement
},
onFilter: function (/**Event*/evt){
var itemEl = evt.item; // HTMLElement on which was `nousedown|tapstart` event.
}
});
```
@ -63,8 +63,28 @@ new Sortable(el, {
### Method
##### closest(el:`String`[, selector:`HTMLElement`]):`HTMLElement|null`
For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
```js
var editableList = new Sortable(list, {
filter: ".js-remove, .js-edit",
onFilter: function (evt) {
var el = editableList.closest(evt.item); // list item
if (editableList.closest(evt.item, ".js-remove")) { // Click on remove button
el.parentNode.removeChild(el); // remove sortable item
}
else if (editableList.closest(evt.item, ".js-edit")) { // Click on edit link
// ...
}
}
})
```
##### toArray():`String[]`
Serializes the sortable's item data-id's into an array of string.
Serializes the sortable's item `data-id`'s into an array of string.
##### sort(order:`String[]`)
@ -84,6 +104,14 @@ sortable.sort(order.reverse()); // apply
### Store
Saving and restoring of the sort.
```html
<ul>
<li data-id="1">order</li>
<li data-id="2">save</li>
<li data-id="3">restore</li>
</ul>
```
```js
new Sortable(el, {
group: "localStorage-example",

92
Sortable.js

@ -51,6 +51,12 @@
return evt;
}
, _dispatchEvent = function (rootEl, name, targetEl) {
rootEl.dispatchEvent(_createEvent(name, targetEl || rootEl));
}
, _customEvents = 'onAdd onUpdate onRemove onStart onEnd onFilter'.split(' ')
, noop = function (){}
, slice = [].slice
@ -70,17 +76,26 @@
// Defaults
options.group = options.group || Math.random();
options.store = options.store || null;
options.handle = options.handle || null;
options.draggable = options.draggable || el.children[0] && el.children[0].nodeName || (/[uo]l/i.test(el.nodeName) ? 'li' : '*');
options.ghostClass = options.ghostClass || 'sortable-ghost';
options.ignore = options.ignore || 'a, img';
var defaults = {
group: Math.random(),
store: null,
handle: null,
draggable: el.children[0] && el.children[0].nodeName || (/[uo]l/i.test(el.nodeName) ? 'li' : '*'),
ghostClass: 'sortable-ghost',
ignore: 'a, img',
filter: null
};
// Set default options
for (var name in defaults) {
options[name] = options[name] || defaults[name];
}
// Define events
'onAdd onUpdate onRemove onStart onEnd'.split(' ').forEach(function (name) {
_customEvents.forEach(function (name) {
options[name] = _bind(this, options[name] || noop);
_on(el, name.substr(2).toLowerCase(), options[name]);
});
@ -97,12 +112,6 @@
// Bind events
_on(el, 'add', options.onAdd);
_on(el, 'update', options.onUpdate);
_on(el, 'remove', options.onRemove);
_on(el, 'start', options.onStart);
_on(el, 'stop', options.onEnd);
_on(el, 'mousedown', this._onTapStart);
_on(el, 'touchstart', this._onTapStart);
supportIEdnd && _on(el, 'selectstart', this._onTapStart);
@ -132,8 +141,25 @@
, target = (touch || evt).target
, options = this.options
, el = this.el
, filter = options.filter
;
// Check filter
if( typeof filter === 'function' && filter.call(this, target, this) ){
_dispatchEvent(el, 'filter', target);
return; // cancel dnd
}
else if( filter ){
filter = filter.split(',').filter(function (criteria) {
return _closest(target, criteria.trim(), el);
});
if (filter.length) {
_dispatchEvent(el, 'filter', target);
return; // cancel dnd
}
}
if( options.handle ){
target = _closest(target, options.handle, el);
}
@ -192,7 +218,7 @@
} catch (err){ }
dragEl.dispatchEvent(_createEvent('start', dragEl));
_dispatchEvent(dragEl, 'start');
}
},
@ -381,17 +407,17 @@
if( !rootEl.contains(dragEl) ){
// Remove event
rootEl.dispatchEvent(_createEvent('remove', dragEl));
_dispatchEvent(rootEl, 'remove', dragEl);
// Add event
dragEl.dispatchEvent(_createEvent('add', dragEl));
_dispatchEvent(dragEl, 'add');
}
else if( dragEl.nextSibling !== nextEl ){
// Update event
dragEl.dispatchEvent(_createEvent('update', dragEl));
_dispatchEvent(dragEl, 'update');
}
dragEl.dispatchEvent(_createEvent('stop', dragEl));
_dispatchEvent(dragEl, 'end');
}
// Set NULL
@ -455,17 +481,27 @@
},
/**
* For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
* @param {HTMLElement} el
* @param {String} [selector] default: `options.draggable`
* @returns {HTMLElement|null}
*/
closest: function (el, selector) {
return _closest(el, selector || this.options.draggable, this.el);
},
/**
* Destroy
*/
destroy: function () {
var el = this.el, options = this.options;
_off(el, 'add', options.onAdd);
_off(el, 'update', options.onUpdate);
_off(el, 'remove', options.onRemove);
_off(el, 'start', options.onStart);
_off(el, 'stop', options.onEnd);
_customEvents.forEach(function (name) {
_off(el, name.substr(2).toLowerCase(), options[name]);
});
_off(el, 'mousedown', this._onTapStart);
_off(el, 'touchstart', this._onTapStart);
_off(el, 'selectstart', this._onTapStart);
@ -610,9 +646,11 @@
i = str.length,
sum = 0
;
while (i--) {
sum += str.charCodeAt(i);
}
return sum.toString(36);
}
@ -625,13 +663,15 @@
find: _find,
bind: _bind,
closest: _closest,
toggleClass: _toggleClass
toggleClass: _toggleClass,
createEvent: _createEvent,
dispatchEvent: _dispatchEvent
};
Sortable.version = '0.4.1';
Sortable.version = '0.5.0';
// Export
return Sortable;
return Sortable;
});

4
Sortable.min.js vendored

File diff suppressed because one or more lines are too long

2
bower.json

@ -1,7 +1,7 @@
{
"name": "Sortable",
"main": "Sortable.js",
"version": "0.4.1",
"version": "0.5.0",
"homepage": "http://rubaxa.github.io/Sortable/",
"authors": [
"RubaXa <ibnRubaXa@gmail.com>"

2
component.json

@ -1,7 +1,7 @@
{
"name": "Sortable",
"main": "Sortable.js",
"version": "0.4.1",
"version": "0.5.0",
"homepage": "http://rubaxa.github.io/Sortable/",
"repo": "RubaXa/Sortable",
"authors": [

226
index.html

@ -10,174 +10,10 @@
<meta name="description" content="Sortable - is a minimalist JavaScript library for modern browsers and touch devices (No jQuery)."/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link href='//fonts.googleapis.com/css?family=Roboto:300' rel='stylesheet' type='text/css'/>
<link href="//rubaxa.github.io/Ply/ply.css" rel="stylesheet" type="text/css"/>
<link href="//fonts.googleapis.com/css?family=Roboto:300" rel="stylesheet" type="text/css"/>
<style>
html {
background-image: -webkit-linear-gradient(bottom, #F4E2C9 20%, #F4D7C9 100%);
background-image: -ms-linear-gradient(bottom, #F4E2C9 20%, #F4D7C9 100%);
background-image: linear-gradient(to bottom, #F4E2C9 20%, #F4D7C9 100%);
}
html, body {
margin: 0;
padding: 0;
position: relative;
color: #464637;
min-height: 100%;
font-size: 20px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
}
h1 {
color: #FF3F00;
font-size: 20px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.container {
width: 80%;
margin: auto;
min-width: 1100px;
max-width: 1300px;
position: relative;
}
@media (min-width: 750px) and (max-width: 970px){
.container {
width: 100%;
min-width: 750px;
}
}
.sortable-ghost {
opacity: .2;
}
img {
border: 0;
vertical-align: middle;
}
.logo {
top: 55px;
left: 30px;
position: absolute;
}
.title {
color: #fff;
padding: 3px 10px;
display: inline-block;
position: relative;
background-color: #FF7373;
z-index: 1000;
}
.title_xl {
padding: 3px 15px;
font-size: 40px;
}
.tile {
width: 22%;
min-width: 240px;
color: #FF7270;
padding: 10px 30px;
text-align: center;
margin-top: 15px;
margin-left: 5px;
margin-right: 30px;
background-color: #fff;
display: inline-block;
vertical-align: top;
}
.tile__name {
cursor: move;
padding-bottom: 10px;
border-bottom: 1px solid #FF7373;
}
.tile__list {
margin-top: 10px;
}
.tile__list:last-child {
margin-right: 0;
min-height: 80px;
}
.tile__list img {
cursor: move;
margin: 10px;
border-radius: 100%;
}
.block {
opacity: 1;
position: absolute;
}
.block__list {
padding: 20px 0;
max-width: 360px;
margin-top: -8px;
margin-left: 5px;
background-color: #fff;
}
.block__list li { cursor: move; }
.block__list_words li {
background-color: #fff;
padding: 10px 40px;
}
.block__list_words .sortable-ghost {
opacity: 0.4;
background-color: #F4E2C9;
}
.block__list_words li:first-letter {
text-transform: uppercase;
}
.block__list_tags {
padding-left: 30px;
}
.block__list_tags:after {
clear: both;
content: '';
display: block;
}
.block__list_tags li {
color: #fff;
float: left;
margin: 8px 20px 10px 0;
padding: 5px 10px;
min-width: 10px;
background-color: #5F9EDF;
text-align: center;
}
.block__list_tags li:first-child:first-letter {
text-transform: uppercase;
}
</style>
<link href="st/app.css" rel="stylesheet" type="text/css"/>
</head>
<body>
@ -218,6 +54,7 @@
</div>
<a name="m"></a>
<div class="container">
<div id="multi" style="margin-left: 30px">
<div><div data-force="5" class="layer title title_xl">Multi</div></div>
@ -244,7 +81,7 @@
<div class="layer tile" data-force="20">
<div class="tile__name">Group C</div>
<div class="tile__list">
o <img src="//fbcdn-profile-a.akamaihd.net/hprofile-ak-frc3/c12.12.156.156/s100x100/303317_320632284665935_15996162_a.jpg"/><!--
<img src="//fbcdn-profile-a.akamaihd.net/hprofile-ak-frc3/c12.12.156.156/s100x100/303317_320632284665935_15996162_a.jpg"/><!--
--><img src="//fbcdn-profile-a.akamaihd.net/hprofile-ak-ash2/c9.9.109.109/s100x100/484507_4207733265938_1693034881_s.jpg"/>
</div>
</div>
@ -253,6 +90,25 @@
</div>
<a name="e"></a>
<div class="container" style="margin-top: 100px">
<div id="filter" style="margin-left: 30px">
<div><div data-force="5" class="layer title title_xl">Editable list</div></div>
<div style="margin-top: -8px; margin-left: 10px" class="block__list block__list_words">
<ul id="editable">
<li>Оля<i class="js-remove"></i></li>
<li>Владимир<i class="js-remove"></i></li>
<li>Алина<i class="js-remove"></i></li>
</ul>
<button id="addUser">Add</button>
</div>
</div>
</div>
<a name="c"></a>
<div class="container" style="margin-top: 100px">
<div style="margin-left: 30px">
<div><div class="layer title title_xl">Code example</div></div>
@ -281,6 +137,16 @@ var sort = new Sortable(container, {
// ..
sort.destroy();
// Editable list
var editableList = new Sortable(editable, {
filter: '.js-remove',
onFilter: function (evt) {
var el = editableList.closest(evt.item); // get dragged item
el && el.parentNode.removeChild(el);
}
});
</code></pre>
</div>
@ -297,6 +163,7 @@ sort.destroy();
<script src="Sortable.js"></script>
<script src="//rubaxa.github.io/Ply/Ply.min.js"></script>
<script>
(function (){
@ -309,7 +176,7 @@ sort.destroy();
}
window.x = new Sortable(foo, {
new Sortable(foo, {
group: "words",
store: {
get: function (sortable) {
@ -345,6 +212,27 @@ sort.destroy();
});
var editableList = new Sortable(editable, {
filter: '.js-remove',
onFilter: function (evt) {
var el = editableList.closest(evt.item);
el && el.parentNode.removeChild(el);
}
});
addUser.onclick = function () {
Ply.dialog('prompt', {
title: 'Add',
form: { name: 'name' }
}).done(function (ui) {
var el = document.createElement('li');
el.innerHTML = ui.data.name + '<i class="js-remove"></i>';
editableList.el.appendChild(el);
});
};
[].forEach.call(multi.getElementsByClassName('tile__list'), function (el){
new Sortable(el, { group: 'photo' });
});

2
package.json

@ -1,7 +1,7 @@
{
"name": "sortable",
"exportName": "Sortable",
"version": "0.4.1",
"version": "0.5.0",
"devDependencies": {
"grunt": "*",
"grunt-version": "*",

211
st/app.css

@ -0,0 +1,211 @@
html {
background-image: -webkit-linear-gradient(bottom, #F4E2C9 20%, #F4D7C9 100%);
background-image: -ms-linear-gradient(bottom, #F4E2C9 20%, #F4D7C9 100%);
background-image: linear-gradient(to bottom, #F4E2C9 20%, #F4D7C9 100%);
}
html, body {
margin: 0;
padding: 0;
position: relative;
color: #464637;
min-height: 100%;
font-size: 20px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
}
h1 {
color: #FF3F00;
font-size: 20px;
font-family: 'Roboto', sans-serif;
font-weight: 300;
text-align: center;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.container {
width: 80%;
margin: auto;
min-width: 1100px;
max-width: 1300px;
position: relative;
}
@media (min-width: 750px) and (max-width: 970px){
.container {
width: 100%;
min-width: 750px;
}
}
.sortable-ghost {
opacity: .2;
}
img {
border: 0;
vertical-align: middle;
}
.logo {
top: 55px;
left: 30px;
position: absolute;
}
.title {
color: #fff;
padding: 3px 10px;
display: inline-block;
position: relative;
background-color: #FF7373;
z-index: 1000;
}
.title_xl {
padding: 3px 15px;
font-size: 40px;
}
.tile {
width: 22%;
min-width: 245px;
color: #FF7270;
padding: 10px 30px;
text-align: center;
margin-top: 15px;
margin-left: 5px;
margin-right: 30px;
background-color: #fff;
display: inline-block;
vertical-align: top;
}
.tile__name {
cursor: move;
padding-bottom: 10px;
border-bottom: 1px solid #FF7373;
}
.tile__list {
margin-top: 10px;
}
.tile__list:last-child {
margin-right: 0;
min-height: 80px;
}
.tile__list img {
cursor: move;
margin: 10px;
border-radius: 100%;
}
.block {
opacity: 1;
position: absolute;
}
.block__list {
padding: 20px 0;
max-width: 360px;
margin-top: -8px;
margin-left: 5px;
background-color: #fff;
}
.block__list li { cursor: move; }
.block__list_words li {
background-color: #fff;
padding: 10px 40px;
}
.block__list_words .sortable-ghost {
opacity: 0.4;
background-color: #F4E2C9;
}
.block__list_words li:first-letter {
text-transform: uppercase;
}
.block__list_tags {
padding-left: 30px;
}
.block__list_tags:after {
clear: both;
content: '';
display: block;
}
.block__list_tags li {
color: #fff;
float: left;
margin: 8px 20px 10px 0;
padding: 5px 10px;
min-width: 10px;
background-color: #5F9EDF;
text-align: center;
}
.block__list_tags li:first-child:first-letter {
text-transform: uppercase;
}
#editable {}
#editable li {
position: relative;
}
#editable i {
-webkit-transition: opacity .2s;
transition: opacity .2s;
opacity: 0;
display: block;
cursor: pointer;
color: #c00;
top: 10px;
right: 40px;
position: absolute;
font-style: normal;
}
#editable li:hover i {
opacity: 1;
}
#filter {}
#filter button {
color: #fff;
width: 100%;
border: none;
outline: 0;
opacity: .5;
margin: 10px 0 0;
transition: opacity .1s ease;
cursor: pointer;
background: #5F9EDF;
padding: 10px 0;
font-size: 20px;
}
#filter button:hover {
opacity: 1;
}
#filter .block__list {
padding-bottom: 0;
}
Loading…
Cancel
Save