@ishitatsuyuki/oruga-next
Version:
UI components for Vue.js and CSS framework agnostic
241 lines (236 loc) • 7.94 kB
JavaScript
'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;