46 lines
1.2 KiB
JavaScript
46 lines
1.2 KiB
JavaScript
/**
|
|
* Search and filter state.
|
|
* Shared between Sidebar and EntryList for coordinated filtering.
|
|
*/
|
|
|
|
const DEBOUNCE_MS = 300
|
|
|
|
export class SearchStore {
|
|
query = $state('') // raw input value — bound to the search input
|
|
debouncedQuery = $state('') // debounced value — used for actual search
|
|
activeGroupId = $state('all') // 'all' or a group id
|
|
refreshTrigger = $state(0) // incremented to force a re-fetch
|
|
#debounceTimer = null
|
|
|
|
/**
|
|
* Update the search query with debouncing.
|
|
* Call this from the input handler instead of setting `query` directly.
|
|
*/
|
|
setSearchQuery(value) {
|
|
this.query = value
|
|
if (this.#debounceTimer) clearTimeout(this.#debounceTimer)
|
|
if (value === '') {
|
|
this.debouncedQuery = ''
|
|
this.#debounceTimer = null
|
|
} else {
|
|
this.#debounceTimer = setTimeout(() => {
|
|
this.debouncedQuery = value
|
|
this.#debounceTimer = null
|
|
}, DEBOUNCE_MS)
|
|
}
|
|
}
|
|
|
|
clear() {
|
|
this.query = ''
|
|
this.debouncedQuery = ''
|
|
this.activeGroupId = 'all'
|
|
}
|
|
|
|
/** Force subscribed components to re-fetch data. */
|
|
refresh() {
|
|
this.refreshTrigger++
|
|
}
|
|
}
|
|
|
|
export const search = new SearchStore()
|