Permalink
Browse files

fix(tabs): Better handling of active tab and transitions (#903)

* [tab] Sniff parent fro fade and lazy vals

* Update tabs.vue


* Update tabs.vue

* [tabs] fix keyboard navigation

* Update tabs.vue

* [tabs] ARIA tweaks
  • Loading branch information...
tmorehouse committed Aug 21, 2017
1 parent d84af70 commit d5b81dd9be766998a81430d5e6e0b4fc93df5fad
Showing with 172 additions and 158 deletions.
  1. +67 −64 docs/components/tabs/README.md
  2. +38 −18 lib/components/tab.vue
  3. +67 −76 lib/components/tabs.vue
@@ -4,71 +4,74 @@
```html
<template>
<div>
<!-- Tabs with card integration -->
<b-card no-body>
<b-tabs small card ref="tabs" v-model="tabIndex">
<b-tab title="General">
I'm the first fading tab
</b-tab>
<b-tab title="Edit profile">
I'm the second tab
<b-card>I'm the card in tab</b-card>
</b-tab>
<b-tab title="Premium Plan" disabled>
Sibzamini!
</b-tab>
</b-tabs>
</b-card>
<div>
<!-- Tabs with card integration -->
<b-card no-body>
<b-tabs small card ref="tabs" v-model="tabIndex">
<b-tab title="General">
I'm the first fading tab
</b-tab>
<b-tab title="Edit profile">
I'm the second tab
<b-card>I'm the card in tab</b-card>
</b-tab>
<b-tab title="Premium Plan" disabled>
Sibzamini!
</b-tab>
<b-tab title="Info">
I'm the last tab
</b-tab>
</b-tabs>
</b-card>
<!-- Control buttons-->
<div class="text-center">
<b-button-group class="mt-2">
<b-btn @click="tabIndex--">Previous</b-btn>
<b-btn @click="tabIndex++">Next</b-btn>
</b-button-group>
<br>
<span class="text-muted">Current Tab: {{tabIndex}}</span>
</div>
<br><br>
<b-card no-body>
<b-tabs card>
<!-- Render Tabs -->
<b-tab :title="`Tab ${i}`" v-for="i in tabs" :key="i">
Tab Contents {{i}}
<b-btn size="sm" variant="danger" class="float-right" @click="()=>closeTab(i)">
Close tab
</b-btn>
</b-tab>
<!-- Control buttons-->
<div class="text-center">
<b-button-group class="mt-2">
<b-btn @click="$refs.tabs.previousTab()">Previous</b-btn>
<b-btn @click="$refs.tabs.nextTab()">Next</b-btn>
</b-button-group>
<br>
<span class="text-muted">Current Tab: {{tabIndex}}</span>
</div>
<!-- Newtab Button (Using tabs slot) -->
<b-nav-item slot="tabs" @click.prevent="newTab" href="#">
+
</b-nav-item>
<br>
<br>
<b-card no-body>
<b-tabs card>
<!-- Render Tabs -->
<b-tab :title="`Tab ${i}`" v-for="i in tabs" :key="i">
Tab Contents {{i}}
<b-btn size="sm" variant="danger" class="float-right" @click="()=>closeTab(i)">Close tab
</b-btn>
</b-tab>
<!-- Newtab Button (Using tabs slot) -->
<b-nav-item slot="tabs" @click.prevent="newTab" href="#">
+
</b-nav-item>
<!-- Render this if no tabs -->
<div slot="empty" class="text-center text-muted">
There are no open tabs
<br> Open a new tab using + button.
</div>
</b-tabs>
</b-card>
<!-- Render this if no tabs -->
<div slot="empty" class="text-center text-muted">
There are no open tabs
<br> Open a new tab using + button.
</div>
</b-tabs>
</b-card>
</div>
</div>
</template>
<script>
export default {
data: {
tabIndex: null,
tabs: [],
tabCounter: 0
},
methods: {
closeTab(x) {
export default {
data: {
tabIndex: 0,
tabs: [],
tabCounter: 0
},
methods: {
closeTab(x) {
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i] === x) {
this.tabs.splice(i, 1);
@@ -78,11 +81,11 @@ export default {
newTab() {
this.tabs.push(this.tabCounter++);
}
}
}
}
</script>
<!-- tabs.vue -->
<!-- tabs-1.vue -->
```
### Basic usage
@@ -103,13 +106,13 @@ export default {
### Cards Integration
Tabs support integrating with bootstrap cards. Just add `card` property. Note
Tabs support integrating with bootstrap cards. Just add the `card` property. Note
that you should add `no-body` prop on `<b-card>` element in order to decorate header
and remove the extra badding
and remove the extra padding.
```html
<b-card no-body>
<b-tabs ref="tabs" v-model="tabIndex" card>
<b-tabs ref="tabs" card>
<b-tab title="Tab 1" active>
Tab Contents
</b-tab>
@@ -1,48 +1,72 @@
<template>
<transition @enter="enter" @before-leave="beforeLeave" mode="out-in">
<transition @before-enter="beforeEnter" @after-enter="afterEnter" @after-leave="afterLeave" mode="out-in">
<component :is="tag"
:id="id || null"
:id="safeId()"
role="tabpanel"
:class="['tab-pane', {show, fade, disabled, active: localActive}]"
:class="tabClasses"
:aria-hidden="localActive ? 'false' : 'true'"
:aria-expanded="localActive ? 'true' : 'false'"
:aria-lablelledby="controlledBy || null"
v-if="localActive || !lazy"
v-show="localActive || lazy"
v-if="localActive || !computedLazy"
v-show="localActive"
ref="panel"
:css="false"
>
<slot></slot>
</component>
</transition>
</template>
<script>
import { idMixin } from '../mixins';
export default {
mixins: [idMixin],
methods: {
enter() {
beforeEnter() {
this.show = false;
},
afterEnter() {
this.show = true;
},
beforeLeave() {
afterLeave() {
this.show = false;
}
},
data() {
return {
fade: false,
localActive: this.active,
lazy: true,
localActive: this.active && !this.disabled,
show: false
};
},
computed: {
tabClasses() {
return [
'tab-pane',
this.show ? 'show' : '',
this.computedFade ? 'fade' : '',
this.disabled ? 'disabled' : '',
this.localActive ? 'active' : ''
];
},
controlledBy() {
return this.buttonId || (this.id ? (this.id + '__BV_tab_button__') : null);
return this.buttonId || this.safeId('__BV_tab_button__');
},
computedFade() {
return this.$parent.fade;
},
computedLazy() {
return this.$parent.lazy;
},
_isTab() {
// For parent sniffing of child
return true;
}
},
props: {
id: {
type: String,
default: ''
active: {
type: Boolean,
default: false
},
tag: {
type: String,
@@ -64,10 +88,6 @@
type: Boolean,
default: false
},
active: {
type: Boolean,
default: false
},
href: {
type: String,
default: '#'
Oops, something went wrong.

0 comments on commit d5b81dd

Please sign in to comment.