Tabs
The VTabs component is a tabs component that allows users to create a tabbed interface for their applications. It offers a range of customization options, including the ability to specify custom classes for active and inactive tabs, show scroll arrows, center the active tab, and render the tabs vertically. Additionally, it can be used as a header in a VCard component, and its style can be customized through the use of CSS variables.
Usage
Basic Usage
You can use the VTabs component in your template like so:
INFO
The VTabs component is registered globally when you install with @morpheme/ui. So you don't need to import it manually.
v-model
To specify the selected tab, you can use the v-model directive to bind the VTabs component to a data property:
<script setup lang="ts">
import {ref} from 'vue';
const selectedTab = ref(0);
</script>
<template>
<VTabs v-model="selectedTab" :items="items" />
</template><script setup lang="ts">
import {ref} from 'vue';
const selectedTab = ref(0);
</script>
<template>
<VTabs v-model="selectedTab" :items="items" />
</template>Custom Active Class
Define custom classes to apply when tab is active (active-class), inactive (inactive-class). Or add common custom class for tabs using default-class.
Show Arrow
Use showArrow to enable arrows to scroll the tab list. Only works if the list is scrollable.
Center Active
Use centerActive to allow the active tab to be position as at the center if possible. Works well when the list is scrollable.
Vertical
Use vertical to render the tabs vertically.
Tabs with Card
Use VTabs as header in a VCard component.
Custom Style
Override CSS variables for VTabs inline through style prop;
Removeable
Makes tab item removable. To automatically handle side effects, items props need to be bound as v-model, otherwise side effect can be applied manually by listening to either remove event or update:items.
Props
| Name | Type | Default |
|---|---|---|
modelValue | Number or String | 0 |
items | Array<TabItem[]> | [] |
itemText | String | 'text' |
color | 'primary' | 'secondary' | 'info' | 'warning' | 'success' | 'error' | 'primary' |
showArrows | Boolean | false |
centerActive | Boolean | false |
removeable | Boolean | false |
vertical | Boolean | false |
defaultWrapperClass | String | '' |
wrapperClass | String | '' |
hideSlider | Boolean | false |
activeClass | String | '' |
inactiveClass | String | '' |
defaultClass | String | '' |
contentClass | String | '' |
sliderClass | String | '' |
removeIcon | String | 'heroicons:trash-solid' |
removeIconSize | String | 'md' |
Events
update:modelValue
Event emitted when active tab changes. It provides new value as argument.
<script setup lang="ts">
import {ref} from 'vue';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onChange = (value: number) => console.log(value);
</script>
<template>
<VTabs v-model="selected" :items="items" @update:modelValue="onChange" />
</template><script setup lang="ts">
import {ref} from 'vue';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onChange = (value: number) => console.log(value);
</script>
<template>
<VTabs v-model="selected" :items="items" @update:modelValue="onChange" />
</template>update:items
Event emitted when list items change. It provides new items as argument.
<script setup lang="ts">
import {ref} from 'vue';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onChange = (items: any[]) => console.log(items);
</script>
<template>
<VTabs v-model="selected" :items="items" @update:items="onChange" />
</template><script setup lang="ts">
import {ref} from 'vue';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onChange = (items: any[]) => console.log(items);
</script>
<template>
<VTabs v-model="selected" :items="items" @update:items="onChange" />
</template>change
Event emitted when active tab changes. It provides new value as argument.
<script setup lang="ts">
import {ref} from 'vue';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onChange = (value: number) => console.log(value);
</script>
<template>
<VTabs v-model="selected" :items="items" @change="onChange" />
</template><script setup lang="ts">
import {ref} from 'vue';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onChange = (value: number) => console.log(value);
</script>
<template>
<VTabs v-model="selected" :items="items" @change="onChange" />
</template>remove
Event emitted when user click the remove tab buttton.
<script setup lang="ts">
import {ref} from 'vue';
import {useToast} from '@/composables';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onRemove = (index: number) => {
useToast()
.confirm('Remove this tab?')
.then(() => {
items.value.splice(index, 1);
});
};
</script>
<template>
<VTabs v-model="selected" :items="items" removeable @remove="onRemove" />
</template><script setup lang="ts">
import {ref} from 'vue';
import {useToast} from '@/composables';
const items = ref([
{
text: 'Item 1',
},
{
text: 'Item 2',
},
]);
const selected = ref(0);
const onRemove = (index: number) => {
useToast()
.confirm('Remove this tab?')
.then(() => {
items.value.splice(index, 1);
});
};
</script>
<template>
<VTabs v-model="selected" :items="items" removeable @remove="onRemove" />
</template>Slots
default
Define custom tab items rendering
Slot Props
| Prop | Value | Description |
|---|---|---|
onClick | function | Index of current tab |
registerRef | function | Current tab original value |
item
Define custom content for the tab.
Slot Props
| Prop | Value | Description |
|---|---|---|
index | number | Index of current tab |
item | any | Current tab original value |
value | any | Current tab label |
active | boolean | Indication whether current tab is active |
previous
Define custom content for the previous arrow. Works when showArrows is set to true.
Slot Props
| Prop | Value | Description |
|---|---|---|
onClick | function | Callback to handle click action |
next
Define custom content for the next arrow. Works when showArrows is set to true.
Slot Props
| Prop | Value | Description |
|---|---|---|
onClick | function | Callback to handle click action |
prepend
Add an item to the start of the tab list
append
Add an item to the end of the tab list
CSS Variables
:root {
--v-tabs-padding-x: theme('padding.4');
--v-tabs-padding-y: theme('padding.2');
--v-tabs-bg-color: theme('colors.transparent');
--v-tabs-border-color: theme('colors.transparent');
--v-tabs-border-radius: theme('borderRadius.DEFAULT');
/* item */
--v-tabs-item-padding-x: theme('padding.4');
--v-tabs-item-padding-y: theme('padding.2');
--v-tabs-item-font-size: theme('fontSize.base');
--v-tabs-item-font-weight: theme('fontWeight.normal');
--v-tabs-item-bg-color: theme('colors.transparent');
--v-tabs-item-text-color: theme('colors.gray.800');
--v-tabs-item-border-color: theme('colors.transparent');
--v-tabs-item-border-radius: theme('borderRadius.DEFAULT');
/* item active */
--v-tabs-item-active-padding-x: var(--v-tabs-item-padding-x);
--v-tabs-item-active-padding-y: var(--v-tabs-item-padding-y);
--v-tabs-item-active-font-size: var(--v-tabs-item-font-size);
--v-tabs-item-active-font-weight: theme('fontWeight.semibold');
--v-tabs-item-active-bg-color: var(--v-tabs-item-bg-color);
--v-tabs-item-active-text-color: var(--v-tabs-item-text-color);
--v-tabs-item-active-border-color: var(--v-tabs-item-border-color);
--v-tabs-item-active-border-radius: var(--v-tabs-item-border-radius);
/* item hover */
--v-tabs-item-hover-padding-x: var(--v-tabs-item-padding-x);
--v-tabs-item-hover-padding-y: var(--v-tabs-item-padding-y);
--v-tabs-item-hover-font-size: var(--v-tabs-item-font-size);
--v-tabs-item-hover-font-weight: var(--v-tabs-item-font-weight);
--v-tabs-item-hover-bg-color: var(--v-tabs-item-bg-color);
--v-tabs-item-hover-text-color: var(--v-tabs-item-text-color);
--v-tabs-item-hover-border-color: var(--v-tabs-item-border-color);
--v-tabs-item-hover-border-radius: var(--v-tabs-item-border-radius);
/* slider */
--v-tabs-slider-height: 3px;
--v-tabs-slider-width: 3rem;
--v-tabs-slider-max-width: theme('maxWidth.md');
--v-tabs-slider-bg-color: theme('colors.primary.500');
--v-tabs-slider-border-color: theme('colors.primary.500');
--v-tabs-slider-border-radius: theme('borderRadius.DEFAULT');
}:root {
--v-tabs-padding-x: theme('padding.4');
--v-tabs-padding-y: theme('padding.2');
--v-tabs-bg-color: theme('colors.transparent');
--v-tabs-border-color: theme('colors.transparent');
--v-tabs-border-radius: theme('borderRadius.DEFAULT');
/* item */
--v-tabs-item-padding-x: theme('padding.4');
--v-tabs-item-padding-y: theme('padding.2');
--v-tabs-item-font-size: theme('fontSize.base');
--v-tabs-item-font-weight: theme('fontWeight.normal');
--v-tabs-item-bg-color: theme('colors.transparent');
--v-tabs-item-text-color: theme('colors.gray.800');
--v-tabs-item-border-color: theme('colors.transparent');
--v-tabs-item-border-radius: theme('borderRadius.DEFAULT');
/* item active */
--v-tabs-item-active-padding-x: var(--v-tabs-item-padding-x);
--v-tabs-item-active-padding-y: var(--v-tabs-item-padding-y);
--v-tabs-item-active-font-size: var(--v-tabs-item-font-size);
--v-tabs-item-active-font-weight: theme('fontWeight.semibold');
--v-tabs-item-active-bg-color: var(--v-tabs-item-bg-color);
--v-tabs-item-active-text-color: var(--v-tabs-item-text-color);
--v-tabs-item-active-border-color: var(--v-tabs-item-border-color);
--v-tabs-item-active-border-radius: var(--v-tabs-item-border-radius);
/* item hover */
--v-tabs-item-hover-padding-x: var(--v-tabs-item-padding-x);
--v-tabs-item-hover-padding-y: var(--v-tabs-item-padding-y);
--v-tabs-item-hover-font-size: var(--v-tabs-item-font-size);
--v-tabs-item-hover-font-weight: var(--v-tabs-item-font-weight);
--v-tabs-item-hover-bg-color: var(--v-tabs-item-bg-color);
--v-tabs-item-hover-text-color: var(--v-tabs-item-text-color);
--v-tabs-item-hover-border-color: var(--v-tabs-item-border-color);
--v-tabs-item-hover-border-radius: var(--v-tabs-item-border-radius);
/* slider */
--v-tabs-slider-height: 3px;
--v-tabs-slider-width: 3rem;
--v-tabs-slider-max-width: theme('maxWidth.md');
--v-tabs-slider-bg-color: theme('colors.primary.500');
--v-tabs-slider-border-color: theme('colors.primary.500');
--v-tabs-slider-border-radius: theme('borderRadius.DEFAULT');
}Standalone Installation
You can also install the Tabs component individually via @morpheme/tabs package:
npm i @morpheme/tabsnpm i @morpheme/tabs<script setup lang="ts">
import VTabs from '@morpheme/tabs';
</script>
<template>
<VTabs :items="items" />
</template><script setup lang="ts">
import VTabs from '@morpheme/tabs';
</script>
<template>
<VTabs :items="items" />
</template>Storybook
View Storybook documentation here.
Morpheme