Skip to content

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 configuration
  • filter (object): Filter configuration
  • sortOptions (array): Available sort options
  • filterOptions (array): Available filter options
  • loading (boolean): Loading state
  • disabled (boolean): Disable component
  • placement (string): Dropdown placement
  • width (number | string): Dropdown width
  • showApplyButton (boolean): Show apply button
  • showClearButton (boolean): Show clear button
  • clearButtonText (string): Clear button text
  • applyButtonText (string): Apply button text

Events

  • update:sort: Sort updated
  • update:filter: Filter updated
  • apply: Configuration applied
  • clear: Configuration cleared
  • change: Configuration changed
  • open: Dropdown opened
  • close: Dropdown closed

Slots

  • trigger: Custom trigger content
  • header: Custom header content
  • footer: Custom footer content
  • empty: Empty state content
  • loading: Loading state content
  • filter-item: Custom filter item render
  • sort-item: Custom sort item render

Usage Examples

  1. 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>
  1. 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>
  1. 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

  1. User Experience:

    • Clear feedback
    • Intuitive controls
    • Responsive design
    • Mobile optimization
  2. Performance:

    • Efficient updates
    • Debounced changes
    • Memory management
    • Optimized rendering
  3. Accessibility:

    • ARIA labels
    • Keyboard navigation
    • Focus management
    • Screen reader support
  4. Mobile:

    • Touch targets
    • Simplified interface
    • Clear feedback
    • Performance optimization

Common Use Cases

  1. 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>
  1. 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>
  1. 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