Git Product home page Git Product logo

vue-draggable-nested-tree's Introduction

vue-draggable-nested-tree vue 可拖拽树, 可跨树拖拽

This project is no longer maintained, please move to the new monorepo he-tree.

本项目不再维护, 请移步到新的 monorepo he-tree.

这是可拖拽树组件. 此组件没有 css, 您需要自己添加您喜欢的样式, 参考 demo, 只有几个样式, 不难. 此组件不负责节点的具体渲染, 暴露了一个节点渲染插槽, 请参考 demo 自行渲染.
This is a draggable tree component. This component does not have css, you need to add your style refer to demo. The demo style is less, not difficult. This component doesn't render node. It exposes a node rendering slot. Please refer to the demo for rendering.

touch

已支持简单触摸(单点).
Support touch(single point).

Donation / 打赏

Paypal | Alipay/支付宝 | Wechat/微信

Indexes

install

npm i vue-draggable-nested-tree

usage

import

import { DraggableTree } from "vue-draggable-nested-tree";
// vue-draggable-nested-tree contains Tree, TreeNode, DraggableTree, DraggableTreeNode
// import the component and register it as global or local component

data

data: [
  { text: "node 1" },
  { text: "node 2" },
  { text: "node 3 undraggable", draggable: false },
  { text: "node 4" },
  { text: "node 4 undroppable", droppable: false },
  {
    text: "node 5",
    children: [
      { text: "node 1" },
      { text: "node 2", children: [{ text: "node 3" }, { text: "node 4" }] },
      {
        text: "node 2 undroppable",
        droppable: false,
        children: [{ text: "node 3" }, { text: "node 4" }],
      },
      {
        text: "node 2",
        children: [
          { text: "node 3" },
          { text: "node 4 undroppable", droppable: false },
        ],
      },
      { text: "node 3" },
      { text: "node 4" },
      { text: "node 3" },
      { text: "node 4" },
      { text: "node 3" },
      { text: "node 4" },
      { text: "node 3" },
      { text: "node 4" },
    ],
  },
];

template

Tree(:data="data" draggable crossTree)
  div(slot-scope="{data, store, vm}")
    //- data is node
    //- store is the tree
    //- vm is node Vue instance, you can get node level by vm.level
    template(v-if="!data.isDragPlaceHolder")
      b(v-if="data.children && data.children.length" @click="store.toggleOpen(data)") {{data.open ? '-' : '+'}} 
      span {{data.text}}

template for old browsers(eg: IE)

//- slot-scope="{data, store, vm}" may not work in old browsers, replace with slot-scope="slot"
Tree(:data="data" draggable crossTree)
  div(slot-scope="slot")
    //- data is node
    //- store is the tree
    //- vm is node Vue instance, you can get node level by vm.level
    template(v-if="!slot.data.isDragPlaceHolder")
      b(v-if="slot.data.children && slot.data.children.length" @click="slot.store.toggleOpen(slot.data)") {{slot.data.open ? '-' : '+'}} 
      span {{slot.data.text}}

api

The 'store' is the tree vm

Tree props

Noraml - Tree props
// base tree
data: {}, // type Array
indent: {default: 16},
activatedClass: {default: 'active'},
openedClass: {default: 'open'},
space: {default: 10}, // space between node, unit px
// draggable tree
preventSelect: {default: true}, // if to prevent drag handler text be selected when drag, excluding input and textarea
getTriggerEl: {type: Function}, // get the el trigger drag, default is node self. arguments(nodeVm)
draggable: {}, // is the tree draggable, default false
droppable: {default: true}, // is the tree droppable, default true
crossTree: {}, // can a node of the tree be dragged into other tree, or receive other tree node

Hooks - Tree props
ondragstart: {type: Function}, // hook. return false to prevent drag. arguments(node, draggableHelperInfo)
ondragend: {type: Function}, // hook. return false to prevent drop. arguments(node, draggableHelperInfo)

draggableHelperInfo

{event, options, store}

Tree properties

// base
rootData, // generated by tree
// draggable
dplh, // drag placeholder. globally unique.
trees, // array, all trees in the app. globally unique.

Tree events

// store is the tree vm
drag(node), // on drag start.
  drop(node, targetTree, oldTree), // after drop.
  change(node, targetTree, oldTree), // after drop, only when the node position changed
  nodeOpenChanged(node); // on a node is closed or open
  • targetTree and oldTree are tree vm.
  • oldTree is available only when cross tree. Otherwise null.
  • if cross tree, both targetTree and oldTree will emit drop and change.

Tree methods

pure(node, withChildren, after)
/*
pure
return a node data without runtime properties.(!: property which starts with '_' will be removed)
withChildren: optional. after: Function, optional
the code about after(t is computed node data):
if (after) {
  return after(t, node) || t
}
return t
*/
getNodeById(id)
getActivated()
getOpened()
activeNode(node, inactiveOld)
toggleActive(node, inactiveOld)
openNode(node, closeOld)
toggleOpen(node, closeOld)
// follow methods are easy, so I paste their soure code
getPureData(after) { return this.pure(this.rootData, true, after).children } // after: Function, optional
deleteNode(node) { return hp.arrayRemove(node.parent.children, node) }
// add node: like array. eg: node.children.push(newNodeData)
// update node: just assign to the node properties directly
isNodeDraggable(node)
isNodeDroppable(node)

node properties

// base
_id
_vm
parent
children: [],
open,
active: false,
style: {},
class: '',
innerStyle: {},
innerClass: '',
innerBackStyle: {},
innerBackClass: {},
// draggable
draggable // default true. Please check 'draggable & droppable' below
droppable // default true. Please check 'draggable & droppable' below
isDragPlaceHolder

node deep properties example

node._vm; // vm
node._vm.level; // 节点层级, 只读
node._vm.store; // tree
node.parent._vm; // parent node vm
node._vm.store;

other

demo css

.he-tree {
  border: 1px solid #ccc;
  padding: 20px;
  width: 300px;
}
.tree-node {
}
.tree-node-inner {
  padding: 5px;
  border: 1px solid #ccc;
  cursor: pointer;
}
.draggable-placeholder {
}
.draggable-placeholder-inner {
  border: 1px dashed #0088f8;
  box-sizing: border-box;
  background: rgba(0, 136, 249, 0.09);
  color: #0088f9;
  text-align: center;
  padding: 0;
  display: flex;
  align-items: center;
}

examples

clone the package, and

npm install
npm run dev

draggable & droppable

A node is default draggable and droppable. You can set draggable and droppable property of a node. The another way is listen event 'drag', traverse all data to set draggable or droppable property.

Traverse tree

Recommend to use my other library tree-helper. It has 2 traverse methods: depthFirstSearch, breadthFirstSearch.

draggable library

draggable-helper is my another library for drag. And it also is using by this component. You can use it to help you drag functions.

vue-draggable-nested-tree's People

Contributors

finstererfred avatar kyung-yeon avatar peimn avatar phphe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-draggable-nested-tree's Issues

i need set data dynamically

how i can need set data dynamically ??

			this.post_api('/admin/site/api/get_menu',{id:this.menuselected})
			.then((response) =>{
		 
				  this.tree =getVueObject(response.tree);
				console.log( );
			});

now work

Releases for breaking changes

When will there be releases?
I'm saying it because the last fix of moving level property from node to vm was a breaking change in my app as I was checking the levels.
It would be nice to have releases for all those breaking changes

Can only drag node once.

Attached is sample template, JS.

I am using vuex to fetch my data from server. After moving an item once usually i am unable to drop that particular item again. This usually happens if i move a child node out from parent. If i dont do the treeChanged method everything seem fine. Its only when i set tree data in change method or in vuex that the issue or bug occurs.

Template:
<DraggableTree :data="tree" :draggable="true" @change="treeChanged">
<div slot-scope="{data, store}">
<template>
<span>{{data.name}}</span>
</template>
</div>
</DraggableTree>

Data:
data() {
return { tree: JSON.parse(JSON.stringify(this.vuexData)) };
},
methods: {
treeChanged(node, targetTree) {
this.tree = targetTree.getPureData();
},
},
Sorry for poor formating, Any help would be greatly appreciated.

rtl languages

can you add an option for rtl languages ?
making the drag from right

Bug in method pure()

Hello,

I found an error in method pure()

pure(node, withChildren) {
const t = Object.assign({}, node)
delete t.id
delete t.parent
//delete t.children // do not remove childrens before recursion
delete t.open
delete t.level
delete t.active
delete t.style
delete t.class
delete t.innerStyle
delete t.innerClass
delete t.innerBackStyle
delete t.innerBackClass
for (const key of Object.keys(t)) {
if (key.startsWith('
')) { // it was key.startsWidth (!)
delete t[key]
}
}

After click on draggable item, certain elements on page become unresponsive.

As in title, I've found that whenever I click on an item in my tree, various elements (like paragraphs) become unresponsive. For instance: if I select part of paragraph (so it's highlited with blue background) and then click on item in the tree, this selection of paragraph cannot be removed. The only way to restore the proper behaviour is refreshing the page.
My guess is some kind of event fires but doesn't end since item wasn't moved (if item is moved this behaviour doesn't occur, however if item is clicked and then moved, it doesn't fix anything).

This bug is visible in examples provided on the main page (https://github.com/phphe/vue-draggable-nested-tree) - Base and MaxLevel. I tested it on newest Chrome, Firefox and Edge - all the same.

Is there some way to bypass that behaviour? My project requires usage of html editor, which becomes unresponsive when user accidentally clicks on an item in the tree.

Emit node new data in drop event

Hello. Try to use you component. In my project case I need to know the new node place data to prevent place node with children to lower level (so need to know the new level). But i can't get it in DROP hook or CHANGE event. I think it would be nice to emit 'dplh' too

i can not use even drop.help

<Tree :data="data" cross-tree="cross-tree" draggable @drop="drop">



<div class="node" @click="select(data)" v-bind:class="{ select: data.id==selectOrganization.id }">
<b v-if="data.children && data.children.length" @click="store.toggleOpen(data)">
{{data.open ? '-' : '+'}} 
{{data.name}}

<status
:status.sync= "data.status"
:options.sync = "options"
@Approval = "approval(data)"
@rejection = "rejection(data)"
/>
<button class="btn btn-primary btn-sm" @click="showAddModal(data)">
<button class="btn btn-warning btn-sm" @click="showEditModal(data)">
<button class="btn btn-danger btn-sm" @click="destroy(data)">




Nested item data,errors out when dragging.

Error in render: "TypeError: Cannot read property 'title' of undefined"

<draggable-tree :data="pages" draggable>
       <div slot-scope="{data, store}">
                <strong v-if="data.children && data.children.length" @click="store.toggleOpen(data)"> 
             {{data.open ?
                    '-' : '+'}}&nbsp;</strong>
                <span>{{data[language].title}}</span>
       </div>
</draggable-tree>

How to get updated array

Hi!
How to get updated array after dragend? It seems that STORE from "ondragend" method return too much data. Is there any other simple way to retrieve updated array

initial data

data: [
              {text: 'node 4'},
              {text: 'node 4 undroppable', droppable: false},
              {text: 'node 5', children: [
                {text: 'node 1'},
                {text: 'node 2', children: [
                  {text: 'node 3'},
                  {text: 'node 4'},
       //ommited

ondragend STORE element of an updated Array

active:(...)
children:(...)
class:(...)
innerBackClass:(...)
innerBackStyle:(...)
innerClass:(...)
innerStyle:(...)
level:(...)
open:(...)
parent:(...)
style:(...)
text:(...)
_droppable:true
_id:"tree_5_node_wIU3u"

How to prevent all child elements? (nested)

I would like to be able to use create a list with maxLevel = 1. I noted that your solution in examples, is to use tree helper to set "droppable" to false, but this does not work as desired if maxLevel = 1, because then you cannot move nodes up/down your tree either, as nothing is "droppable"!

Even when I set :droppable="false" and :draggable="true", I still cannot move nodes up/down the tree.

DEMO: https://codepen.io/neomarine/pen/GwKqGW

How to add children item to array

I want add new item to array.

I have tree:

tree1data: [
    {text: 'node 1', children: []},
    {text: 'node 2'},
    {text: 'node 3'},
    {text: 'node 4'},
]

I'm using

this.tree1data[0].children.push({
    text: 'Child Test'
})

How make it work

Fix for: push with childs

If we push any item with childs to Data, like this in Base Example:

     this.originalData.push({text: "HELLO", children: [{text: "HELLO 2"}]})

we get an error "TypeError: Cannot read property 'level' of undefined"

Fix for that in TreeNode.vue->watch handler, need to add recursive init for new arrays:

insert

        th.breadthFirstSearch(data, (node, k, parent) => {
          if(parent===undefined) parent=this.$parent.data;
          data._vm.store.compeleteNode(node, parent)
        });

instead of just

        this.store.compeleteNode(data, this.$parent.data)

edited: fixed top level parent definition

Now, it works good

Drag to reorder donot work at deepest level (my fix in PR attached)

At level 2 all nodes can be re-ordered by drag&drop, but at level 3 it seems not.
I found only the first position which is next to the upper level, is allowed to drop. see below.
level-3-912104040124

I've limited the max level to 3, using the codes in MaxLevel, is that relative to this problem?

version:
vue-draggable-nested-tree 2.1.6

Bug with computed tree

// UPDATE : you can read only the next message that is mode relevant

hi, and thanks for the great component !

I have a strange bug :
when dragging I don't have any placeHolder showing, and when dropping nothing changes place.

The JS console doesn't show any error until drop
then, when dropping on Firefox :

TypeError: dplh._vm is undefined
drop vue-draggable-nested-tree.es.js:1245
drop draggable-helper.es.js:185
wrapper drag-event-service.es.js:55

and on Chrome :

Uncaught TypeError: Cannot read property 'store' of undefined
    at Object.drop (vue-draggable-nested-tree.es.js?b1f6:1245)
    at drop (draggable-helper.es.js?f2fc:185)
    at wrapper (drag-event-service.es.js?32db:55)

I tried to reproduce the bug for the purpose of this message on codepen and ... it works perfectly :/
https://codepen.io/krodelabestiole/pen/PxXveP

I was wondering if it has something to do with styles so I disabled every style with firefox, and it still works perfectly in codepen, and not in my app...

So I guess it might be incompatible with some library I am using or any variable name I gave that conflict with vdnt (a quick search shows I'm not using store, data nor vm)

Here are my dependencies in case it might be relevant :

    "vue": "^2.5.17",
    "vue-router": "^3.0.1",
    "vuetify": "^1.3.0",
    "vuex": "^3.0.1"
    "@vue/cli-plugin-babel": "^3.1.1",
    "@vue/cli-plugin-eslint": "^3.1.1",
    "@vue/cli-service": "^3.1.1",
    "axios": "^0.18.0",
    "babel-eslint": "^10.0.1",
    "clone-deep": "^4.0.1",
    "eslint": "^5.8.0",
    "eslint-plugin-vue": "^5.0.0-0",
    "node-sass": "^4.10.0",
    "normalizr": "^3.3.0",
    "sass-loader": "^7.1.0",
    "stylus": "^0.54.5",
    "stylus-loader": "^3.0.1",
    "vue-cli-plugin-axios": "^0.0.4",
    "vue-cli-plugin-vuetify": "^0.4.6",
    "vue-draggable-nested-tree": "^2.2.2",
    "vue-template-compiler": "^2.5.17",
    "vuetify-loader": "^1.0.5"

Node Position Within Level

First of all: cool component! 👍

How can I obtain the position of a node within a level?

Example:
I have 3 nodes: "Orange", "Banana" and "Kiwi" - all on the root level.
I drag the Banana node to the first position; just before Orange.
So the new order of the nodes will be "Banana", "Orange, "Kiwi".
Using the @change handler, how can I obtain if Banana became the first node?

Thank you!

disabled prop?

I guess that right now I could disable dragging the tree by setting cross-tree to false, but that's more using a bug than a feature.

I have a tree that - using your component - is used for both reordering the tree and allowing users to edit node-attributes.

To distinguish the two, I would like a setting that turns off drag&drop for the whole tree.

Ideally the prop would also turn off the start-drag effect (right now you can still pick it up, you just can't drop it anywhere).

Unable to get raw jason data after changing the nodes

hi, my requirement is I want to save the node JSON data .. so whenever I change the node tree and console out the node array it outputs all dom kind of runtime properties. but I want to get newly modified JSON data in plain JSON format just data only . how can I achieve that.

Please Help me, please provide an example that would be very helpful.

Different css for each node.

Hi, first thnaks for your work, I really appreciate it.

I started to make a prototupy for something I want to develop. See: https://codepen.io/ldlcjulian/pen/GwxJKY

I need to generate 2 type of item for this tree. Let me explain, I would like to have "groups" and "layouts".

Groups can contain layouts. But Groups need to have different style, is it possible ?

Regards.

Rejec drop based on node parent property?

I'm facing a little problem. When I call :ondragend="dragEnd".

This is my node structure:

{
    description:"Minima nihil quia maiores sunt perferendis dolorem aut.",
    id: 2,
    identifier: "10000.10000",
    open: true,
    type: 1,
    value: "Repellat ut veniam labore enim quasi ad temporibus."
}

Inside the dragEnd function when I try to access the parent node it always return the previous parent node. How can I solve this?

dragEnd(node) {
    if (node.parent.type != 1) {
        return false
    }
}

in version 2.1.7 drag & drop is broken

I had two installs - one with 2.1.6 , one with 2.1.7 - in both cases upgrading to 2.1.7 means that the drag and drop no longer works. One of them is a pretty clean vue-cli-3 install.

Downgrading to 2.1.6 fixes it.

Console log

Hello,

there are some console.logs in this package ('drag start', 'drag end'). Could you please remove them, because it doesn't seem right :)

限制拖放层级时placeholder未正确删除

onDragEnd(node, nodeVm, store, event, draggable_helper_option, draggable_helper_store) {
      var from = node.level
      var toLevel = store._data.dplh.level
      var toParent = store._data.dplh.parent

      var drop = true

      // 第一层只能拖放到第一层
      if ((from === 1 && toLevel !== 1) || (toLevel === 1 && from !== 1)) {
        drop = false
      }
      // 只有第一层和分组层可以放入
      if (typeof(toParent.type) !== 'undefined' && toParent.type !== 'Group') {
        drop = false
      }
      return drop
}

我想要的限制是:
1/ 第一层元素只能在第一层元素间拖放
2/ 其他元素只能放入第一层下面或者类型为“Group"的节点下面(第一层类型未定义)

在使用中有时功能是正常的,有时在不能拖放的时候,placeholder未正确删除,导致树上有空白出现

Alignment issue with adding a child node

When adding a new child node to an existing node I'm getting an alignment issue. I am unable to drag the child node anywhere either. Once the parent node has been clicked, dragged and dropped the child node moves into the correct alignment and acts normally.

The code adding a new child.
node.children.push({id: data.id, title: data.title, description: '', is_published: data.is_published});

As you can see the 'Contact' node has a child but it's alignment is out.
first

After the 'Contact' node has been dragged and dropped the child node moves into it's correct alignment.
second

Let me know if you need any more information, thanks in advance!

TextEditor not clickable when within node when dragging=true, Enable Dragging only by selected child element

I am attempting to display a draggable (re-organizable) list of text editors, each editable by clicking on them (editable on click). The problem is, when :draggable="true", it will prevent all clicks passed to the text editor from triggering anything/working.

Additionally, I have successfully managed to enable dragging only when a child element (with ref="dragThis") to drag the element, but it only works if :draggable="true".

So, I am in a bit of a pickle it seems.

The text editor I am using is Vue Medium Editor

How can remove an item ?

I'm trying to make a menu builder like wordpress. Draggable items are change dynamically by user. I want to remove an item when delete button was clicked. But I don't know how to do that. I can't find related index in tree data.

I can handle deleteable item in my delete function. Is there a way to find and delete the relevant index ?

Re-Publish to npm

Hi phphe,
could you please republish the version with the changed
project.json to npm in order to make the webjar.org work?

And thank you for you great component ;)

Is there a way to prevent the placeholder appearing when dragging into root level?

Our project have a constraint that there should only be one node at the root level.
So we use following codes to prevent that happening, it works:

dragEndHook(node) {
  return !node._vm.store.dplh.parent.isRoot; // return false to stop dropping
}

But we found the placeHolder still appears at root level, that may mislead our users by let them think the root level is permitted for node to be dropped.
Is there a way to prevent the placeholder appearing?

version:
vue-draggable-nested-tree 2.1.6

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.