PageHeaderFilters β
Componente per la riga filtri nella topbar delle pagine di interrogazione. Si integra con PageLayout tramite lo slot filters e supporta 6 tipi di filtro tramite discriminated union TypeScript.
Import β
import {
PageHeaderFilters,
type PageHeaderFiltersConfiguration,
type PageHeaderFilterItem,
type FilterOption,
type DateRangeValue,
getActiveFilterCount,
} from '@/components/layout/topbar/page-header-filters';2
3
4
5
6
7
8
Uso base β
<PageLayout title="Movimenti Magazzino" filters={<PageHeaderFilters config={filtersConfig} />}>
{/* contenuto pagina */}
</PageLayout>2
3
Configurazione β
L'oggetto PageHeaderFiltersConfiguration definisce il comportamento globale e la lista dei filtri:
const filtersConfig: PageHeaderFiltersConfiguration = {
maxRows: 2, // righe visibili prima del collapse
onReset: handleReset, // callback reset tutti i filtri
resetLabel: 'Reset filtri', // etichetta bottone reset
expandLabel: 'Mostra altri filtri', // etichetta bottone espandi
collapseLabel: 'Mostra meno filtri', // etichetta bottone comprimi
items: [
/* ... filtri ... */
],
};2
3
4
5
6
7
8
9
10
Tipi di filtro β
text β Campo di testo libero β
{
type: 'text',
id: 'codice',
label: 'Codice Articolo',
placeholder: 'Es. ART-001',
value: codiceArticolo, // string
onChange: setCodiceArticolo, // (value: string) => void
}2
3
4
5
6
7
8
select β Dropdown con opzioni predefinite β
{
type: 'select',
id: 'magazzino',
label: 'Magazzino',
placeholder: 'Tutti i magazzini',
value: magazzino, // string
onChange: setMagazzino, // (value: string) => void
options: [
{ value: 'centrale', label: 'Centrale' },
{ value: 'nord', label: 'Nord' },
],
}2
3
4
5
6
7
8
9
10
11
12
combobox β Select con ricerca integrata β
{
type: 'combobox',
id: 'cliente',
label: 'Cliente',
placeholder: 'Cerca cliente...',
searchPlaceholder: 'Digita per cercare...',
value: cliente, // string
onChange: setCliente, // (value: string) => void
options: clientiOptions, // FilterOption[]
onSearch: async (q) => fetchClienti(q), // ricerca asincrona (opzionale)
disabled: isLoading, // disabilita durante il caricamento
}2
3
4
5
6
7
8
9
10
11
12
date β DatePicker β
{
type: 'date',
id: 'data-documento',
label: 'Data Documento',
placeholder: 'Seleziona data',
value: dataDocumento, // Date | null
onChange: setDataDocumento, // (value: Date | null) => void
minDate: new Date('2024-01-01'), // vincolo opzionale
}2
3
4
5
6
7
8
9
dateRange β Intervallo di date β
{
type: 'dateRange',
id: 'periodo',
label: 'Periodo',
value: periodo, // { from: Date | null, to: Date | null }
onChange: setPeriodo, // (value: DateRangeValue) => void
}2
3
4
5
6
7
checkbox β Filtro booleano β
{
type: 'checkbox',
id: 'solo-confermati',
label: 'Solo confermati',
value: soloConfermati, // boolean
onChange: setSoloConfermati, // (value: boolean) => void
}2
3
4
5
6
7
ProprietΓ comuni a tutti i filtri β
| ProprietΓ | Tipo | Descrizione |
|---|---|---|
id | string | Identificatore univoco del filtro |
label | string | Etichetta visibile |
hidden | boolean | Nasconde il filtro condizionalmente |
disabled | boolean | Disabilita l'input |
width | string | Classe Tailwind per la larghezza (es. 'w-64') |
classNames | object | Classi CSS per wrapper, label, input |
Filtri condizionali con hidden β
Usa hidden per mostrare/nascondere filtri in base allo stato di altri filtri:
{
id: 'motivo-annullamento',
type: 'text',
label: 'Motivo Annullamento',
value: motivoAnnullamento,
onChange: setMotivoAnnullamento,
hidden: stato !== 'annullato', // visibile solo se stato === 'annullato'
}2
3
4
5
6
7
8
Conteggio filtri attivi β
import { getActiveFilterCount } from '@/components/layout/topbar/page-header-filters';
const count = getActiveFilterCount(filtersConfig.items);
// Conta i filtri con valore diverso dal default (stringa vuota, false, null, ecc.)2
3
4
Integrazione con AG Grid β
PageHeaderFilters Γ¨ fully controlled: il componente parent possiede tutti i valori tramite value/onChange. Questo rende immediato il collegamento con AG Grid.
Pattern: ricerca globale con quickFilter β
Il filtro quickFilterText di AG Grid cerca in tutte le colonne. Si collega con un filtro text nella barra filtri:
import { DataGrid } from '@/components/ui/data-grid';
import type { GridApi, GridReadyEvent } from 'ag-grid-community';
// Stato ricerca globale
const [ricercaGlobale, setRicercaGlobale] = useState('');
const gridApiRef = useRef<GridApi | null>(null);
// Sincronizza con AG Grid
useEffect(() => {
gridApiRef.current?.setGridOption('quickFilterText', ricercaGlobale);
}, [ricercaGlobale]);
// Filtro nella configurazione (primo item per massima visibilita')
const filtersConfig: PageHeaderFiltersConfiguration = {
items: [
{
id: 'ricerca-globale',
type: 'text',
label: 'Ricerca',
placeholder: 'Cerca in tutte le colonne...',
value: ricercaGlobale,
onChange: setRicercaGlobale,
width: 'w-64',
},
// ... altri filtri specifici
],
};
// Cattura gridApi al mount della griglia
const handleGridReady = (event: GridReadyEvent) => {
gridApiRef.current = event.api;
};
<DataGrid
rowData={data}
columnDefs={columnDefs}
onGridReady={handleGridReady}
/>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Pattern: filtri specifici + ricerca globale combinati β
Per pagine con sia filtri specifici che ricerca globale, usa un approccio a due livelli:
- Filtri specifici (magazzino, stato, ecc.) β filtrano
rowDatalato client (o via API) - Ricerca globale β usa
quickFilterTextdi AG Grid sui dati gia' filtrati
// I filtri specifici controllano quali righe passare alla griglia
const filteredData = allData.filter((row) => {
if (magazzino && row.magazzino !== magazzino) return false;
if (stato && row.stato !== stato) return false;
if (tipoMovimento && row.tipoMovimento !== tipoMovimento) return false;
return true;
});
// La ricerca globale opera sul sottoinsieme gia' filtrato
<DataGrid
rowData={filteredData} // pre-filtrato dai filtri specifici
columnDefs={columnDefs}
onGridReady={handleGridReady} // quickFilterText applicato via useEffect
/>2
3
4
5
6
7
8
9
10
11
12
13
14
Accesso completo all'API AG Grid β
DataGrid espone l'intera API di AG Grid tramite onGridReady. La GridApi permette di:
setGridOption('quickFilterText', value)β ricerca globalesetFilterModel(model)β filtri colonna programmaticiexportDataAsCsv()β export datigetSelectedRows()β righe selezionaterefreshCells()β aggiornamento celle
Vedi la documentazione AG Grid per l'elenco completo.
Comportamento responsive β
- Desktop: I filtri si dispongono in
flex-wrap. UnResizeObservermisura quali items superanomaxRowsrighe e li nasconde. Il bottone "Mostra altri filtri (N)" espande la vista. - Mobile (<768px): Layout a colonna, ogni filtro occupa la larghezza completa. Gli overflow vengono mostrati in un bottom sheet modale.
- Reset: Il bottone reset appare solo quando ci sono filtri attivi. Un badge mostra il conteggio.
Best practices β
- Ordine filtri: metti i filtri piu' usati nelle prime posizioni (visibili senza espandere)
- maxRows: usa
1per pochi filtri primari,2(default) per la maggior parte delle pagine - hidden: usa per nascondere filtri contestuali (es. "Motivo Annullamento" solo se stato === "annullato")
- onReset: fornisci sempre per pagine di interrogazione β il reset deve riportare tutti i filtri ai valori iniziali
- Performance: per combobox con molte opzioni, usa
onSearchasincrono. Il debouncing e' gestito internamente (300ms) - Ricerca globale: posizionala come primo filtro e usa
width: 'w-64'per darle risalto
Accessibilita' β
- Container con
role="search"earia-label="Filtri" - Ogni input ha un
Labelassociato viahtmlFor - Il bottone expand usa
aria-expanded - Il bottone reset ha
aria-labelcon conteggio filtri attivi
Demo β
La pagina di test /test-pagelayout-with-filters mostra un esempio completo con:
- Tutti i tipi di filtro
- DataGrid AG Grid con dati mock
- Ricerca globale collegata a
quickFilterText - Filtri specifici che controllano
rowData - Filtro condizionale
hidden - Reset di tutti i filtri