Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
# Nested Sort

A JavaScript library to create a nested list of elements
Nested Sort is a vanilla JavaScript library, without any dependencies, which helps you to sort a nested list of items via drag and drop. Unfortunately, it does not support touch screens yet.

![](demo.gif)

## Docs

[https://gilgaz.com/demo/nested-sort](https://gilgaz.com/demo/nested-sort)

## Download

* [CDN copies](https://www.jsdelivr.com/package/npm/nested-sort) [![](https://data.jsdelivr.com/v1/package/npm/nested-sort/badge)](https://www.jsdelivr.com/package/npm/nested-sort)


## Installation

Using npm:
```shell
$ npm i nested-sort
$ npm install nested-sort
```

## Developer environment requirements
Using yarn:
```shell
$ yarn add nested-sort
```

## Contribution

### Developer environment requirements

To run this project, you will need:

- Node.js >= v10.5.0, use nvm - [install instructions](https://github.com/creationix/nvm#install-script)
- Yarn >= v1.7.0 - [install instructions ("Alternatives" tab)](https://yarnpkg.com/en/docs/install#alternatives-rc)

## Running tests
### Running tests

```sh
yarn
Expand All @@ -30,7 +42,7 @@ yarn test --watch
yarn test:coverage
```

## Dev mode
### Dev mode

When developing you can run:

Expand All @@ -40,10 +52,4 @@ yarn watch

This will regenerate the build files each time a source file is changed and serve on http://127.0.0.1:5000.

### Previewing umd build in the browser

If your package works in the browser, you can open `dev/index.html` to try it out.

## Demo

There are some samples in the `dev` folder. In order to see them in action, first run `yarn watch` and then navigate to: `http://127.0.0.1:5000/dev/`
You can navigate to http://127.0.0.1:5000/dev/ in order to see the samples.
Binary file added demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions dev/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ body {
.nested-sort .ns-targeted {
outline: 2px solid green;
}

.list-group .ns-dragged {
background-color: rgb(255, 255, 115);
}

.list-group .ns-targeted {
background-color: rgb(115, 255, 117);
}
1 change: 1 addition & 0 deletions dev/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ <h1>Samples</h1>
<li><a href="server-rendered-multiple-lists.html" target="_blank">Server-rendered Multiple Lists</a></li>
<li><a href="data-driven-list.html" target="_blank">Data-driven List</a></li>
<li><a href="mapped-data-driven-list.html" target="_blank">Mapped Data-driven List</a></li>
<li><a href="styling-data-driven-list.html" target="_blank">Styling</a></li>
</ol>

</div>
Expand Down
59 changes: 59 additions & 0 deletions dev/styling-data-driven-list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Nested Sort Styling</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet">
<link href="css/main.css" rel="stylesheet">
</head>
<body>

<div class="container">
<h1>Styling</h1>

<p>The main goal is to create a tree-like list of nested items. You should be able to achieve that by simply dragging and dropping the items using your mouse. Touch screens are not supported yet! 😐</p>

<p>
You can use the listClassNames and listItemClassNames props on the Config object to assign custom class names to
your list and list items. This helps with integrating your preferred front-end toolkit styling into your
Nested Sort lists. Below, you can see an example for Twitter Bootstrap v4 styling:
</p>

<h2>Twitter Bootstrap [Data-driven List]</h2>
<div id="bootstrap"></div>

</div>

<!-- Scripts -->
<script src="../dist/nested-sort.umd.js"></script>
<script>
(function() {
const data = [
{ id: 1, text: 'One' },
{ id: 11, text: 'One-One', parent: 1 },
{ id: 2, text: 'Two' },
{ id: 3, text: 'Three' },
{ id: 12, text: 'One-Two', parent: 1 },
{ id: 111, text: 'One-One-One', parent: 11 },
{ id: 112, text: 'One-One-Two', parent: 11 },
{ id: 113, text: 'One-One-Three', parent: 11 },
]

new NestedSort({
actions: {
onDrop: function (data) {
console.log(data)
}
},
data: data,
el: '#bootstrap',
listClassNames: ['list-group'],
listItemClassNames: ['list-group-item']
});
})();
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nested-sort",
"version": "4.0.0",
"version": "4.1.0",
"author": "Hesam Bahrami (Genzo)",
"description": "A JavaScript library to create a nested list of elements",
"main": "dist/nested-sort.cjs.js",
Expand Down
18 changes: 15 additions & 3 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class NestedSort {
* @param {number} droppingEdge
* @param {string} el
* @param {array|string} listClassNames
* @param {array|string} listItemClassNames
* @param {object} [propertyMap={}]
*/
constructor({
Expand All @@ -17,6 +18,7 @@ class NestedSort {
droppingEdge = 15,
el,
listClassNames,
listItemClassNames,
propertyMap = {}
} = {}) {
this.data = data;
Expand All @@ -27,6 +29,7 @@ class NestedSort {
this.draggedNode = null;
this.targetedNode = null;
this.listClassNames = this.createListClassNamesArray(listClassNames)
this.listItemClassNames = this.createListClassNamesArray(listItemClassNames)
this.propertyMap = propertyMap
this.actions = {
onDrop
Expand Down Expand Up @@ -101,7 +104,16 @@ class NestedSort {
}

addListAttributes() {
this.getSortableList().classList.add(...this.listClassNames)
const list = this.getSortableList()

list.classList.add(...this.listClassNames)
list.querySelectorAll('ul').forEach(ul => {
ul.classList.add(...this.listClassNames)
})

list.querySelectorAll('li').forEach(li => {
li.classList.add(...this.listItemClassNames)
})
}

initDragAndDrop() {
Expand Down Expand Up @@ -339,8 +351,8 @@ class NestedSort {
}

initPlaceholderList() {
this.placeholderUl = document.createElement('ul');
this.placeholderUl.classList.add(this.classNames.placeholder);
this.placeholderUl = document.createElement('ul')
this.placeholderUl.classList.add(this.classNames.placeholder, ...this.listClassNames)
}

getPlaceholderList() {
Expand Down
116 changes: 111 additions & 5 deletions src/main.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,113 @@
import NestedSort from './main'

describe('sample', () => {
it('should work', () => {
expect(true).toBeTruthy();
});
});
describe('NestedSort', () => {
const dynamicListWrapperId = 'dynamic-list-wrapper-id'

beforeEach(() => {
document.body.innerHTML = `<div id="${dynamicListWrapperId}"></div>`
})

describe('How it deals with List Class Names', () => {
it('should convert the listClassNames prop on the initialisation and assign it to this.listClassNames', () => {
[
'class1 class2',
['class1', 'class2'],
].forEach(listClassNames => {
const ns = new NestedSort({
data: [
{ id: 1, text: 'Item 1' }
],
el: `#${dynamicListWrapperId}`,
listClassNames,
})

expect(ns.listClassNames).toEqual([
'class1',
'class2',
])
})
})

it('should assign the class names to the main list and all the nested ones', () => {
const listClassNames = ['class1', 'class2']
const ns = new NestedSort({
data: [
{ id: 1, text: 'Item 1' },
{ id: 11, text: 'Item 1-1', parent: 1 },
{ id: 2, text: 'Item 2' },
{ id: 21, text: 'Item 2-1', parent: 2 },
{ id: 3, text: 'Item 3' },
],
el: `#${dynamicListWrapperId}`,
listClassNames,
})

const list = ns.getSortableList()
const nestedLists = list.querySelectorAll('ul')
const lists = [
list,
...nestedLists
]

expect(nestedLists.length).toBe(2)
lists.forEach(ul => {
expect(Object.values(ul.classList)).toEqual(listClassNames)
})
})

it('should assign the class names to the placeholder list when initialising it', () => {
const listClassNames = ['class1', 'class2']
const ns = new NestedSort({
data: [
{ id: 1, text: 'Item 1' }
],
el: `#${dynamicListWrapperId}`,
listClassNames,
})

ns.initPlaceholderList()

expect(ns.placeholderUl.nodeName).toBe('UL')
expect(Object.values(ns.placeholderUl.classList)).toEqual(expect.arrayContaining(listClassNames))
})
})

describe('How it deals with List Item Class Names', () => {
it('should convert the listItemClassNames prop on the initialisation and assign it to this.listItemClassNames', () => {
[
'class1 class2',
['class1', 'class2'],
].forEach(listItemClassNames => {
const ns = new NestedSort({
data: [
{ id: 1, text: 'Item 1' }
],
el: `#${dynamicListWrapperId}`,
listItemClassNames,
})
expect(ns.listItemClassNames).toEqual([
'class1',
'class2',
])
})
})

it('should assign the listItemClassNames to all the list items', () => {
const listItemClassNames = ['class1', 'class2']
const ns = new NestedSort({
data: [
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' },
],
el: `#${dynamicListWrapperId}`,
listItemClassNames,
})

const list = ns.getSortableList()
list.querySelectorAll('li').forEach(li => {
expect(Object.values(li.classList)).toEqual(listItemClassNames)
})
})
})
})