Skip to content

Commit

Permalink
feat(QTabs): Detect changes in tre structure of the internal tabs and…
Browse files Browse the repository at this point in the history
… recalculate scroll indicators

ref quasarframework#5443
  • Loading branch information
pdanpdan committed Nov 8, 2019
1 parent 9663bd0 commit 8ade8ff
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 2 deletions.
83 changes: 83 additions & 0 deletions docs/src/examples/QTabs/DynamicTabs.vue
@@ -0,0 +1,83 @@
<template>
<div class="q-pa-md">
<div class="q-gutter-y-md" style="max-width: 600px">
<q-list>
<q-item v-for="item in allTabs" :key="item.tab.name" tag="label" dense v-ripple>
<q-item-section side>
<q-checkbox :value="item.selected" @input="status => { setTabSelected(item.tab, status) }" />
</q-item-section>

<q-item-section>
<q-item-label>{{ item.tab.label }}</q-item-label>
</q-item-section>

<q-item-section side>
<q-icon :name="item.tab.icon" />
</q-item-section>
</q-item>
</q-list>

<q-toolbar class="bg-purple text-white shadow-2 rounded-borders">
<q-btn flat label="Homepage" />
<q-space />

<!--
notice shrink property since we are placing it
as child of QToolbar
-->
<q-tabs
v-model="tab"
inline-label
shrink
stretch
>
<q-tab v-for="tab in tabs" :key="tab.name" v-bind="tab" />
</q-tabs>
</q-toolbar>
</div>
</div>
</template>

<script>
const allTabs = [
{ name: 'mails', icon: 'mail', label: 'Mails' },
{ name: 'alarms', icon: 'alarm', label: 'Alarms' },
{ name: 'movies', icon: 'movie', label: 'Movies' },
{ name: 'photos', icon: 'photo', label: 'Photos' },
{ name: 'videos', icon: 'slow_motion_video', label: 'Videos' },
{ name: 'addressbook', icon: 'people', label: 'Address Book' }
]
export default {
data () {
return {
tab: 'mails',
tabs: allTabs.slice(0, 1)
}
},
computed: {
allTabs () {
return allTabs.map(tab => ({
tab,
selected: this.tabs.indexOf(tab) > -1
}))
}
},
methods: {
setTabSelected (tab, status) {
if (status === true) {
this.tabs.push(tab)
}
else {
const index = this.tabs.indexOf(tab)
if (index > -1) {
this.tabs.splice(index, 1)
}
}
}
}
}
</script>
4 changes: 4 additions & 0 deletions docs/src/pages/vue-components/tabs.md
Expand Up @@ -78,6 +78,10 @@ Notice we need to specify the `shrink` prop. By default, QTabs tries to expand t

<doc-example title="Tabs in a QToolbar" file="QTabs/TabsInToolbar" />

### Dynamic update

<doc-example title="Dynamic tabs" file="QTabs/DynamicTabs" />

### Along with QTabsPanel

::: tip
Expand Down
104 changes: 104 additions & 0 deletions ui/dev/components/components/tabs-dynamic.vue
@@ -0,0 +1,104 @@
<template>
<q-card class="q-ma-md" :style="{ maxWidth: width + 'px' }">
<q-list>
<q-item tag="label" dark class="bg-black" dense v-ripple no-refocus>
<q-item-section>
<q-item-label>Width</q-item-label>
</q-item-section>
<q-item-section side>
<q-input dark dense borderless input-class="text-right" v-model.number="width" type="number" :min="300" />
</q-item-section>
</q-item>
<q-item v-for="item in allTabs" :key="item.tab.name" tag="label" dense v-ripple>
<q-item-section side>
<q-checkbox :value="item.selected" @input="status => { setTabSelected(item.tab, status) }" />
</q-item-section>

<q-item-section>
<q-item-label>{{ item.tab.label }}</q-item-label>
</q-item-section>

<q-item-section side>
<q-icon :name="item.tab.icon" />
</q-item-section>
</q-item>
</q-list>

<q-card-actions class="bg-black">
<q-toolbar class="bg-purple text-white">
<q-btn flat label="Homepage" />
<q-space />

<!--
notice shrink property since we are placing it
as child of QToolbar
-->
<q-tabs
v-model="tab"
inline-label
shrink
stretch
>
<q-tab v-for="t in tabs" :key="t.name" v-bind="t" />
</q-tabs>
</q-toolbar>
</q-card-actions>

<q-card-actions class="bg-black">
<q-tabs
class="bg-primary text-white full-width"
v-model="tab"
inline-label
shrink
stretch
>
<q-tab v-for="t in tabs" :key="t.name" v-bind="t" />
</q-tabs>
</q-card-actions>
</q-card>
</template>

<script>
const allTabs = [
{ name: 'mails', icon: 'mail', label: 'Mails' },
{ name: 'alarms', icon: 'alarm', label: 'Alarms' },
{ name: 'movies', icon: 'movie', label: 'Movies' },
{ name: 'photos', icon: 'photo', label: 'Photos' },
{ name: 'videos', icon: 'slow_motion_video', label: 'Videos' },
{ name: 'addressbook', icon: 'people', label: 'Address Book' }
]
export default {
data () {
return {
tab: 'mails',
width: 300,
tabs: allTabs.slice(0, 1)
}
},
computed: {
allTabs () {
return allTabs.map(tab => ({
tab,
selected: this.tabs.indexOf(tab) > -1
}))
}
},
methods: {
setTabSelected (tab, status) {
if (status === true) {
this.tabs.push(tab)
}
else {
const index = this.tabs.indexOf(tab)
if (index > -1) {
this.tabs.splice(index, 1)
}
}
}
}
}
</script>
5 changes: 4 additions & 1 deletion ui/src/components/tabs/QRouteTab.js
Expand Up @@ -14,7 +14,8 @@ export default Vue.extend({
},

inject: {
__activateRoute: {}
__activateRoute: {},
__recalculateScroll: {}
},

watch: {
Expand Down Expand Up @@ -61,10 +62,12 @@ export default Vue.extend({
},

mounted () {
this.__recalculateScroll()
this.$router !== void 0 && this.__checkActivation()
},

beforeDestroy () {
this.__recalculateScroll()
this.__activateRoute({ remove: true, name: this.name })
},

Expand Down
11 changes: 10 additions & 1 deletion ui/src/components/tabs/QTab.js
Expand Up @@ -18,7 +18,8 @@ export default Vue.extend({
console.error('QTab/QRouteTab components need to be child of QTabsBar')
}
},
__activateTab: {}
__activateTab: {},
__recalculateScroll: {}
},

props: {
Expand Down Expand Up @@ -141,6 +142,14 @@ export default Vue.extend({
}
},

mounted () {
this.__recalculateScroll()
},

beforeDestroy () {
this.__recalculateScroll()
},

render (h) {
return this.__renderTab(h, 'div')
}
Expand Down
10 changes: 10 additions & 0 deletions ui/src/components/tabs/QTabs.js
Expand Up @@ -5,6 +5,7 @@ import QResizeObserver from '../resize-observer/QResizeObserver.js'

import { stop } from '../../utils/event.js'
import slot from '../../utils/slot.js'
import debounce from '../../utils/debounce.js'

function getIndicatorClass (color, top, vertical) {
const pos = vertical === true
Expand Down Expand Up @@ -45,6 +46,7 @@ export default Vue.extend({
provide () {
return {
tabs: this.tabs,
__recalculateScroll: this.__recalculateScroll,
__activateTab: this.__activateTab,
__activateRoute: this.__activateRoute
}
Expand Down Expand Up @@ -204,6 +206,13 @@ export default Vue.extend({
}
},

__recalculateScroll () {
this.$el !== void 0 && this.__updateContainer({
width: this.$el.offsetWidth,
height: this.$el.offsetHeight
})
},

__updateContainer ({ width, height }) {
const scroll = this.vertical === true
? this.$refs.content.scrollHeight > height + 1
Expand Down Expand Up @@ -343,6 +352,7 @@ export default Vue.extend({

created () {
this.buffer = []
this.__updateContainer = debounce(this.__updateContainer, 200)
},

beforeDestroy () {
Expand Down

0 comments on commit 8ade8ff

Please sign in to comment.