From 1902e394fc141b6a55c593ca475f5bc32499dc5a Mon Sep 17 00:00:00 2001
From: 07akioni <07akioni2@gmail.com>
Date: Tue, 25 Feb 2020 18:47:41 +0800
Subject: [PATCH] refactor(tree): wip
---
src/Tree/src/ChildNodesExpandTransition.js | 51 -----
src/Tree/src/Tree.js | 255 ++++++++++++---------
src/Tree/src/TreeNode.js | 60 ++++-
src/Tree/src/TreeNodeCheckbox.vue | 4 +-
src/Tree/src/TreeNodeContent.vue | 10 +-
src/Tree/src/TreeNodeSwitcher.vue | 26 ++-
src/Tree/src/utils.js | 8 +
styles/Tree.scss | 22 +-
styles/themes/dark/components/Tree.scss | 7 +-
styles/themes/light/components/Tree.scss | 7 +-
10 files changed, 254 insertions(+), 196 deletions(-)
delete mode 100644 src/Tree/src/ChildNodesExpandTransition.js
create mode 100644 src/Tree/src/utils.js
diff --git a/src/Tree/src/ChildNodesExpandTransition.js b/src/Tree/src/ChildNodesExpandTransition.js
deleted file mode 100644
index 5e252c946..000000000
--- a/src/Tree/src/ChildNodesExpandTransition.js
+++ /dev/null
@@ -1,51 +0,0 @@
-export default {
- props: {
- transitionDisabled: {
- type: Boolean,
- default: false
- }
- },
- methods: {
- handleBeforeLeave () {
- this.$el.style.maxHeight = this.$el.offsetHeight + 'px'
- this.$el.style.height = this.$el.offsetHeight + 'px'
- this.$el.getBoundingClientRect()
- },
- handleLeave () {
- // debugger
- this.$el.style.maxHeight = 0
- this.$el.getBoundingClientRect()
- },
- handleEnter () {
- this.$nextTick().then(() => {
- this.$el.style.height = this.$el.offsetHeight + 'px'
- this.$el.style.maxHeight = 0
- this.$el.getBoundingClientRect()
- this.$el.style.maxHeight = this.$el.style.height
- })
- },
- handleAfterEnter () {
- this.$el.style.height = null
- this.$el.style.maxHeight = null
- }
- },
- beforeDestroy () {
- if (this.transitionDisabled) {
- const parent = this.$el.parentElement
- if (parent) parent.removeChild(this.$el)
- }
- },
- render (h) {
- return h('transition', {
- props: {
- name: 'n-fade-in-height-expand-transition'
- },
- on: {
- beforeLeave: this.handleBeforeLeave,
- leave: this.handleLeave,
- enter: this.handleEnter,
- afterEnter: this.handleAfterEnter
- }
- }, this.$slots.default)
- }
-}
diff --git a/src/Tree/src/Tree.js b/src/Tree/src/Tree.js
index 151ea9b5c..deed01d5c 100644
--- a/src/Tree/src/Tree.js
+++ b/src/Tree/src/Tree.js
@@ -1,53 +1,46 @@
-import {
- treedOptions,
- dropIsValid,
- applyDrop,
- linkedCascaderOptions,
- menuOptions
-} from '../../_utils/data/menuModel'
-
import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import NTreeNode from './TreeNode'
-import NTreeChildNodesExpandTransition from './ChildNodesExpandTransition'
+import NFadeInHeightExpandTransition from '../../_transition/FadeInHeightExpandTransition'
+import { isLeaf, isLoaded } from './utils'
-function createNode (node, h, self) {
+function createNode (node, h, treeInstance) {
const listeners = {
- 'switcher-click': self.handleSwitcherClick,
- select: self.handleSelect,
- dragenter: self.handleDragEnter,
- dragstart: self.handleDragStart,
- dragleave: self.handleDragLeave,
- drop: self.handleDrop,
- check: self.handleCheck
+ 'switcher-click': treeInstance.handleSwitcherClick,
+ select: treeInstance.handleSelect,
+ dragenter: treeInstance.handleDragEnter,
+ dragstart: treeInstance.handleDragStart,
+ dragleave: treeInstance.handleDragLeave,
+ drop: treeInstance.handleDrop,
+ check: treeInstance.handleCheck
}
- const expanded = self.syntheticExpandedKeys.includes(node.key)
+ const expanded = treeInstance.syntheticExpandedKeys.includes(node.key)
const props = {
data: node,
expanded,
- selected: self.syntheticSelectedKeys.includes(node.key),
- draggable: self.draggable,
- checkable: self.checkable,
- drop: self.drop,
- blockNode: self.blockNode,
- checked: self.syntheticCheckedKeys.includes(node.key)
+ selected: treeInstance.syntheticSelectedKeys.includes(node.key),
+ draggable: treeInstance.draggable,
+ checkable: treeInstance.checkable,
+ drop: treeInstance.drop,
+ blockNode: treeInstance.blockNode,
+ checked: treeInstance.syntheticCheckedKeys.includes(node.key)
}
return h(NTreeNode, {
props,
on: listeners,
key: node.key
},
- [!node.isLeaf
- ? h(NTreeChildNodesExpandTransition, {
+ [!isLeaf(node)
+ ? h(NFadeInHeightExpandTransition, {
props: {
- transitionDisabled: self.transitionDisabled
+ transitionDisabled: treeInstance.transitionDisabled
}
},
[
- expanded
+ expanded && node.children
? h('ul', {
staticClass: 'n-tree-children-wrapper'
- }, node.children.map(child => createNode(child, h, self)))
+ }, node.children.map(child => createNode(child, h, treeInstance)))
: null
]
)
@@ -55,25 +48,36 @@ function createNode (node, h, self) {
)
}
-function convertRootedOptionsToVNodeTree (root, h, self) {
- return root.children.map(child => createNode(child, h, self))
+function convertOptionsToVNodeTree (options, h, treeInstance) {
+ return options.map(child => createNode(child, h, treeInstance))
}
export default {
name: 'NTree',
mixins: [ withapp, themeable ],
+ model: {
+ prop: 'selected-keys',
+ event: 'selected-keys-change'
+ },
+ provide () {
+ return { NTree: this }
+ },
props: {
data: {
type: Array,
default: null
},
+ autoExpandParent: {
+ type: Boolean,
+ default: true
+ },
checkable: {
type: Boolean,
default: false
},
draggable: {
type: Boolean,
- default: true
+ default: false
},
blockNode: {
type: Boolean,
@@ -83,6 +87,10 @@ export default {
type: Array,
default: null
},
+ disabled: {
+ type: Boolean,
+ default: false
+ },
defaultCheckedKeys: {
type: Array,
default: null
@@ -99,18 +107,10 @@ export default {
type: Array,
default: null
},
- lazy: {
+ remote: {
type: Boolean,
default: false
},
- allowDrop: {
- type: Function,
- default: () => true
- },
- allowDrag: {
- type: Function,
- default: () => true
- },
multiple: {
type: Boolean,
default: false
@@ -119,36 +119,15 @@ export default {
type: String,
default: ''
},
- onExpand: {
+ onLoad: {
type: Function,
- default: () => {
- return (node, next) => {
- next()
- }
- }
- },
- onSelect: {
- type: Function,
- default: () => {
- return (node, next) => {
- next()
- }
- }
- },
- onDrop: {
- type: Function,
- default: () => {
- return (node, next) => {
- next()
- }
- }
+ default: null
}
},
created () {
- this.internalCheckedKeys = this.defaultCheckedKeys || this.internalCheckedKeys
- this.internalExpandedKeys = this.defaultExpandedKeys || this.internalExpandedKeys
- this.internalSelectedKeys = this.defaultSelectedKeys || this.internalSelectedKeys
- this.treeData = treedOptions(this.data)
+ this.internalCheckedKeys = this.defaultCheckedKeys || []
+ this.internalExpandedKeys = this.defaultExpandedKeys || []
+ this.internalSelectedKeys = this.defaultSelectedKeys || []
},
data () {
return {
@@ -160,18 +139,16 @@ export default {
draggingNode: null,
droppingNodeKey: null,
expandTimerId: null,
- transitionDisabled: false
+ transitionDisabled: false,
+ loadingKeys: []
}
},
watch: {
- data (newData) {
- this.treeData = treedOptions(newData)
+ data () {
this.internalExpandedKeys = []
this.internalCheckedKeys = []
this.internalSelectedKeys = []
- this.draggingNodeKey = null
- this.draggingNode = null
- this.droppingNodeKey = null
+ this.loadingKeys = []
this.expandTimerId = null
}
},
@@ -208,25 +185,33 @@ export default {
}
},
methods: {
- getSelectedKeys () {
- return this.syntheticSelectedKeys
- },
- getCheckedKeys () {
- return this.syntheticCheckedKeys
- },
- getExpandedKeys () {
- return this.syntheticExpandedKeys
- },
disableTransition () {
this.transitionDisabled = true
},
enableTransition () {
this.transitionDisabled = false
},
+ resetDragStatus () {
+ this.draggingNodeKey = null
+ this.draggingNode = null
+ this.droppingNodeKey = null
+ },
handleCheck (node, checked) {
- if (!this.hasCheckedKeys) {
- if (checked) {
+ if (this.disabled || node.disabled) return
+ if (checked) {
+ if (this.hasCheckedKeys) {
+ this.$emit('checked-keys-change', this.syntheticCheckedKeys.concat([node.key]))
+ } else {
this.internalCheckedKeys.push(node.key)
+ }
+ } else {
+ if (this.hasCheckedKeys) {
+ const checkedKeysAfterChange = this.syntheticCheckedKeys
+ checkedKeysAfterChange.splice(
+ checkedKeysAfterChange.findIndex(key => key === node.key),
+ 1
+ )
+ this.$emit('checked-keys-change', checkedKeysAfterChange)
} else {
this.internalCheckedKeys.splice(
this.internalCheckedKeys.findIndex(key => key === node.key),
@@ -235,79 +220,121 @@ export default {
}
}
},
- handleDrop (node, dropType) {
- const drop = [this.draggingNode, node, dropType]
- if (dropIsValid(drop)) {
- this.disableTransition()
- this.$nextTick().then(() => {
- applyDrop(drop)
- return this.$nextTick()
- }).then(() => {
- this.enableTransition()
- })
- }
- this.draggingNodeKey = null
- this.draggingNode = null
- },
toggleExpand (node) {
- const index = this.syntheticExpandedKeys.findIndex(expandNodeId => expandNodeId === node.key)
+ if (this.disabled) return
+ const index = this.syntheticExpandedKeys
+ .findIndex(expandNodeId => expandNodeId === node.key)
if (~index) {
- this.$emit('collapse', node)
if (!this.hasExpandedKeys) {
this.internalExpandedKeys.splice(index, 1)
+ this.$emit('expanded-keys-change', this.internalExpandedKeys)
+ } else {
+ const expandedKeysAfterChange = Array.from(this.syntheticExpandedKeys)
+ expandedKeysAfterChange.splice(index, 1)
+ this.$emit(
+ 'expanded-keys-change',
+ expandedKeysAfterChange
+ )
}
} else {
- if (!node.isLeaf) {
+ if (!isLeaf(node)) {
this.$emit('expand', node)
if (!this.hasExpandedKeys) {
this.internalExpandedKeys.push(node.key)
+ this.$emit('expanded-keys-change', this.internalExpandedKeys)
+ } else {
+ this.$emit(
+ 'expanded-keys-change',
+ this.syntheticExpandedKeys.concat(node.key)
+ )
}
}
}
},
handleSwitcherClick (node) {
+ if (this.disabled || node.disabled) return
this.toggleExpand(node)
},
handleSelect (node) {
+ if (this.disabled || node.disabled) return
this.$emit('select', node)
if (this.internalSelectedKeys.includes(node.key)) this.internalSelectedKeys = []
else this.internalSelectedKeys = [node.key]
},
- handleDragEnter (node) {
- this.$emit('dragenter', node)
+ handleDragEnter ({ event, node }) {
+ if (!this.draggable || this.disabled || node.disabled) return
+ this.$emit('dragenter', { event, node })
+ if (!this.autoExpandParent) return
this.droppingNodeKey = node.key
if (node.key === this.draggingNodeKey) return
- if (!this.syntheticExpandedKeys.includes(node.key) && !node.isLeaf) {
+ if (
+ !this.syntheticExpandedKeys.includes(node.key) &&
+ !isLeaf(node)
+ ) {
window.clearTimeout(this.expandTimerId)
- this.expandTimerId = window.setTimeout(() => {
- if (this.droppingNodeKey === node.key && !this.syntheticExpandedKeys.includes(node.key)) {
+ const expand = () => {
+ if (
+ this.droppingNodeKey === node.key &&
+ !this.syntheticExpandedKeys.includes(node.key)
+ ) {
if (!this.hasExpandedKeys) {
this.internalExpandedKeys.push(node.key)
+ this.$emit('expanded-keys-change', this.internalExpandedKeys)
+ } else {
+ this.$emit('expanded-keys-change', this.syntheticExpandedKeys.concat(node.key))
}
- this.$emit('expand', node.key)
}
+ }
+ if (!isLoaded(node)) {
+ if (!this.loadingKeys.includes(node.key)) {
+ this.loadingKeys.push(node.key)
+ }
+ this
+ .onLoad(node)
+ .then(() => {
+ this.loadingKeys.splice(
+ this.loadingKeys.find(key => key === node.key),
+ 1
+ )
+ expand()
+ })
+ return
+ }
+ this.expandTimerId = window.setTimeout(() => {
+ expand()
this.expandTimerId = null
}, 800)
}
},
- handleDragLeave (node) {
+ handleDragLeave ({ event, node }) {
+ if (!this.draggable || this.disabled || node.disabled) return
this.droppingNodeKey = null
- this.$emit('dragleave', node)
+ this.$emit('dragleave', { event, node })
},
- handleDragStart (node) {
+ handleDragStart ({ event, node }) {
+ if (!this.draggable || this.disabled || node.disabled) return
this.draggingNodeKey = node.key
this.draggingNode = node
- this.$emit('dragstart', node)
+ this.$emit('dragstart', { event, node })
+ },
+ handleDrop ({ event, node, dropPosition }) {
+ if (!this.draggable || this.disabled || node.disabled) return
+ const drop = {
+ event,
+ node,
+ dragNode: this.draggingNode,
+ dropPosition
+ }
+ this.$emit('drop', drop)
+ this.resetDragStatus()
}
},
render (h) {
- const lOptions = linkedCascaderOptions(this.treeData, 'multiple-all-options')
- const mOptions = menuOptions(lOptions)[0]
return h('div', {
staticClass: 'n-tree',
class: {
[`n-${this.syntheticTheme}-theme`]: this.syntheticTheme
}
- }, convertRootedOptionsToVNodeTree(mOptions, h, this))
+ }, convertOptionsToVNodeTree(this.data, h, this))
}
}
diff --git a/src/Tree/src/TreeNode.js b/src/Tree/src/TreeNode.js
index 727d20c2f..e4ac27a0c 100644
--- a/src/Tree/src/TreeNode.js
+++ b/src/Tree/src/TreeNode.js
@@ -1,9 +1,15 @@
import NTreeNodeSwitcher from './TreeNodeSwitcher.vue'
import NTreeNodeCheckbox from './TreeNodeCheckbox.vue'
import NTreeNodeContent from './TreeNodeContent.vue'
+import { isLeaf, isLoaded } from './utils'
export default {
name: 'NTreeNode',
+ inject: {
+ NTree: {
+ default: null
+ }
+ },
props: {
data: {
type: Object,
@@ -38,27 +44,58 @@ export default {
default: null
}
},
+ computed: {
+ loading () {
+ return this.NTree.loadingKeys.includes(this.data.key)
+ }
+ },
methods: {
handleSwitcherClick () {
- this.$emit('switcher-click', this.data)
+ const node = this.data
+ const NTree = this.NTree
+ if (NTree.remote && !isLeaf(node) && !isLoaded(node)) {
+ if (!NTree.loadingKeys.includes(node.key)) {
+ NTree.loadingKeys.push(node.key)
+ }
+ NTree.onLoad &&
+ NTree.onLoad(node)
+ .then(() => {
+ NTree.loadingKeys.splice(
+ NTree.loadingKeys.find(key => key === node.key),
+ 1
+ )
+ this.$emit('switcher-click', node)
+ })
+ } else {
+ this.$emit('switcher-click', node)
+ }
},
handleContentClick () {
this.$emit('select', this.data)
},
- handleDragOver () {
- this.$emit('dragover', this.data)
+ handleDragOver (e) {
+ this.$emit('dragover', { event: e, node: this.data })
},
- handleDragEnter () {
- this.$emit('dragenter', this.data)
+ handleDragEnter (e) {
+ this.$emit('dragenter', { event: e, node: this.data })
},
- handleDragStart () {
- this.$emit('dragstart', this.data)
+ handleDragStart (e) {
+ this.$emit('dragstart', { event: e, node: this.data })
},
- handleDragLeave () {
- this.$emit('dragleave', this.data)
+ handleDragLeave (e) {
+ this.$emit('dragleave', { event: e, node: this.data })
+ },
+ handleDragEnd (e) {
+ this.$emit('dragend', { event: e, node: this.data })
+ this.resetDragStatus()
},
handleDrop (e, dropPosition) {
- this.$emit('drop', this.data, dropPosition)
+ this.$emit('drop', {
+ event: e,
+ node: this.data,
+ dropPosition
+ })
+ this.NTree.resetDragStatus()
},
handleCheck (checked) {
this.$emit('check', this.data, checked)
@@ -71,7 +108,8 @@ export default {
h(NTreeNodeSwitcher, {
props: {
expanded: this.expanded,
- hide: this.data.isLeaf
+ loading: this.loading,
+ hide: isLeaf(this.data)
},
on: {
click: this.handleSwitcherClick
diff --git a/src/Tree/src/TreeNodeCheckbox.vue b/src/Tree/src/TreeNodeCheckbox.vue
index cbb2a2047..d6a09e1f1 100644
--- a/src/Tree/src/TreeNodeCheckbox.vue
+++ b/src/Tree/src/TreeNodeCheckbox.vue
@@ -2,7 +2,7 @@