Compare commits
27 Commits
v1.4.2
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d4606f0d57 | ||
![]() |
7b677a6589 | ||
![]() |
a21f4b8adf | ||
![]() |
5b2fea32ad | ||
![]() |
347a927e90 | ||
![]() |
0f04f7edba | ||
![]() |
5500a36492 | ||
![]() |
66bc19baaa | ||
![]() |
97daf67f9a | ||
![]() |
d1e12aad85 | ||
![]() |
d5dd9b88c9 | ||
![]() |
ccb1067713 | ||
![]() |
d5705f92e8 | ||
![]() |
ae494ce25f | ||
![]() |
c59015fdf7 | ||
![]() |
5f94ebf51b | ||
![]() |
53320cc235 | ||
![]() |
d73b4c1829 | ||
![]() |
15f33d187d | ||
![]() |
a67e39ce31 | ||
![]() |
69ffc1da0f | ||
![]() |
31c9225441 | ||
![]() |
64c56af961 | ||
![]() |
a792ee3910 | ||
![]() |
aa3359155f | ||
![]() |
c1270b880b | ||
![]() |
d8a5da1e0e |
29
.eslintrc
Normal file
29
.eslintrc
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:prettier/recommended",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"no-console": "warn"
|
||||
},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/__tests__/*.{j,t}s?(x)",
|
||||
"**/tests/unit/**/*.spec.{j,t}s?(x)"
|
||||
],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,4 +1,3 @@
|
||||
hooks:
|
||||
pre-commit:
|
||||
- npm run standard
|
||||
pre-commit: npm run lint-staged
|
||||
commit-msg: commitlint -E HUSKY_GIT_PARAMS
|
@@ -1,5 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
presets: ['@vue/cli-plugin-babel/preset']
|
||||
}
|
||||
|
32
dev/App.vue
32
dev/App.vue
@@ -14,6 +14,11 @@
|
||||
default-leaf-node-name="new leaf"
|
||||
v-bind:default-expanded="false"
|
||||
>
|
||||
<template v-slot:leafNameDisplay="slotProps">
|
||||
<span>
|
||||
{{ slotProps.model.name }} <span class="muted">#{{ slotProps.model.id }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-slot:addTreeNodeIcon="slotProps">
|
||||
<span class="icon">📂</span>
|
||||
</template>
|
||||
@@ -31,18 +36,22 @@
|
||||
</template>
|
||||
<template v-slot:treeNodeIcon="slotProps">
|
||||
<span class="icon">
|
||||
{{ (slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded) ? '🌲' : '' }}</span>
|
||||
{{
|
||||
slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded
|
||||
? '🌲'
|
||||
: ''
|
||||
}}</span
|
||||
>
|
||||
</template>
|
||||
</vue-tree-list>
|
||||
<button @click="getNewTree">Get new tree</button>
|
||||
<pre>
|
||||
{{ newTree }}
|
||||
</pre
|
||||
>
|
||||
{{ newTree }}
|
||||
</pre>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {VueTreeList, Tree, TreeNode} from '../src'
|
||||
import { VueTreeList, Tree, TreeNode } from '../src'
|
||||
export default {
|
||||
components: {
|
||||
VueTreeList
|
||||
@@ -105,23 +114,23 @@ export default {
|
||||
console.log(params)
|
||||
},
|
||||
|
||||
drop: function({node, src, target}) {
|
||||
drop: function({ node, src, target }) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('drop', node, src, target)
|
||||
},
|
||||
|
||||
dropBefore: function({node, src, target}) {
|
||||
dropBefore: function({ node, src, target }) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('drop-before', node, src, target)
|
||||
},
|
||||
|
||||
dropAfter: function({node, src, target}) {
|
||||
dropAfter: function({ node, src, target }) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('drop-after', node, src, target)
|
||||
},
|
||||
|
||||
addNode() {
|
||||
var node = new TreeNode({name: 'new node', isLeaf: false})
|
||||
var node = new TreeNode({ name: 'new node', isLeaf: false })
|
||||
if (!this.data.children) this.data.children = []
|
||||
this.data.addChildren(node)
|
||||
},
|
||||
@@ -171,4 +180,9 @@ export default {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: gray;
|
||||
font-size: 80%;
|
||||
}
|
||||
</style>
|
||||
|
1364
package-lock.json
generated
1364
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-tree-list",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.0",
|
||||
"description": "A vue component for tree structure. Support adding treenode/leafnode, editing node's name and dragging.",
|
||||
"author": "ayou",
|
||||
"scripts": {
|
||||
@@ -9,14 +9,12 @@
|
||||
"test:unit": "vue-cli-service test:unit --watch",
|
||||
"test:coverage": "vue-cli-service test:unit --coverage",
|
||||
"lint": "vue-cli-service lint",
|
||||
"commit": "npx git-cz",
|
||||
"lint-staged": "lint-staged",
|
||||
"commit": "git-cz",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"main": "dist/vue-tree-list.umd.min.js",
|
||||
"dependencies": {
|
||||
"core-js": "^3.4.3",
|
||||
"vue": "^2.6.10"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.1.0",
|
||||
"@vue/cli-plugin-eslint": "^4.1.0",
|
||||
@@ -24,38 +22,25 @@
|
||||
"@vue/cli-service": "^4.1.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"core-js": "^3.4.3",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"git-cz": "^4.7.4",
|
||||
"husky": "^4.2.1",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"less": "^3.10.3",
|
||||
"less-loader": "^5.0.0",
|
||||
"lint-staged": "^10.0.4",
|
||||
"prettier": "^1.19.1",
|
||||
"prettier-eslint-cli": "^5.0.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": "warn"
|
||||
},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/__tests__/*.{j,t}s?(x)",
|
||||
"**/tests/unit/**/*.spec.{j,t}s?(x)"
|
||||
],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
"lint-staged": {
|
||||
"**/*.{js,json,md,vue}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
|
15
prettier.config.js
Normal file
15
prettier.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
printWidth: 100,
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: true,
|
||||
bracketSpacing: true,
|
||||
jsxBracketSameLine: false,
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
requirePragma: false,
|
||||
insertPragma: false,
|
||||
htmlWhitespaceSensitivity: 'css'
|
||||
}
|
131
readme.md
131
readme.md
@@ -1,14 +1,29 @@
|
||||
[](https://github.com/ParadeTo/vue-tree-list/actions)
|
||||
|
||||
# vue-tree-list
|
||||
|
||||
A vue component for tree structure. Support adding treenode/leafnode, editing node's name and dragging.
|
||||
|
||||

|
||||
|
||||
[Live Demo](http://paradeto.com/vue-tree-list/)
|
||||
|
||||
# install
|
||||
|
||||
Install the plugin then you can use the component globally.
|
||||
|
||||
```js
|
||||
import Vue from 'vue'
|
||||
import VueTreeList from 'vue-tree-list'
|
||||
|
||||
Vue.use(VueTreeList)
|
||||
```
|
||||
|
||||
Or just register locally like the example below.
|
||||
|
||||
# use
|
||||
``npm install vue-tree-list``
|
||||
|
||||
`npm install vue-tree-list`
|
||||
|
||||
```html
|
||||
<template>
|
||||
@@ -22,7 +37,13 @@ A vue component for tree structure. Support adding treenode/leafnode, editing no
|
||||
:model="data"
|
||||
default-tree-node-name="new node"
|
||||
default-leaf-node-name="new leaf"
|
||||
v-bind:default-expanded="false">
|
||||
v-bind:default-expanded="false"
|
||||
>
|
||||
<template v-slot:leafNameDisplay="slotProps">
|
||||
<span>
|
||||
{{ slotProps.model.name }} <span class="muted">#{{ slotProps.model.id }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<span class="icon" slot="addTreeNodeIcon">📂</span>
|
||||
<span class="icon" slot="addLeafNodeIcon">+</span>
|
||||
<span class="icon" slot="editNodeIcon">📃</span>
|
||||
@@ -43,7 +64,7 @@ A vue component for tree structure. Support adding treenode/leafnode, editing no
|
||||
components: {
|
||||
VueTreeList
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
newTree: {},
|
||||
data: new Tree([
|
||||
@@ -80,32 +101,32 @@ A vue component for tree structure. Support adding treenode/leafnode, editing no
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDel (node) {
|
||||
onDel(node) {
|
||||
console.log(node)
|
||||
node.remove()
|
||||
},
|
||||
|
||||
onChangeName (params) {
|
||||
onChangeName(params) {
|
||||
console.log(params)
|
||||
},
|
||||
|
||||
onAddNode (params) {
|
||||
onAddNode(params) {
|
||||
console.log(params)
|
||||
},
|
||||
|
||||
onClick (params) {
|
||||
onClick(params) {
|
||||
console.log(params)
|
||||
},
|
||||
|
||||
addNode () {
|
||||
addNode() {
|
||||
var node = new TreeNode({ name: 'new node', isLeaf: false })
|
||||
if (!this.data.children) this.data.children = []
|
||||
this.data.addChildren(node)
|
||||
},
|
||||
|
||||
getNewTree () {
|
||||
getNewTree() {
|
||||
var vm = this
|
||||
function _dfs (oldNode) {
|
||||
function _dfs(oldNode) {
|
||||
var newNode = {}
|
||||
|
||||
for (var k in oldNode) {
|
||||
@@ -124,8 +145,7 @@ A vue component for tree structure. Support adding treenode/leafnode, editing no
|
||||
}
|
||||
|
||||
vm.newTree = _dfs(vm.data)
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -150,58 +170,71 @@ A vue component for tree structure. Support adding treenode/leafnode, editing no
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.muted {
|
||||
color: gray;
|
||||
font-size: 80%;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
# props
|
||||
## props of vue-tree-list
|
||||
| name | type | default | description |
|
||||
|:-----:|:-------:|:------------:|:----:|
|
||||
model | TreeNode | - | You can use `const head = new Tree([])` to generate a tree with the head of `TreeNode` type
|
||||
default-tree-node-name | string | New node node | Default name for new treenode
|
||||
default-leaf-node-name | string | New leaf node | Default name for new leafnode
|
||||
default-expanded | boolean | true | Tree is expanded or not
|
||||
|
||||
## props of vue-tree-list
|
||||
|
||||
| name | type | default | description |
|
||||
| :--------------------: | :------: | :-----------: | :-----------------------------------------------------------------------------------------: |
|
||||
| model | TreeNode | - | You can use `const head = new Tree([])` to generate a tree with the head of `TreeNode` type |
|
||||
| default-tree-node-name | string | New node node | Default name for new treenode |
|
||||
| default-leaf-node-name | string | New leaf node | Default name for new leafnode |
|
||||
| default-expanded | boolean | true | Tree is expanded or not |
|
||||
|
||||
## props of TreeNode
|
||||
|
||||
### attributes
|
||||
| name | type | default | description |
|
||||
|:-----:|:-------:|:------------:|:----:|
|
||||
id | string, number | current timestamp | The node's id
|
||||
isLeaf | boolean | false | The node is leaf or not
|
||||
dragDisabled | boolean | false | Forbid dragging tree node
|
||||
addTreeNodeDisabled | boolean | false | Show `addTreeNode` button or not
|
||||
addLeafNodeDisabled | boolean | false | Show `addLeafNode` button or not
|
||||
editNodeDisabled | boolean | false | Show `editNode` button or not
|
||||
delNodeDisabled | boolean | false | Show `delNode` button or not
|
||||
children | array | null | The children of node
|
||||
|
||||
| name | type | default | description |
|
||||
| :-----------------: | :------------: | :---------------: | :------------------------------: |
|
||||
| id | string, number | current timestamp | The node's id |
|
||||
| isLeaf | boolean | false | The node is leaf or not |
|
||||
| dragDisabled | boolean | false | Forbid dragging tree node |
|
||||
| addTreeNodeDisabled | boolean | false | Show `addTreeNode` button or not |
|
||||
| addLeafNodeDisabled | boolean | false | Show `addLeafNode` button or not |
|
||||
| editNodeDisabled | boolean | false | Show `editNode` button or not |
|
||||
| delNodeDisabled | boolean | false | Show `delNode` button or not |
|
||||
| children | array | null | The children of node |
|
||||
|
||||
### methods
|
||||
| name | params | description |
|
||||
|:-----:|:-------:|:----:|
|
||||
changeName | name | Change node's name
|
||||
addChildren | children: object, array | Add children to node
|
||||
remove | - | Remove node from the tree
|
||||
moveInto | target: TreeNode | Move node into another node
|
||||
insertBefore | target: TreeNode | Move node before another node
|
||||
insertAfter | target: TreeNode | Move node after another node
|
||||
|
||||
| name | params | description |
|
||||
| :----------: | :---------------------: | :---------------------------: |
|
||||
| changeName | name | Change node's name |
|
||||
| addChildren | children: object, array | Add children to node |
|
||||
| remove | - | Remove node from the tree |
|
||||
| moveInto | target: TreeNode | Move node into another node |
|
||||
| insertBefore | target: TreeNode | Move node before another node |
|
||||
| insertAfter | target: TreeNode | Move node after another node |
|
||||
|
||||
# events
|
||||
| name | params | description |
|
||||
|:-----:|:-------:|:----:|
|
||||
click | TreeNode | Trigger when clicking a tree node
|
||||
change-name | {'id', 'oldName', 'newName'} | Trigger after changing a node's name
|
||||
delete-node | TreeNode | Trigger when clicking `delNode` button. You can call `remove` of `TreeNode` to remove the node.
|
||||
add-node | TreeNode | Trigger after adding a new node
|
||||
drop | {node, src, target} | Trigger after dropping a node into another. node: the draggable node, src: the draggable node's parent, target: the node that draggable node will drop into
|
||||
drop-before | {node, src, target} | Trigger after dropping a node before another. node: the draggable node, src: the draggable node's parent, target: the node that draggable node will drop before
|
||||
drop-after | {node, src, target} | Trigger after dropping a node after another. node: the draggable node, src: the draggable node's parent, target: the node that draggable node will drop after
|
||||
|
||||
| name | params | description |
|
||||
| :---------: | :--------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| click | TreeNode | Trigger when clicking a tree node. You can call `toggle` of `TreeNode` to toggle the folder node. |
|
||||
| change-name | {'id', 'oldName', 'newName'} | Trigger after changing a node's name |
|
||||
| delete-node | TreeNode | Trigger when clicking `delNode` button. You can call `remove` of `TreeNode` to remove the node. |
|
||||
| add-node | TreeNode | Trigger after adding a new node |
|
||||
| drop | {node, src, target} | Trigger after dropping a node into another. node: the draggable node, src: the draggable node's parent, target: the node that draggable node will drop into |
|
||||
| drop-before | {node, src, target} | Trigger after dropping a node before another. node: the draggable node, src: the draggable node's parent, target: the node that draggable node will drop before |
|
||||
| drop-after | {node, src, target} | Trigger after dropping a node after another. node: the draggable node, src: the draggable node's parent, target: the node that draggable node will drop after |
|
||||
|
||||
# customize operation icons
|
||||
|
||||
The component has default icons for `addTreeNodeIcon`, `addLeafNodeIcon`, `editNodeIcon`, `delNodeIcon`, `leafNodeIcon`, `treeNodeIcon` button, but you can also customize them and can access `model`, `root`, `expanded` as below:
|
||||
|
||||
```html
|
||||
<template v-slot:leafNameDisplay="slotProps">
|
||||
<span>{{ slotProps.model.name }} #{{ slotProps.model.id }}</span>
|
||||
</template>
|
||||
<template v-slot:addTreeNodeIcon="slotProps">
|
||||
<span class="icon">📂</span>
|
||||
</template>
|
||||
@@ -219,6 +252,8 @@ The component has default icons for `addTreeNodeIcon`, `addLeafNodeIcon`, `editN
|
||||
</template>
|
||||
<template v-slot:treeNodeIcon="slotProps">
|
||||
<span class="icon">
|
||||
{{ (slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded) ? '🌲' : '' }}</span>
|
||||
{{ (slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded) ?
|
||||
'🌲' : '' }}</span
|
||||
>
|
||||
</template>
|
||||
```
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import {traverseTree} from './tools'
|
||||
import { traverseTree } from './tools'
|
||||
/**
|
||||
* Tree data struct
|
||||
* Created by ayou on 2017/7/20.
|
||||
@@ -11,7 +11,7 @@ import {traverseTree} from './tools'
|
||||
*/
|
||||
export class TreeNode {
|
||||
constructor(data) {
|
||||
const {id, isLeaf} = data
|
||||
const { id, isLeaf } = data
|
||||
this.id = typeof id === 'undefined' ? new Date().valueOf() : id
|
||||
this.parent = null
|
||||
this.children = null
|
||||
@@ -149,7 +149,7 @@ export class TreeNode {
|
||||
|
||||
export class Tree {
|
||||
constructor(data) {
|
||||
this.root = new TreeNode({name: 'root', isLeaf: false, id: 0})
|
||||
this.root = new TreeNode({ name: 'root', isLeaf: false, id: 0 })
|
||||
this.initNode(this.root, data)
|
||||
return this.root
|
||||
}
|
||||
|
@@ -4,11 +4,11 @@
|
||||
v-if="model.name !== 'root'"
|
||||
:id="model.id"
|
||||
class="vtl-node"
|
||||
:class="{'vtl-leaf-node': model.isLeaf, 'vtl-tree-node': !model.isLeaf}"
|
||||
:class="{ 'vtl-leaf-node': model.isLeaf, 'vtl-tree-node': !model.isLeaf }"
|
||||
>
|
||||
<div
|
||||
class="vtl-border vtl-up"
|
||||
:class="{'vtl-active': isDragEnterUp}"
|
||||
:class="{ 'vtl-active': isDragEnterUp }"
|
||||
@drop="dropBefore"
|
||||
@dragenter="dragEnterUp"
|
||||
@dragover="dragOverUp"
|
||||
@@ -27,40 +27,25 @@
|
||||
@mouseout="mouseOut"
|
||||
@click.stop="click"
|
||||
>
|
||||
<span
|
||||
class="vtl-caret vtl-is-small"
|
||||
v-if="model.children && model.children.length > 0"
|
||||
>
|
||||
<i
|
||||
class="vtl-icon"
|
||||
:class="caretClass"
|
||||
@click.prevent.stop="toggle"
|
||||
></i>
|
||||
<span class="vtl-caret vtl-is-small" v-if="model.children && model.children.length > 0">
|
||||
<i class="vtl-icon" :class="caretClass" @click.prevent.stop="toggle"></i>
|
||||
</span>
|
||||
|
||||
<span v-if="model.isLeaf">
|
||||
<slot
|
||||
name="leafNodeIcon"
|
||||
:expanded="expanded"
|
||||
:model="model"
|
||||
:root="rootNode"
|
||||
>
|
||||
<slot name="leafNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
|
||||
<i class="vtl-icon vtl-menu-icon vtl-icon-file"></i>
|
||||
</slot>
|
||||
</span>
|
||||
<span v-else>
|
||||
<slot
|
||||
name="treeNodeIcon"
|
||||
:expanded="expanded"
|
||||
:model="model"
|
||||
:root="rootNode"
|
||||
>
|
||||
<slot name="treeNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
|
||||
<i class="vtl-icon vtl-menu-icon vtl-icon-folder"></i>
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<div class="vtl-node-content" v-if="!editable">
|
||||
{{ model.name }}
|
||||
<slot name="leafNameDisplay" :expanded="expanded" :model="model" :root="rootNode">
|
||||
{{ model.name }}
|
||||
</slot>
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
@@ -73,58 +58,30 @@
|
||||
/>
|
||||
<div class="vtl-operation" v-show="isHover">
|
||||
<span
|
||||
title="add tree node"
|
||||
:title="defaultAddTreeNodeTitle"
|
||||
@click.stop.prevent="addChild(false)"
|
||||
v-if="!model.isLeaf && !model.addTreeNodeDisabled"
|
||||
>
|
||||
<slot
|
||||
name="addTreeNodeIcon"
|
||||
:expanded="expanded"
|
||||
:model="model"
|
||||
:root="rootNode"
|
||||
>
|
||||
<slot name="addTreeNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
|
||||
<i class="vtl-icon vtl-icon-folder-plus-e"></i>
|
||||
</slot>
|
||||
</span>
|
||||
<span
|
||||
title="add leaf node"
|
||||
:title="defaultAddLeafNodeTitle"
|
||||
@click.stop.prevent="addChild(true)"
|
||||
v-if="!model.isLeaf && !model.addLeafNodeDisabled"
|
||||
>
|
||||
<slot
|
||||
name="addLeafNodeIcon"
|
||||
:expanded="expanded"
|
||||
:model="model"
|
||||
:root="rootNode"
|
||||
>
|
||||
<slot name="addLeafNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
|
||||
<i class="vtl-icon vtl-icon-plus"></i>
|
||||
</slot>
|
||||
</span>
|
||||
<span
|
||||
title="edit"
|
||||
@click.stop.prevent="setEditable"
|
||||
v-if="!model.editNodeDisabled"
|
||||
>
|
||||
<slot
|
||||
name="editNodeIcon"
|
||||
:expanded="expanded"
|
||||
:model="model"
|
||||
:root="rootNode"
|
||||
>
|
||||
<span title="edit" @click.stop.prevent="setEditable" v-if="!model.editNodeDisabled">
|
||||
<slot name="editNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
|
||||
<i class="vtl-icon vtl-icon-edit"></i>
|
||||
</slot>
|
||||
</span>
|
||||
<span
|
||||
title="delete"
|
||||
@click.stop.prevent="delNode"
|
||||
v-if="!model.delNodeDisabled"
|
||||
>
|
||||
<slot
|
||||
name="delNodeIcon"
|
||||
:expanded="expanded"
|
||||
:model="model"
|
||||
:root="rootNode"
|
||||
>
|
||||
<span title="delete" @click.stop.prevent="delNode" v-if="!model.delNodeDisabled">
|
||||
<slot name="delNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
|
||||
<i class="vtl-icon vtl-icon-trash"></i>
|
||||
</slot>
|
||||
</span>
|
||||
@@ -134,7 +91,7 @@
|
||||
<div
|
||||
v-if="model.children && model.children.length > 0 && expanded"
|
||||
class="vtl-border vtl-bottom"
|
||||
:class="{'vtl-active': isDragEnterBottom}"
|
||||
:class="{ 'vtl-active': isDragEnterBottom }"
|
||||
@drop="dropAfter"
|
||||
@dragenter="dragEnterBottom"
|
||||
@dragover="dragOverBottom"
|
||||
@@ -143,7 +100,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="{'vtl-tree-margin': model.name !== 'root'}"
|
||||
:class="{ 'vtl-tree-margin': model.name !== 'root' }"
|
||||
v-show="model.name === 'root' || expanded"
|
||||
v-if="isFolder"
|
||||
>
|
||||
@@ -155,6 +112,9 @@
|
||||
:model="model"
|
||||
:key="model.id"
|
||||
>
|
||||
<template v-slot:leafNameDisplay="slotProps">
|
||||
<slot name="leafNameDisplay" v-bind="slotProps" />
|
||||
</template>
|
||||
<template v-slot:addTreeNodeIcon="slotProps">
|
||||
<slot name="addTreeNodeIcon" v-bind="slotProps" />
|
||||
</template>
|
||||
@@ -179,12 +139,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {TreeNode} from './Tree.js'
|
||||
import {addHandler, removeHandler} from './tools.js'
|
||||
import { TreeNode } from './Tree.js'
|
||||
import { addHandler, removeHandler } from './tools.js'
|
||||
|
||||
let compInOperation = null
|
||||
|
||||
export default {
|
||||
name: 'vue-tree-list',
|
||||
data: function() {
|
||||
return {
|
||||
isHover: false,
|
||||
@@ -201,11 +162,19 @@ export default {
|
||||
},
|
||||
defaultLeafNodeName: {
|
||||
type: String,
|
||||
default: 'New leaf node'
|
||||
default: 'Leaf Node'
|
||||
},
|
||||
defaultTreeNodeName: {
|
||||
type: String,
|
||||
default: 'New tree node'
|
||||
default: 'Tree Node'
|
||||
},
|
||||
defaultAddTreeNodeTitle: {
|
||||
type: String,
|
||||
default: 'Add Tree Node'
|
||||
},
|
||||
defaultAddLeafNodeTitle: {
|
||||
type: String,
|
||||
default: 'Add Leaf Node'
|
||||
},
|
||||
defaultExpanded: {
|
||||
type: Boolean,
|
||||
@@ -231,7 +200,7 @@ export default {
|
||||
|
||||
treeNodeClass() {
|
||||
const {
|
||||
model: {dragDisabled, disabled},
|
||||
model: { dragDisabled, disabled },
|
||||
isDragEnterNode
|
||||
} = this
|
||||
|
||||
@@ -265,7 +234,8 @@ export default {
|
||||
this.rootNode.$emit('change-name', {
|
||||
id: this.model.id,
|
||||
oldName: oldName,
|
||||
newName: e.target.value
|
||||
newName: e.target.value,
|
||||
node: this.model
|
||||
})
|
||||
},
|
||||
|
||||
@@ -282,8 +252,16 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
setUnEditable() {
|
||||
setUnEditable(e) {
|
||||
this.editable = false
|
||||
var oldName = this.model.name
|
||||
this.model.changeName(e.target.value)
|
||||
this.rootNode.$emit('change-name', {
|
||||
id: this.model.id,
|
||||
oldName: oldName,
|
||||
newName: e.target.value,
|
||||
eventType: 'blur'
|
||||
})
|
||||
},
|
||||
|
||||
toggle() {
|
||||
@@ -302,13 +280,16 @@ export default {
|
||||
},
|
||||
|
||||
click() {
|
||||
this.rootNode.$emit('click', this.model)
|
||||
this.rootNode.$emit('click', {
|
||||
toggle: this.toggle,
|
||||
...this.model
|
||||
})
|
||||
},
|
||||
|
||||
addChild(isLeaf) {
|
||||
const name = isLeaf ? this.defaultLeafNodeName : this.defaultTreeNodeName
|
||||
this.expanded = true
|
||||
var node = new TreeNode({name, isLeaf})
|
||||
var node = new TreeNode({ name, isLeaf })
|
||||
this.model.addChildren(node, true)
|
||||
this.rootNode.$emit('add-node', node)
|
||||
},
|
||||
@@ -332,7 +313,8 @@ export default {
|
||||
},
|
||||
dragEnter() {
|
||||
if (!compInOperation) return
|
||||
if (this.model.isLeaf) return
|
||||
if (compInOperation.model.id === this.model.id || !compInOperation || this.model.isLeaf)
|
||||
return
|
||||
this.isDragEnterNode = true
|
||||
},
|
||||
dragLeave() {
|
||||
|
11
src/index.js
11
src/index.js
@@ -2,7 +2,12 @@
|
||||
* Created by ayou on 17/7/21.
|
||||
*/
|
||||
|
||||
import VueTreeList from "./VueTreeList";
|
||||
import { Tree, TreeNode } from "./Tree";
|
||||
import VueTreeList from './VueTreeList'
|
||||
import { Tree, TreeNode } from './Tree'
|
||||
|
||||
export { Tree, TreeNode, VueTreeList };
|
||||
VueTreeList.install = Vue => {
|
||||
Vue.component(VueTreeList.name, VueTreeList)
|
||||
}
|
||||
|
||||
export default VueTreeList
|
||||
export { Tree, TreeNode, VueTreeList }
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
var handlerCache
|
||||
|
||||
export const addHandler = function (element, type, handler) {
|
||||
export const addHandler = function(element, type, handler) {
|
||||
handlerCache = handler
|
||||
if (element.addEventListener) {
|
||||
element.addEventListener(type, handler, false)
|
||||
@@ -15,7 +15,7 @@ export const addHandler = function (element, type, handler) {
|
||||
}
|
||||
}
|
||||
|
||||
export const removeHandler = function (element, type) {
|
||||
export const removeHandler = function(element, type) {
|
||||
if (element.removeEventListener) {
|
||||
element.removeEventListener(type, handlerCache, false)
|
||||
} else if (element.detachEvent) {
|
||||
@@ -26,7 +26,7 @@ export const removeHandler = function (element, type) {
|
||||
}
|
||||
|
||||
// depth first search
|
||||
export const traverseTree = (root) => {
|
||||
export const traverseTree = root => {
|
||||
var newRoot = {}
|
||||
|
||||
for (var k in root) {
|
||||
|
@@ -47,7 +47,7 @@ exports[`Render render correctly 1`] = `
|
||||
<div class="vtl-node-content">
|
||||
Node 2
|
||||
</div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="add tree node"><i class="vtl-icon vtl-icon-folder-plus-e"></i></span> <span title="add leaf node"><i class="vtl-icon vtl-icon-plus"></i></span> <span title="edit"><i class="vtl-icon vtl-icon-edit"></i></span> <span title="delete"><i class="vtl-icon vtl-icon-trash"></i></span></div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="Add Tree Node"><i class="vtl-icon vtl-icon-folder-plus-e"></i></span> <span title="Add Leaf Node"><i class="vtl-icon vtl-icon-plus"></i></span> <span title="edit"><i class="vtl-icon vtl-icon-edit"></i></span> <span title="delete"><i class="vtl-icon vtl-icon-trash"></i></span></div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
@@ -61,7 +61,7 @@ exports[`Render render correctly 1`] = `
|
||||
<div class="vtl-node-content">
|
||||
Node 3
|
||||
</div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="add tree node"><i class="vtl-icon vtl-icon-folder-plus-e"></i></span> <span title="add leaf node"><i class="vtl-icon vtl-icon-plus"></i></span> <span title="edit"><i class="vtl-icon vtl-icon-edit"></i></span> <span title="delete"><i class="vtl-icon vtl-icon-trash"></i></span></div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="Add Tree Node"><i class="vtl-icon vtl-icon-folder-plus-e"></i></span> <span title="Add Leaf Node"><i class="vtl-icon vtl-icon-plus"></i></span> <span title="edit"><i class="vtl-icon vtl-icon-edit"></i></span> <span title="delete"><i class="vtl-icon vtl-icon-trash"></i></span></div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
|
@@ -11,7 +11,7 @@ exports[`Slot render slot correctly 1`] = `
|
||||
<div class="vtl-node-content">
|
||||
Node 1
|
||||
</div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="add tree node"><span class="add-tree-node-icon">📂</span></span> <span title="add leaf node"><span class="icon">+</span></span> <span title="edit"><span class="icon">📃</span></span> <span title="delete"><span></span></span></div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="Add Tree Node"><span class="add-tree-node-icon">📂</span></span> <span title="Add Leaf Node"><span class="icon">+</span></span> <span title="edit"><span class="icon">📃</span></span> <span title="delete"><span></span></span></div>
|
||||
</div>
|
||||
<div class="vtl-border vtl-bottom"></div>
|
||||
</div>
|
||||
@@ -42,7 +42,7 @@ exports[`Slot render slot correctly 1`] = `
|
||||
<div class="vtl-node-content">
|
||||
Node 2
|
||||
</div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="add tree node"><span class="add-tree-node-icon">📂</span></span> <span title="add leaf node"><span class="icon">+</span></span> <span title="edit"><span class="icon">📃</span></span> <span title="delete"><span class="del-node-icon">✂️</span></span></div>
|
||||
<div class="vtl-operation" style="display: none;"><span title="Add Tree Node"><span class="add-tree-node-icon">📂</span></span> <span title="Add Leaf Node"><span class="icon">+</span></span> <span title="edit"><span class="icon">📃</span></span> <span title="delete"><span class="del-node-icon">✂️</span></span></div>
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import {mount} from '@vue/test-utils'
|
||||
import {Tree, VueTreeList} from '@/index'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Tree, VueTreeList } from '@/index'
|
||||
|
||||
describe('Drag', () => {
|
||||
let wrapper
|
||||
@@ -36,14 +36,14 @@ describe('Drag', () => {
|
||||
pid: 0
|
||||
}
|
||||
])
|
||||
wrapper = mount(VueTreeList, {propsData: {model: new Tree([])}})
|
||||
wrapper.setProps({model: tree})
|
||||
wrapper = mount(VueTreeList, { propsData: { model: new Tree([]) } })
|
||||
wrapper.setProps({ model: tree })
|
||||
})
|
||||
|
||||
it('drag before', done => {
|
||||
const $tree2 = wrapper.find('#t2 .vtl-node-main')
|
||||
const $tree1Up = wrapper.find('#t1 .vtl-up')
|
||||
$tree2.trigger('dragstart', { dataTransfer: { setData: () => {} }})
|
||||
$tree2.trigger('dragstart', { dataTransfer: { setData: () => {} } })
|
||||
$tree1Up.trigger('drop')
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.find('.vtl-node').attributes('id')).toBe('t2')
|
||||
@@ -54,10 +54,15 @@ describe('Drag', () => {
|
||||
it('drag after', done => {
|
||||
const $tree3 = wrapper.find('#t3 .vtl-node-main')
|
||||
const $tree1Bottom = wrapper.find('#t1 .vtl-bottom')
|
||||
$tree3.trigger('dragstart', { dataTransfer: { setData: () => {} }})
|
||||
$tree3.trigger('dragstart', { dataTransfer: { setData: () => {} } })
|
||||
$tree1Bottom.trigger('drop')
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.findAll('.vtl-tree-node').at(2).attributes('id')).toBe('t3')
|
||||
expect(
|
||||
wrapper
|
||||
.findAll('.vtl-tree-node')
|
||||
.at(2)
|
||||
.attributes('id')
|
||||
).toBe('t3')
|
||||
done()
|
||||
})
|
||||
})
|
||||
@@ -65,7 +70,7 @@ describe('Drag', () => {
|
||||
it('drag into', done => {
|
||||
const $tree3 = wrapper.find('#t3 .vtl-node-main')
|
||||
const $tree1 = wrapper.find('#t1 .vtl-node-main')
|
||||
$tree3.trigger('dragstart', { dataTransfer: { setData: () => {} }})
|
||||
$tree3.trigger('dragstart', { dataTransfer: { setData: () => {} } })
|
||||
$tree1.trigger('drop')
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.find('#t1 + .vtl-tree-margin .vtl-node').attributes('id')).toBe('t3')
|
||||
@@ -77,7 +82,7 @@ describe('Drag', () => {
|
||||
const snapshot = wrapper.html()
|
||||
const $tree1 = wrapper.find('#t1 .vtl-node-main')
|
||||
const $tree1Child = wrapper.find('#t12 .vtl-node-main')
|
||||
$tree1.trigger('dragstart', { dataTransfer: { setData: () => {} }})
|
||||
$tree1.trigger('dragstart', { dataTransfer: { setData: () => {} } })
|
||||
$tree1Child.trigger('drop')
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.html()).toBe(snapshot)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import {mount} from '@vue/test-utils'
|
||||
import {Tree, VueTreeList} from '@/index'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Tree, VueTreeList } from '@/index'
|
||||
|
||||
describe('Operation', () => {
|
||||
let wrapper
|
||||
@@ -26,8 +26,8 @@ describe('Operation', () => {
|
||||
pid: 0
|
||||
}
|
||||
])
|
||||
wrapper = mount(VueTreeList, {propsData: {model: new Tree([])}})
|
||||
wrapper.setProps({model: tree})
|
||||
wrapper = mount(VueTreeList, { propsData: { model: new Tree([]) } })
|
||||
wrapper.setProps({ model: tree })
|
||||
})
|
||||
|
||||
it('delete leaf node', done => {
|
||||
@@ -51,7 +51,7 @@ describe('Operation', () => {
|
||||
})
|
||||
|
||||
it('add leaf node', done => {
|
||||
const $node1AddLeafNode = wrapper.find('#t1 [title="add leaf node"]')
|
||||
const $node1AddLeafNode = wrapper.find('#t1 [title="Add Leaf Node"]')
|
||||
$node1AddLeafNode.trigger('click')
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.findAll('.vtl-leaf-node').length).toBe(2)
|
||||
@@ -60,7 +60,7 @@ describe('Operation', () => {
|
||||
})
|
||||
|
||||
it('add tree node', done => {
|
||||
const $node1AddTreeNode = wrapper.find('#t1 [title="add tree node"]')
|
||||
const $node1AddTreeNode = wrapper.find('#t1 [title="Add Tree Node"]')
|
||||
$node1AddTreeNode.trigger('click')
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.findAll('.vtl-tree-node').length).toBe(3)
|
||||
@@ -75,7 +75,7 @@ describe('Operation', () => {
|
||||
const $input = wrapper.find('#t1 .vtl-input')
|
||||
$input.element.value = 'New Node 1'
|
||||
$input.trigger('input')
|
||||
var event = new KeyboardEvent('keyup', {keyCode: 13})
|
||||
var event = new KeyboardEvent('keyup', { keyCode: 13 })
|
||||
window.dispatchEvent(event)
|
||||
Vue.nextTick(() => {
|
||||
expect(wrapper.find('#t1').text()).toBe('New Node 1')
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import {mount} from '@vue/test-utils'
|
||||
import {Tree, VueTreeList} from '@/index'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Tree, VueTreeList } from '@/index'
|
||||
|
||||
describe('Render', () => {
|
||||
it('render correctly', () => {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import {mount} from '@vue/test-utils'
|
||||
import {Tree, VueTreeList} from '@/index'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Tree, VueTreeList } from '@/index'
|
||||
|
||||
describe('Slot', () => {
|
||||
let wrapper
|
||||
@@ -27,29 +27,41 @@ describe('Slot', () => {
|
||||
}
|
||||
])
|
||||
wrapper = mount(VueTreeList, {
|
||||
propsData: {model: new Tree([])},
|
||||
propsData: { model: new Tree([]) },
|
||||
scopedSlots: {
|
||||
addTreeNodeIcon() {
|
||||
return <span class="add-tree-node-icon">📂</span>
|
||||
return <span class='add-tree-node-icon'>📂</span>
|
||||
},
|
||||
addLeafNodeIcon() {
|
||||
return <span class="icon">+</span>
|
||||
return <span class='icon'>+</span>
|
||||
},
|
||||
editNodeIcon() {
|
||||
return <span class="icon">📃</span>
|
||||
return <span class='icon'>📃</span>
|
||||
},
|
||||
delNodeIcon(slotProps) {
|
||||
return (slotProps.model.isLeaf || !slotProps.model.children) ? <span class="del-node-icon">✂️</span> : <span />
|
||||
return slotProps.model.isLeaf || !slotProps.model.children ? (
|
||||
<span class='del-node-icon'>✂️</span>
|
||||
) : (
|
||||
<span />
|
||||
)
|
||||
},
|
||||
leafNodeIcon() {
|
||||
return <span class="icon">🍃</span>
|
||||
return <span class='icon'>🍃</span>
|
||||
},
|
||||
treeNodeIcon(slotProps) {
|
||||
return <span class="tree-node-icon icon">{ slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded ? '🌲' : '❀' }</span>
|
||||
return (
|
||||
<span class='tree-node-icon icon'>
|
||||
{slotProps.model.children &&
|
||||
slotProps.model.children.length > 0 &&
|
||||
!slotProps.expanded
|
||||
? '🌲'
|
||||
: '❀'}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
wrapper.setProps({model: tree})
|
||||
wrapper.setProps({ model: tree })
|
||||
})
|
||||
|
||||
it('render slot correctly', () => {
|
||||
|
5
vue.config.js
Normal file
5
vue.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
css: {
|
||||
extract: false
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user