Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(Tree): Implements expanding the folding child element #3

Merged
merged 7 commits into from
Feb 1, 2021
30 changes: 27 additions & 3 deletions packages/element3/src/components/Tree/src/TreeMain.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,25 @@ export default {
checked: { type: Array as PropType<ID[]>, default: () => [] },
showCheckbox: Boolean,
checkOnClickNode: Boolean,
checkStrictly: Boolean
checkStrictly: Boolean,

iconClass: { type: String, default: 'el-icon-caret-right' },
renderAfterExpand: { type: Boolean, default: true },
accordion: Boolean,
autoExpandParent: { type: Boolean, default: true },
expandOnClickNode: { type: Boolean, default: true },
expanded: { type: Array as PropType<ID[]>, default: () => [] },
defaultExpandAll: Boolean
},
emits: ['update:modelValue', 'update:checked'],
emits: ['update:modelValue', 'update:checked', 'update:expanded'],
setup(props, ctx) {
const elTree = getCurrentInstance().proxy
provide('elTree', elTree)
const tree = new Tree(props.modelValue, props.defaultNodeKey)
ctx.emit('update:modelValue', tree.rawNodesProxy)
const rootChildren = computed(() => tree.root.children)

tree.rootProxy.setStrictly(props.checkStrictly)

watchEffect(
() => {
tree.setCheckedByIds(props.checked)
Expand All @@ -57,6 +65,22 @@ export default {
ctx.emit('update:checked', tree.getCheckedIds())
})

if (props.defaultExpandAll) {
tree.expandAll()
}
watchEffect(
() => {
tree.expandNodeByIds(props.expanded)
},
{
flush: 'post'
// exec after wait component flush
}
)
watchEffect(() => {
ctx.emit('update:expanded', tree.getExpandedNodeIds())
})

return {
tree,
rootChildren
Expand Down
44 changes: 39 additions & 5 deletions packages/element3/src/components/Tree/src/TreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,37 @@
class="el-tree-node"
:class="{
'is-checked': node.isChecked,
'is-focusable': !node.isDisabled
'is-focusable': !node.isDisabled,
'is-expanded': node.isExpanded
}"
role="TreeNode"
:aria-checked="node.isChecked"
:aria-disabled="node.isDisabled"
tabindex="-1"
:id="'TreeNode' + node.id"
:data-node-id="node.id"
@click.stop="onClickTreeNode"
>
<div
class="el-tree-node__content"
:style="{ 'padding-left': node.level * elTree.indent + 'px' }"
@click="onClickTreeNodeContent"
>
<span
:class="[
{ expanded: node.isExpanded, 'is-leaf': node.isLeaf },
'el-tree-node__expand-icon',
elTree.iconClass
]"
@click.stop="onClickTreeNodeExpand"
>
</span>
<el-checkbox
v-if="elTree.showCheckbox"
:modelValue="node.isChecked"
:indeterminate="node.isIndeterminate"
:disabled="node.isDisabled"
@click.prevent="onClickCheckbox"
@click.prevent.stop="onClickCheckbox"
>
</el-checkbox>
<span
Expand All @@ -35,7 +46,13 @@
></el-node-content>
</div>
<el-collapse-transition>
<div class="el-tree-node__children" role="group">
<div
v-show="node.isExpanded"
v-if="!elTree.renderAfterExpand || node.isRendered"
class="el-tree-node__children"
role="group"
:aria-expanded="node.isExpanded"
>
<el-tree-node
v-for="child in node.children"
:key="child.id"
Expand Down Expand Up @@ -67,7 +84,13 @@ export default {
},

setup(props) {
const elTree = inject('elTree', { indent: 10, checkOnClickNode: false })
const elTree = inject('elTree', {
indent: 10,
checkOnClickNode: false,
accordion: false,
autoExpandParent: true,
expandOnClickNode: true
})
const onClickTreeNodeContent = () => {
if (!elTree.checkOnClickNode) {
return
Expand All @@ -80,10 +103,21 @@ export default {
}
props.node.setChecked()
}
const onClickTreeNodeExpand = () => {
if (elTree.accordion) props.node.collapse()
else props.node.expand(undefined, elTree.autoExpandParent)
}
const onClickTreeNode = () => {
if (elTree.expandOnClickNode) {
onClickTreeNodeExpand()
}
}
return {
elTree,
onClickCheckbox,
onClickTreeNodeContent
onClickTreeNodeContent,
onClickTreeNode,
onClickTreeNodeExpand
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions packages/element3/src/components/Tree/src/entity/Tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,29 @@ export class Tree<RawNode extends RawNodeBase> {

return ids
}

expandNodeByIds(ids: ID[]): void {
this.rootProxy.depthEach((currentNode: TreeNode) => {
if (ids.includes(currentNode.id)) {
currentNode.expand(true)
}
})
}

getExpandedNodeIds(): ID[] {
const ids = []
this.rootProxy.depthEach((currentNode: TreeNode) => {
if (currentNode !== this.rootProxy && currentNode.isExpanded) {
ids.push(currentNode.id)
}
})

return ids
}

expandAll(v = true): void {
this.root.depthEach((currentNode) => {
currentNode.expand(v, false)
})
}
}
45 changes: 40 additions & 5 deletions packages/element3/src/components/Tree/src/entity/TreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,20 @@ export class TreeNode implements TreeNodePublicProp {
label: string
parent: TreeNode
children: TreeNode[] = []
private _isLeaf = false
private _isChecked = false
private _isStrictly = false
private _isDisabled = false
private _isLeaf: boolean
private _isChecked: boolean
private _isStrictly: boolean
private _isDisabled: boolean
private _isExpanded: boolean
private _isRendered = false

get isRendered(): boolean {
return this._isRendered
}

get isExpanded(): boolean {
return this._isExpanded
}

get isDisabled(): boolean {
return this._isDisabled
Expand Down Expand Up @@ -88,14 +98,17 @@ export class TreeNode implements TreeNodePublicProp {
isLeaf = false,
isChecked = false,
isStrictly = false,
isDisabled = false
isDisabled = false,
isExpanded = false
} = {}
) {
this.id = id ?? idSeed++
this.label = label
this._isLeaf = isLeaf
this._isStrictly = isStrictly
this._isDisabled = isDisabled
this._isExpanded = isExpanded

this.setChecked(isChecked)

this.appendChild(...children)
Expand Down Expand Up @@ -206,4 +219,26 @@ export class TreeNode implements TreeNodePublicProp {
dfs(this, 1)
isFunction(downToUpCallBack) && downToUpCallBack(this, this.parent, 0)
}

expand(v = !this._isExpanded, isAutoExpandParent = false): void {
if (this.isLeaf) {
return
}
this._isExpanded = v
if (v) {
this._isRendered = true
}
if (isAutoExpandParent) {
this.parent?.expand(true, true)
}
}

collapse(v = !this._isExpanded): void {
const parent = this.parent
if (!parent) {
return
}
parent.children.forEach((node) => node.expand(false))
this.expand(v)
}
}
79 changes: 72 additions & 7 deletions packages/element3/src/components/Tree/tests/TreeMain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ describe('TreeMain.vue', () => {
]
const wrapper = mount(TreeMain, {
props: {
modelValue: rawNodes
modelValue: rawNodes,
renderAfterExpand: false
}
})

Expand All @@ -36,7 +37,8 @@ describe('TreeMain.vue', () => {
const rawNodes = []
const wrapper = mount(TreeMain, {
props: {
modelValue: rawNodes
modelValue: rawNodes,
renderAfterExpand: false
}
})

Expand All @@ -45,7 +47,7 @@ describe('TreeMain.vue', () => {
it('reactive tree data', async () => {
const wrapper = mount({
template: `
<el-Tree-main v-model="nodes"></el-Tree-main>
<el-Tree-main v-model="nodes" :renderAfterExpand="false"></el-Tree-main>
`,
components: { elTreeMain: TreeMain },
setup() {
Expand Down Expand Up @@ -90,7 +92,7 @@ describe('TreeMain.vue', () => {
it('reactive tree data(OptionsAPI)', async () => {
const wrapper = mount({
template: `
<el-Tree-main v-model="nodes"></el-Tree-main>
<el-Tree-main v-model="nodes" :renderAfterExpand="false"></el-Tree-main>
`,
components: { elTreeMain: TreeMain },
data() {
Expand Down Expand Up @@ -129,7 +131,7 @@ describe('TreeMain.vue', () => {
it('Realize node multi - selection function', async () => {
const wrapper = mount({
template: `
<el-Tree-main v-model="nodes" v-model:checked="checked" show-checkbox></el-Tree-main>
<el-Tree-main v-model="nodes" v-model:checked="checked" show-checkbox :renderAfterExpand="false"></el-Tree-main>
`,
components: { elTreeMain: TreeMain },
setup() {
Expand Down Expand Up @@ -173,7 +175,7 @@ describe('TreeMain.vue', () => {
it('Based on the check-on-click-node implementation, whether the node is selected when the node is clicked', async () => {
const wrapper = mount({
template: `
<el-tree-main v-model="nodes" v-model:checked="checked" show-checkbox check-on-click-node></el-tree-main>
<el-tree-main v-model="nodes" v-model:checked="checked" show-checkbox check-on-click-node :renderAfterExpand="false"></el-tree-main>
`,
components: { elTreeMain: TreeMain },
setup() {
Expand Down Expand Up @@ -206,7 +208,7 @@ describe('TreeMain.vue', () => {
it('Implement, in the case of displaying checkboxes, whether to strictly follow the parent-child discordant practice', async () => {
const wrapper = mount({
template: `
<el-tree-main v-model="nodes" v-model:checked="checked" show-checkbox check-strictly></el-tree-main>
<el-tree-main v-model="nodes" v-model:checked="checked" show-checkbox check-strictly :renderAfterExpand="false"></el-tree-main>
`,
components: { elTreeMain: TreeMain },
setup() {
Expand Down Expand Up @@ -237,4 +239,67 @@ describe('TreeMain.vue', () => {
await node2.trigger('click')
expect(wrapper.vm.checked).toEqual([2])
})

it('expand a node', async () => {
const rawNodes = [
{
id: 1,
label: 'Node1',
children: [
{
id: 11,
label: 'Node1-1'
}
]
}
]
const wrapper = mount(TreeMain, {
props: {
modelValue: rawNodes,
renderAfterExpand: false
}
})
await wrapper.find('#TreeNode1').trigger('click')
expect(wrapper.find('#TreeNode1').classes()).toContain('is-expanded')
})

it('expand a node and vModel expanded', async () => {
const rawNodes = [
{
id: 1,
label: 'Node1',
children: [
{
id: 11,
label: 'Node1-1'
}
]
},
{
id: 2,
label: 'Node2',
children: [
{
id: 21,
label: 'Node2-1'
}
]
}
]
const expanded = ref([1])
const wrapper = mount(TreeMain, {
props: {
modelValue: rawNodes,
renderAfterExpand: false,
expanded: expanded,
'onUpdate:expanded'(v) {
expanded.value = v
}
}
})
await nextTick()
await wrapper.find('#TreeNode2').trigger('click')
expect(wrapper.find('#TreeNode1').classes()).toContain('is-expanded')
expect(expanded.value).toEqual([1, 2])
})
})
Loading