Skip to content

Commit 60fe6fe

Browse files
committed
[Tabs] Add default fade effect and transition-duration prop.
1 parent 5e4abe6 commit 60fe6fe

File tree

4 files changed

+61
-13
lines changed

4 files changed

+61
-13
lines changed

docs/pages/components/Tabs.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,11 @@ Add `justified` to `<tabs>` to use justified style.
144144

145145
### Props
146146

147-
Name | Type | Default | Required | Description
148-
---------------- | ---------- | -------- | -------- | -----------------------
149-
`v-model` | Number | | | The current tab index, use this to manual change tab index.
150-
`justified` | Boolean | false | | Use justified style.
147+
Name | Type | Default | Required | Description
148+
---------------- | ---------- | -------- | -------- | -----------------------
149+
`v-model` | Number | | | The current tab index, use this to manual change tab index.
150+
`justified` | Boolean | false | | Use justified style.
151+
`transition-duration` | Number | 150 | | The tabs show / hide transition time in ms. Use 0 to disable transitions.
151152

152153
### Slots
153154

src/components/tabs/Tab.vue

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,72 @@
11
<template>
2-
<div class="tab-pane" role="tabpanel" :class="{active:active}" v-show="active">
2+
<div class="tab-pane" :class="{fade: transition > 0}" role="tabpanel">
33
<slot></slot>
44
</div>
55
</template>
66

77
<script>
8+
import domUtils from '../../utils/domUtils'
9+
10+
const ACTIVE_CLASS = 'active'
11+
const IN_CLASS = 'in'
12+
813
export default {
914
props: {
1015
title: {
1116
type: String,
12-
'default': 'Tab Title'
17+
default: 'Tab Title'
1318
},
1419
htmlTitle: {
1520
type: Boolean,
16-
'default': false
21+
default: false
1722
},
1823
disabled: {
1924
type: Boolean,
20-
'default': false
25+
default: false
2126
},
2227
group: {
2328
type: String
2429
},
2530
pullRight: {
2631
type: Boolean,
27-
'default': false
32+
default: false
2833
}
2934
},
3035
data () {
3136
return {
32-
active: true
37+
active: true,
38+
transition: 150
39+
}
40+
},
41+
watch: {
42+
active (active) {
43+
if (active) {
44+
setTimeout(() => {
45+
domUtils.addClass(this.$el, ACTIVE_CLASS)
46+
this.$el.offsetHeight
47+
domUtils.addClass(this.$el, IN_CLASS)
48+
}, this.transition)
49+
} else {
50+
domUtils.removeClass(this.$el, IN_CLASS)
51+
setTimeout(() => {
52+
domUtils.removeClass(this.$el, ACTIVE_CLASS)
53+
}, this.transition)
54+
}
3355
}
3456
},
3557
created () {
36-
let self = this
3758
try {
38-
self.$parent.tabs.push(self)
59+
this.$parent.tabs.push(this)
3960
} catch (e) {
40-
throw new Error('Tab parent must be Tabs.')
61+
throw new Error('<tab> parent must be <tabs>.')
62+
}
63+
},
64+
methods: {
65+
show () {
66+
this.$nextTick(() => {
67+
domUtils.addClass(this.$el, ACTIVE_CLASS)
68+
domUtils.addClass(this.$el, IN_CLASS)
69+
})
4170
}
4271
}
4372
}

src/components/tabs/Tabs.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
justified: {
4343
type: Boolean,
4444
default: false
45+
},
46+
transitionDuration: {
47+
type: Number,
48+
default: 150
4549
}
4650
},
4751
data () {
@@ -61,6 +65,12 @@
6165
this.activeIndex = this.value
6266
}
6367
if (this.tabs.length) {
68+
this.tabs.forEach((tab, i) => {
69+
tab.transition = this.transitionDuration
70+
if (i === this.activeIndex) {
71+
tab.show()
72+
}
73+
})
6474
this.selectCurrent()
6575
}
6676
},

test/unit/specs/Tabs.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ describe('Tabs', () => {
8888
expect(activeContent[0].textContent).to.contain('tab1')
8989
utils.triggerEvent(nav.querySelectorAll('li')[0].querySelector('a'), 'click')
9090
await vm.$nextTick()
91+
await utils.sleep(350)
9192
activeTab = nav.querySelectorAll('.active')
9293
expect(activeTab.length).to.equal(1)
9394
expect(activeTab[0].querySelector('a').textContent).to.equal('tab0')
@@ -117,9 +118,11 @@ describe('Tabs', () => {
117118
let tab = nav.querySelectorAll('li')[1].querySelector('a')
118119
utils.triggerEvent(tab, 'click')
119120
await vm.$nextTick()
121+
await utils.sleep(350)
120122
// Double click should be fine
121123
utils.triggerEvent(tab, 'click')
122124
await vm.$nextTick()
125+
await utils.sleep(350)
123126
let activeTab = nav.querySelectorAll('.active')
124127
expect(activeTab.length).to.equal(1)
125128
expect(activeTab[0].querySelector('a').textContent).to.equal('Profile')
@@ -137,6 +140,7 @@ describe('Tabs', () => {
137140
expect(tab1.className).to.equal('disabled')
138141
utils.triggerEvent(tab1.querySelector('a'), 'click')
139142
await vm.$nextTick()
143+
await utils.sleep(350)
140144
expect(tab1.className).to.equal('disabled')
141145
let activeContent = content.querySelectorAll('.tab-pane.active')
142146
expect(activeContent.length).to.equal(1)
@@ -146,6 +150,7 @@ describe('Tabs', () => {
146150
expect(tab2.className).to.equal('disabled')
147151
utils.triggerEvent(tab2.querySelector('a'), 'click')
148152
await vm.$nextTick()
153+
await utils.sleep(350)
149154
expect(tab2.className).to.equal('disabled')
150155
activeContent = content.querySelectorAll('.tab-pane.active')
151156
expect(activeContent.length).to.equal(1)
@@ -169,6 +174,7 @@ describe('Tabs', () => {
169174
let spy = sinon.spy(window, 'alert')
170175
utils.triggerEvent(nav.querySelectorAll('li')[2].querySelector('a'), 'click')
171176
await vm.$nextTick()
177+
await utils.sleep(350)
172178
sinon.assert.called(spy)
173179
window.alert = _savedAlert
174180
})
@@ -180,11 +186,13 @@ describe('Tabs', () => {
180186
let tab5 = nav.querySelector('li.dropdown')
181187
utils.triggerEvent(tab5.querySelectorAll('a')[0], 'click')
182188
await vm.$nextTick()
189+
await utils.sleep(350)
183190
expect(tab5.querySelector('.dropdown-menu')).to.exist
184191
expect(tab5.className).to.contain('dropdown')
185192
expect(tab5.className).to.contain('open')
186193
utils.triggerEvent(tab5.querySelector('.dropdown-menu').querySelector('li').querySelector('a'), 'click')
187194
await vm.$nextTick()
195+
await utils.sleep(350)
188196
expect(tab5.className).to.contain('active')
189197
let activeContent = content.querySelectorAll('.tab-pane.active')
190198
expect(activeContent.length).to.equal(1)

0 commit comments

Comments
 (0)