|
@@ -1,10 +1,13 @@
|
|
-import Vue from 'vue'
|
|
|
|
|
|
+// eslint-disable-next-line no-unused
|
|
|
|
+import { h, Fragment } from 'vue'
|
|
import { mapState } from 'vuex'
|
|
import { mapState } from 'vuex'
|
|
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
|
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
|
|
|
|
|
import './tab_switcher.scss'
|
|
import './tab_switcher.scss'
|
|
|
|
|
|
-export default Vue.component('tab-switcher', {
|
|
|
|
|
|
+const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
|
|
|
|
+
|
|
|
|
+export default {
|
|
name: 'TabSwitcher',
|
|
name: 'TabSwitcher',
|
|
props: {
|
|
props: {
|
|
renderOnlyFocused: {
|
|
renderOnlyFocused: {
|
|
@@ -31,26 +34,31 @@ export default Vue.component('tab-switcher', {
|
|
required: false,
|
|
required: false,
|
|
type: Boolean,
|
|
type: Boolean,
|
|
default: false
|
|
default: false
|
|
|
|
+ },
|
|
|
|
+ bodyScrollLock: {
|
|
|
|
+ required: false,
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: false
|
|
}
|
|
}
|
|
},
|
|
},
|
|
data () {
|
|
data () {
|
|
return {
|
|
return {
|
|
- active: this.$slots.default.findIndex(_ => _.tag)
|
|
|
|
|
|
+ active: findFirstUsable(this.slots())
|
|
}
|
|
}
|
|
},
|
|
},
|
|
computed: {
|
|
computed: {
|
|
activeIndex () {
|
|
activeIndex () {
|
|
// In case of controlled component
|
|
// In case of controlled component
|
|
if (this.activeTab) {
|
|
if (this.activeTab) {
|
|
- return this.$slots.default.findIndex(slot => this.activeTab === slot.key)
|
|
|
|
|
|
+ return this.slots().findIndex(slot => this.activeTab === slot.props.key)
|
|
} else {
|
|
} else {
|
|
return this.active
|
|
return this.active
|
|
}
|
|
}
|
|
},
|
|
},
|
|
isActive () {
|
|
isActive () {
|
|
return tabName => {
|
|
return tabName => {
|
|
- const isWanted = slot => slot.data && slot.data.attrs['data-tab-name'] === tabName
|
|
|
|
- return this.$slots.default.findIndex(isWanted) === this.activeIndex
|
|
|
|
|
|
+ const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
|
|
|
+ return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
|
}
|
|
}
|
|
},
|
|
},
|
|
settingsModalVisible () {
|
|
settingsModalVisible () {
|
|
@@ -61,9 +69,9 @@ export default Vue.component('tab-switcher', {
|
|
})
|
|
})
|
|
},
|
|
},
|
|
beforeUpdate () {
|
|
beforeUpdate () {
|
|
- const currentSlot = this.$slots.default[this.active]
|
|
|
|
- if (!currentSlot.tag) {
|
|
|
|
- this.active = this.$slots.default.findIndex(_ => _.tag)
|
|
|
|
|
|
+ const currentSlot = this.slots()[this.active]
|
|
|
|
+ if (!currentSlot.props) {
|
|
|
|
+ this.active = findFirstUsable(this.slots())
|
|
}
|
|
}
|
|
},
|
|
},
|
|
methods: {
|
|
methods: {
|
|
@@ -73,9 +81,16 @@ export default Vue.component('tab-switcher', {
|
|
this.setTab(index)
|
|
this.setTab(index)
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
+ // DO NOT put it to computed, it doesn't work (caching?)
|
|
|
|
+ slots () {
|
|
|
|
+ if (this.$slots.default()[0].type === Fragment) {
|
|
|
|
+ return this.$slots.default()[0].children
|
|
|
|
+ }
|
|
|
|
+ return this.$slots.default()
|
|
|
|
+ },
|
|
setTab (index) {
|
|
setTab (index) {
|
|
if (typeof this.onSwitch === 'function') {
|
|
if (typeof this.onSwitch === 'function') {
|
|
- this.onSwitch.call(null, this.$slots.default[index].key)
|
|
|
|
|
|
+ this.onSwitch.call(null, this.slots()[index].key)
|
|
}
|
|
}
|
|
this.active = index
|
|
this.active = index
|
|
if (this.scrollableTabs) {
|
|
if (this.scrollableTabs) {
|
|
@@ -83,27 +98,28 @@ export default Vue.component('tab-switcher', {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- render (h) {
|
|
|
|
- const tabs = this.$slots.default
|
|
|
|
|
|
+ render () {
|
|
|
|
+ const tabs = this.slots()
|
|
.map((slot, index) => {
|
|
.map((slot, index) => {
|
|
- if (!slot.tag) return
|
|
|
|
|
|
+ const props = slot.props
|
|
|
|
+ if (!props) return
|
|
const classesTab = ['tab', 'button-default']
|
|
const classesTab = ['tab', 'button-default']
|
|
const classesWrapper = ['tab-wrapper']
|
|
const classesWrapper = ['tab-wrapper']
|
|
if (this.activeIndex === index) {
|
|
if (this.activeIndex === index) {
|
|
classesTab.push('active')
|
|
classesTab.push('active')
|
|
classesWrapper.push('active')
|
|
classesWrapper.push('active')
|
|
}
|
|
}
|
|
- if (slot.data.attrs.image) {
|
|
|
|
|
|
+ if (props.image) {
|
|
return (
|
|
return (
|
|
<div class={classesWrapper.join(' ')}>
|
|
<div class={classesWrapper.join(' ')}>
|
|
<button
|
|
<button
|
|
- disabled={slot.data.attrs.disabled}
|
|
|
|
|
|
+ disabled={props.disabled}
|
|
onClick={this.clickTab(index)}
|
|
onClick={this.clickTab(index)}
|
|
class={classesTab.join(' ')}
|
|
class={classesTab.join(' ')}
|
|
type="button"
|
|
type="button"
|
|
>
|
|
>
|
|
- <img src={slot.data.attrs.image} title={slot.data.attrs['image-tooltip']}/>
|
|
|
|
- {slot.data.attrs.label ? '' : slot.data.attrs.label}
|
|
|
|
|
|
+ <img src={props.image} title={props['image-tooltip']}/>
|
|
|
|
+ {props.label ? '' : props.label}
|
|
</button>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
)
|
|
@@ -111,25 +127,26 @@ export default Vue.component('tab-switcher', {
|
|
return (
|
|
return (
|
|
<div class={classesWrapper.join(' ')}>
|
|
<div class={classesWrapper.join(' ')}>
|
|
<button
|
|
<button
|
|
- disabled={slot.data.attrs.disabled}
|
|
|
|
|
|
+ disabled={props.disabled}
|
|
onClick={this.clickTab(index)}
|
|
onClick={this.clickTab(index)}
|
|
class={classesTab.join(' ')}
|
|
class={classesTab.join(' ')}
|
|
type="button"
|
|
type="button"
|
|
>
|
|
>
|
|
- {!slot.data.attrs.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={slot.data.attrs.icon}/>)}
|
|
|
|
|
|
+ {!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)}
|
|
<span class="text">
|
|
<span class="text">
|
|
- {slot.data.attrs.label}
|
|
|
|
|
|
+ {props.label}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
)
|
|
})
|
|
})
|
|
|
|
|
|
- const contents = this.$slots.default.map((slot, index) => {
|
|
|
|
- if (!slot.tag) return
|
|
|
|
|
|
+ const contents = this.slots().map((slot, index) => {
|
|
|
|
+ const props = slot.props
|
|
|
|
+ if (!props) return
|
|
const active = this.activeIndex === index
|
|
const active = this.activeIndex === index
|
|
const classes = [ active ? 'active' : 'hidden' ]
|
|
const classes = [ active ? 'active' : 'hidden' ]
|
|
- if (slot.data.attrs.fullHeight) {
|
|
|
|
|
|
+ if (props.fullHeight) {
|
|
classes.push('full-height')
|
|
classes.push('full-height')
|
|
}
|
|
}
|
|
const renderSlot = (!this.renderOnlyFocused || active)
|
|
const renderSlot = (!this.renderOnlyFocused || active)
|
|
@@ -140,7 +157,7 @@ export default Vue.component('tab-switcher', {
|
|
<div class={classes}>
|
|
<div class={classes}>
|
|
{
|
|
{
|
|
this.sideTabBar
|
|
this.sideTabBar
|
|
- ? <h1 class="mobile-label">{slot.data.attrs.label}</h1>
|
|
|
|
|
|
+ ? <h1 class="mobile-label">{props.label}</h1>
|
|
: ''
|
|
: ''
|
|
}
|
|
}
|
|
{renderSlot}
|
|
{renderSlot}
|
|
@@ -153,10 +170,14 @@ export default Vue.component('tab-switcher', {
|
|
<div class="tabs">
|
|
<div class="tabs">
|
|
{tabs}
|
|
{tabs}
|
|
</div>
|
|
</div>
|
|
- <div ref="contents" class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')} v-body-scroll-lock={this.settingsModalVisible}>
|
|
|
|
|
|
+ <div
|
|
|
|
+ ref="contents"
|
|
|
|
+ class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
|
|
|
|
+ v-body-scroll-lock={this.bodyScrollLock}
|
|
|
|
+ >
|
|
{contents}
|
|
{contents}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
)
|
|
}
|
|
}
|
|
-})
|
|
|
|
|
|
+}
|