UITabs
UITabs
A versatile tabs component that provides an interface for organizing and switching between different content sections, with support for custom styling, icons, and dynamic content.
<template> <UITabs v-model="activeTab" :tabs="tabs" :variant="'default'" @change="handleTabChange" > <template #tab="{ tab }"> <div class="flex items-center space-x-2"> <component v-if="tab.icon" :is="tab.icon" class="w-5 h-5" /> <span>{{ tab.label }}</span> <UIBadge v-if="tab.count" size="sm" variant="gray" > {{ tab.count }} </UIBadge> </div> </template>
<template #panel="{ tab }"> <div class="p-4"> {{ tab.content }} </div> </template> </UITabs></template>
<script setup lang="ts">interface Tab { id: string label: string icon?: any count?: number content?: string}
const tabs = ref<Tab[]>([ { id: 'details', label: 'Details', content: 'Details content' }, { id: 'settings', label: 'Settings', content: 'Settings content' }])
const activeTab = ref('details')
const handleTabChange = (tabId: string) => { console.log('Tab changed:', tabId)}</script>Props
modelValue(string): Active tab IDtabs(array): Tab definitionsvariant(string): Visual variantvertical(boolean): Vertical layoutstretch(boolean): Full width tabsdisabled(boolean): Disable all tabssize(string): Tab sizeanimated(boolean): Content animationlazy(boolean): Lazy load panels
Events
update:modelValue: Active tab changedchange: Tab selection changedclick: Tab clickedfocus: Tab focusedblur: Tab blurred
Slots
tab: Custom tab contentpanel: Custom panel contentprefix: Content before tabssuffix: Content after tabs
Usage Examples
- Basic Tabs:
<template> <UITabs v-model="activeTab" :tabs="tabs" /></template>
<script setup>const activeTab = ref('overview')const tabs = [ { id: 'overview', label: 'Overview', content: 'Overview content' }, { id: 'details', label: 'Details', content: 'Details content' }]</script>- Custom Tab Content:
<template> <UITabs v-model="activeView" :tabs="viewTabs" class="w-full" > <template #tab="{ tab }"> <div class="flex items-center space-x-3 px-4 py-2"> <component :is="tab.icon" :class="[ 'w-5 h-5', activeView === tab.id ? 'text-primary-600' : 'text-gray-400' ]" />
<div> <div class="font-medium"> {{ tab.label }} </div> <div class="text-sm text-gray-500"> {{ tab.description }} </div> </div>
<UIBadge v-if="tab.count" :variant="getBadgeVariant(tab)" > {{ tab.count }} </UIBadge> </div> </template>
<template #panel="{ tab }"> <component :is="tab.component" v-bind="tab.props" /> </template> </UITabs></template>
<script setup>import { HomeIcon, UsersIcon, CogIcon} from '@gohighlevel/ghl-icons/24/outline'
const activeView = ref('dashboard')const viewTabs = [ { id: 'dashboard', label: 'Dashboard', description: 'Overview and stats', icon: HomeIcon, component: DashboardView }, { id: 'users', label: 'Users', description: 'Manage users', icon: UsersIcon, count: 12, component: UsersView }, { id: 'settings', label: 'Settings', description: 'System settings', icon: CogIcon, component: SettingsView }]
const getBadgeVariant = (tab) => { switch (tab.id) { case 'users': return 'primary' default: return 'gray' }}</script>- Vertical Tabs:
<template> <div class="flex h-[600px]"> <UITabs v-model="activeSection" :tabs="sections" :vertical="true" class="w-64 border-r" > <template #tab="{ tab }"> <div class="flex items-center space-x-3 p-3"> <tab.icon class="w-5 h-5" /> <span>{{ tab.label }}</span> </div> </template>
<template #panel="{ tab }"> <div class="p-6"> <h2 class="text-lg font-medium mb-4"> {{ tab.label }} </h2> <component :is="tab.component" /> </div> </template> </UITabs> </div></template>Best Practices
-
User Experience:
- Clear labels
- Visual feedback
- Smooth transitions
- Consistent layout
-
Accessibility:
- ARIA roles
- Keyboard navigation
- Focus management
- Screen reader support
-
Performance:
- Lazy loading
- Content caching
- Efficient updates
- Memory management
-
Implementation:
- State persistence
- Error handling
- Loading states
- Dynamic content
Common Use Cases
- Settings Panel:
<template> <UITabs v-model="activeSettings" :tabs="settingsTabs" > <template #panel="{ tab }"> <div class="space-y-6 p-6"> <h3 class="text-lg font-medium"> {{ tab.label }} </h3>
<component :is="tab.component" v-model="settings[tab.id]" @change="handleSettingChange" />
<div class="flex justify-end"> <UIButton type="primary" :loading="saving" @click="saveSettings" > Save Changes </UIButton> </div> </div> </template> </UITabs></template>
<script setup>const settingsTabs = [ { id: 'general', label: 'General', component: GeneralSettings }, { id: 'notifications', label: 'Notifications', component: NotificationSettings }, { id: 'security', label: 'Security', component: SecuritySettings }]</script>- Product Details:
<template> <div class="product-details"> <UITabs v-model="activeTab" :tabs="productTabs" class="w-full" > <template #tab="{ tab }"> <div class="flex items-center justify-center px-6 py-3"> <span>{{ tab.label }}</span> <UIBadge v-if="tab.count" size="sm" variant="gray" class="ml-2" > {{ tab.count }} </UIBadge> </div> </template>
<template #panel="{ tab }"> <div class="p-6"> <Suspense> <component :is="tab.component" :product-id="productId" />
<template #fallback> <UISkeleton :rows="5" /> </template> </Suspense> </div> </template> </UITabs> </div></template>- Form Wizard:
<template> <div class="form-wizard"> <UITabs v-model="currentStep" :tabs="steps" :disabled="true" > <template #tab="{ tab, index }"> <div class="flex items-center" :class="{ 'text-primary-600': isStepComplete(index) }" > <div class="w-8 h-8 rounded-full flex items-center justify-center" :class="getStepClass(index)" > <CheckIcon v-if="isStepComplete(index)" class="w-5 h-5" /> <span v-else> {{ index + 1 }} </span> </div>
<span class="ml-2">{{ tab.label }}</span> </div> </template>
<template #panel="{ tab }"> <div class="p-6"> <component :is="tab.component" v-model="formData[tab.id]" @valid="setStepValidity" @complete="nextStep" />
<div class="flex justify-between mt-6"> <UIButton v-if="currentStep !== 'step1'" @click="previousStep" > Back </UIButton>
<UIButton v-if="currentStep !== lastStep" type="primary" :disabled="!isStepValid" @click="nextStep" > Continue </UIButton>
<UIButton v-else type="primary" :disabled="!isFormValid" @click="submitForm" > Submit </UIButton> </div> </div> </template> </UITabs> </div></template>