Skip to content

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

Selected: []searchValue:
vue
<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

Selected: []searchValue:
vue
<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.

vue
<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

This is helper text!!
vue
<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

NameDescriptionTypeDefaultRequired
modelValueThe selected filter values. Supports v-model binding for selection state management.Array | String[]No
optionsThe list of filter options. Each option should have the structure: { column: string, isSelected: boolean, text: string, value: string, subtext?: string, avatar?: string }Array[]Yes
labelLabel for the filter input field.String''No
placeholderPlaceholder text for the filter input field.String''No
disabledDisables the filter input, preventing user interaction.BooleanfalseNo
filterableEnables the advanced filter menu with column-based filtering.BooleanfalseNo
idUnique identifier for the filter component. Used for accessibility and DOM manipulation.String'spr-filter'No
filterMenuList of advanced filter menu categories. Each item should have: { columnName: string, field: string, isFilterVisible?: boolean, count?: number }Array[]No
filterDataData for the advanced filter menu. Similar structure to options but specifically for the advanced filter.Array[]No
loadingIndicates if the advanced filter menu is in a loading state.BooleanfalseNo
fillingIndicates if the main filter dropdown is in a loading state.BooleanfalseNo
searchSearch query for the main filter. Supports v-model:search binding.String''No
searchFilterSearch query for the advanced filter menu. Supports v-model:searchFilter binding.String''No
widthWidth of the filter component. Accepts CSS width values (px, %, rem, etc.).String'100%'No
deselectedValue of the deselected filter option. Used to remove selections from outside the component.String''No
hasSearchApiEnables external search API integration for the main filter. When true, local filtering is disabled.BooleanfalseNo
hasAvatarEnables avatar display for filter options. Use the avatar property in options to provide image URLs.BooleanfalseNo
helperTextHelper text displayed below the filter component. Useful for providing additional context or instructions.String''No
errorEnables error state styling for the filter. Use with validation patterns.BooleanfalseNo
hasAdvancedFilterApiEnables external search API integration for the advanced filter menu. When true, local filtering is disabled.BooleanfalseNo

Events

NamePayload TypeDescriptionUsage
getFilterDataStringTriggered when fetching filter data for a specific column in the advanced filter. The payload is the column field name.@getFilterData="handleGetFilterData"
update:modelValueArrayUpdates the selected filter values. Used internally for v-model binding.Handled by v-model binding
update:searchStringUpdates the search query for the main filter. Used internally for v-model:search binding.Handled by v-model:search binding
update:searchFilterStringUpdates the search query for the advanced filter menu. Used internally for v-model:searchFilter binding.Handled by v-model:searchFilter binding
selectedFilterArrayEmits the selected filter options from the advanced filter menu. Contains all selected options across all columns.@selectedFilter="handleSelectedFilter"
infiniteScrollTriggerBooleanTriggered when infinite scrolling is activated for the main filter. Used for loading more data in paginated scenarios.@infiniteScrollTrigger="loadMoreOptions"
infiniteScrollFilterTriggerStringTriggered when infinite scrolling is activated for the advanced filter. The payload is the current column being scrolled.@infiniteScrollFilterTrigger="loadMoreFilterOptions"

Slots

Slot NameDescriptionUsage
defaultSlot for customizing the filter input field. Replaces the default input component entirely.
<spr-filter>
  <custom-input />
</spr-filter>
loadingSlot for displaying a custom loading state in the advanced filter menu. Used when loading filter options.
<template #loading>
  <custom-loader />
</template>
emptySlot for displaying a custom empty state in the advanced filter menu. Used when no filter options are found.
<template #empty>
  <empty-state message="No filters found" />
</template>
loading-stateSlot for displaying a custom loading state in the main filter dropdown. Used when loading main options.
<template #loading-state>
  <custom-loader />
</template>
empty-stateSlot for displaying a custom empty state in the main filter dropdown. Used when no options match the search.
<template #empty-state>
  <empty-state message="No results found" />
</template>

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

  1. 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.
vue
<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>
  1. Advanced Filter Infinite Scroll:
    • Listen for the infiniteScrollFilterTrigger event which provides the column field being scrolled.
    • Load additional data for that specific column.
vue
<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.

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

vue
<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:

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

vue
<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>

Product Uses

Sprout HR