feat: 🎸 #47

This commit is contained in:
youxingzhi
2020-01-03 23:19:55 +08:00
parent 332402dee6
commit fbd370e9e5
7 changed files with 893 additions and 625 deletions

View File

@@ -12,18 +12,33 @@
: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"
<span class="icon" slot="addTreeNodeIcon">📂</span> >
<span class="icon" slot="addLeafNodeIcon"></span> <template v-slot:addTreeNodeIcon="slotProps">
<span class="icon" slot="editNodeIcon">📃</span> <span class="icon">📂</span>
<span class="icon" slot="delNodeIcon"></span> </template>
<span class="icon" slot="leafNodeIcon">🍃</span> <template v-slot:addLeafNodeIcon="slotProps">
<span class="icon" slot="treeNodeIcon">🌲</span> <span class="icon"></span>
</template>
<template v-slot:editNodeIcon="slotProps">
<span class="icon">📃</span>
</template>
<template v-slot:delNodeIcon="slotProps">
<span class="icon"></span>
</template>
<template v-slot:leafNodeIcon="slotProps">
<span class="icon">🍃</span>
</template>
<template v-slot:treeNodeIcon="slotProps">
<span class="icon">
{{ (slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded) ? '🌲' : '' }}</span>
</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>

View File

@@ -199,13 +199,26 @@ drop-before | {node, src, target} | Trigger after dropping a node before another
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-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: 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
<span class="icon" slot="addTreeNodeIcon">📂</span> <template v-slot:addTreeNodeIcon="slotProps">
<span class="icon" slot="addLeafNodeIcon"></span> <span class="icon">📂</span>
<span class="icon" slot="editNodeIcon">📃</span> </template>
<span class="icon" slot="delNodeIcon">✂️</span> <template v-slot:addLeafNodeIcon="slotProps">
<span class="icon" slot="leafNodeIcon">🍃</span> <span class="icon"></span>
<span class="icon" slot="treeNodeIcon">🌲</span> </template>
<template v-slot:editNodeIcon="slotProps">
<span class="icon">📃</span>
</template>
<template v-slot:delNodeIcon="slotProps">
<span class="icon">✂️</span>
</template>
<template v-slot:leafNodeIcon="slotProps">
<span class="icon">🍃</span>
</template>
<template v-slot:treeNodeIcon="slotProps">
<span class="icon">
{{ (slotProps.model.children && slotProps.model.children.length > 0 && !slotProps.expanded) ? '🌲' : '' }}</span>
</template>
``` ```

View File

@@ -1,3 +1,4 @@
import {traverseTree} from './tools'
/** /**
* Tree data struct * Tree data struct
* Created by ayou on 2017/7/20. * Created by ayou on 2017/7/20.
@@ -8,9 +9,10 @@
* dragDisabled: decide if it can be dragged * dragDisabled: decide if it can be dragged
* disabled: desabled all operation * disabled: desabled all operation
*/ */
const TreeNode = function (data) { export class TreeNode {
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
this.isLeaf = !!isLeaf this.isLeaf = !!isLeaf
@@ -23,11 +25,11 @@ const TreeNode = function (data) {
} }
} }
TreeNode.prototype.changeName = function (name) { changeName(name) {
this.name = name this.name = name
} }
TreeNode.prototype.addChildren = function (children) { addChildren(children) {
if (!this.children) { if (!this.children) {
this.children = [] this.children = []
} }
@@ -48,14 +50,14 @@ TreeNode.prototype.addChildren = function (children) {
} }
// remove self // remove self
TreeNode.prototype.remove = function () { remove() {
const parent = this.parent const parent = this.parent
const index = parent.findChildIndex(this) const index = parent.findChildIndex(this)
parent.children.splice(index, 1) parent.children.splice(index, 1)
} }
// remove child // remove child
TreeNode.prototype._removeChild = function (child) { _removeChild(child) {
for (var i = 0, len = this.children.length; i < len; i++) { for (var i = 0, len = this.children.length; i < len; i++) {
if (this.children[i] === child) { if (this.children[i] === child) {
this.children.splice(i, 1) this.children.splice(i, 1)
@@ -64,7 +66,7 @@ TreeNode.prototype._removeChild = function (child) {
} }
} }
TreeNode.prototype.isTargetChild = function (target) { isTargetChild(target) {
let parent = target.parent let parent = target.parent
while (parent) { while (parent) {
if (parent === this) { if (parent === this) {
@@ -75,7 +77,7 @@ TreeNode.prototype.isTargetChild = function (target) {
return false return false
} }
TreeNode.prototype.moveInto = function (target) { moveInto(target) {
if (this.name === 'root' || this === target) { if (this.name === 'root' || this === target) {
return return
} }
@@ -99,7 +101,7 @@ TreeNode.prototype.moveInto = function (target) {
target.children.unshift(this) target.children.unshift(this)
} }
TreeNode.prototype.findChildIndex = function (child) { findChildIndex(child) {
var index var index
for (let i = 0, len = this.children.length; i < len; i++) { for (let i = 0, len = this.children.length; i < len; i++) {
if (this.children[i] === child) { if (this.children[i] === child) {
@@ -110,7 +112,7 @@ TreeNode.prototype.findChildIndex = function (child) {
return index return index
} }
TreeNode.prototype._beforeInsert = function (target) { _canInsert(target) {
if (this.name === 'root' || this === target) { if (this.name === 'root' || this === target) {
return false return false
} }
@@ -126,27 +128,33 @@ TreeNode.prototype._beforeInsert = function (target) {
return true return true
} }
TreeNode.prototype.insertBefore = function (target) { insertBefore(target) {
if (!this._beforeInsert(target)) return if (!this._canInsert(target)) return
const pos = target.parent.findChildIndex(target) const pos = target.parent.findChildIndex(target)
target.parent.children.splice(pos, 0, this) target.parent.children.splice(pos, 0, this)
} }
TreeNode.prototype.insertAfter = function (target) { insertAfter(target) {
if (!this._beforeInsert(target)) return if (!this._canInsert(target)) return
const pos = target.parent.findChildIndex(target) const pos = target.parent.findChildIndex(target)
target.parent.children.splice(pos + 1, 0, this) target.parent.children.splice(pos + 1, 0, this)
} }
function Tree (data) { toString() {
return JSON.stringify(traverseTree(this))
}
}
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) this.initNode(this.root, data)
return this.root return this.root
} }
Tree.prototype.initNode = function (node, data) { initNode(node, data) {
for (let i = 0, len = data.length; i < len; i++) { for (let i = 0, len = data.length; i < len; i++) {
var _data = data[i] var _data = data[i]
@@ -157,6 +165,4 @@ Tree.prototype.initNode = function (node, data) {
node.addChildren(child) node.addChildren(child)
} }
} }
}
exports.Tree = Tree
exports.TreeNode = TreeNode

View File

@@ -1,38 +1,60 @@
<template> <template>
<div class='vtl'> <div class="vtl">
<div <div
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 class="vtl-border vtl-up" :class="{'vtl-active': isDragEnterUp}" <div
class="vtl-border vtl-up"
:class="{'vtl-active': isDragEnterUp}"
@drop="dropBefore" @drop="dropBefore"
@dragenter="dragEnterUp" @dragenter="dragEnterUp"
@dragover='dragOverUp' @dragover="dragOverUp"
@dragleave="dragLeaveUp" /> @dragleave="dragLeaveUp"
<div :class="treeNodeClass" />
<div
:class="treeNodeClass"
:draggable="!model.dragDisabled" :draggable="!model.dragDisabled"
@dragstart='dragStart' @dragstart="dragStart"
@dragover='dragOver' @dragover="dragOver"
@dragenter='dragEnter' @dragenter="dragEnter"
@dragleave='dragLeave' @dragleave="dragLeave"
@drop='drop' @drop="drop"
@dragend='dragEnd' @dragend="dragEnd"
@mouseover='mouseOver' @mouseover="mouseOver"
@mouseout='mouseOut' @mouseout="mouseOut"
@click.stop='click'> @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>
<span v-if="model.isLeaf"> <span v-if="model.isLeaf">
<slot name="leafNodeIcon"> <slot
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 name="treeNodeIcon" :expanded="expanded"> <slot
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>
@@ -40,52 +62,114 @@
<div class="vtl-node-content" v-if="!editable"> <div class="vtl-node-content" v-if="!editable">
{{ model.name }} {{ model.name }}
</div> </div>
<input v-else class="vtl-input" type="text" ref="nodeInput" :value="model.name" @input="updateName" @blur="setUnEditable"> <input
v-else
class="vtl-input"
type="text"
ref="nodeInput"
:value="model.name"
@input="updateName"
@blur="setUnEditable"
/>
<div class="vtl-operation" v-show="isHover"> <div class="vtl-operation" v-show="isHover">
<span title="add tree node" @click.stop.prevent="addChild(false)" v-if="!model.isLeaf && !model.addTreeNodeDisabled"> <span
<slot name="addTreeNodeIcon"> title="add tree node"
@click.stop.prevent="addChild(false)"
v-if="!model.isLeaf && !model.addTreeNodeDisabled"
>
<slot
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 title="add leaf node" @click.stop.prevent="addChild(true)" v-if="!model.isLeaf && !model.addLeafNodeDisabled"> <span
<slot name="addLeafNodeIcon"> title="add leaf node"
@click.stop.prevent="addChild(true)"
v-if="!model.isLeaf && !model.addLeafNodeDisabled"
>
<slot
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 title="edit" @click.stop.prevent="setEditable" v-if="!model.editNodeDisabled"> <span
<slot name="editNodeIcon"> 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> <i class="vtl-icon vtl-icon-edit"></i>
</slot> </slot>
</span> </span>
<span title="delete" @click.stop.prevent="delNode" v-if="!model.delNodeDisabled"> <span
<slot name="delNodeIcon"> 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> <i class="vtl-icon vtl-icon-trash"></i>
</slot> </slot>
</span> </span>
</div> </div>
</div> </div>
<div v-if="model.children && model.children.length > 0 && expanded" <div
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"
@dragleave="dragLeaveBottom"></div> @dragleave="dragLeaveBottom"
></div>
</div> </div>
<div :class="{'vtl-tree-margin': model.name !== 'root'}" v-show="model.name === 'root' || expanded" v-if="isFolder"> <div
<item v-for="model in model.children" :class="{'vtl-tree-margin': model.name !== 'root'}"
v-show="model.name === 'root' || expanded"
v-if="isFolder"
>
<item
v-for="model in model.children"
:default-tree-node-name="defaultTreeNodeName" :default-tree-node-name="defaultTreeNodeName"
:default-leaf-node-name="defaultLeafNodeName" :default-leaf-node-name="defaultLeafNodeName"
:default-expanded="defaultExpanded" :default-expanded="defaultExpanded"
:model="model" :model="model"
:key='model.id'> :key="model.id"
<slot name="addTreeNodeIcon" slot="addTreeNodeIcon" /> >
<slot name="addLeafNodeIcon" slot="addLeafNodeIcon" /> <template v-slot:addTreeNodeIcon="slotProps">
<slot name="editNodeIcon" slot="editNodeIcon" /> <slot name="addTreeNodeIcon" v-bind="slotProps" />
<slot name="delNodeIcon" slot="delNodeIcon" /> </template>
<slot name="leafNodeIcon" slot="leafNodeIcon" /> <template v-slot:addLeafNodeIcon="slotProps">
<slot name="addLeafNodeIcon" v-bind="slotProps" />
</template>
<template v-slot:editNodeIcon="slotProps">
<slot name="editNodeIcon" v-bind="slotProps" />
</template>
<template v-slot:delNodeIcon="slotProps">
<slot name="delNodeIcon" v-bind="slotProps" />
</template>
<template v-slot:leafNodeIcon="slotProps">
<slot name="leafNodeIcon" v-bind="slotProps" />
</template>
<template v-slot:treeNodeIcon="slotProps"> <template v-slot:treeNodeIcon="slotProps">
<slot name="treeNodeIcon" v-bind="slotProps" /> <slot name="treeNodeIcon" v-bind="slotProps" />
</template> </template>
@@ -129,21 +213,25 @@
} }
}, },
computed: { computed: {
rootNode() {
var node = this.$parent
while (node._props.model.name !== 'root') {
node = node.$parent
}
return node
},
caretClass() { caretClass() {
return this.expanded ? 'vtl-icon-caret-down' : 'vtl-icon-caret-right' return this.expanded ? 'vtl-icon-caret-down' : 'vtl-icon-caret-right'
}, },
isFolder() { isFolder() {
return this.model.children && return this.model.children && this.model.children.length
this.model.children.length
}, },
treeNodeClass() { treeNodeClass() {
const { const {
model: { model: {dragDisabled, disabled},
dragDisabled,
disabled
},
isDragEnterNode isDragEnterNode
} = this } = this
@@ -172,15 +260,17 @@
}, },
methods: { methods: {
updateName(e) { updateName(e) {
var oldName = this.model.name; var oldName = this.model.name
this.model.changeName(e.target.value) this.model.changeName(e.target.value)
var node = this.getRootNode(); this.rootNode.$emit('change-name', {
node.$emit('change-name', {'id': this.model.id, 'oldName': oldName, 'newName': e.target.value}) id: this.model.id,
oldName: oldName,
newName: e.target.value
})
}, },
delNode() { delNode() {
var node = this.getRootNode() this.rootNode.$emit('delete-node', this.model)
node.$emit('delete-node', this.model)
}, },
setEditable() { setEditable() {
@@ -212,8 +302,7 @@
}, },
click() { click() {
var node = this.getRootNode() this.rootNode.$emit('click', this.model)
node.$emit('click', this.model);
}, },
addChild(isLeaf) { addChild(isLeaf) {
@@ -221,15 +310,14 @@
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)
var root = this.getRootNode(); this.rootNode.$emit('add-node', node)
root.$emit('add-node', node)
}, },
dragStart(e) { dragStart(e) {
if (!(this.model.dragDisabled || this.model.disabled)) { if (!(this.model.dragDisabled || this.model.disabled)) {
compInOperation = this compInOperation = this
// for firefox // for firefox
e.dataTransfer.setData("data","data"); e.dataTransfer.setData('data', 'data')
e.dataTransfer.effectAllowed = 'move' e.dataTransfer.effectAllowed = 'move'
return true return true
} }
@@ -252,11 +340,14 @@
}, },
drop() { drop() {
if (!compInOperation) return if (!compInOperation) return
const oldParent = compInOperation.model.parent; const oldParent = compInOperation.model.parent
compInOperation.model.moveInto(this.model) compInOperation.model.moveInto(this.model)
this.isDragEnterNode = false this.isDragEnterNode = false
var node = this.getRootNode(); this.rootNode.$emit('drop', {
node.$emit('drop', {target: this.model, node: compInOperation.model, src: oldParent}) target: this.model,
node: compInOperation.model,
src: oldParent
})
}, },
dragEnterUp() { dragEnterUp() {
@@ -273,11 +364,14 @@
}, },
dropBefore() { dropBefore() {
if (!compInOperation) return if (!compInOperation) return
const oldParent = compInOperation.model.parent; const oldParent = compInOperation.model.parent
compInOperation.model.insertBefore(this.model) compInOperation.model.insertBefore(this.model)
this.isDragEnterUp = false this.isDragEnterUp = false
var node = this.getRootNode(); this.rootNode.$emit('drop-before', {
node.$emit('drop-before', {target: this.model, node: compInOperation.model, src: oldParent}) target: this.model,
node: compInOperation.model,
src: oldParent
})
}, },
dragEnterBottom() { dragEnterBottom() {
@@ -294,18 +388,14 @@
}, },
dropAfter() { dropAfter() {
if (!compInOperation) return if (!compInOperation) return
const oldParent = compInOperation.model.parent; const oldParent = compInOperation.model.parent
compInOperation.model.insertAfter(this.model) compInOperation.model.insertAfter(this.model)
this.isDragEnterBottom = false this.isDragEnterBottom = false
var node = this.getRootNode(); this.rootNode.$emit('drop-after', {
node.$emit('drop-after', {target: this.model, node: compInOperation.model, src: oldParent}) target: this.model,
}, node: compInOperation.model,
getRootNode() { src: oldParent
var node = this.$parent })
while (node._props.model.name !== 'root') {
node = node.$parent
}
return node;
} }
} }
} }
@@ -348,31 +438,30 @@
} }
.vtl-icon-file:before { .vtl-icon-file:before {
content: "\e906"; content: '\e906';
} }
.vtl-icon-folder:before { .vtl-icon-folder:before {
content: "\e907"; content: '\e907';
} }
.vtl-icon-caret-down:before { .vtl-icon-caret-down:before {
content: "\e901"; content: '\e901';
} }
.vtl-icon-caret-right:before { .vtl-icon-caret-right:before {
content: "\e900"; content: '\e900';
} }
.vtl-icon-edit:before { .vtl-icon-edit:before {
content: "\e902"; content: '\e902';
} }
.vtl-icon-folder-plus-e:before { .vtl-icon-folder-plus-e:before {
content: "\e903"; content: '\e903';
} }
.vtl-icon-plus:before { .vtl-icon-plus:before {
content: "\e904"; content: '\e904';
} }
.vtl-icon-trash:before { .vtl-icon-trash:before {
content: "\e905"; content: '\e905';
} }
.vtl-border { .vtl-border {
height: 5px; height: 5px;
&.vtl-up { &.vtl-up {
@@ -412,7 +501,6 @@
} }
} }
.vtl-item { .vtl-item {
cursor: pointer; cursor: pointer;
} }

View File

@@ -4,7 +4,7 @@
var handlerCache var handlerCache
exports.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 @@ exports.addHandler = function (element, type, handler) {
} }
} }
exports.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) {
@@ -25,7 +25,21 @@ exports.removeHandler = function (element, type) {
} }
} }
// exports.fireFocusEvent = function (ele) { // depth first search
// var event = new FocusEvent() export const traverseTree = (root) => {
// ele.dispatch(event) var newRoot = {}
// }
for (var k in root) {
if (k !== 'children' && k !== 'parent') {
newRoot[k] = root[k]
}
}
if (root.children && root.children.length > 0) {
newRoot.children = []
for (var i = 0, len = root.children.length; i < len; i++) {
newRoot.children.push(traverseTree(root.children[i]))
}
}
return newRoot
}

View File

@@ -0,0 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Slot render slot correctly 1`] = `
<div class="vtl">
<!---->
<div class="">
<div class="vtl">
<div id="t1" class="vtl-node vtl-tree-node">
<div class="vtl-border vtl-up"></div>
<div draggable="true" class="vtl-node-main"><span class="vtl-caret vtl-is-small"><i class="vtl-icon vtl-icon-caret-down"></i></span> <span><span class="tree-node-icon icon">❀</span></span>
<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>
<div class="vtl-border vtl-bottom"></div>
</div>
<div class="vtl-tree-margin">
<div class="vtl">
<div id="t11" class="vtl-node vtl-leaf-node">
<div class="vtl-border vtl-up"></div>
<div draggable="true" class="vtl-node-main">
<!----> <span><span class="icon">🍃</span></span>
<div class="vtl-node-content">
Node 1-1
</div>
<div class="vtl-operation" style="display: none;">
<!---->
<!----> <span title="edit"><span class="icon">📃</span></span> <span title="delete"><span class="del-node-icon">✂️</span></span></div>
</div>
<!---->
</div>
<!---->
</div>
</div>
</div>
<div class="vtl">
<div id="t2" class="vtl-node vtl-tree-node">
<div class="vtl-border vtl-up"></div>
<div draggable="true" class="vtl-node-main">
<!----> <span><span class="tree-node-icon icon">❀</span></span>
<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>
<!---->
</div>
<!---->
</div>
</div>
</div>
`;

79
tests/unit/slot.spec.js Normal file
View File

@@ -0,0 +1,79 @@
import Vue from 'vue'
import {mount} from '@vue/test-utils'
import {Tree, VueTreeList} from '@/index'
describe('Slot', () => {
let wrapper
beforeEach(() => {
const tree = new Tree([
{
name: 'Node 1',
id: 't1',
pid: 0,
children: [
{
name: 'Node 1-1',
id: 't11',
isLeaf: true,
pid: 't1'
}
]
},
{
name: 'Node 2',
id: 't2',
pid: 0
}
])
wrapper = mount(VueTreeList, {
propsData: {model: new Tree([])},
scopedSlots: {
addTreeNodeIcon() {
return <span class="add-tree-node-icon">📂</span>
},
addLeafNodeIcon() {
return <span class="icon"></span>
},
editNodeIcon() {
return <span class="icon">📃</span>
},
delNodeIcon(slotProps) {
return (slotProps.model.isLeaf || !slotProps.model.children) ? <span class="del-node-icon"></span> : <span />
},
leafNodeIcon() {
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>
}
}
})
wrapper.setProps({model: tree})
})
it('render slot correctly', () => {
expect(wrapper).toMatchSnapshot()
})
it('toggle tree node show different icon', done => {
const $caretDown = wrapper.find('.vtl-icon-caret-down')
expect(wrapper.find('#t1 .tree-node-icon').text()).toBe('❀')
$caretDown.trigger('click')
Vue.nextTick(() => {
expect(wrapper.exists('.vtl-icon-caret-right')).toBe(true)
expect(wrapper.find('#t1 .tree-node-icon').text()).toBe('🌲')
done()
})
})
it('dont show ✂️ after add child ', done => {
const $addTreeNodeIcon = wrapper.find('#t2 .add-tree-node-icon')
expect(wrapper.find('#t2 .del-node-icon').exists()).toBe(true)
$addTreeNodeIcon.trigger('click')
Vue.nextTick(() => {
expect(wrapper.find('#t2 .del-node-icon').exists()).toBe(false)
done()
})
})
})