Filter bar

Rândul de filtre de deasupra unei liste — un singur model în toate aplicațiile. Filtrele se aplică pe loc, fiecare se golește cu ✕-ul lui, iar ✕-ul de la capătul barei le golește pe toate. Nu e o familie nouă de controale: toate cele opt feluri de filtru sunt controale care existau deja în DS.

Markup canonic · components/filter-bar.json

sursa = JSON · generat de scripts/build-catalog.py

Facturi emise

31 de rezultate

Anatomie

.filter-hd     titlul + numărul; numărul doar cât lista e filtrată
┌──────────────────────────────────────────────────────────────────┐
│ Facturi emise │ 31 de rezultate                                  │
└──────────────────────────────────────────────────────────────────┘
.filter-bar    16px sub antet · 8px între filtre
┌──────────────────────────────────────────────────────────────────┐
│ [Toate|Încasate|…] [Emisă +1   ✕] [1 sep – 30 sep  ✕]            │
│ [Orice sumă   ▾] ☐ Doar restante [omv           ✕]          (✕)  │
└──────────────────────────────────────────────────────────────────┘
               (✕) = .filter-reset, ✕-ul general: la capătul rândului,
               doar de la două filtre aplicate; căutarea îi face loc
.tbl-wrap      bara stă ÎN carcasa tabelului (.tbl-filters), peste un
               .tbl.compact — un gutter de 14px: titlul, primul filtru
               și prima coloană pe aceeași linie (table.json → filtered)

ordinea    segment → alegeri (o valoare, mai multe, entitate)
           → intervale (perioadă, sumă) → da/nu → căutarea → ✕-ul general
câte       cel mult 6 filtre pe o listă, căutarea inclusă
lățimi     părți egale din rând, oricare ar fi valoarea (peste, elipsă);
           rupere sub 120px; căutarea umple rândul până la capăt și se
           strânge cu 48px când apare ✕-ul general — restul nu se mișcă
mobil      sub 1024px două pe rând; segmentul și căutarea pe rând întreg

Cele opt filtre

Fiecare are o stare neutră — cea care nu filtrează, numită de placeholder — și o stare aleasă. Tot ce ține o valoare primește ✕; segmentul și checkbox-ul nu, fiindcă neutrul lor e deja la un click.

O valoarevariants.select
neutru
ales
Mai multe valorivariants.multi-select
neutru
ales
Entitatevariants.entity
neutru
ales
Perioadăvariants.period
neutru
ales
Sumăvariants.amount
neutru
ales
Segmentvariants.segment
neutru
ales
Da / nuvariants.boolean
neutru
ales
FiltruCândControlulNeutruGolireAplicare
Căutaretext liber, în mai multe câmpuri.input-group + type=search„Caută client sau descriere…"✕ în addondebounce 500ms
O valoarelistă închisă, o alegere.select-anchor + .select-trigger„Toate tipurile"✕ în locul săgețiipe loc
Mai multe valorilistă închisă, mai multe (statusuri)același + .popover-item.check-item„Toate statusurile"✕ în locul săgețiipe loc, la fiecare bifă
EntitateUN client / furnizor din mii.typeahead + .input-group„Toți clienții"✕ în addonpe loc, la alegere
Perioadăo zi sau un interval de date.select-trigger + .calendar„Oricând"✕ în locul săgețiipe loc, la fiecare click
Sumăde la / până la.select-trigger + .filter-range„Orice sumă"✕ în locul săgețiidebounce 500ms
Segmentîmpărțirea principală, 2–4 opțiuni.tabs ca radiogroup„Toate" (prima)—, neutrul e la un clickpe loc
Da / nuo întrebare pusă listei.check-itemnebifat—, debifareape loc

Deschise

Listele sunt cele ale controalelor de bază — .popover cu bifa de brand pe rândul ales, .calendar în interval cu scurtături, .typeahead cu rândul de meta pentru cod. Nimic din popover nu are buton de „Aplică": fiecare click filtrează lista din spate.

o valoare · alegerea închide lista
mai multe valori · lista rămâne deschisă
entitate · scrii, alegi un cod
sumă · de la / până la
perioadă · interval + scurtături

Golire și resetare

încearcă: scrie în căutare, bifează, schimbă segmentul — apoi ✕-ul căutării sau ✕-ul general

Facturi emise

ClientDatăSumă
CeRegula
✕Apare doar cât controlul ține o valoare. Golește doar filtrul lui, pe loc — sare debounce-ul, altfel lista ar mai rula 500ms căutarea veche — și întoarce focusul pe control.
„Filtrat"Cel puțin un filtru aplicat diferit de neutru. Se citește de pe filtrul aplicat, după debounce — nu de pe textul din câmp —, deci numărul apare în clipa în care lista îl urmează.
NumărulDoar cât lista e filtrată și prima pagină a venit: „1 rezultat", „4 rezultate", „31 de rezultate". Nefiltrată ar număra tot ce s-a înregistrat vreodată.
✕-ul general.filter-reset: .btn.secondary.icon-only de 40px cu tooltip, ultimul în bară, la capătul rândului, doar de la două filtre aplicate — cu unul singur ar dubla ✕-ul acelui filtru. Căutarea se strânge ca să-i facă loc. Readuce toate controalele la neutru — segmentul și checkbox-ul inclusiv. Focusul trece pe primul control din bară: butonul dispare odată cu filtrarea. Secondary — acțiunea neutră, o treaptă peste ✕-ul ghost al unui câmp; nu .danger (negrul e al ștergerii) și nu .primary (acțiunea principală a ecranului e alta).
CombinareȘI între filtre; SAU între valorile aceluiași filtru cu mai multe valori.
PersistențăNiciuna: filtrele trăiesc cât pagina, iar revenirea pe pagină pornește nefiltrată. Mutarea lor în URL e o decizie pentru toate listele deodată, nu pe ecran.
ÎncărcareLista își arată singură încărcarea; bara rămâne activă. Un select ale cărui opțiuni vin de la server e :disabled cu „Se încarcă…" până sosesc.

Zero rezultate · .tbl-empty

Trei motive, trei propoziții. Doar lista goală din cauza filtrelor primește ieșirea — pe o listă goală fiindcă nu există încă nimic, „Resetează filtrele" n-ar avea ce face.

Nu am putut încărca facturile.
Nicio factură emisă încă.
Nicio factură nu se potrivește cu filtrarea.

Mobil · sub 1024px

aceeași bară, același model — fără foaie de filtre, fără „Aplică"

[ Toate | Încasate | Neîncasate      ]   segmentul: rând întreg
[ Emisă +1     ✕ ] [ Oricând       ▾ ]   două pe rând, câte o jumătate
[ Orice sumă   ▾ ] [ ☐ Doar restante  ]
[ Caută client sau descriere…   ] (✕)    căutarea + ✕-ul general

câmpurile de text urcă la 16px (bono-ds.css, regula de zoom iOS)