A draggable tree by pure javascript.
npm install --save draggable-tree
// demo using React
import DraggableTree from 'draggable-tree';
class App extends React.Component {
constructor() {
super();
this.tree = null;
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
let tree = DraggableTree.create({
map: new Map(),
list: [],
mountDom: ".layers",
multiSelect: true
});
tree.createNode(null, {
data: "<div>item 1</div>"
});
tree.createNode(tree.getRootList()[0], {
data: "<div>item 2</div>"
});
tree.createNode(tree.getRootList()[0], {
data: "<div>item 3</div>"
});
tree.createNode(tree.getRootList()[0], {
data: "<div>item 4</div>"
});
tree.createNode(tree.getRootList()[0], {
data: "<div>item 5</div>"
});
tree.createNode(null, {
data: "<div>item 6</div>"
});
tree.createNode(null, {
data: "<div>item 7</div>"
});
this.tree = tree;
}
handleClick(e) {
if(e.target === e.currentTarget) {
this.tree.clearSelected();
}
}
render() {
let { location } = this.props;
return (
<div style={{padding: '100px', backgroundColor: '#fff'}} onClick={ this.handleClick }>
<div className={ "layers" } onMouseDown={ e => { e.stopPropagation() }} />
</div>
)
}
}
// see examples/index.html
<script src="../lib/DraggableTree.js"></script>
<script>
(function () {
// tree0
let tree = DraggableTree.create({
map: new Map(),
list: [],
mountDom: "#draggable-tree",
changed: function (actionType) {
console.log(actionType);
sessionStorage.setItem("draggable-tree-data", JSON.stringify({
rootList: tree.getRootList(),
map: Array.from(tree.getMap().entries())
}));
},
multiSelect: true
});
tree.createNode();
tree.createNode();
tree.createNode();
tree.createNode(tree.getRootList()[0]);
tree.createNode(tree.getMap().get(tree.getRootList()[0]).children[0], {
data: "<div class='test'>12345</div>"
});
// tree1
let treeStorageData = JSON.parse(sessionStorage.getItem("draggable-tree-data")) || {};
DraggableTree.create({
map: new Map(treeStorageData.map || []),
list: treeStorageData.rootList,
mountDom: "#draggable-tree-1",
// changed: 123,
});
}());
</script>
The list(rootList) is an array of nodeIds, the map is an Map with { id => node }
.
Moving type only has two scene: drop in(will move node to target's children);move up(will move node before target).See dropTargetType
in dragover & drop events.
use with:
let options = {
map: new Map(),
list: [],
mountDom: "#draggable-tree",
changed: function (actionType) {
console.log(actionType);
sessionStorage.setItem("draggable-tree-data", JSON.stringify({
rootList: tree.getRootList(),
map: Array.from(tree.getMap().entries())
}));
},
multiSelect: true
};
let tree = DraggableTree.create(options);
tree.createNode();
tree.createNode();
tree.createNode();
all options:
Prop | Type | Description |
Default |
---|---|---|---|
map | Map() | key : unique id value : Node |
new Map() |
list / rootList | Array | id list of top parents of the tree(those node has no parentId ) |
[] |
mountDom | HTMLElement or query(selecting by querySelector ) |
mounting dom | - |
multiSelect | Boolean | enable multiple selecting | false |
&events | - | set event like: { ...options, click: function {} } | - |
// node data struct
{
//
id: ${a unique id},
parentId: ${parentId},
children: ${children id list(Array)},
data: ${will render with innderHTML}
// ...
// you can set your own properties with createNode(parentId, nodeData)
}
use with:
// create a tree
let options = {
map: new Map(),
list: [],
mountDom: "#draggable-tree",
changed: function (actionType) {
console.log(actionType);
sessionStorage.setItem("draggable-tree-data", JSON.stringify({
rootList: tree.getRootList(),
map: Array.from(tree.getMap().entries())
}));
},
multiSelect: true
};
let tree = DraggableTree.create(options);
// getTrees
let trees = DraggableTree.getTrees();
trees[0].rempoveAll(); // a Tree
trees[1].createNode();
all events:
Function | Arguments | Description |
---|---|---|
create | options | create a tree, return a Tree (Object) |
getTrees | - | return an array: [ Tree, Tree, Tree, //... ] |
use with:
let tree = DraggableTree.create({
map: new Map(),
list: [],
mountDom: "#draggable-tree",
changed: function (actionType) {
console.log(actionType);
sessionStorage.setItem("draggable-tree-data", JSON.stringify({
rootList: tree.getRootList(),
map: Array.from(tree.getMap().entries())
}));
},
multiSelect: true
});
tree.createNode();
tree.createNode().createNode();
All functions return the Tree itself(except getRootList()
& getMap()
, which specified the return value), functions list:
Function | Arguments | Description |
---|---|---|
getRootList | - | get id list of top parents of the tree(those node has no parentId ); return Array |
getMap | - | get all nodes map; return Map |
createNode | parentId = tree.topParent, node = {} |
create a new Node, use parentId to create a child node; use node(Object ) to define custom properties |
clearSelected | - | clear selected |
remove | - | remove selected node |
removeAll | - | remove all nodes(like clearing the tree) |
toggleMultiSelect | - | toggle multi-select option |
render | list = [], map = new Map() | re-render the tree by new list & map |
setEvents | options = {} | an object contains events like: { click: function () {}, dragStart: function () {}, //... } |
use with:
// init
let tree = DraggableTree.create({
map: new Map(),
list: [],
mountDom: "#draggable-tree",
changed: function (actionType) {
console.log(actionType);
sessionStorage.setItem("draggable-tree-data", JSON.stringify({
rootList: tree.getRootList(),
map: Array.from(tree.getMap().entries())
}));
},
multiSelect: true
});
// set event
tree[0].setEvents({
changed: function (actionType) {
console.log(actionType);
sessionStorage.setItem("draggable-tree-data", JSON.stringify({
rootList: tree.getRootList(),
map: Array.from(tree.getMap().entries())
}));
console.log(tree, tree.getRootList(), new Map(Array.from(tree.getMap().entries())));
// sync tree2
tree2.render(tree.getRootList(), new Map(Array.from(tree.getMap().entries())));
},
click: function () {
// console.log(arguments);
}
});
events list:
Event | arguments | Description |
---|---|---|
click | draggingNodeId |
callback |
dragStart | event , draggingNodeId |
callback |
dragOver | event , draggingNodeId , doropTargetNodeId , dropTargetType |
callback, dropTargetType is a value of { in: 1, upon: 2 } |
dragLeave | event , draggingNodeId |
callback |
dragEnd | event , draggingNodeId |
callback |
beforeDrop | event , draggingNodeId , doropTargetNodeId , dropTargetType |
before drop; if return false, drop event will be interrupted |
drop | event , draggingNodeId , doropTargetNodeId , dropTargetType |
callback |
changed | actionType , |
callback |
https://blog.azlar.cc/demos/draggable-tree/.
or
git clone git@github.com:azlarsin/draggable-tree.git
cd draggable-tree
npm install
npm start
- 主逻辑、打包
- 事件逻辑,目前在考虑要不要支持多事件绑定(暂不支持)
- last holder 逻辑,会增添一些判断
- demo
- 发布到 npm
- api (区分开 npm 与 script 引用)
- readme
- collapse 功能
- readme - api (区分开 npm 与 script 引用)
- 逻辑优化,给同事用后感觉有些 api 不完善;例如:drop 前没有 before drop 用于阻止 drop 操作带来的数据变化(有时候需要与后台交互,获得成功答复后才可继续操作)
[ ] 将 node 的必要属性切换为私有命名:id => __id
,data => __data
- 将 node 修改为对象,使用
new Node(options)
来创建 node
- firefox 不可拖拽
- npm 引用失败
- 当传入的 data 为 html 时,dragOver、drop 事件失败(e.target 为传入的 dom)而造成的逻辑问题