Compare commits

..

19 Commits

Author SHA1 Message Date
youxingzhi
7b677a6589 1.5.0 2020-11-28 22:11:56 +08:00
youxingzhi
a21f4b8adf chore: 🤖 rm npx 2020-11-28 22:11:42 +08:00
youxingzhi
5b2fea32ad feat: 🎸 #84, add default title props 2020-11-28 22:11:42 +08:00
ayou
347a927e90 Merge pull request #93 from TheFoot/feature/node-expand-collapse
Feature: Added toggle() method to the node passed to TreeNode.click()…
2020-10-28 09:56:11 +08:00
Barry Jones
0f04f7edba Feature: Added toggle() method to the node passed to TreeNode.click() handler. 2020-09-08 15:35:17 +01:00
youxingzhi
5500a36492 1.4.6 2020-07-02 09:59:35 +08:00
ayou
66bc19baaa Merge pull request #78 from zoispag/feat/74_slot_for_display_name
Feat: Add slot for display node/leaf name 
2020-07-02 09:57:17 +08:00
Zois Pagoulatos
97daf67f9a Add slot for display node/leaf name (#74) 2020-07-01 11:26:10 +02:00
youxingzhi
d1e12aad85 1.4.5 2020-05-30 22:13:14 +08:00
youxingzhi
d5dd9b88c9 fix: 🐛 #72 2020-05-30 22:12:00 +08:00
youxingzhi
ccb1067713 1.4.4 2020-04-25 19:44:54 +08:00
ayou
d5705f92e8 Merge pull request #68 from laashub/master
update: after blur emit the event for changeName
2020-04-10 11:48:34 +08:00
tristan-tsl
ae494ce25f update: after blur emit the event for changeName 2020-04-06 13:11:15 +08:00
tristan-tsl
c59015fdf7 update: after blur emit the event for changeName 2020-04-06 13:05:32 +08:00
ayou
5f94ebf51b Merge pull request #65 from ParadeTo/feature-code-format
chore: 🤖 add code format tool
2020-01-30 13:36:42 +08:00
youxingzhi
53320cc235 chore: 🤖 change lint-staged conf 2020-01-30 13:34:24 +08:00
youxingzhi
d73b4c1829 chore: 🤖 add code format tool 2020-01-30 12:01:36 +08:00
ayou
15f33d187d Merge pull request #64 from ParadeTo/fix-#62
fix: 🐛 #62
2020-01-30 10:44:55 +08:00
youxingzhi
a67e39ce31 fix: 🐛 #62 2020-01-30 10:43:51 +08:00
19 changed files with 1591 additions and 220 deletions

29
.eslintrc Normal file
View 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
}
}
]
}

View File

@@ -1,4 +1,3 @@
hooks: hooks:
pre-commit: pre-commit: npm run lint-staged
- npm run standard
commit-msg: commitlint -E HUSKY_GIT_PARAMS commit-msg: commitlint -E HUSKY_GIT_PARAMS

View File

@@ -1,5 +1,3 @@
module.exports = { module.exports = {
presets: [ presets: ['@vue/cli-plugin-babel/preset']
'@vue/cli-plugin-babel/preset'
]
} }

View File

@@ -14,6 +14,11 @@
default-leaf-node-name="new leaf" 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>
<template v-slot:addTreeNodeIcon="slotProps"> <template v-slot:addTreeNodeIcon="slotProps">
<span class="icon">📂</span> <span class="icon">📂</span>
</template> </template>
@@ -31,18 +36,22 @@
</template> </template>
<template v-slot:treeNodeIcon="slotProps"> <template v-slot:treeNodeIcon="slotProps">
<span class="icon"> <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> </template>
</vue-tree-list> </vue-tree-list>
<button @click="getNewTree">Get new tree</button> <button @click="getNewTree">Get new tree</button>
<pre> <pre>
{{ newTree }} {{ newTree }}
</pre </pre>
>
</div> </div>
</template> </template>
<script> <script>
import {VueTreeList, Tree, TreeNode} from '../src' import { VueTreeList, Tree, TreeNode } from '../src'
export default { export default {
components: { components: {
VueTreeList VueTreeList
@@ -105,23 +114,23 @@ export default {
console.log(params) console.log(params)
}, },
drop: function({node, src, target}) { drop: function({ node, src, target }) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('drop', node, src, target) console.log('drop', node, src, target)
}, },
dropBefore: function({node, src, target}) { dropBefore: function({ node, src, target }) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('drop-before', node, src, target) console.log('drop-before', node, src, target)
}, },
dropAfter: function({node, src, target}) { dropAfter: function({ node, src, target }) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('drop-after', node, src, target) console.log('drop-after', node, src, target)
}, },
addNode() { 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 = [] if (!this.data.children) this.data.children = []
this.data.addChildren(node) this.data.addChildren(node)
}, },
@@ -171,4 +180,9 @@ export default {
cursor: pointer; cursor: pointer;
} }
} }
.muted {
color: gray;
font-size: 80%;
}
</style> </style>

1338
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "vue-tree-list", "name": "vue-tree-list",
"version": "1.4.3", "version": "1.5.0",
"description": "A vue component for tree structure. Support adding treenode/leafnode, editing node's name and dragging.", "description": "A vue component for tree structure. Support adding treenode/leafnode, editing node's name and dragging.",
"author": "ayou", "author": "ayou",
"scripts": { "scripts": {
@@ -9,7 +9,8 @@
"test:unit": "vue-cli-service test:unit --watch", "test:unit": "vue-cli-service test:unit --watch",
"test:coverage": "vue-cli-service test:unit --coverage", "test:coverage": "vue-cli-service test:unit --coverage",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint",
"commit": "npx git-cz", "lint-staged": "lint-staged",
"commit": "git-cz",
"prepublish": "npm run build" "prepublish": "npm run build"
}, },
"main": "dist/vue-tree-list.umd.min.js", "main": "dist/vue-tree-list.umd.min.js",
@@ -23,38 +24,23 @@
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"core-js": "^3.4.3", "core-js": "^3.4.3",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-vue": "^5.0.0",
"git-cz": "^4.7.4",
"husky": "^4.2.1",
"jest-serializer-vue": "^2.0.2", "jest-serializer-vue": "^2.0.2",
"less": "^3.10.3", "less": "^3.10.3",
"less-loader": "^5.0.0", "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": "^2.6.10",
"vue-template-compiler": "^2.6.10" "vue-template-compiler": "^2.6.10"
}, },
"eslintConfig": { "lint-staged": {
"root": true, "**/*.{js,json,md,vue}": [
"env": { "prettier --write"
"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
}
}
] ]
}, },
"browserslist": [ "browserslist": [

15
prettier.config.js Normal file
View 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'
}

118
readme.md
View File

@@ -1,6 +1,7 @@
[![Actions Status](https://github.com/ParadeTo/vue-tree-list/workflows/Test/badge.svg)](https://github.com/ParadeTo/vue-tree-list/actions) [![Actions Status](https://github.com/ParadeTo/vue-tree-list/workflows/Test/badge.svg)](https://github.com/ParadeTo/vue-tree-list/actions)
# vue-tree-list # vue-tree-list
A vue component for tree structure. Support adding treenode/leafnode, editing node's name and dragging. A vue component for tree structure. Support adding treenode/leafnode, editing node's name and dragging.
![vue-tree-demo.gif](https://raw.githubusercontent.com/ParadeTo/vue-tree-list/master/img/demo.gif) ![vue-tree-demo.gif](https://raw.githubusercontent.com/ParadeTo/vue-tree-list/master/img/demo.gif)
@@ -21,7 +22,8 @@ Vue.use(VueTreeList)
Or just register locally like the example below. Or just register locally like the example below.
# use # use
``npm install vue-tree-list``
`npm install vue-tree-list`
```html ```html
<template> <template>
@@ -35,7 +37,13 @@ Or just register locally like the example below.
:model="data" :model="data"
default-tree-node-name="new node" default-tree-node-name="new node"
default-leaf-node-name="new leaf" 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="addTreeNodeIcon">📂</span>
<span class="icon" slot="addLeafNodeIcon"></span> <span class="icon" slot="addLeafNodeIcon"></span>
<span class="icon" slot="editNodeIcon">📃</span> <span class="icon" slot="editNodeIcon">📃</span>
@@ -56,7 +64,7 @@ Or just register locally like the example below.
components: { components: {
VueTreeList VueTreeList
}, },
data () { data() {
return { return {
newTree: {}, newTree: {},
data: new Tree([ data: new Tree([
@@ -93,32 +101,32 @@ Or just register locally like the example below.
} }
}, },
methods: { methods: {
onDel (node) { onDel(node) {
console.log(node) console.log(node)
node.remove() node.remove()
}, },
onChangeName (params) { onChangeName(params) {
console.log(params) console.log(params)
}, },
onAddNode (params) { onAddNode(params) {
console.log(params) console.log(params)
}, },
onClick (params) { onClick(params) {
console.log(params) console.log(params)
}, },
addNode () { 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 = [] if (!this.data.children) this.data.children = []
this.data.addChildren(node) this.data.addChildren(node)
}, },
getNewTree () { getNewTree() {
var vm = this var vm = this
function _dfs (oldNode) { function _dfs(oldNode) {
var newNode = {} var newNode = {}
for (var k in oldNode) { for (var k in oldNode) {
@@ -137,8 +145,7 @@ Or just register locally like the example below.
} }
vm.newTree = _dfs(vm.data) vm.newTree = _dfs(vm.data)
}, }
} }
} }
</script> </script>
@@ -163,58 +170,71 @@ Or just register locally like the example below.
cursor: pointer; cursor: pointer;
} }
} }
</style>
.muted {
color: gray;
font-size: 80%;
}
</style>
``` ```
# props # 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 ## props of TreeNode
### attributes ### attributes
| name | type | default | description |
|:-----:|:-------:|:------------:|:----:| | name | type | default | description |
id | string, number | current timestamp | The node's id | :-----------------: | :------------: | :---------------: | :------------------------------: |
isLeaf | boolean | false | The node is leaf or not | id | string, number | current timestamp | The node's id |
dragDisabled | boolean | false | Forbid dragging tree node | isLeaf | boolean | false | The node is leaf or not |
addTreeNodeDisabled | boolean | false | Show `addTreeNode` button or not | dragDisabled | boolean | false | Forbid dragging tree node |
addLeafNodeDisabled | boolean | false | Show `addLeafNode` button or not | addTreeNodeDisabled | boolean | false | Show `addTreeNode` button or not |
editNodeDisabled | boolean | false | Show `editNode` button or not | addLeafNodeDisabled | boolean | false | Show `addLeafNode` button or not |
delNodeDisabled | boolean | false | Show `delNode` button or not | editNodeDisabled | boolean | false | Show `editNode` button or not |
children | array | null | The children of node | delNodeDisabled | boolean | false | Show `delNode` button or not |
| children | array | null | The children of node |
### methods ### methods
| name | params | description |
|:-----:|:-------:|:----:| | name | params | description |
changeName | name | Change node's name | :----------: | :---------------------: | :---------------------------: |
addChildren | children: object, array | Add children to node | changeName | name | Change node's name |
remove | - | Remove node from the tree | addChildren | children: object, array | Add children to node |
moveInto | target: TreeNode | Move node into another node | remove | - | Remove node from the tree |
insertBefore | target: TreeNode | Move node before another node | moveInto | target: TreeNode | Move node into another node |
insertAfter | target: TreeNode | Move node after another node | insertBefore | target: TreeNode | Move node before another node |
| insertAfter | target: TreeNode | Move node after another node |
# events # events
| name | params | description |
|:-----:|:-------:|:----:| | name | params | description |
click | TreeNode | Trigger when clicking a tree node | :---------: | :--------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------: |
change-name | {'id', 'oldName', 'newName'} | Trigger after changing a node's name | click | TreeNode | Trigger when clicking a tree node. You can call `toggle` of `TreeNode` to toggle the folder node. |
delete-node | TreeNode | Trigger when clicking `delNode` button. You can call `remove` of `TreeNode` to remove the node. | change-name | {'id', 'oldName', 'newName'} | Trigger after changing a node's name |
add-node | TreeNode | Trigger after adding a new node | delete-node | TreeNode | Trigger when clicking `delNode` button. You can call `remove` of `TreeNode` to remove the 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 | add-node | TreeNode | Trigger after adding a new node |
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 | {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-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 | 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 # 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: 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 ```html
<template v-slot:leafNameDisplay="slotProps">
<span>{{ slotProps.model.name }} #{{ slotProps.model.id }}</span>
</template>
<template v-slot:addTreeNodeIcon="slotProps"> <template v-slot:addTreeNodeIcon="slotProps">
<span class="icon">📂</span> <span class="icon">📂</span>
</template> </template>
@@ -232,6 +252,8 @@ The component has default icons for `addTreeNodeIcon`, `addLeafNodeIcon`, `editN
</template> </template>
<template v-slot:treeNodeIcon="slotProps"> <template v-slot:treeNodeIcon="slotProps">
<span class="icon"> <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> </template>
``` ```

View File

@@ -1,4 +1,4 @@
import {traverseTree} from './tools' import { traverseTree } from './tools'
/** /**
* Tree data struct * Tree data struct
* Created by ayou on 2017/7/20. * Created by ayou on 2017/7/20.
@@ -11,7 +11,7 @@ import {traverseTree} from './tools'
*/ */
export class TreeNode { export class TreeNode {
constructor(data) { constructor(data) {
const {id, isLeaf} = data const { id, isLeaf } = data
this.id = typeof id === 'undefined' ? new Date().valueOf() : id this.id = typeof id === 'undefined' ? new Date().valueOf() : id
this.parent = null this.parent = null
this.children = null this.children = null
@@ -149,7 +149,7 @@ export class TreeNode {
export class Tree { export class Tree {
constructor(data) { 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) this.initNode(this.root, data)
return this.root return this.root
} }

View File

@@ -4,11 +4,11 @@
v-if="model.name !== 'root'" v-if="model.name !== 'root'"
:id="model.id" :id="model.id"
class="vtl-node" 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 <div
class="vtl-border vtl-up" class="vtl-border vtl-up"
:class="{'vtl-active': isDragEnterUp}" :class="{ 'vtl-active': isDragEnterUp }"
@drop="dropBefore" @drop="dropBefore"
@dragenter="dragEnterUp" @dragenter="dragEnterUp"
@dragover="dragOverUp" @dragover="dragOverUp"
@@ -27,40 +27,25 @@
@mouseout="mouseOut" @mouseout="mouseOut"
@click.stop="click" @click.stop="click"
> >
<span <span class="vtl-caret vtl-is-small" v-if="model.children && model.children.length > 0">
class="vtl-caret vtl-is-small" <i class="vtl-icon" :class="caretClass" @click.prevent.stop="toggle"></i>
v-if="model.children && model.children.length > 0"
>
<i
class="vtl-icon"
:class="caretClass"
@click.prevent.stop="toggle"
></i>
</span> </span>
<span v-if="model.isLeaf"> <span v-if="model.isLeaf">
<slot <slot name="leafNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
name="leafNodeIcon"
:expanded="expanded"
:model="model"
:root="rootNode"
>
<i class="vtl-icon vtl-menu-icon vtl-icon-file"></i> <i class="vtl-icon vtl-menu-icon vtl-icon-file"></i>
</slot> </slot>
</span> </span>
<span v-else> <span v-else>
<slot <slot name="treeNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
name="treeNodeIcon"
:expanded="expanded"
:model="model"
:root="rootNode"
>
<i class="vtl-icon vtl-menu-icon vtl-icon-folder"></i> <i class="vtl-icon vtl-menu-icon vtl-icon-folder"></i>
</slot> </slot>
</span> </span>
<div class="vtl-node-content" v-if="!editable"> <div class="vtl-node-content" v-if="!editable">
{{ model.name }} <slot name="leafNameDisplay" :expanded="expanded" :model="model" :root="rootNode">
{{ model.name }}
</slot>
</div> </div>
<input <input
v-else v-else
@@ -73,58 +58,30 @@
/> />
<div class="vtl-operation" v-show="isHover"> <div class="vtl-operation" v-show="isHover">
<span <span
title="add tree node" :title="defaultAddTreeNodeTitle"
@click.stop.prevent="addChild(false)" @click.stop.prevent="addChild(false)"
v-if="!model.isLeaf && !model.addTreeNodeDisabled" v-if="!model.isLeaf && !model.addTreeNodeDisabled"
> >
<slot <slot name="addTreeNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
name="addTreeNodeIcon"
:expanded="expanded"
:model="model"
:root="rootNode"
>
<i class="vtl-icon vtl-icon-folder-plus-e"></i> <i class="vtl-icon vtl-icon-folder-plus-e"></i>
</slot> </slot>
</span> </span>
<span <span
title="add leaf node" :title="defaultAddLeafNodeTitle"
@click.stop.prevent="addChild(true)" @click.stop.prevent="addChild(true)"
v-if="!model.isLeaf && !model.addLeafNodeDisabled" v-if="!model.isLeaf && !model.addLeafNodeDisabled"
> >
<slot <slot name="addLeafNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
name="addLeafNodeIcon"
:expanded="expanded"
:model="model"
:root="rootNode"
>
<i class="vtl-icon vtl-icon-plus"></i> <i class="vtl-icon vtl-icon-plus"></i>
</slot> </slot>
</span> </span>
<span <span title="edit" @click.stop.prevent="setEditable" v-if="!model.editNodeDisabled">
title="edit" <slot name="editNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
@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> <i class="vtl-icon vtl-icon-edit"></i>
</slot> </slot>
</span> </span>
<span <span title="delete" @click.stop.prevent="delNode" v-if="!model.delNodeDisabled">
title="delete" <slot name="delNodeIcon" :expanded="expanded" :model="model" :root="rootNode">
@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> <i class="vtl-icon vtl-icon-trash"></i>
</slot> </slot>
</span> </span>
@@ -134,7 +91,7 @@
<div <div
v-if="model.children && model.children.length > 0 && expanded" v-if="model.children && model.children.length > 0 && expanded"
class="vtl-border vtl-bottom" class="vtl-border vtl-bottom"
:class="{'vtl-active': isDragEnterBottom}" :class="{ 'vtl-active': isDragEnterBottom }"
@drop="dropAfter" @drop="dropAfter"
@dragenter="dragEnterBottom" @dragenter="dragEnterBottom"
@dragover="dragOverBottom" @dragover="dragOverBottom"
@@ -143,7 +100,7 @@
</div> </div>
<div <div
:class="{'vtl-tree-margin': model.name !== 'root'}" :class="{ 'vtl-tree-margin': model.name !== 'root' }"
v-show="model.name === 'root' || expanded" v-show="model.name === 'root' || expanded"
v-if="isFolder" v-if="isFolder"
> >
@@ -155,6 +112,9 @@
:model="model" :model="model"
:key="model.id" :key="model.id"
> >
<template v-slot:leafNameDisplay="slotProps">
<slot name="leafNameDisplay" v-bind="slotProps" />
</template>
<template v-slot:addTreeNodeIcon="slotProps"> <template v-slot:addTreeNodeIcon="slotProps">
<slot name="addTreeNodeIcon" v-bind="slotProps" /> <slot name="addTreeNodeIcon" v-bind="slotProps" />
</template> </template>
@@ -179,8 +139,8 @@
</template> </template>
<script> <script>
import {TreeNode} from './Tree.js' import { TreeNode } from './Tree.js'
import {addHandler, removeHandler} from './tools.js' import { addHandler, removeHandler } from './tools.js'
let compInOperation = null let compInOperation = null
@@ -202,11 +162,19 @@ export default {
}, },
defaultLeafNodeName: { defaultLeafNodeName: {
type: String, type: String,
default: 'New leaf node' default: 'Leaf Node'
}, },
defaultTreeNodeName: { defaultTreeNodeName: {
type: String, type: String,
default: 'New tree node' default: 'Tree Node'
},
defaultAddTreeNodeTitle: {
type: String,
default: 'Add Tree Node'
},
defaultAddLeafNodeTitle: {
type: String,
default: 'Add Leaf Node'
}, },
defaultExpanded: { defaultExpanded: {
type: Boolean, type: Boolean,
@@ -232,7 +200,7 @@ export default {
treeNodeClass() { treeNodeClass() {
const { const {
model: {dragDisabled, disabled}, model: { dragDisabled, disabled },
isDragEnterNode isDragEnterNode
} = this } = this
@@ -266,7 +234,8 @@ export default {
this.rootNode.$emit('change-name', { this.rootNode.$emit('change-name', {
id: this.model.id, id: this.model.id,
oldName: oldName, oldName: oldName,
newName: e.target.value newName: e.target.value,
node: this.model
}) })
}, },
@@ -283,8 +252,16 @@ export default {
}) })
}, },
setUnEditable() { setUnEditable(e) {
this.editable = false 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() { toggle() {
@@ -303,13 +280,16 @@ export default {
}, },
click() { click() {
this.rootNode.$emit('click', this.model) this.rootNode.$emit('click', {
toggle: this.toggle,
...this.model
})
}, },
addChild(isLeaf) { addChild(isLeaf) {
const name = isLeaf ? this.defaultLeafNodeName : this.defaultTreeNodeName const name = isLeaf ? this.defaultLeafNodeName : this.defaultTreeNodeName
this.expanded = true this.expanded = true
var node = new TreeNode({name, isLeaf}) var node = new TreeNode({ name, isLeaf })
this.model.addChildren(node, true) this.model.addChildren(node, true)
this.rootNode.$emit('add-node', node) this.rootNode.$emit('add-node', node)
}, },
@@ -333,7 +313,8 @@ export default {
}, },
dragEnter() { dragEnter() {
if (!compInOperation) return if (!compInOperation) return
if (this.model.isLeaf) return if (compInOperation.model.id === this.model.id || !compInOperation || this.model.isLeaf)
return
this.isDragEnterNode = true this.isDragEnterNode = true
}, },
dragLeave() { dragLeave() {

View File

@@ -2,12 +2,12 @@
* Created by ayou on 17/7/21. * Created by ayou on 17/7/21.
*/ */
import VueTreeList from "./VueTreeList"; import VueTreeList from './VueTreeList'
import { Tree, TreeNode } from "./Tree"; import { Tree, TreeNode } from './Tree'
VueTreeList.install = Vue => { VueTreeList.install = Vue => {
Vue.component(VueTreeList.name, VueTreeList); Vue.component(VueTreeList.name, VueTreeList)
}; }
export default VueTreeList; export default VueTreeList
export { Tree, TreeNode, VueTreeList }; export { Tree, TreeNode, VueTreeList }

View File

@@ -4,7 +4,7 @@
var handlerCache var handlerCache
export const addHandler = function (element, type, handler) { export const addHandler = function(element, type, handler) {
handlerCache = handler handlerCache = handler
if (element.addEventListener) { if (element.addEventListener) {
element.addEventListener(type, handler, false) 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) { if (element.removeEventListener) {
element.removeEventListener(type, handlerCache, false) element.removeEventListener(type, handlerCache, false)
} else if (element.detachEvent) { } else if (element.detachEvent) {
@@ -26,7 +26,7 @@ export const removeHandler = function (element, type) {
} }
// depth first search // depth first search
export const traverseTree = (root) => { export const traverseTree = root => {
var newRoot = {} var newRoot = {}
for (var k in root) { for (var k in root) {

View File

@@ -47,7 +47,7 @@ exports[`Render render correctly 1`] = `
<div class="vtl-node-content"> <div class="vtl-node-content">
Node 2 Node 2
</div> </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>
<!----> <!---->
</div> </div>
@@ -61,7 +61,7 @@ exports[`Render render correctly 1`] = `
<div class="vtl-node-content"> <div class="vtl-node-content">
Node 3 Node 3
</div> </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>
<!----> <!---->
</div> </div>

View File

@@ -11,7 +11,7 @@ exports[`Slot render slot correctly 1`] = `
<div class="vtl-node-content"> <div class="vtl-node-content">
Node 1 Node 1
</div> </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>
<div class="vtl-border vtl-bottom"></div> <div class="vtl-border vtl-bottom"></div>
</div> </div>
@@ -42,7 +42,7 @@ exports[`Slot render slot correctly 1`] = `
<div class="vtl-node-content"> <div class="vtl-node-content">
Node 2 Node 2
</div> </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>
<!----> <!---->
</div> </div>

View File

@@ -1,6 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import {mount} from '@vue/test-utils' import { mount } from '@vue/test-utils'
import {Tree, VueTreeList} from '@/index' import { Tree, VueTreeList } from '@/index'
describe('Drag', () => { describe('Drag', () => {
let wrapper let wrapper
@@ -36,14 +36,14 @@ describe('Drag', () => {
pid: 0 pid: 0
} }
]) ])
wrapper = mount(VueTreeList, {propsData: {model: new Tree([])}}) wrapper = mount(VueTreeList, { propsData: { model: new Tree([]) } })
wrapper.setProps({model: tree}) wrapper.setProps({ model: tree })
}) })
it('drag before', done => { it('drag before', done => {
const $tree2 = wrapper.find('#t2 .vtl-node-main') const $tree2 = wrapper.find('#t2 .vtl-node-main')
const $tree1Up = wrapper.find('#t1 .vtl-up') const $tree1Up = wrapper.find('#t1 .vtl-up')
$tree2.trigger('dragstart', { dataTransfer: { setData: () => {} }}) $tree2.trigger('dragstart', { dataTransfer: { setData: () => {} } })
$tree1Up.trigger('drop') $tree1Up.trigger('drop')
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.find('.vtl-node').attributes('id')).toBe('t2') expect(wrapper.find('.vtl-node').attributes('id')).toBe('t2')
@@ -54,10 +54,15 @@ describe('Drag', () => {
it('drag after', done => { it('drag after', done => {
const $tree3 = wrapper.find('#t3 .vtl-node-main') const $tree3 = wrapper.find('#t3 .vtl-node-main')
const $tree1Bottom = wrapper.find('#t1 .vtl-bottom') const $tree1Bottom = wrapper.find('#t1 .vtl-bottom')
$tree3.trigger('dragstart', { dataTransfer: { setData: () => {} }}) $tree3.trigger('dragstart', { dataTransfer: { setData: () => {} } })
$tree1Bottom.trigger('drop') $tree1Bottom.trigger('drop')
Vue.nextTick(() => { 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() done()
}) })
}) })
@@ -65,7 +70,7 @@ describe('Drag', () => {
it('drag into', done => { it('drag into', done => {
const $tree3 = wrapper.find('#t3 .vtl-node-main') const $tree3 = wrapper.find('#t3 .vtl-node-main')
const $tree1 = wrapper.find('#t1 .vtl-node-main') const $tree1 = wrapper.find('#t1 .vtl-node-main')
$tree3.trigger('dragstart', { dataTransfer: { setData: () => {} }}) $tree3.trigger('dragstart', { dataTransfer: { setData: () => {} } })
$tree1.trigger('drop') $tree1.trigger('drop')
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.find('#t1 + .vtl-tree-margin .vtl-node').attributes('id')).toBe('t3') expect(wrapper.find('#t1 + .vtl-tree-margin .vtl-node').attributes('id')).toBe('t3')
@@ -77,7 +82,7 @@ describe('Drag', () => {
const snapshot = wrapper.html() const snapshot = wrapper.html()
const $tree1 = wrapper.find('#t1 .vtl-node-main') const $tree1 = wrapper.find('#t1 .vtl-node-main')
const $tree1Child = wrapper.find('#t12 .vtl-node-main') const $tree1Child = wrapper.find('#t12 .vtl-node-main')
$tree1.trigger('dragstart', { dataTransfer: { setData: () => {} }}) $tree1.trigger('dragstart', { dataTransfer: { setData: () => {} } })
$tree1Child.trigger('drop') $tree1Child.trigger('drop')
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.html()).toBe(snapshot) expect(wrapper.html()).toBe(snapshot)

View File

@@ -1,6 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import {mount} from '@vue/test-utils' import { mount } from '@vue/test-utils'
import {Tree, VueTreeList} from '@/index' import { Tree, VueTreeList } from '@/index'
describe('Operation', () => { describe('Operation', () => {
let wrapper let wrapper
@@ -26,8 +26,8 @@ describe('Operation', () => {
pid: 0 pid: 0
} }
]) ])
wrapper = mount(VueTreeList, {propsData: {model: new Tree([])}}) wrapper = mount(VueTreeList, { propsData: { model: new Tree([]) } })
wrapper.setProps({model: tree}) wrapper.setProps({ model: tree })
}) })
it('delete leaf node', done => { it('delete leaf node', done => {
@@ -51,7 +51,7 @@ describe('Operation', () => {
}) })
it('add leaf node', done => { 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') $node1AddLeafNode.trigger('click')
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.findAll('.vtl-leaf-node').length).toBe(2) expect(wrapper.findAll('.vtl-leaf-node').length).toBe(2)
@@ -60,7 +60,7 @@ describe('Operation', () => {
}) })
it('add tree node', done => { 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') $node1AddTreeNode.trigger('click')
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.findAll('.vtl-tree-node').length).toBe(3) expect(wrapper.findAll('.vtl-tree-node').length).toBe(3)
@@ -75,7 +75,7 @@ describe('Operation', () => {
const $input = wrapper.find('#t1 .vtl-input') const $input = wrapper.find('#t1 .vtl-input')
$input.element.value = 'New Node 1' $input.element.value = 'New Node 1'
$input.trigger('input') $input.trigger('input')
var event = new KeyboardEvent('keyup', {keyCode: 13}) var event = new KeyboardEvent('keyup', { keyCode: 13 })
window.dispatchEvent(event) window.dispatchEvent(event)
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.find('#t1').text()).toBe('New Node 1') expect(wrapper.find('#t1').text()).toBe('New Node 1')

View File

@@ -1,5 +1,5 @@
import {mount} from '@vue/test-utils' import { mount } from '@vue/test-utils'
import {Tree, VueTreeList} from '@/index' import { Tree, VueTreeList } from '@/index'
describe('Render', () => { describe('Render', () => {
it('render correctly', () => { it('render correctly', () => {

View File

@@ -1,6 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import {mount} from '@vue/test-utils' import { mount } from '@vue/test-utils'
import {Tree, VueTreeList} from '@/index' import { Tree, VueTreeList } from '@/index'
describe('Slot', () => { describe('Slot', () => {
let wrapper let wrapper
@@ -27,29 +27,41 @@ describe('Slot', () => {
} }
]) ])
wrapper = mount(VueTreeList, { wrapper = mount(VueTreeList, {
propsData: {model: new Tree([])}, propsData: { model: new Tree([]) },
scopedSlots: { scopedSlots: {
addTreeNodeIcon() { addTreeNodeIcon() {
return <span class="add-tree-node-icon">📂</span> return <span class='add-tree-node-icon'>📂</span>
}, },
addLeafNodeIcon() { addLeafNodeIcon() {
return <span class="icon"></span> return <span class='icon'></span>
}, },
editNodeIcon() { editNodeIcon() {
return <span class="icon">📃</span> return <span class='icon'>📃</span>
}, },
delNodeIcon(slotProps) { 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() { leafNodeIcon() {
return <span class="icon">🍃</span> return <span class='icon'>🍃</span>
}, },
treeNodeIcon(slotProps) { 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', () => { it('render slot correctly', () => {

View File

@@ -2,4 +2,4 @@ module.exports = {
css: { css: {
extract: false extract: false
} }
}; }