UNPKG

@ishitatsuyuki/oruga-next

Version:

UI components for Vue.js and CSS framework agnostic

241 lines (236 loc) 7.94 kB
'use strict'; var vue = require('vue'); var helpers = require('./helpers.js'); var Icon = require('./Icon-172f9998.js'); var InjectedChildMixin = require('./InjectedChildMixin-5f524fc3.js'); var SlotComponent = require('./SlotComponent-b10f11e8.js'); var TabbedMixin = (cmp) => vue.defineComponent({ mixins: [InjectedChildMixin.ProviderParentMixin(cmp, InjectedChildMixin.Sorted)], components: { [Icon.script.name]: Icon.script, [SlotComponent.SlotComponent.name]: SlotComponent.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() { return this.activeId !== undefined && this.activeId !== null ? this.childItems.filter((i) => i.newValue === this.activeId)[0] : this.items[0]; }, activeIndex() { return this.childItems.findIndex((item => item.newValue === this.activeId)); }, items() { 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) { 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, forward) { let direction = forward ? 1 : -1; let newIndex = startingIndex; for (; newIndex !== this.activeIndex; newIndex = helpers.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 = helpers.mod((this.activeIndex + 1), this.childItems.length); this.clickFirstViableChild(newIndex, true); }, /** * Go to the previous item or wrap around */ prev() { let newIndex = helpers.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) { const oldValue = this.activeId; const oldTab = oldValue !== undefined && oldValue !== null ? this.childItems.filter((i) => i.newValue === oldValue)[0] : this.items[0]; this.activeId = newId; if (oldTab && this.activeItem) { oldTab.deactivate(this.activeItem.index); this.activeItem.activate(oldTab.index); } } } }); var TabbedChildMixin = (parentCmp) => vue.defineComponent({ mixins: [InjectedChildMixin.InjectedChildMixin(parentCmp, InjectedChildMixin.Sorted$1)], props: { /** * Item value (it will be used as v-model of wrapper component) */ value: [String, Number], /** * Item label */ label: String, /** * Icon on the left */ icon: String, /** * Icon pack */ iconPack: String, /** * Show/hide item */ visible: { type: Boolean, default: true }, /** * Header class of the item */ headerClass: [String, Array, Object] }, data() { return { transitionName: undefined, newValue: this.value }; }, computed: { isActive() { return this.parent.activeItem === this; }, elementClasses() { return []; } }, methods: { /** * Activate element, alter animation name based on the index. */ activate(oldIndex) { this.transitionName = this.index < oldIndex ? this.parent.vertical ? 'slide-down' : 'slide-next' : this.parent.vertical ? 'slide-up' : 'slide-prev'; }, /** * Deactivate element, alter animation name based on the index. */ deactivate(newIndex) { this.transitionName = newIndex < this.index ? this.parent.vertical ? 'slide-down' : 'slide-next' : this.parent.vertical ? 'slide-up' : 'slide-prev'; } }, render() { // if destroy apply v-if if (this.parent.destroyOnHide) { if (!this.isActive || !this.visible) return; } const content = this.$slots.default ? this.$slots.default() : []; const vnode = vue.withDirectives(vue.h('div', { class: this.elementClasses, 'data-id': `${parentCmp}-${this.newValue}` }, content), [[vue.vShow, this.isActive && this.visible]]); // check animated prop if (this.parent.animated) { return vue.h(vue.Transition, { 'name': this.transitionName, 'onBeforeEnter': () => { this.parent.isTransitioning = true; }, 'onAfterEnter': () => { this.parent.isTransitioning = false; } }, () => ([vnode])); } return vnode; } }); exports.TabbedChildMixin = TabbedChildMixin; exports.TabbedMixin = TabbedMixin;