Skip to content

Commit

Permalink
feat: adds full support for nested lists (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
schne324 authored Feb 3, 2018
1 parent 38ef378 commit 74fcf23
Show file tree
Hide file tree
Showing 8 changed files with 466 additions and 456 deletions.
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dragon Drop

Keyboard accessible drag-and-drop reorder list.
Keyboard/assistive technology accessible drag-and-drop reorder list.

<img alt="Dragon Drop" src="/demo/dragondrop_sticker.png" width="400" />

Expand Down Expand Up @@ -86,55 +86,86 @@ https://unpkg.com/drag-on-drop

### `new DragonDrop(container, [options])`

#### `container` (required)
The one and only required parameter is the list HTMLElement that contains the sortable items.
#### `container` _HTMLElement|Array_ (required)

Either a single container element or an array of container elements.

#### `options` _Object_ (optional)

##### `item` _String_

The selector for the drag items (qualified within container). Defaults to
```js
'li'
```

##### `handle` _String_

The selector for the keyboard handle (qualified within the container and the selector provided for `item`). If set to `false`, the entire item will be used as the handle. Defaults to
```ks
'button'
```

##### `activeClass` _String_

The class to be added to the item being dragged. Defaults to

```js
'dragon-active'
```

##### `inactiveClass` _String_

The class to be added to all of the other items when an item is being dragged. Defaults

```js
'dragon-inactive'
```

##### `nested` _Boolean_
Set to true if nested lists are being used (click and keydown events will not bubble up (`e.stopPropagation()` will be applied))

Set to true if nested lists are being used (click and keydown events will not bubble up (`e.stopPropagation()` will be applied)). For nested lists, you MUST pass `DragonDrop` an array of containers as the 1st parameter (see example below).

__NOTE:__ *there is a 99% chance that you'll need to use [:scope selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope) to target only a given list's items (because dragon drop would otherwise include the sub list's items for example). Using `:scope` selectors will allow you to target direct descendant children (example: `:scope > li`).*

```js
const lists = Array.from(document.querySelectorAll('.dragon-list'));
const dragons = new DragonDrop(lists, {
nested: true,
handle: false,
item: ':scope > li' // IMPORTANT! a selector that targets only a single list's items
});
const [ topLevel, sublist1, sublist2 ] = dragons;

topLevel.on('grabbed', () => console.log('top-most container item grabbed'));
sublist1.on('grabbed', () => console.log('sublist 1 item grabbed'));
sublist2.on('grabbed', () => console.log('sublist 1 item grabbed'));
```

##### `announcement` _Object_

The live region announcement configuration object containing the following properties:

###### `grabbed` _Function_

The function called when an item is picked up. The currently grabbed element along with an array of all items are passed as arguments respectively. The function should return a string of text to be announced in the live region. Defaults to

```js
el => `Item ${el.innerText} grabbed`
```

###### `dropped` _Function_

The function called when an item is dropped. The newly dropped item along with an array of all items are passed as arguments respectively. The function should return a string of text to be announced in the live region. Defaults to

```js
el => `Item ${el.innerText} dropped`
```

###### `reorder` _Function_

The function called when the list has been reordered. The newly dropped item along with an array of items are passed as arguments respectively. The function should return a string of text to be announced in the live region. Defaults to

```js
(el, items) => {
const pos = items.indexOf(el) + 1;
Expand All @@ -144,19 +175,24 @@ The function called when the list has been reordered. The newly dropped item alo
```

###### `cancel` _Function_

The function called when the reorder is cancelled (via ESC). No arguments passed in. Defaults to

```js
() => 'Reordering cancelled'
```

## Properties

```js
const dragonDrop = new DragonDrop(container);
```
### `dragonDrop.items` _Array_

An array of each of the sortable item element references.

### `dragonDrop.handles` _Array_

An array of each of the handle item element references. If instance doesn't have handles, this will be identical to `dragonDrop.items`.

### Example with options
Expand All @@ -180,15 +216,19 @@ const dragonDrop = new DragonDrop(list, {
```

## Events

Dragon drop emit events when important stuff happens.

### `dragonDrop.on('grabbed', callback)`

Fires when an item is grabbed (with keyboard or mouse). The callback is passed the container along with the grabbed item.

### `dragonDrop.on('dropped', callback)`

Fires when an item is dropped (with keyboard or mouse). The callback is passed the container and the grabbed item.

### `dragonDrop.on('reorder', callback)`

Fires when an list is reordered. The callback is passed the container along with the item.

### Example use of events
Expand Down
67 changes: 25 additions & 42 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,23 @@ <h3 id="nested">Nested lists</h3>
<li class="top-level"><span>About Us</span></li>
<li class="top-level">
<span>Products</span>
<ul id="sub-1">
<ul id="sub-1" class="sublist">
<li><span>Product 1</span></li>
<li><span>Product 2</span></li>
<li><span>Product 3</span></li>
</ul>
</li>
<li class="top-level">
<span>Events</span>
<ul id="sub-2">
<ul id="sub-2" class="sublist">
<li><span>Event 1</span></li>
<li><span>Event 2</span></li>
<li><span>Event 3</span></li>
</ul>
</li>
<li class="top-level">
<span>Resources</span>
<ul id="sub-3">
<ul id="sub-3" class="sublist">
<li><span>Resource 1</span></li>
<li><span>Resource 2</span></li>
<li><span>Resource 3</span></li>
Expand All @@ -142,33 +142,30 @@ <h3 id="nested">Nested lists</h3>
<code data-language="js">
import DragonDrop from 'dragon-drop';

const demo3 = document.getElementById('demo-3');
const sublist1 = document.getElementById('sub-1');
const sublist2 = document.getElementById('sub-2');
const sublist3 = document.getElementById('sub-3');
const config = {
nested: true,
var lists = Array.prototype.slice.call(document.querySelectorAll('#demo-3, .sublist'));
var dragons = new DragonDrop(lists, {
handle: false,
nested: true,
item: ':scope > li',
announcement: {
grabbed: el => `${el.querySelector('span').innerText} grabbed`,
dropped: el => `${el.querySelector('span')} dropped`,
reorder: (el, items) => {
const pos = items.indexOf(el) + 1;
const text = el.querySelector('span').innerText;
return `The rankings have been updated, ${text} is now ${pos} of ${items.length}`;
grabbed: function (el) { return el.querySelector('span').innerText + ' grabbed'; },
dropped: function (el) { return el.querySelector('span').innerText + ' dropped'; },
reorder: function (el, items) {
var pos = items.indexOf(el) + 1;
var text = el.querySelector('span').innerText;
return 'The order has changed, ' + text + ' is now item ' + pos + ' of ' + items.length;
},
cancel: 'Reranking cancelled.'
cancel: function () { return 'Reorder cancelled.'; }
}
};

new DragonDrop(demo3, {
...config,
item: '.top-level' // important so we don't target the sub lis
});

new dragonDrop(sublist1, config);
new dragonDrop(sublist2, config);
new dragonDrop(sublist3, config);
// when we pass DragonDrop an array, we get an array of instances back
dragons.forEach(dragon => {
dragon
.on('grabbed', function (container, item) { console.log('grabbed: ', item); })
.on('dropped', function (container, item) { console.log('dropped: ', item); })
.on('reorder', function (container, item) { console.log('reorder: ', item); })
});
</code>
</pre>
</section>
Expand Down Expand Up @@ -226,9 +223,11 @@ <h3 id="nested">Nested lists</h3>

demo2.on('announcement', onAnnouncement);

var demo3Config = {
var demo3lists = Array.prototype.slice.call(document.querySelectorAll('#demo-3, .sublist'));
var demo3Dragons = new DragonDrop(demo3lists, {
handle: false,
nested: true,
item: ':scope > li',
announcement: {
grabbed: function (el) { return el.querySelector('span').innerText + ' grabbed'; },
dropped: function (el) { return el.querySelector('span').innerText + ' dropped'; },
Expand All @@ -239,27 +238,11 @@ <h3 id="nested">Nested lists</h3>
},
cancel: function () { return 'Reorder cancelled.'; }
}
};

var demo3 = new DragonDrop(document.getElementById('demo-3'), {
handle: false,
nested: true,
item: '.top-level',
announcement: demo3Config.announcement
});
demo3.on('announcement', onAnnouncement);

var demo3a = new DragonDrop(document.getElementById('sub-1'), demo3Config);
demo3a.on('announcement', onAnnouncement);

var demo3b = new DragonDrop(document.getElementById('sub-2'), demo3Config);
demo3b.on('announcement', onAnnouncement);

var demo3c = new DragonDrop(document.getElementById('sub-3'), demo3Config);
demo3c.on('announcement', onAnnouncement);
demo3Dragons.forEach(instance => instance.on('announcement', onAnnouncement));

var checkbox = document.getElementById('display-region');

checkbox.addEventListener('change', function () {
liveLog.classList.toggle('active');
});
Expand Down
67 changes: 25 additions & 42 deletions dist/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,23 @@ <h3 id="nested">Nested lists</h3>
<li class="top-level"><span>About Us</span></li>
<li class="top-level">
<span>Products</span>
<ul id="sub-1">
<ul id="sub-1" class="sublist">
<li><span>Product 1</span></li>
<li><span>Product 2</span></li>
<li><span>Product 3</span></li>
</ul>
</li>
<li class="top-level">
<span>Events</span>
<ul id="sub-2">
<ul id="sub-2" class="sublist">
<li><span>Event 1</span></li>
<li><span>Event 2</span></li>
<li><span>Event 3</span></li>
</ul>
</li>
<li class="top-level">
<span>Resources</span>
<ul id="sub-3">
<ul id="sub-3" class="sublist">
<li><span>Resource 1</span></li>
<li><span>Resource 2</span></li>
<li><span>Resource 3</span></li>
Expand All @@ -142,33 +142,30 @@ <h3 id="nested">Nested lists</h3>
<code data-language="js">
import DragonDrop from 'dragon-drop';

const demo3 = document.getElementById('demo-3');
const sublist1 = document.getElementById('sub-1');
const sublist2 = document.getElementById('sub-2');
const sublist3 = document.getElementById('sub-3');
const config = {
nested: true,
var lists = Array.prototype.slice.call(document.querySelectorAll('#demo-3, .sublist'));
var dragons = new DragonDrop(lists, {
handle: false,
nested: true,
item: ':scope > li',
announcement: {
grabbed: el => `${el.querySelector('span').innerText} grabbed`,
dropped: el => `${el.querySelector('span')} dropped`,
reorder: (el, items) => {
const pos = items.indexOf(el) + 1;
const text = el.querySelector('span').innerText;
return `The rankings have been updated, ${text} is now ${pos} of ${items.length}`;
grabbed: function (el) { return el.querySelector('span').innerText + ' grabbed'; },
dropped: function (el) { return el.querySelector('span').innerText + ' dropped'; },
reorder: function (el, items) {
var pos = items.indexOf(el) + 1;
var text = el.querySelector('span').innerText;
return 'The order has changed, ' + text + ' is now item ' + pos + ' of ' + items.length;
},
cancel: 'Reranking cancelled.'
cancel: function () { return 'Reorder cancelled.'; }
}
};

new DragonDrop(demo3, {
...config,
item: '.top-level' // important so we don't target the sub lis
});

new dragonDrop(sublist1, config);
new dragonDrop(sublist2, config);
new dragonDrop(sublist3, config);
// when we pass DragonDrop an array, we get an array of instances back
dragons.forEach(dragon => {
dragon
.on('grabbed', function (container, item) { console.log('grabbed: ', item); })
.on('dropped', function (container, item) { console.log('dropped: ', item); })
.on('reorder', function (container, item) { console.log('reorder: ', item); })
});
</code>
</pre>
</section>
Expand Down Expand Up @@ -226,9 +223,11 @@ <h3 id="nested">Nested lists</h3>

demo2.on('announcement', onAnnouncement);

var demo3Config = {
var demo3lists = Array.prototype.slice.call(document.querySelectorAll('#demo-3, .sublist'));
var demo3Dragons = new DragonDrop(demo3lists, {
handle: false,
nested: true,
item: ':scope > li',
announcement: {
grabbed: function (el) { return el.querySelector('span').innerText + ' grabbed'; },
dropped: function (el) { return el.querySelector('span').innerText + ' dropped'; },
Expand All @@ -239,27 +238,11 @@ <h3 id="nested">Nested lists</h3>
},
cancel: function () { return 'Reorder cancelled.'; }
}
};

var demo3 = new DragonDrop(document.getElementById('demo-3'), {
handle: false,
nested: true,
item: '.top-level',
announcement: demo3Config.announcement
});
demo3.on('announcement', onAnnouncement);

var demo3a = new DragonDrop(document.getElementById('sub-1'), demo3Config);
demo3a.on('announcement', onAnnouncement);

var demo3b = new DragonDrop(document.getElementById('sub-2'), demo3Config);
demo3b.on('announcement', onAnnouncement);

var demo3c = new DragonDrop(document.getElementById('sub-3'), demo3Config);
demo3c.on('announcement', onAnnouncement);
demo3Dragons.forEach(instance => instance.on('announcement', onAnnouncement));

var checkbox = document.getElementById('display-region');

checkbox.addEventListener('change', function () {
liveLog.classList.toggle('active');
});
Expand Down
Loading

0 comments on commit 74fcf23

Please sign in to comment.