Overview
The Facets module manages faceted search functionality, including facet definitions, filters, filter selection state, and sticky filters. It handles fetching facets from the API and coordinating filter state with the search module.
State
The Facets module maintains the following state:
Current query for facets request
Dictionary of facets by facet ID
Dictionary of all filters by filter ID
Mapping of facet IDs to group IDs
Filters that should be preselected on load
Filters that persist across searches
status
'initial' | 'loading' | 'success' | 'error'
Current request status
Origin of the facets request
Additional request parameters
Module configuration options
Configuration
filtersStrategyForRequest
'all' | 'selected' | 'none'
default: "'all'"
Which filters to include in facets request:
'all' - Include all filters
'selected' - Only include selected filters
'none' - Don’t include filters
Getters
Array of all facets with their filters populated
Array of currently selected filters
selectedFiltersForRequest
Selected filters formatted for API request based on filtersStrategyForRequest
Selected filters grouped by facet ID
Computed facets request object
Mutations
setQuery
(state, query: string) => void
Update the query for facets
setFacet
(state, facet: Facet) => void
Add or update a single facet
removeFacet
(state, { id: string }) => void
Remove a facet by ID
setFilters
(state, filters: Filter[]) => void
Add or update multiple filters
mutateFilter
(state, { filter: Filter, newFilterState: Partial<Filter> }) => void
Update properties of an existing filter
removeFilter
(state, { id: string }) => void
Remove a single filter
removeFilters
(state, filters: Filter[]) => void
Remove multiple filters
setPreselectedFilters
(state, filters: Filter[]) => void
Set filters that should be preselected
setFacetGroup
(state, { facetId: string, groupId: string }) => void
Assign a facet to a group
setStickyFilter
(state, filter: Filter) => void
Mark a filter as sticky
removeStickyFilter
(state, filter: Filter) => void
Remove sticky status from a filter
setStatus
(state, status: Status) => void
Update request status
setOrigin
(state, origin: string | null) => void
Set the origin of facets request
setParams
(state, params: Record<string, unknown>) => void
Update request parameters
Actions
fetchFacetsResponse
(context) => Promise<FacetsResponse>
Fetch facets from the API without updating state
fetchAndSaveFacetsResponse
(context) => Promise<FacetsResponse>
Fetch facets and update module state
cancelFetchAndSaveFacetsResponse
(context) => Promise<void>
Cancel any in-progress facets request
saveOrigin
(context, origin: string) => void
Save the origin of the facets request
Events Emitted
Emitted when user selects/deselects a filter
Emitted when user clears filters
Emitted when facets data is updated
Emitted when selected filters change
Usage Examples
Basic Filter Selection
import { useStore } from 'vuex'
import type { Filter } from '@empathyco/x-types'
const store = useStore ()
// Get filter from facets
const facets = store . getters [ 'x/facets/facets' ]
const categoryFacet = facets . find ( f => f . id === 'category' )
const filter = categoryFacet . filters [ 0 ]
// Toggle filter selection
store . commit ( 'x/facets/mutateFilter' , {
filter ,
newFilterState: { selected: ! filter . selected }
})
// Fetch new search results with filter applied
await store . dispatch ( 'x/search/fetchAndSaveSearchResponse' )
Sticky Filters
// Mark a filter as sticky (persists across searches)
const filter = {
id: 'brand:nike' ,
facetId: 'brand' ,
label: 'Nike' ,
modelName: 'SimpleFilter' ,
selected: true ,
totalResults: 150
}
store . commit ( 'x/facets/setStickyFilter' , filter )
// Sticky filter will remain selected even when query changes
store . commit ( 'x/search/setQuery' , 'running shoes' )
await store . dispatch ( 'x/search/fetchAndSaveSearchResponse' )
// Remove sticky status
store . commit ( 'x/facets/removeStickyFilter' , filter )
Preselected Filters
// Set filters that should be preselected on page load
const preselectedFilters = [
{
id: 'category:shoes' ,
facetId: 'category' ,
label: 'Shoes' ,
modelName: 'SimpleFilter' ,
selected: true
}
]
store . commit ( 'x/facets/setPreselectedFilters' , preselectedFilters )
Facet Groups
// Organize facets into groups
store . commit ( 'x/facets/setFacetGroup' , {
facetId: 'color' ,
groupId: 'attributes'
})
store . commit ( 'x/facets/setFacetGroup' , {
facetId: 'size' ,
groupId: 'attributes'
})
store . commit ( 'x/facets/setFacetGroup' , {
facetId: 'brand' ,
groupId: 'taxonomy'
})
Clear Filters
// Clear all selected filters
const selectedFilters = store . getters [ 'x/facets/selectedFilters' ]
selectedFilters . forEach ( filter => {
store . commit ( 'x/facets/mutateFilter' , {
filter ,
newFilterState: { selected: false }
})
})
await store . dispatch ( 'x/search/fetchAndSaveSearchResponse' )
Component Integration
< template >
< div class = "facets" >
< div v-for = " facet in facets " : key = " facet . id " class = "facet" >
< h3 class = "facet-title" > {{ facet . label }} </ h3 >
< div class = "facet-filters" >
< button
v-for = " filter in facet . filters "
: key = " filter . id "
: class = " { selected: filter . selected } "
@ click = " toggleFilter ( filter ) "
>
{{ filter . label }} ({{ filter . totalResults }})
</ button >
</ div >
</ div >
< button
v-if = " hasSelectedFilters "
@ click = " clearAllFilters "
>
Clear All Filters
</ button >
</ div >
</ template >
< script setup lang = "ts" >
import { computed } from 'vue'
import { useStore } from 'vuex'
import type { Filter , Facet } from '@empathyco/x-types'
const store = useStore ()
const facets = computed < Facet []>(() => store . getters [ 'x/facets/facets' ])
const selectedFilters = computed < Filter []>(() => store . getters [ 'x/facets/selectedFilters' ])
const hasSelectedFilters = computed (() => selectedFilters . value . length > 0 )
const toggleFilter = async ( filter : Filter ) => {
store . commit ( 'x/facets/mutateFilter' , {
filter ,
newFilterState: { selected: ! filter . selected }
})
// Reset to page 1 when filtering
store . commit ( 'x/search/setPage' , 1 )
await store . dispatch ( 'x/search/fetchAndSaveSearchResponse' )
}
const clearAllFilters = async () => {
selectedFilters . value . forEach ( filter => {
store . commit ( 'x/facets/mutateFilter' , {
filter ,
newFilterState: { selected: false }
})
})
store . commit ( 'x/search/setPage' , 1 )
await store . dispatch ( 'x/search/fetchAndSaveSearchResponse' )
}
</ script >
Hierarchical Filters
< template >
< div class = "hierarchical-facet" >
< FilterTree : filters = " rootFilters " @ toggle = " toggleFilter " />
</ div >
</ template >
< script setup lang = "ts" >
import { computed } from 'vue'
import type { HierarchicalFilter } from '@empathyco/x-types'
import FilterTree from './FilterTree.vue'
interface Props {
facetId : string
}
const props = defineProps < Props >()
const store = useStore ()
const facet = computed (() => {
return store . state . x . facets . facets [ props . facetId ]
})
const rootFilters = computed (() => {
return facet . value ?. filters . filter (
( f : HierarchicalFilter ) => ! f . parentId
)
})
</ script >
Filter Types
Simple Filter
import type { Filter } from '@empathyco/x-types'
interface SimpleFilter extends Filter {
id : string
facetId : string
label : string
selected : boolean
totalResults : number
modelName : 'SimpleFilter'
}
Hierarchical Filter
import type { Filter } from '@empathyco/x-types'
interface HierarchicalFilter extends Filter {
id : string
facetId : string
label : string
selected : boolean
totalResults : number
modelName : 'HierarchicalFilter'
parentId : string | null
children : HierarchicalFilter []
}
Number Range Filter
import type { Filter } from '@empathyco/x-types'
interface NumberRangeFilter extends Filter {
id : string
facetId : string
label ?: string
selected : boolean
modelName : 'NumberRangeFilter'
range : {
min : number
max : number
}
}
Type Reference
Source: /home/daytona/workspace/source/packages/x-components/src/x-modules/facets/store/module.ts:1
Resources
x-types Type definitions for facets and filters
Search Module Related search functionality