useSearchToolbar
Panoramica
useSearchToolbar gestisce lo stato dei filtri in modo programmatico. Utile quando vuoi separare la logica di ricerca dalla UI, costruire toolbar custom o riusare la logica in più viste.
I 4 layer avanzati (preset, rule builder, group-by, ricerche salvate) sono tutti opzionali. Se non passi le opzioni rispettive, il composable ritorna comunque le proprietà ma con defaults innocui (Set vuoto, no-op handlers, ecc.) — così il consumer può usare la stessa interfaccia senza branch condizionali.
Firma
function useSearchToolbar<T extends Record<string, unknown>>(
options: UseSearchToolbarOptions<T>
): UseSearchToolbarReturn<T>
Opzioni
interface UseSearchToolbarOptions<T extends Record<string, unknown>> {
// Core
fields: FieldConfig[]
initialValues?: Partial<T>
onApply?: (state: SearchState) => void
onReset?: (filters: T) => void
// Preset (opzionale)
presets?: PresetFilterGroup[]
initialActivePresets?: string[]
// Rule builder (opzionale)
ruleBuilderFields?: RuleFieldDefinition[]
initialRules?: RuleGroup
// Group by (opzionale)
groupByFields?: GroupByFieldConfig[]
initialGroupBy?: GroupByEntry[]
// Saved searches (opzionale)
savedSearchAdapter?: SavedSearchAdapter
}
onApply: riceve sempre lo stato completo SearchState con filters, activePresets, rules e groupByFields. Quando i layer non sono configurati i campi sono valori vuoti ([], null).Esempio: filtri base
import { useSearchToolbar, buildPostgrestQuery } from '@pzeta/vue-ricerca'
const { filters, isValid, hasActiveFilters, activeFilterCount, apply, reset, setField } =
useSearchToolbar({
fields,
initialValues: { stato: 'attivo' },
onApply: (state) => {
const query = buildPostgrestQuery(state.filters, fields)
// fetch(...)
},
})
Esempio: setup completo
import { useSearchToolbar, buildPostgrestQueryFromState } from '@pzeta/vue-ricerca'
const {
filters,
rules,
activePresets,
groupByFields,
savedSearches,
searchState,
apply,
} = useSearchToolbar({
fields,
presets: presetGroups,
ruleBuilderFields: ruleFields,
groupByFields: groupByConfig,
savedSearchAdapter: adapter,
onApply: (state) => {
const query = buildPostgrestQueryFromState(state, {
fields,
presets: presetGroups,
ruleBuilderFields: ruleFields,
groupByFields: groupByConfig,
})
// fetch con query
},
})
Return — stato base
| Proprietà | Tipo | Descrizione |
|---|---|---|
filters | Ref<T> | Stato reattivo dei filtri (con merge di initialValues su buildDefaultValues(fields)) |
errors | ComputedRef<Record<string, string>> | Errori di validazione range (numberrange / daterange con from > to) |
isValid | ComputedRef<boolean> | true se nessun errore di validazione |
hasActiveFilters | ComputedRef<boolean> | true se almeno un filtro è diverso dal default |
activeFilterCount | ComputedRef<number> | Numero di filtri attivi (range count = 2 se entrambi from/to settati) |
apply() | () => void | Chiama onApply(searchState.value) se isValid.value |
reset() | () => void | Resetta filtri, preset, rule builder e group-by ai valori iniziali |
setField(key, value) | (string, unknown) => void | Imposta un singolo campo |
searchState | ComputedRef<SearchState> | Stato combinato reattivo (filtri + preset + rules + groupBy) |
Return — preset
| Proprietà | Tipo | Descrizione |
|---|---|---|
activePresets | Ref<Set<string>> | Set degli ID preset attivi |
togglePreset(id) | (string) => void | Attiva/disattiva un preset (gestisce exclusive per gruppo) |
clearPresets() | () => void | Disattiva tutti i preset |
activePresetCount | ComputedRef<number> | Numero preset attivi |
Senza opzione presets: activePresets è Ref(new Set()), togglePreset/clearPresets sono no-op.
Return — rule builder
| Proprietà | Tipo | Descrizione |
|---|---|---|
rules | Ref<RuleGroup> | AST radice (sempre presente, anche vuoto) |
addRule(groupId?) | (string?) => void | Aggiunge regola vuota (al gruppo radice se groupId omesso) |
removeRule(id) | (string) => void | Rimuove regola per ID |
updateRule(id, patch) | (string, Partial<Rule>) => void | Aggiorna parzialmente una regola |
addGroup(parentId?) | (string?) => void | Aggiunge sotto-gruppo |
removeGroup(id) | (string) => void | Rimuove un gruppo |
setCombinator(id, c) | (string, 'AND' | 'OR') => void | Cambia combinatore di un gruppo |
moveRuleUp(id) / moveRuleDown(id) | (string) => void | Riordina regole nel gruppo padre |
hasRules | ComputedRef<boolean> | true se almeno una regola completa |
ruleCount | ComputedRef<number> | Numero totale di regole (foglie dell'AST) |
ruleBuilderFields), così puoi caricare regole programmaticamente da una ricerca salvata. La UI del rule builder viene renderizzata solo se passi ruleBuilderFields al SearchToolbar.Return — group by
| Proprietà | Tipo | Descrizione |
|---|---|---|
groupByFields | Ref<GroupByEntry[]> | Raggruppamenti attivi in ordine di priorità |
setGroupBy(fields) | (GroupByEntry[]) => void | Imposta i raggruppamenti (passa [] per rimuoverli) |
Return — ricerche salvate
| Proprietà | Tipo | Descrizione |
|---|---|---|
savedSearches | Ref<SavedSearch[]> | Lista ricerche salvate caricate dall'adapter |
savedSearchesLoading | Ref<boolean> | true durante operazioni async |
canUpdateSearch | boolean | true se l'adapter implementa update |
canArchiveSearch | boolean | true se l'adapter implementa archive |
saveSearch(name, opts?) | async | Salva la ricerca corrente con searchState.value |
updateSearch(id, patch) | async | Aggiorna metadati (nome, isDefault, isShared, sharedWith) |
archiveSearch(id) | async | Soft delete (richiede archive sull'adapter) |
loadSearch(id) | (string) => void | Ripristina filtri, preset, rules, groupBy da una ricerca |
deleteSearch(id) | async | Elimina ricerca per ID |
setDefaultSearch(id) | async | Imposta o rimuove (null) la ricerca default |
Senza opzione savedSearchAdapter: savedSearches è Ref([]), tutti i metodi sono no-op che ritornano Promise<void>, canUpdateSearch/canArchiveSearch sono false.
Validazione range
Il composable valida automaticamente i campi daterange e numberrange. Se from > to, errors contiene un messaggio e apply() non chiama onApply (esce silenziosamente):
// filters.prezzoMin = 500, filters.prezzoMax = 100
// → errors.value = { prezzo: 'Il valore "Da" non può essere superiore al valore "A"' }
// → isValid.value = false
// → apply() esce senza chiamare onApply
Pattern: caricare una ricerca salvata
loadSearch aggiorna in cascata filters, activePresets, rules e groupByFields con i valori dello snapshot — utile per implementare un selettore "ricerche recenti":
const { savedSearches, loadSearch, apply } = useSearchToolbar({ fields, savedSearchAdapter })
function applyPreset(id: string) {
loadSearch(id)
apply()
}