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/tabs
npm 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.