draggable-tree

A draggable tree by pure javascript.


License
ISC
Install
npm install draggable-tree@0.0.2

Documentation

draggable-tree

A draggable tree by pure javascript.

How to use

use npm

1. install

npm install --save draggable-tree

2. import & use

// 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>
        )
    }
}

directly using script tag

// 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>

Api

important

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.

init

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 (tree node)

// 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)
}

functions

DraggableTree(Trees)

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, //... ]

Tree(created by DraggableTree.create())

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 () {}, //... }

events

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

Demo

https://blog.azlar.cc/demos/draggable-tree/.

or

git clone git@github.com:azlarsin/draggable-tree.git
cd draggable-tree
npm install
npm start

todo

  • 主逻辑、打包
  • 事件逻辑,目前在考虑要不要支持多事件绑定(暂不支持)
  • 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

bug fixes

  • firefox 不可拖拽
  • npm 引用失败
  • 当传入的 data 为 html 时,dragOver、drop 事件失败(e.target 为传入的 dom)而造成的逻辑问题