2021-04-25 10:23:16 +00:00
|
|
|
// eslint-disable-next-line no-unused
|
2022-03-21 19:29:51 +00:00
|
|
|
import { h, Fragment } from 'vue'
|
2020-07-23 06:41:22 +00:00
|
|
|
import { mapState } from 'vuex'
|
2020-10-20 19:54:43 +00:00
|
|
|
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
2018-08-27 19:22:25 +00:00
|
|
|
|
|
|
|
import './tab_switcher.scss'
|
|
|
|
|
2021-04-25 10:23:16 +00:00
|
|
|
const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
|
2021-04-18 11:58:02 +00:00
|
|
|
|
|
|
|
export default {
|
2018-08-27 19:22:25 +00:00
|
|
|
name: 'TabSwitcher',
|
2019-08-12 17:01:38 +00:00
|
|
|
props: {
|
|
|
|
renderOnlyFocused: {
|
|
|
|
required: false,
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
},
|
|
|
|
onSwitch: {
|
|
|
|
required: false,
|
2019-11-08 16:14:01 +00:00
|
|
|
type: Function,
|
|
|
|
default: undefined
|
2019-08-12 17:01:38 +00:00
|
|
|
},
|
2019-09-08 10:44:29 +00:00
|
|
|
activeTab: {
|
2019-08-12 17:01:38 +00:00
|
|
|
required: false,
|
2019-11-08 16:14:01 +00:00
|
|
|
type: String,
|
|
|
|
default: undefined
|
2019-08-12 17:01:38 +00:00
|
|
|
},
|
|
|
|
scrollableTabs: {
|
|
|
|
required: false,
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
2020-05-03 14:36:12 +00:00
|
|
|
},
|
|
|
|
sideTabBar: {
|
|
|
|
required: false,
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
2021-04-18 12:39:06 +00:00
|
|
|
},
|
|
|
|
bodyScrollLock: {
|
|
|
|
required: false,
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
2019-08-12 17:01:38 +00:00
|
|
|
}
|
|
|
|
},
|
2018-08-27 19:22:25 +00:00
|
|
|
data () {
|
|
|
|
return {
|
2022-03-21 19:29:51 +00:00
|
|
|
active: findFirstUsable(this.slots())
|
2018-08-27 19:22:25 +00:00
|
|
|
}
|
|
|
|
},
|
2019-08-10 18:48:05 +00:00
|
|
|
computed: {
|
|
|
|
activeIndex () {
|
|
|
|
// In case of controlled component
|
|
|
|
if (this.activeTab) {
|
2022-04-10 03:49:22 +00:00
|
|
|
return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key)
|
2019-08-10 18:48:05 +00:00
|
|
|
} else {
|
|
|
|
return this.active
|
|
|
|
}
|
2020-07-23 06:41:22 +00:00
|
|
|
},
|
2022-03-24 22:01:20 +00:00
|
|
|
isActive () {
|
|
|
|
return tabName => {
|
2022-03-27 09:50:00 +00:00
|
|
|
const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
2022-03-27 09:58:28 +00:00
|
|
|
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
2022-03-24 22:01:20 +00:00
|
|
|
}
|
|
|
|
},
|
2020-07-23 09:20:48 +00:00
|
|
|
settingsModalVisible () {
|
2020-07-23 06:41:22 +00:00
|
|
|
return this.settingsModalState === 'visible'
|
|
|
|
},
|
|
|
|
...mapState({
|
|
|
|
settingsModalState: state => state.interface.settingsModalState
|
|
|
|
})
|
2019-08-10 18:48:05 +00:00
|
|
|
},
|
2019-07-05 07:02:14 +00:00
|
|
|
beforeUpdate () {
|
2021-04-18 12:03:28 +00:00
|
|
|
const currentSlot = this.slots()[this.active]
|
2021-04-25 10:23:16 +00:00
|
|
|
if (!currentSlot.props) {
|
2021-04-18 12:03:28 +00:00
|
|
|
this.active = findFirstUsable(this.slots())
|
2019-07-05 07:02:14 +00:00
|
|
|
}
|
|
|
|
},
|
2018-08-27 19:22:25 +00:00
|
|
|
methods: {
|
2020-09-03 12:45:13 +00:00
|
|
|
clickTab (index) {
|
2019-08-12 17:01:38 +00:00
|
|
|
return (e) => {
|
|
|
|
e.preventDefault()
|
2020-09-03 12:45:13 +00:00
|
|
|
this.setTab(index)
|
|
|
|
}
|
|
|
|
},
|
2021-04-18 12:03:28 +00:00
|
|
|
// DO NOT put it to computed, it doesn't work (caching?)
|
|
|
|
slots () {
|
2022-03-21 19:29:51 +00:00
|
|
|
if (this.$slots.default()[0].type === Fragment) {
|
|
|
|
return this.$slots.default()[0].children
|
|
|
|
}
|
2022-03-18 11:32:36 +00:00
|
|
|
return this.$slots.default()
|
2021-04-18 12:03:28 +00:00
|
|
|
},
|
2020-09-03 12:45:13 +00:00
|
|
|
setTab (index) {
|
|
|
|
if (typeof this.onSwitch === 'function') {
|
2021-04-18 12:03:28 +00:00
|
|
|
this.onSwitch.call(null, this.slots()[index].key)
|
2020-09-03 12:45:13 +00:00
|
|
|
}
|
|
|
|
this.active = index
|
|
|
|
if (this.scrollableTabs) {
|
|
|
|
this.$refs.contents.scrollTop = 0
|
2019-01-17 19:25:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2021-04-25 10:23:16 +00:00
|
|
|
render () {
|
2021-04-18 12:03:28 +00:00
|
|
|
const tabs = this.slots()
|
2019-07-05 07:02:14 +00:00
|
|
|
.map((slot, index) => {
|
2021-04-25 10:23:16 +00:00
|
|
|
const props = slot.props
|
2021-04-18 11:58:02 +00:00
|
|
|
if (!props) return
|
2020-11-24 10:32:42 +00:00
|
|
|
const classesTab = ['tab', 'button-default']
|
2019-07-05 07:02:14 +00:00
|
|
|
const classesWrapper = ['tab-wrapper']
|
2019-08-10 18:48:05 +00:00
|
|
|
if (this.activeIndex === index) {
|
2019-07-05 07:02:14 +00:00
|
|
|
classesTab.push('active')
|
|
|
|
classesWrapper.push('active')
|
|
|
|
}
|
2021-04-18 11:58:02 +00:00
|
|
|
if (props.image) {
|
2019-07-24 19:35:52 +00:00
|
|
|
return (
|
2019-08-10 04:26:29 +00:00
|
|
|
<div class={classesWrapper.join(' ')}>
|
2019-07-24 19:35:52 +00:00
|
|
|
<button
|
2021-04-18 11:58:02 +00:00
|
|
|
disabled={props.disabled}
|
2020-09-03 12:45:13 +00:00
|
|
|
onClick={this.clickTab(index)}
|
2021-02-01 19:07:09 +00:00
|
|
|
class={classesTab.join(' ')}
|
|
|
|
type="button"
|
|
|
|
>
|
2021-04-18 11:58:02 +00:00
|
|
|
<img src={props.image} title={props['image-tooltip']}/>
|
|
|
|
{props.label ? '' : props.label}
|
2019-07-24 19:35:52 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
2019-07-05 07:02:14 +00:00
|
|
|
return (
|
2019-08-10 04:26:29 +00:00
|
|
|
<div class={classesWrapper.join(' ')}>
|
2019-07-05 07:02:14 +00:00
|
|
|
<button
|
2021-04-18 11:58:02 +00:00
|
|
|
disabled={props.disabled}
|
2020-09-03 12:45:13 +00:00
|
|
|
onClick={this.clickTab(index)}
|
2020-05-25 13:10:14 +00:00
|
|
|
class={classesTab.join(' ')}
|
2020-05-27 00:32:57 +00:00
|
|
|
type="button"
|
2020-05-25 13:10:14 +00:00
|
|
|
>
|
2021-04-18 11:58:02 +00:00
|
|
|
{!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)}
|
2020-05-27 00:32:57 +00:00
|
|
|
<span class="text">
|
2021-04-18 11:58:02 +00:00
|
|
|
{props.label}
|
2020-05-27 00:32:57 +00:00
|
|
|
</span>
|
2020-05-25 13:10:14 +00:00
|
|
|
</button>
|
2019-07-05 07:02:14 +00:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
})
|
2019-01-17 19:25:50 +00:00
|
|
|
|
2021-04-18 12:03:28 +00:00
|
|
|
const contents = this.slots().map((slot, index) => {
|
2021-04-25 10:23:16 +00:00
|
|
|
const props = slot.props
|
2021-04-18 11:58:02 +00:00
|
|
|
if (!props) return
|
2019-08-10 18:48:05 +00:00
|
|
|
const active = this.activeIndex === index
|
2020-05-23 23:06:55 +00:00
|
|
|
const classes = [ active ? 'active' : 'hidden' ]
|
2021-04-18 11:58:02 +00:00
|
|
|
if (props.fullHeight) {
|
2020-05-23 23:06:55 +00:00
|
|
|
classes.push('full-height')
|
|
|
|
}
|
2020-05-28 18:26:33 +00:00
|
|
|
const renderSlot = (!this.renderOnlyFocused || active)
|
|
|
|
? slot
|
|
|
|
: ''
|
|
|
|
|
|
|
|
return (
|
2020-05-27 00:32:57 +00:00
|
|
|
<div class={classes}>
|
|
|
|
{
|
|
|
|
this.sideTabBar
|
2021-04-18 11:58:02 +00:00
|
|
|
? <h1 class="mobile-label">{props.label}</h1>
|
2020-05-27 00:32:57 +00:00
|
|
|
: ''
|
|
|
|
}
|
2020-05-28 18:26:33 +00:00
|
|
|
{renderSlot}
|
2020-05-27 00:32:57 +00:00
|
|
|
</div>
|
|
|
|
)
|
2019-01-17 19:25:50 +00:00
|
|
|
})
|
|
|
|
|
2018-08-27 19:22:25 +00:00
|
|
|
return (
|
2020-05-03 14:36:12 +00:00
|
|
|
<div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}>
|
2019-01-17 20:05:58 +00:00
|
|
|
<div class="tabs">
|
|
|
|
{tabs}
|
|
|
|
</div>
|
2021-04-18 12:39:06 +00:00
|
|
|
<div
|
|
|
|
ref="contents"
|
|
|
|
class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
|
|
|
|
v-body-scroll-lock={this.bodyScrollLock}
|
|
|
|
>
|
2019-01-17 20:05:58 +00:00
|
|
|
{contents}
|
|
|
|
</div>
|
2018-11-21 19:08:27 +00:00
|
|
|
</div>
|
|
|
|
)
|
2018-08-27 19:22:25 +00:00
|
|
|
}
|
2021-04-18 11:58:02 +00:00
|
|
|
}
|