# LivaFilterChips
Package: @dataliva/livalib
Language: tr
Description: @dataliva/livalib renkli noktalı, sayaçlı aç/kapa süzgeç çipleri (tek ya da çoklu seçim).

Bir listeyi türe ya da duruma göre süzmek için renkli noktalı, isteğe bağlı
sayaçlı aç/kapa çipleri. Her çip sade bir `<button>`'dır (DevExtreme bileşeni
değildir), bu yüzden DevExtreme seçenekleri aktarılmaz. Seçim denetimlidir:
seçili anahtarları `value` ile verirsiniz, her tıklamada yeni değer `onChange`
ile gelir. Seçili bir çipe yeniden tıklamak onu seçimden çıkarır.

```tsx
import { LivaFilterChips, type LivaFilterChipItem } from "@dataliva/livalib";
```

## Çoklu seçim

Varsayılan `selectionMode` `'multiple'`'dır: `value` bir anahtar dizisidir,
`onChange` güncel diziyi verir (yeni seçilen anahtar dizinin sonuna eklenir).
Boş dizinin ne anlama geldiğine bileşen karar vermez; süzmeyi siz yaparsınız.
Örnekte boş seçim "tümü" anlamına gelir.

Bileşen kendi "temizle" düğmesini çizmez. Arama kutusu gibi başka filtrelerle
birlikte sıfırlanması gerekiyorsa temizleme düğmesini bileşenin dışına koyun.

**Örnek — Çoklu seçim (varsayılan), renk ve sayaç:**

```tsx
type EventType = "login" | "change" | "export" | "error";

const TYPES: { key: EventType; text: string; color: string }[] = [
  { key: "login", text: "Oturum", color: "primary" },
  { key: "change", text: "Veri değişikliği", color: "#10b981" },
  { key: "export", text: "Dışa aktarma", color: "warning" },
  { key: "error", text: "Hata", color: "danger" },
];
// EVENTS: { id: number; type: EventType; text: string }[]

const [types, setTypes] = useState<EventType[]>([]);

const items: LivaFilterChipItem<EventType>[] = TYPES.map((t) => ({
  ...t,
  count: EVENTS.filter((e) => e.type === t.key).length,
}));
// Boş seçim = süzme yok (bu karar bileşenin değil, uygulamanın).
const rows = types.length ? EVENTS.filter((e) => types.includes(e.type)) : EVENTS;

<div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
  <LivaFilterChips
    items={items}
    value={types}
    onChange={setTypes}
    ariaLabel="Olay türü"
  />
  {types.length > 0 && (
    <LivaButton text="Filtreleri temizle" variant="text" size="small" onClick={() => setTypes([])} />
  )}
</div>

<ul className="list-disc pl-6 text-sm">
  {rows.map((e) => (
    <li key={e.id}>{e.text}</li>
  ))}
</ul>
```

## Tek seçim

`selectionMode="single"` ile `value` tek bir anahtar ya da `null`'dır. Başka
bir çipe tıklamak seçimi değiştirir; seçili çipe yeniden tıklamak `null`
yayar. `value` ve `onChange` tipleri `selectionMode`'a göre ayrışır, yani
TypeScript tek seçimde dizi, çoklu seçimde tekil değer vermenize izin vermez.

`disableEmpty` sayacı `0` olan çipleri pasifleştirir; seçilselerdi liste
tamamen boşalırdı. Seçili bir çip sayacı `0`'a düşse bile tıklanabilir kalır ki
kullanıcı onu kaldırabilsin. `count` verilmemiş çipler bu kuraldan etkilenmez.

**Örnek — Tek seçim ve sayacı 0 olan çipi pasifleştirme (disableEmpty):**

```tsx
const STATUS_ITEMS: LivaFilterChipItem[] = [
  { key: "draft", text: "Taslak", color: "secondary", count: 4 },
  { key: "pending", text: "Onayda", color: "warning", count: 7 },
  { key: "approved", text: "Onaylandı", color: "success", count: 12 },
  { key: "rejected", text: "Reddedildi", color: "danger", count: 0 },
];

const [status, setStatus] = useState<string | null>(null);

<LivaFilterChips
  selectionMode="single"
  disableEmpty
  items={STATUS_ITEMS}
  value={status}
  onChange={setStatus}
  ariaLabel="Durum"
/>
```

## Sayaç, ipucu ve devre dışı çip

`count` verilen çipte etiketin sağında soluk, sabit genişlikli rakamlarla bir
sayaç çizilir; `count` yoksa sayaç alanı hiç çizilmez. Sayaç varsayılan olarak
tarayıcının yereline göre binlik ayraçla (`count.toLocaleString()`)
biçimlenir; `formatCount` ile değiştirilir. `hint` çipin üzerine gelince
görünen ipucudur (`title`), `disabled` ise çipi seçimden bağımsız olarak
pasifleştirir.

Anahtarlar `string` ya da `number` olabilir; bileşen anahtar tipine göre
geneldir (`LivaFilterChipItem<number>[]` + `number[]` değer gibi).

**Örnek — Sayı anahtarlar, formatCount, hint ve disabled:**

```tsx
const LEVEL_ITEMS: LivaFilterChipItem<number>[] = [
  { key: 2, text: "Bilgi", color: "primary", count: 128450 },
  { key: 3, text: "Uyarı", color: "warning", count: 3120 },
  { key: 4, text: "Hata", color: "danger", count: 87 },
  {
    key: 5,
    text: "Kritik",
    color: "#9f1239",
    count: 0,
    disabled: true,
    hint: "Bu ortamda kritik seviye kaydı tutulmuyor",
  },
];

const compactCount = (n: number) =>
  n >= 1000
    ? `${(n / 1000).toLocaleString("tr-TR", { maximumFractionDigits: 1 })} B`
    : n.toLocaleString("tr-TR");

const [levels, setLevels] = useState<number[]>([4]);

<LivaFilterChips
  items={LEVEL_ITEMS}
  value={levels}
  onChange={setLevels}
  formatCount={compactCount}
  ariaLabel="Seviye"
/>
```

## Boyut ve nokta

`size` çipin iç boşluğunu, yazı boyunu ve noktanın çapını birlikte değiştirir
(`'small' | 'medium' | 'large'`, varsayılan `'medium'`). `showDot={false}`
renkli noktayı gizler; renk yine üzerine gelince çerçevede ve seçili çipin
zemininde görünür. Çipler sığmadığında alt satıra kayar.

**Örnek — Boyutlar ve noktasız çipler (showDot={false}):**

```tsx
<LivaFilterChips size="small" items={PERIOD_ITEMS} value={periods} onChange={setPeriods} />
<LivaFilterChips size="medium" items={PERIOD_ITEMS} value={periods} onChange={setPeriods} />
<LivaFilterChips size="large" items={PERIOD_ITEMS} value={periods} onChange={setPeriods} />

<LivaFilterChips showDot={false} items={PERIOD_ITEMS} value={periods} onChange={setPeriods} />
```

## Renk

Her öğenin `color` alanı bir tema rengi adı ya da herhangi bir CSS rengi
(`"#10b981"`, `"var(--marka-rengi)"`) alır. Verilmezse temanın birincil rengi
kullanılır. Tema adları şu değişkenlere çevrilir:

| `color` | Kullanılan değişken |
|------|------|
| `primary` | `--dx-color-primary` |
| `secondary` | `--dx-color-icon` |
| `success` | `--dx-color-success` |
| `warning` | `--dx-color-warning` |
| `danger` | `--dx-color-danger` |

Renk; noktayı, üzerine gelince çerçeveyi, klavye odak halkasını ve seçili
çipin çerçevesi ile zemin tonunu belirler. Zemin tonu rengin temanın yüzey
rengiyle karıştırılmasıyla elde edildiği için koyu temada da metin okunur
kalır.

## Erişilebilirlik

- Kök öğe `role="group"` taşır; `ariaLabel` grubun adıdır ("Olay türü",
  "Durum"…). Verilmesi önerilir.
- Her çip `aria-pressed` ile seçili olup olmadığını bildirir; klavyeyle Tab ile
  gezilir, Enter/Boşluk ile seçilir. Renkli nokta ekran okuyuculardan gizlidir.
- Yüksek karşıtlık modunda (`forced-colors`) seçili çip kalın bir çerçeveyle
  gösterilir; `prefers-reduced-motion` açıkken renk geçiş animasyonu kapanır.

## Prop'lar

`K` = `LivaFilterChipKey` (`string | number`), varsayılan `string`.

| Prop | Tip | Varsayılan |
|------|------|---------|
| `items` | `LivaFilterChipItem<K>[]` | — (zorunlu) |
| `selectionMode` | `'multiple' \| 'single'` | `'multiple'` |
| `value` | `K[]` (çoklu) · `K \| null` (tek) | — (zorunlu) |
| `onChange` | `(value: K[]) => void` (çoklu) · `(value: K \| null) => void` (tek) | — (zorunlu) |
| `ariaLabel` | `string` | — |
| `disableEmpty` | `boolean` | `false` (sayacı `0` olan seçili olmayan çipleri pasifleştirir) |
| `formatCount` | `(count: number) => string` | — (verilmezse `count.toLocaleString()`) |
| `showDot` | `boolean` | `true` |
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
| `className` | `string` | `''` |
| `style` | `CSSProperties` | — |

### LivaFilterChipItem

| Alan | Tip | Varsayılan |
|------|------|---------|
| `key` | `K` | — (zorunlu) |
| `text` | `string` | — (zorunlu) |
| `color` | `LivaFilterChipColor` | — (temanın birincil rengi) |
| `count` | `number` | — (verilmezse sayaç çizilmez) |
| `disabled` | `boolean` | — |
| `hint` | `string` | — (üzerine gelince görünen ipucu) |

`LivaFilterChipColor`:
`'primary' | 'secondary' | 'success' | 'warning' | 'danger'` ya da herhangi bir
CSS rengi.

Dışa aktarılan tipler: `LivaFilterChipKey`, `LivaFilterChipColor`,
`LivaFilterChipItem`, `LivaFilterChipsProps` (iki seçim kipinin birleşimi),
`LivaFilterChipsMultipleProps`, `LivaFilterChipsSingleProps`.
