Filter Component
The Filter
component is a versatile and powerful filtering solution designed for complex data filtering scenarios. It supports both simple search functionality and advanced filtering options with category-based filtering, making it ideal for applications that require robust data exploration capabilities.
Features
- Multi-select Filtering: Select multiple options from dropdown menus with checkbox controls.
- Advanced Filter Menu: Create complex filter combinations with category-based filtering through a nested menu system.
- Infinite Scrolling: Built-in support for loading large datasets incrementally for better performance.
- Search Functionality: Search capabilities for both main options and advanced filter options.
- External API Integration: Seamless integration with external search and filter APIs.
- Avatar Support: Display user avatars or other visual identifiers alongside filter options.
- Customizable UI: Extensive slot system for customizing appearance and behavior.
- Accessibility: Keyboard navigation and ARIA support for better accessibility.
- Error Handling: Built-in error state and helper text support for validation scenarios.
- Responsive Design: Adapts to different screen sizes and container widths.
Basic Usage
<template>
<spr-filter v-model="selectedOptions" v-model:search="searchValue" :options="options" label="Search" hasAvatar />
</template>
<script setup>
import { ref } from 'vue';
const options = ref([
{ column: '', isSelected: false, text: 'sample 1', value: 'sample1' },
{ column: '', isSelected: false, text: 'sample 2', value: 'sample2' },
{ column: '', isSelected: false, text: 'sample 3', value: 'sample3' },
{ column: '', isSelected: false, text: 'sample 4', value: 'sample4' },
{ column: '', isSelected: false, text: 'sample 5', value: 'sample5' },
]);
const selectedOptions = ref([]);
const searchValue = ref('');
</script>
Filterable
<template>
<spr-filter
v-model="selectedOptions1"
v-model:search="searchValue1"
:options="options1"
label="Search"
:filterMenu="filterMenuList1"
:filterData="filterMenuOptions1"
filterable
/>
</template>
<script setup>
const options = ref([
{ column: '', isSelected: false, text: 'sample 1', value: 'sample1' },
{ column: '', isSelected: false, text: 'sample 2', value: 'sample2' },
{ column: '', isSelected: false, text: 'sample 3', value: 'sample3' },
{ column: '', isSelected: false, text: 'sample 4', value: 'sample4' },
{ column: '', isSelected: false, text: 'sample 5', value: 'sample5' },
]);
const filterMenuList = ref([
{ count: 0, isFilterVisible: false, columnName: 'Employee Type', field: 'employeeType' },
{ count: 0, isFilterVisible: false, columnName: 'Department', field: 'department' },
{ count: 0, isFilterVisible: false, columnName: 'Location', field: 'location' },
{ count: 0, isFilterVisible: false, columnName: 'Region', field: 'region' },
{ count: 0, isFilterVisible: false, columnName: 'Job Level', field: 'jobLevel' },
]);
const filterMenuOptions = [
{ column: 'location', isSelected: false, text: 'sample 1', value: 'sample1' },
{ column: 'location', isSelected: false, text: 'sample 2', value: 'sample2' },
{ column: 'location', isSelected: false, text: 'sample 3', value: 'sample3' },
{ column: 'location', isSelected: false, text: 'sample 4', value: 'sample4' },
];
const selectedOptions = ref([]);
const searchValue = ref('');
</script>
Deselect
This example demonstrates how to remove selected options from outside the component.
<template>
<div class="spr-space-y-3">
<div class="spr-flex spr-gap-2">
<div v-for="selected in selectedOptions2">
<spr-button hasIcon size="small" tone="danger" variant="secondary" @click="removeSelected(selected.value)">
{{ selected.value }}
<Icon icon="ph:trash" />
</spr-button>
</div>
</div>
<spr-filter
v-model="selectedOptions2"
v-model:search="searchValue2"
id="search-filter"
:deselected="deselected"
:options="options"
label="Search"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { Icon } from '@iconify/vue';
import SprButton from '@/components/button/button.vue';
const options = ref([
{ column: '', isSelected: false, text: 'sample 1', value: 'sample1' },
{ column: '', isSelected: false, text: 'sample 2', value: 'sample2' },
{ column: '', isSelected: false, text: 'sample 3', value: 'sample3' },
{ column: '', isSelected: false, text: 'sample 4', value: 'sample4' },
{ column: '', isSelected: false, text: 'sample 5', value: 'sample5' },
]);
const selectedOptions2 = ref([]);
const searchValue2 = ref('');
const deselected = ref('');
const removeSelected = (removeSelected) => {
deselected.value = removeSelected;
};
</script>
Error State
<template>
<div class="spr-space-y-3">
<spr-filter
v-model="selectedOptions2"
v-model:search="searchValue2"
id="search-filter-display-text"
:deselected="deselected"
:options="options"
helper-text="This is helper text!!"
/>
<spr-filter
v-model="selectedOptions2"
v-model:search="searchValue2"
id="search-filter-error"
:deselected="deselected"
:options="options"
error
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { Icon } from '@iconify/vue';
import SprButton from '@/components/button/button.vue';
const options = ref([
{ column: '', isSelected: false, text: 'sample 1', value: 'sample1' },
{ column: '', isSelected: false, text: 'sample 2', value: 'sample2' },
{ column: '', isSelected: false, text: 'sample 3', value: 'sample3' },
{ column: '', isSelected: false, text: 'sample 4', value: 'sample4' },
{ column: '', isSelected: false, text: 'sample 5', value: 'sample5' },
]);
const selectedOptions2 = ref([]);
const searchValue2 = ref('');
</script>
API Reference
Props
Name | Description | Type | Default | Required |
---|---|---|---|---|
modelValue | The selected filter values. Supports v-model binding for selection state management. | Array | String | [] | No |
options | The list of filter options. Each option should have the structure: { column: string, isSelected: boolean, text: string, value: string, subtext?: string, avatar?: string } | Array | [] | Yes |
label | Label for the filter input field. | String | '' | No |
placeholder | Placeholder text for the filter input field. | String | '' | No |
disabled | Disables the filter input, preventing user interaction. | Boolean | false | No |
filterable | Enables the advanced filter menu with column-based filtering. | Boolean | false | No |
id | Unique identifier for the filter component. Used for accessibility and DOM manipulation. | String | 'spr-filter' | No |
filterMenu | List of advanced filter menu categories. Each item should have: { columnName: string, field: string, isFilterVisible?: boolean, count?: number } | Array | [] | No |
filterData | Data for the advanced filter menu. Similar structure to options but specifically for the advanced filter. | Array | [] | No |
loading | Indicates if the advanced filter menu is in a loading state. | Boolean | false | No |
filling | Indicates if the main filter dropdown is in a loading state. | Boolean | false | No |
search | Search query for the main filter. Supports v-model:search binding. | String | '' | No |
searchFilter | Search query for the advanced filter menu. Supports v-model:searchFilter binding. | String | '' | No |
width | Width of the filter component. Accepts CSS width values (px, %, rem, etc.). | String | '100%' | No |
deselected | Value of the deselected filter option. Used to remove selections from outside the component. | String | '' | No |
hasSearchApi | Enables external search API integration for the main filter. When true, local filtering is disabled. | Boolean | false | No |
hasAvatar | Enables avatar display for filter options. Use the avatar property in options to provide image URLs. | Boolean | false | No |
helperText | Helper text displayed below the filter component. Useful for providing additional context or instructions. | String | '' | No |
error | Enables error state styling for the filter. Use with validation patterns. | Boolean | false | No |
hasAdvancedFilterApi | Enables external search API integration for the advanced filter menu. When true, local filtering is disabled. | Boolean | false | No |
Events
Name | Payload Type | Description | Usage |
---|---|---|---|
getFilterData | String | Triggered when fetching filter data for a specific column in the advanced filter. The payload is the column field name. | @getFilterData="handleGetFilterData" |
update:modelValue | Array | Updates the selected filter values. Used internally for v-model binding. | Handled by v-model binding |
update:search | String | Updates the search query for the main filter. Used internally for v-model:search binding. | Handled by v-model:search binding |
update:searchFilter | String | Updates the search query for the advanced filter menu. Used internally for v-model:searchFilter binding. | Handled by v-model:searchFilter binding |
selectedFilter | Array | Emits the selected filter options from the advanced filter menu. Contains all selected options across all columns. | @selectedFilter="handleSelectedFilter" |
infiniteScrollTrigger | Boolean | Triggered when infinite scrolling is activated for the main filter. Used for loading more data in paginated scenarios. | @infiniteScrollTrigger="loadMoreOptions" |
infiniteScrollFilterTrigger | String | Triggered when infinite scrolling is activated for the advanced filter. The payload is the current column being scrolled. | @infiniteScrollFilterTrigger="loadMoreFilterOptions" |
Slots
Slot Name | Description | Usage |
---|---|---|
default | Slot for customizing the filter input field. Replaces the default input component entirely. |
|
loading | Slot for displaying a custom loading state in the advanced filter menu. Used when loading filter options. |
|
empty | Slot for displaying a custom empty state in the advanced filter menu. Used when no filter options are found. |
|
loading-state | Slot for displaying a custom loading state in the main filter dropdown. Used when loading main options. |
|
empty-state | Slot for displaying a custom empty state in the main filter dropdown. Used when no options match the search. |
|
Advanced Features
Infinite Scrolling
The component supports infinite scrolling for both the main filter and the advanced filter menu. This feature is particularly useful for handling large datasets that should be loaded incrementally to improve performance.
Implementation
- Main Filter Infinite Scroll:
- Listen for the
infiniteScrollTrigger
event which is emitted when the user scrolls to the bottom of the main filter dropdown. - Load additional data and append it to the
options
array.
- Listen for the
<spr-filter v-model="selectedOptions" :options="options" @infiniteScrollTrigger="loadMoreOptions" />
<script setup>
const loadMoreOptions = () => {
// Load more data from your API
const newOptions = await fetchMoreOptions(page.value++);
options.value = [...options.value, ...newOptions];
};
</script>
- Advanced Filter Infinite Scroll:
- Listen for the
infiniteScrollFilterTrigger
event which provides the column field being scrolled. - Load additional data for that specific column.
- Listen for the
<spr-filter
v-model="selectedOptions"
:options="options"
:filter-menu="filterMenu"
:filter-data="filterData"
filterable
@infiniteScrollFilterTrigger="loadMoreFilterOptions"
/>
<script setup>
const loadMoreFilterOptions = (column) => {
// Load more filter options for the specific column
const newFilterOptions = await fetchMoreFilterOptions(column, page.value++);
filterData.value = [...filterData.value, ...newFilterOptions];
};
</script>
External Search API Integration
The Filter component supports integration with external search APIs for both the main filter and advanced filter menu. This allows for server-side filtering and searching.
Main Filter Search API
Enable the hasSearchApi
prop to use an external API for filtering the main options. When enabled, the component doesn't filter options locally but relies on the external API to provide filtered results.
<spr-filter
v-model="selectedOptions"
v-model:search="searchValue"
:options="options"
:has-search-api="true"
@update:search="handleSearchChange"
/>
<script setup>
const handleSearchChange = async (query) => {
// Call your external API with the search query
options.value = await searchApi(query);
};
</script>
Advanced Filter Search API
Enable the hasAdvancedFilterApi
prop to use an external API for the advanced filter menu search. This works similarly to the main search API but for the advanced filter menu.
<spr-filter
v-model="selectedOptions"
:filter-menu="filterMenu"
:filter-data="filterData"
:has-advanced-filter-api="true"
filterable
@update:searchFilter="handleAdvancedSearch"
/>
<script setup>
const handleAdvancedSearch = async (query) => {
// Call your external API with the search query and current column
filterData.value = await searchAdvancedApi(query, selectedColumn.value);
};
</script>
Avatar Support
The Filter component can display avatars alongside filter options to provide visual cues or user representations.
Enable the hasAvatar
prop to display avatars for filter options. Provide an avatar
property in each option object:
<spr-filter v-model="selectedOptions" :options="optionsWithAvatars" has-avatar />
<script setup>
const optionsWithAvatars = ref([
{
column: '',
isSelected: false,
text: 'John Doe',
value: 'john',
avatar: 'https://example.com/avatars/john.jpg',
},
{
column: '',
isSelected: false,
text: 'Jane Smith',
value: 'jane',
avatar: 'https://example.com/avatars/jane.jpg',
},
]);
</script>
If an avatar URL is not provided, the component will display an initial-based avatar using the first letter of the text
property.
External Deselection
The Filter component allows for deselecting options from outside the component, which is useful for implementing custom selection interfaces like external chips or badges.
<template>
<div class="spr-flex spr-gap-2">
<div v-for="selected in selectedOptions">
<spr-button @click="removeSelected(selected.value)">
{{ selected.text }}
<Icon icon="ph:x" />
</spr-button>
</div>
</div>
<spr-filter v-model="selectedOptions" :options="options" :deselected="deselectedOption" />
</template>
<script setup>
const selectedOptions = ref([]);
const deselectedOption = ref('');
const removeSelected = (value) => {
deselectedOption.value = value;
// Reset the deselected value after it's been processed
setTimeout(() => {
deselectedOption.value = '';
}, 100);
};
</script>