import { defineComponent } from 'vue' import Icon from '../components/icon/Icon.vue' import SlotComponent from './SlotComponent' import { default as ProviderParentMixin, Sorted } from './ProviderParentMixin' import { mod } from './helpers' export default (cmp: string) => defineComponent({ mixins: [ProviderParentMixin(cmp, Sorted)], components: { [Icon.name]: Icon, [SlotComponent.name]: SlotComponent }, emits: ['update:modelValue'], props: { /** @model */ modelValue: [String, Number], /** * Color of the control, optional * @values primary, info, success, warning, danger, and any other custom color */ variant: [String, Object], /** * Tab size, optional * @values small, medium, large */ size: String, animated: { type: Boolean, default: true }, /** Show tab in vertical layout */ vertical: { type: Boolean, default: false }, /** * Position of the tab, optional * @values centered, right */ position: String, /** Destroy tab on hide */ destroyOnHide: { type: Boolean, default: false } }, data() { return { activeId: this.modelValue, contentHeight: 0, isTransitioning: false } }, computed: { activeItem(): any { return this.activeId !== undefined && this.activeId !== null ? this.childItems.filter((i: any) => i.newValue === this.activeId)[0] : this.items[0] }, activeIndex(): number { return this.childItems.findIndex((item => item.newValue === this.activeId)) }, items(): any[] { return this.sortedItems } }, watch: { /** * When v-model is changed set the new active tab. */ modelValue(value) { if (this.activeId !== value) { this.performAction(value) } } }, methods: { /** * Child click listener, emit input event and change active child. */ childClick(child: any) { if (this.activeId !== child.newValue) { this.performAction(child.newValue) this.$emit('update:modelValue', this.activeId) } }, /** * Select the first 'viable' child, starting at startingIndex and in the direction specified * by the boolean parameter forward. In other words, first try to select the child at index * startingIndex, and if it is not visible or it is disabled, then go to the index in the * specified direction until either returning to startIndex or finding a viable child item. */ clickFirstViableChild(startingIndex: number, forward: Boolean) { let direction = forward ? 1 : -1; let newIndex = startingIndex for(; newIndex !== this.activeIndex; newIndex = mod((newIndex + direction), this.childItems.length)) { // Break if the item at this index is viable (not disabled and is visible) if(this.childItems[newIndex].visible && !this.childItems[newIndex].disabled) { break } } this.childClick(this.childItems[newIndex]) }, /** * Go to the next item or wrap around */ next() { let newIndex = mod((this.activeIndex + 1), this.childItems.length) this.clickFirstViableChild(newIndex, true) }, /** * Go to the previous item or wrap around */ prev() { let newIndex = mod(this.activeIndex - 1, this.childItems.length ) this.clickFirstViableChild(newIndex, false) }, /** * Go to the first viable item */ homePressed() { if (this.childItems.length < 1) { return } this.clickFirstViableChild(0, true) }, /** * Go to the last viable item */ endPressed() { if (this.childItems.length < 1) { return } this.clickFirstViableChild(this.childItems.length - 1, false) }, /** * Activate next child and deactivate prev child */ performAction(newId: number) { const oldValue = this.activeId const oldTab = oldValue !== undefined && oldValue !== null ? this.childItems.filter((i: any) => i.newValue === oldValue)[0] : this.items[0] this.activeId = newId if (oldTab && this.activeItem) { oldTab.deactivate(this.activeItem.index) this.activeItem.activate(oldTab.index) } } } })