UISegmentedControl
UISegmentedControl
A versatile segmented control component that allows users to select between multiple mutually exclusive options, with support for icons, badges, and custom styling.
<template> <UISegmentedControl v-model="selectedView" :options="viewOptions" :size="'md'" class="w-full max-w-md" > <template #option="{ option }"> <div class="flex items-center space-x-2"> <component :is="option.icon" class="w-5 h-5" /> <span>{{ option.label }}</span> <UIBadge v-if="option.count" size="sm" variant="gray" > {{ option.count }} </UIBadge> </div> </template> </UISegmentedControl></template>
<script setup lang="ts">import { ListIcon, GridIcon, TableIcon} from '@gohighlevel/ghl-icons/24/outline'
interface ViewOption { value: string label: string icon: any count?: number}
const viewOptions = ref<ViewOption[]>([ { value: 'list', label: 'List', icon: ListIcon, count: 12 }, { value: 'grid', label: 'Grid', icon: GridIcon, count: 12 }, { value: 'table', label: 'Table', icon: TableIcon, count: 12 }])
const selectedView = ref('list')</script>Props
modelValue(string | number): Selected option valueoptions(array): Array of option objectssize(string): Component size (‘sm’ | ‘md’ | ‘lg’)disabled(boolean): Disable controlfullWidth(boolean): Fill container widthname(string): Input name for formsrequired(boolean): Mark as requiredvariant(string): Visual variantrounded(boolean): Use rounded corners
Events
update:modelValue: Selection changechange: Value changedfocus: Control focusedblur: Control blurred
Slots
option: Custom option templateprefix: Content before optionssuffix: Content after optionsbadge: Custom badge content
Usage Examples
- Basic Usage:
<template> <UISegmentedControl v-model="selectedTab" :options="[ { value: 'all', label: 'All' }, { value: 'active', label: 'Active' }, { value: 'archived', label: 'Archived' } ]" /></template>
<script setup>const selectedTab = ref('all')</script>- With Icons and Badges:
<template> <UISegmentedControl v-model="viewMode" :options="viewModes" size="lg" class="mb-4" > <template #option="{ option }"> <div class="flex items-center gap-2"> <component :is="option.icon" :class="[ 'w-5 h-5', option.value === viewMode ? 'text-primary-600' : 'text-gray-500' ]" />
<span>{{ option.label }}</span>
<UIBadge v-if="option.count" :variant="getBadgeVariant(option)" size="sm" > {{ option.count }} </UIBadge> </div> </template> </UISegmentedControl>
<component :is="currentViewComponent" v-bind="viewProps" /></template>
<script setup>import { InboxIcon, ArchiveIcon, TrashIcon} from '@gohighlevel/ghl-icons/24/outline'
const viewModes = [ { value: 'inbox', label: 'Inbox', icon: InboxIcon, count: 12 }, { value: 'archived', label: 'Archived', icon: ArchiveIcon, count: 5 }, { value: 'trash', label: 'Trash', icon: TrashIcon, count: 2 }]
const viewMode = ref('inbox')
const getBadgeVariant = (option) => { switch (option.value) { case 'inbox': return 'primary' case 'archived': return 'success' case 'trash': return 'error' default: return 'gray' }}</script>- Form Integration:
<template> <form @submit.prevent="handleSubmit"> <div class="space-y-4"> <label class="block"> <span class="text-sm font-medium"> Notification Preferences </span>
<UISegmentedControl v-model="formData.notifications" :options="notificationOptions" name="notifications" required class="mt-1" /> </label>
<label class="block"> <span class="text-sm font-medium"> Theme </span>
<UISegmentedControl v-model="formData.theme" :options="themeOptions" name="theme" size="sm" class="mt-1" /> </label>
<UIButton type="submit"> Save Preferences </UIButton> </div> </form></template>
<script setup>const formData = reactive({ notifications: 'all', theme: 'light'})
const notificationOptions = [ { value: 'all', label: 'All' }, { value: 'important', label: 'Important Only' }, { value: 'none', label: 'None' }]
const themeOptions = [ { value: 'light', label: 'Light' }, { value: 'dark', label: 'Dark' }, { value: 'system', label: 'System' }]</script>Best Practices
-
Visual Design:
- Clear active state
- Consistent spacing
- Appropriate sizing
- Visual hierarchy
-
User Experience:
- Immediate feedback
- Touch targets
- Clear labels
- Logical grouping
-
Accessibility:
- Keyboard navigation
- ARIA labels
- Focus states
- Role attributes
-
Responsiveness:
- Mobile friendly
- Flexible width
- Touch support
- Overflow handling
Common Use Cases
- View Switcher:
<template> <div class="space-y-4"> <UISegmentedControl v-model="displayMode" :options="displayOptions" class="w-full sm:w-auto" />
<div :class="getLayoutClass"> <ItemCard v-for="item in items" :key="item.id" :item="item" :display-mode="displayMode" /> </div> </div></template>
<script setup>const displayMode = ref('grid')const displayOptions = [ { value: 'grid', label: 'Grid View' }, { value: 'list', label: 'List View' }]
const getLayoutClass = computed(() => ({ 'grid grid-cols-3 gap-4': displayMode.value === 'grid', 'space-y-4': displayMode.value === 'list'}))</script>- Filter Controls:
<template> <div class="filter-controls space-y-4"> <UISegmentedControl v-model="filters.status" :options="statusOptions" @change="applyFilters" />
<UISegmentedControl v-model="filters.timeRange" :options="timeRangeOptions" size="sm" @change="applyFilters" />
<DataTable :data="filteredData" :loading="loading" /> </div></template>- Wizard Steps:
<template> <div class="wizard"> <UISegmentedControl v-model="currentStep" :options="steps" :disabled="true" class="mb-8" > <template #option="{ option }"> <div class="flex items-center space-x-2"> <div class="w-6 h-6 rounded-full flex items-center justify-center" :class="getStepClass(option)" > <span v-if="isStepComplete(option)"> <CheckIcon class="w-4 h-4" /> </span> <span v-else> {{ option.step }} </span> </div> <span>{{ option.label }}</span> </div> </template> </UISegmentedControl>
<component :is="currentStepComponent" v-bind="stepProps" @complete="nextStep" @back="previousStep" /> </div></template>