UISortFilter
UISortFilter
A versatile component that combines sorting and filtering functionality for data views, with support for multiple filter types and customizable sorting options.
<template> <UISortFilter v-model:sort="sortConfig" v-model:filter="filterConfig" :sortOptions="sortOptions" :filterOptions="filterOptions" @apply="handleApply" > <template #trigger> <UIButton type="default"> <FilterIcon class="w-5 h-5 mr-2" /> {{ activeFiltersCount ? `${activeFiltersCount} active` : 'Filter & Sort' }} </UIButton> </template> </UISortFilter></template>
<script setup lang="ts">interface SortOption { label: string value: string direction?: 'asc' | 'desc'}
interface FilterOption { id: string label: string type: 'select' | 'date' | 'boolean' | 'number' options?: { label: string; value: any }[]}
const sortConfig = ref({ field: 'created_at', direction: 'desc'})
const filterConfig = ref({})
const sortOptions: SortOption[] = [ { label: 'Date Created', value: 'created_at' }, { label: 'Name', value: 'name' }, { label: 'Status', value: 'status' }]
const filterOptions: FilterOption[] = [ { id: 'status', label: 'Status', type: 'select', options: [ { label: 'Active', value: 'active' }, { label: 'Inactive', value: 'inactive' } ] }, { id: 'created_at', label: 'Created Date', type: 'date' }]
const activeFiltersCount = computed(() => { return Object.keys(filterConfig.value).length})
const handleApply = (config: { sort: any; filter: any }) => { console.log('Applied:', config)}</script>Props
sort(object): Sort configurationfilter(object): Filter configurationsortOptions(array): Available sort optionsfilterOptions(array): Available filter optionsloading(boolean): Loading statedisabled(boolean): Disable componentplacement(string): Dropdown placementwidth(number | string): Dropdown widthshowApplyButton(boolean): Show apply buttonshowClearButton(boolean): Show clear buttonclearButtonText(string): Clear button textapplyButtonText(string): Apply button text
Events
update:sort: Sort updatedupdate:filter: Filter updatedapply: Configuration appliedclear: Configuration clearedchange: Configuration changedopen: Dropdown openedclose: Dropdown closed
Slots
trigger: Custom trigger contentheader: Custom header contentfooter: Custom footer contentempty: Empty state contentloading: Loading state contentfilter-item: Custom filter item rendersort-item: Custom sort item render
Usage Examples
- Basic Sort & Filter:
<template> <UISortFilter v-model:sort="sort" v-model:filter="filter" :sortOptions="sortOptions" :filterOptions="filterOptions" /></template>
<script setup>const sort = ref({ field: 'name', direction: 'asc' })const filter = ref({})const sortOptions = [ { label: 'Name', value: 'name' }, { label: 'Date', value: 'date' }]const filterOptions = [ { id: 'status', label: 'Status', type: 'select', options: [ { label: 'Active', value: true }, { label: 'Inactive', value: false } ] }]</script>- Data Table Filter:
<template> <div class="space-y-4"> <div class="flex items-center justify-between"> <h2 class="text-lg font-medium"> Users </h2>
<UISortFilter v-model:sort="tableSort" v-model:filter="tableFilters" :sortOptions="sortOptions" :filterOptions="filterOptions" :loading="loading" class="w-72" @apply="fetchData" > <template #trigger> <UIButton type="default" class="w-full justify-between" > <div class="flex items-center"> <FilterIcon class="w-5 h-5 mr-2" /> {{ activeFiltersCount ? `${activeFiltersCount} filters` : 'Filter & Sort' }} </div> <ChevronDownIcon class="w-5 h-5" /> </UIButton> </template>
<template #filter-item="{ option, value, onChange }"> <div class="space-y-2"> <label class="text-sm font-medium"> {{ option.label }} </label>
<component :is="getFilterComponent(option.type)" v-model="value" v-bind="getFilterProps(option)" @update:modelValue="onChange" /> </div> </template> </UISortFilter> </div>
<UITable :data="tableData" :columns="columns" :loading="loading" /> </div></template>
<script setup>const tableSort = ref({ field: 'created_at', direction: 'desc' })const tableFilters = ref({})const loading = ref(false)
const sortOptions = [ { label: 'Name', value: 'name' }, { label: 'Email', value: 'email' }, { label: 'Created Date', value: 'created_at' }, { label: 'Last Login', value: 'last_login' }]
const filterOptions = [ { id: 'role', label: 'Role', type: 'select', options: [ { label: 'Admin', value: 'admin' }, { label: 'User', value: 'user' } ] }, { id: 'status', label: 'Status', type: 'select', options: [ { label: 'Active', value: 'active' }, { label: 'Inactive', value: 'inactive' } ] }, { id: 'created_at', label: 'Created Date', type: 'date' }]
const getFilterComponent = (type: string) => { switch (type) { case 'select': return UISelect case 'date': return UIDatePicker default: return UIInput }}
const getFilterProps = (option: any) => { switch (option.type) { case 'select': return { options: option.options, placeholder: `Select ${option.label.toLowerCase()}` } case 'date': return { type: 'date', placeholder: 'Select date' } default: return {} }}
const fetchData = async () => { loading.value = true try { const response = await fetch('/api/users', { params: { sort: tableSort.value, filters: tableFilters.value } }) tableData.value = response.data } finally { loading.value = false }}</script>- Advanced Filtering:
<template> <UISortFilter v-model:sort="sort" v-model:filter="filters" :sortOptions="sortOptions" :filterOptions="filterOptions" :width="400" > <template #header> <div class="p-4 border-b"> <h3 class="text-lg font-medium"> Filter & Sort </h3> <p class="text-sm text-gray-500"> Configure display options </p> </div> </template>
<template #filter-item="{ option, value, onChange }"> <div class="space-y-2 p-4 border-b"> <div class="flex items-center justify-between"> <label class="text-sm font-medium"> {{ option.label }} </label>
<UITooltip v-if="option.description" :content="option.description" > <InfoIcon class="w-4 h-4 text-gray-400" /> </UITooltip> </div>
<component :is="getFilterComponent(option)" v-model="value" v-bind="getFilterProps(option)" @update:modelValue="onChange" /> </div> </template>
<template #footer> <div class="p-4 bg-gray-50 flex justify-end space-x-2"> <UIButton variant="outline" @click="resetFilters" > Reset </UIButton>
<UIButton type="primary" :loading="applying" @click="applyFilters" > Apply Filters </UIButton> </div> </template> </UISortFilter></template>Best Practices
-
User Experience:
- Clear feedback
- Intuitive controls
- Responsive design
- Mobile optimization
-
Performance:
- Efficient updates
- Debounced changes
- Memory management
- Optimized rendering
-
Accessibility:
- ARIA labels
- Keyboard navigation
- Focus management
- Screen reader support
-
Mobile:
- Touch targets
- Simplified interface
- Clear feedback
- Performance optimization
Common Use Cases
- List View Controls:
<template> <div class="flex items-center space-x-4"> <UISearch v-model="search" placeholder="Search..." class="w-64" />
<UISortFilter v-model:sort="sort" v-model:filter="filters" :sortOptions="sortOptions" :filterOptions="filterOptions" class="flex-1" />
<UIButton type="primary" :loading="loading" @click="applyChanges" > Apply </UIButton> </div></template>- Analytics Filters:
<template> <UISortFilter v-model:sort="sort" v-model:filter="filters" :sortOptions="metricSortOptions" :filterOptions="metricFilterOptions" class="w-full" > <template #filter-item="{ option }"> <div class="space-y-2"> <label class="text-sm font-medium"> {{ option.label }} </label>
<component :is="option.type === 'date' ? UIDateRangePicker : UISelect" v-model="filters[option.id]" v-bind="option.props" /> </div> </template> </UISortFilter></template>- Grid View Controls:
<template> <div class="flex items-center justify-between"> <UISortFilter v-model:sort="sort" v-model:filter="filters" :sortOptions="sortOptions" :filterOptions="filterOptions" />
<div class="flex items-center space-x-2"> <UISegmentedControl v-model="viewType" :options="viewOptions" />
<UISelect v-model="perPage" :options="perPageOptions" size="sm" /> </div> </div></template>Component Composition
The UISortFilter component works well with:
- UIButton for triggers
- UISelect for options
- UIDatePicker for dates
- UIInput for text/numbers
- UICheckbox for boolean filters
- UITooltip for help text
- UIPopover for dropdowns
- UIBadge for counts