Skip to content

Instantly share code, notes, and snippets.

@katerlouis
Last active August 28, 2019 20:11
Show Gist options
  • Save katerlouis/e57774ace94d03a92608618fb4040f17 to your computer and use it in GitHub Desktop.
Save katerlouis/e57774ace94d03a92608618fb4040f17 to your computer and use it in GitHub Desktop.
vue tabs infinite loop problem
<template>
<div class="cfg-tabs">
<div class="cfg-tabs-links">
<button :ref="'link'+i" class="cfg-tabs-link" @click="selectTab($event, tab)" v-for="(tab, i) in tabs" :key="i">
{{ tab.title }}
</button>
<div class="cfg-tabs-active-indicator" :style="activeIndicatorStyles"></div>
</div>
<div class="cfg-tab-content">
<slot></slot>
</div>
</div>
</template>
<script>
/**
* 2Do:
* – select zero tab when no `selected` tag` is provided
*/
import { TweenMax } from "gsap"
import $ from "jquery"
export default {
name: 'Tabs',
components: {
Typography: require('./Typography').default,
},
data () {
return {
tabs: [],
activeIndicatorStyles: {
// 2do: set this data on created and get it from real css
left: "73px",
width: "50px",
}
}
},
created() {
// this.$children is not working, and even if it would
// the docs state you can't be sure $children will represent
// the correct dom order, so it's unreliable as the single source of truth
// this.tabs = this.$slots.default
this.tabs = this.$children
// set active index based on a prop
// in case the user defines a different active tab
//
// position activeindicator based on active tab
},
mounted() {
// some internal stuff in vue needs to happen
// before we can fully use `this.$refs` –
// the weird part is, that if you console.log(this.$refs)
// it is actually undefined, but chrome will fill it in
// as soon as it's available... that's why it seems like
// `this.$refs` is availalbe... when it actually is not
// so when you try to access a specific ref, javascript throws an error
this.$nextTick(() => {
// find first item where selected prop is true
const getActiveTab = (tab) => tab.selected === true
const activeTab = this.tabs.find(getActiveTab)
const activeTabIndex = this.tabs.findIndex(getActiveTab)
const activeTabLinkEl = $(this.$refs["link"+activeTabIndex])
const activeTabLinkPosition = $(activeTabLinkEl).position()
const activeTabLinkWidth = $(activeTabLinkEl).outerWidth()
// show active tab content
this.tabs.forEach(tab => {
tab.isActive = activeTab.title === tab.title
})
TweenMax.fromTo(this.activeIndicatorStyles, .3, {
left: activeTabLinkPosition.left + activeTabLinkWidth / 2,
width: 0
}, {
left: activeTabLinkPosition.left + "px",
width: activeTabLinkWidth + "px",
ease: Back.easeOut.config(1.1),
delay: 0,
})
})
},
methods: {
selectTab($event, selectedTab) {
this.tabs.forEach(tab => {
tab.isActive = selectedTab.title === tab.title
})
this.positionActiveIndicator($event.target)
// 2do:
// animate the active indicator
// animate height change and maybe transition between old and new tab.. which.. well.. damn we can't access the slots so easily:F
},
positionActiveIndicator(clickedElement) {
const position = $(clickedElement).position()
TweenMax.to(this.activeIndicatorStyles, .3, {
left: position.left + "px",
width: $(clickedElement).outerWidth() + "px",
// ease: Back.easeOut.config(1.3),
ease: Power3.easeOut,
})
}
},
}
</script>
<style lang="css" scoped>
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment