---
title: "Progress Bar"
description: "Boyutu bilinen bir işin ilerlemesi: çubuk veya segmentler. Dolgu ve sayı tek bir spring ile hareket eder; iş bitince çubuk yeşile döner ve onay işareti çizilir."
---

<Callout type="info" title="Ne zaman kullanılır">
Toplamı bilinen işler: ürün aktarımı, eşitleme, birkaç eşit kurulum adımı. Limit ve kota göstermek için [Meter](https://builders.ikas.com/tr/docs/app-development/ui-kit/components/meter), sıralı akışlar için [Steps](https://builders.ikas.com/tr/docs/app-development/ui-kit/components/steps) kullanın.
</Callout>

## Kurulum

```bash
npx shadcn@latest add @ikas/progress-bar
```

Registry tanımı yoksa önce [Kurulum](https://builders.ikas.com/tr/docs/app-development/ui-kit/installation) adımlarını tamamlayın.

## Örnekler

### Çubuk

Boyutu bilinen bir aktarım. Dolgu ve yüzde tek bir spring üzerinde hareket ettiği için birbirinden ayrılmaz; sona gelindiğinde onay işareti çizilir. `hint` çubuğun altında kalan süreyi ya da hatayı ve tekrar dene aksiyonunu gösterir; `status="danger"` hatayı renkle de belirtir.

**Örnek: Çubuk** (`progress-bar/basic`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/progress-bar/basic)

```tsx
import * as React from "react"
import { ClockIcon, TriangleAlertIcon } from "lucide-react"

import { Button } from "@/components/ui/button"
import { ProgressBar } from "@/components/ikas/progress-bar"

/** An import with a known size: the fill and the percentage move on one spring; at the end a check draws in. */
export default function ProgressBarBasic() {
  const [value, setValue] = React.useState(24)
  const [failed, setFailed] = React.useState(false)
  const streaming = React.useRef<ReturnType<typeof setInterval>>(undefined)
  React.useEffect(() => () => clearInterval(streaming.current), [])

  // Progress never goes backwards: each update can only move it forward.
  const advance = (by: number) => setValue((v) => Math.min(100, v + by))
  const stream = () => {
    clearInterval(streaming.current)
    let n = 0
    streaming.current = setInterval(() => {
      advance(3)
      if (++n >= 10) clearInterval(streaming.current)
    }, 100)
  }
  const label = value >= 100 ? "Aktarım tamamlandı" : value >= 60 ? "Görseller işleniyor" : "Ürünler aktarılıyor"
  const hint = failed ? (
    <span className="flex items-center gap-2 text-red-subtle-foreground">
      <TriangleAlertIcon /> Bağlantı koptu.
      <Button size="xs" variant="outline" onClick={() => setFailed(false)}>
        Tekrar dene
      </Button>
    </span>
  ) : value >= 100 ? (
    "1.240 ürün aktarıldı"
  ) : (
    <span className="flex items-center gap-1.5">
      <ClockIcon /> Yaklaşık {Math.max(1, Math.round((100 - value) / 25))} dakika kaldı
    </span>
  )

  return (
    <div className="flex w-full max-w-md flex-col gap-6">
      <ProgressBar value={value} label={label} status={failed ? "danger" : "auto"} hint={hint} />
      <div className="flex flex-wrap justify-center gap-2 border-t pt-5">
        <Button size="sm" variant="outline" disabled={failed} onClick={() => advance(12)}>
          +12
        </Button>
        <Button size="sm" variant="outline" disabled={failed} onClick={() => advance(35)}>
          +35
        </Button>
        <Button size="sm" variant="outline" disabled={failed} onClick={stream}>
          Akış (10 güncelleme)
        </Button>
        <Button size="sm" variant="outline" disabled={failed || value >= 100} onClick={() => (clearInterval(streaming.current), setFailed(true))}>
          Hata
        </Button>
        <Button size="sm" variant="ghost" onClick={() => (clearInterval(streaming.current), setFailed(false), setValue(0))}>
          Sıfırla
        </Button>
      </div>
    </div>
  )
}
```

### Segmentler

Birkaç eşit adım için `variant="segments"`. `value` biten adım sayısı, `max` segment sayısıdır. Birden çok adım atlandığında segmentler sırayla dolar, geri gidildiğinde uzak uçtan boşalır. `segmentType` ile görünümü seçin: `skew` Launchpad başlığıyla aynıdır.

**Örnek: Segmentler** (`progress-bar/segments`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/progress-bar/segments)

```tsx
import * as React from "react"

import { Button } from "@/components/ui/button"
import { ProgressBar, type ProgressSegmentType } from "@/components/ikas/progress-bar"
import { SegmentedControl } from "@/components/ikas/segmented-control"

const types: { value: ProgressSegmentType; label: string }[] = [
  { value: "skew", label: "Eğik" },
  { value: "block", label: "Blok" },
  { value: "pill", label: "Hap" },
  { value: "notch", label: "Çentikli" },
  { value: "dot", label: "Nokta" },
  { value: "diamond", label: "Elmas" },
]
const names = ["Hedef", "Kitle", "İçerik", "Bütçe", "Önizleme"]

/** A few equal steps. Jumping fills the segments one after another; going back drains them from the far end. */
export default function ProgressBarSegments() {
  const [done, setDone] = React.useState(2)
  const [type, setType] = React.useState<ProgressSegmentType>("skew")
  const [failed, setFailed] = React.useState(false)
  const go = (to: number) => (setFailed(false), setDone(Math.max(0, Math.min(5, to))))

  return (
    <div className="flex w-full max-w-md flex-col gap-6">
      <ProgressBar
        variant="segments"
        segmentType={type}
        value={done}
        max={5}
        label="Kampanya kurulumu"
        status={failed ? "danger" : "auto"}
        hint={failed ? `${names[done]} adımı kaydedilemedi` : done >= 5 ? "Kampanya yayında" : `Sıradaki: ${names[done]}`}
      />
      <div className="flex flex-col items-center gap-3 border-t pt-5">
        <div className="flex flex-wrap justify-center gap-2">
          <Button size="sm" variant="outline" onClick={() => go(done + 1)}>İleri</Button>
          <Button size="sm" variant="outline" onClick={() => go(done - 1)}>Geri</Button>
          <Button size="sm" variant="outline" onClick={() => go(done + 3)}>3 adım atla</Button>
          <Button size="sm" variant="outline" disabled={done >= 5} onClick={() => setFailed(true)}>Hata</Button>
        </div>
        <SegmentedControl aria-label="Segment tipi" mode="radio" size="sm" value={type} onValueChange={setType} options={types} />
      </div>
    </div>
  )
}
```

### Belirsiz

Toplam henüz bilinmiyorsa `indeterminate`: çubuk boyunca bir parça gider. Toplam geldiğinde dolgu sıfırdan değil, o parçanın bulunduğu yerden başlar.

**Örnek: Belirsiz** (`progress-bar/indeterminate`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/progress-bar/indeterminate)

```tsx
import * as React from "react"

import { Button } from "@/components/ui/button"
import { ProgressBar } from "@/components/ikas/progress-bar"

/** No total yet: a bar travels the track. When the total arrives the fill starts where that bar is, not from zero. */
export default function ProgressBarIndeterminate() {
  const [total, setTotal] = React.useState<number | null>(null)
  const [moved, setMoved] = React.useState(0)

  return (
    <div className="flex w-full max-w-md flex-col gap-6">
      <ProgressBar
        indeterminate={total === null}
        value={moved}
        max={total ?? 100}
        label={total === null ? "Hazırlanıyor…" : "Siparişler aktarılıyor"}
        valueFormat={(v, max) => `${new Intl.NumberFormat("tr-TR").format(Math.round(v))} / ${new Intl.NumberFormat("tr-TR").format(max)}`}
        hint={total === null ? "Süre hesaplanıyor…" : "Yaklaşık 3 dakika kaldı"}
      />
      <div className="flex flex-wrap justify-center gap-2 border-t pt-5">
        <Button size="sm" variant="outline" disabled={total !== null} onClick={() => (setTotal(1000), setMoved(320))}>
          Toplam bilindi
        </Button>
        <Button size="sm" variant="outline" disabled={total === null} onClick={() => setMoved((m) => Math.min(1000, m + 170))}>
          İlerlet
        </Button>
        <Button size="sm" variant="ghost" onClick={() => (setTotal(null), setMoved(0))}>
          Baştan
        </Button>
      </div>
    </div>
  )
}
```

## Kullanım ilkeleri

**Yapın:**

- İlerlemeyi yalnızca ileri götürün; değeri geri almayın.
- Toplam henüz yoksa indeterminate ile başlayın, toplam gelince value ve max verin.
- Segmentleri 3–8 eşit adım için kullanın.
- Hatayı hint'te yazın ve yanına tekrar dene aksiyonu koyun.

**Yapmayın:**

- Yüzde uyduran sahte bir çubuk göstermeyin.
- Sayfanın ortasında spinner ile ilerleme anlatmayın.
- Kota ve limitler için kullanmayın; o iş Meter'ın.

## Erişilebilirlik

- Çubuk `role="progressbar"` taşır; `label` verildiğinde erişilebilir adı odur, verilmezse "İlerleme" okunur.
- `aria-valuetext` görünen değerle aynıdır (`valueFormat` dahil); belirsiz durumda "Hazırlanıyor" okunur.
- `prefers-reduced-motion` açıkken dolgu animasyonsuz değişir, belirsiz çubuk yerinde yumuşakça yanıp söner.

## API

### `ProgressBar`

| Prop | Tip | Varsayılan | Açıklama |
| --- | --- | --- | --- |
| `value` | `number` | `0` | Tamamlanan iş. 0 ile max arasına sıkıştırılır. Segmentlerde biten adım sayısı. |
| `max` | `number` | `100` | Toplam iş. Segmentlerde segment sayısı (3–8 iyi okunur). |
| `label` | `string` | - | Çubuğun üstünde gösterilir ve erişilebilir adı olarak kullanılır. Yeni etiket yukarı kayarak gelir. |
| `valueFormat` | `(value: number, max: number) => string` | - | Sağdaki değerin metni. Varsayılan çubukta "%NN", segmentlerde "02/05". |
| `hideValue` | `boolean` | `false` | Sağdaki değeri gizler. |
| `status` | `ProgressBarStatus \| "auto"` | `"auto"` | Çalışırken neutral, duraklatıldığında warning, hatada danger. auto sona gelindiğinde success olur. |
| `indeterminate` | `boolean` | `false` | Toplam henüz yok: çubuk boyunca bir parça gider. false olduğunda dolgu o parçanın bulunduğu yerden başlar. |
| `hint` | `React.ReactNode` | - | Çubuğun altında tek satır: kalan süre, hata ve tekrar dene. Yeni metin yukarı kayarak gelir. |
| `variant` | `"bar" \| "segments"` | `"bar"` | Sürekli iş için bar, birkaç eşit adım için segments. |
| `segmentType` | `ProgressSegmentType` | `"skew"` | Segment görünümü: skew, block, pill, notch, dot, diamond. skew Launchpad başlığıyla aynıdır. |
