---
title: "Sortable Stack"
description: "Kullanıcının tutamaçtan sürükleyerek veya klavyeyle sıraladığı dikey liste. Satırlar yay animasyonuyla kayar, son taşıma geri alınabilir."
---

<Callout type="info" title="Ne zaman kullanılır">
Sırayı kullanıcının belirlediği listeler: öncelikler, kart içindeki iş listesi, menü veya alan sırası. Yukarı/aşağı ok butonları ya da elle yazılmış sürükle-bırak yerine bunu kullanın.
</Callout>

## Kurulum

```bash
npx shadcn@latest add @ikas/sortable-stack
```

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

## Örnekler

### Bugünkü işler

Kart içinde bir iş listesi. Sürükleme yalnızca tutamaçtan başlar, böylece satırdaki diğer butonlar çalışmaya devam eder. Kaldırılan satır 1.02 büyür ve gölgesi belirir; diğer satırlar `SPRING` ile kayar. Son taşıma "Sırayı geri al" ile geri alınır.

**Örnek: Bugünkü işler** (`sortable-stack/tasks`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/sortable-stack/tasks)

```tsx
import { ChevronRightIcon } from "lucide-react"

import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardAction, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import {
  SortableStack,
  SortableStackGroup,
  SortableStackHandle,
  SortableStackItem,
  SortableStackItemContent,
  SortableStackUndo,
} from "@/components/ikas/sortable-stack"

const tasks = [
  {
    id: "ship",
    title: "Kargolanmayı bekleyen siparişler",
    hint: "En eskisi 2 gün önce",
    count: 12,
    status: "neutral",
  },
  {
    id: "returns",
    title: "İade talepleri",
    hint: "Onay bekliyor",
    count: 3,
    status: "neutral",
  },
  {
    id: "stock",
    title: "Stoğu azalan ürünler",
    hint: "5 adetin altında",
    count: 5,
    status: "warning",
  },
  {
    id: "transfer",
    title: "Havale onayları",
    hint: "Ödeme kontrol edilecek",
    count: 4,
    status: "neutral",
  },
  {
    id: "reviews",
    title: "Yanıt bekleyen yorumlar",
    hint: "Son 24 saat",
    count: 2,
    status: "neutral",
  },
] as const

export default function SortableStackTasks() {
  return (
    <SortableStack label="Bugünkü işler" defaultItems={[...tasks]} getItemLabel={(task) => task.title}>
      {(items) => (
        <Card className="w-full max-w-md">
          <CardHeader>
            <CardTitle>Bugünkü işler</CardTitle>
            <CardDescription>Önce yapmak istediğinizi en üste taşıyın.</CardDescription>
            <CardAction>
              <SortableStackUndo />
            </CardAction>
          </CardHeader>
          <SortableStackGroup className="px-2.5">
            {items.map((task) => (
              <SortableStackItem key={task.id} id={task.id}>
                <SortableStackHandle />
                <SortableStackItemContent>
                  <p className="truncate text-sm font-medium">{task.title}</p>
                  <p className="truncate text-[13px] text-muted-foreground">{task.hint}</p>
                </SortableStackItemContent>
                <Badge status={task.status} dot={task.status === "warning"}>
                  {task.count}
                </Badge>
                <Button variant="ghost" size="icon-sm" aria-label={`${task.title} sayfasını aç`} className="-mr-1.5 text-icon hover:text-foreground">
                  <ChevronRightIcon data-anim="nudge" />
                </Button>
              </SortableStackItem>
            ))}
          </SortableStackGroup>
        </Card>
      )}
    </SortableStack>
  )
}
```

## Yapı

Hazır düzen için `renderItem` vermek yeterlidir: tutamaç, içerik ve geri alma butonu kendiliğinden gelir. Kendi düzeniniz için parçaları birleştirin; `children` fonksiyon olursa öğeleri güncel sırasıyla alır.

```tsx
<SortableStack label="Bugünkü işler" defaultItems={tasks} getItemLabel={(task) => task.title}>
  {(items) => (
    <Card>
      <CardHeader>
        <CardTitle>Bugünkü işler</CardTitle>
        <CardAction>
          <SortableStackUndo />
        </CardAction>
      </CardHeader>
      <CardContent>
        <SortableStackGroup>
          {items.map((task) => (
            <SortableStackItem key={task.id} id={task.id}>
              <SortableStackHandle />
              <SortableStackItemContent>{task.title}</SortableStackItemContent>
            </SortableStackItem>
          ))}
        </SortableStackGroup>
      </CardContent>
    </Card>
  )}
</SortableStack>
```

Her öğenin sabit bir `id`'si olmalıdır; satır taşınırken odak ve içerik ona bağlı kalır. Kök kendi boşluk bırakmaz, bu yüzden geri alma butonu bir `CardAction` içine sığar.

<Callout type="info" title="Sırayı kaydetme">
Sıra kalıcı olacaksa `onItemsChange` içinde kaydedin. Bu callback sürükleme bırakıldığında bir kez, klavyeyle her taşımada ve geri almada yeni sırayla çağrılır; sürükleme sırasındaki ara sıralar için çağrılmaz.
</Callout>

## Kullanım ilkeleri

**Yapın:**

- Satır başına sabit bir id verin ve React key olarak da onu kullanın.
- getItemLabel ile her satıra okunabilir bir ad verin; taşıma duyuruları onu kullanır.
- Uzun metinleri SortableStackItemContent içinde kısaltın.

**Yapmayın:**

- Tüm satırı sürüklenebilir yapmayın; sürükleme tutamaçtan başlar.
- Başlığı ve geri alma butonunu SortableStackGroup içine koymayın.
- Yukarı/aşağı ok butonlarıyla sıralama yapmayın.

## Erişilebilirlik

- Tutamaç odaktayken ↑ / ↓ satırı bir adım, Home / End en başa veya en sona taşır.
- Satırlar `aria-posinset` ve `aria-setsize` taşır; her taşıma bir canlı bölgeden Türkçe duyurulur.
- Geri alma, taşınan satıra odağı geri verir.
- `prefers-reduced-motion` açıkken satırlar animasyonsuz yer değiştirir.

## API

### `SortableStack`

| Prop | Tip | Varsayılan | Açıklama |
| --- | --- | --- | --- |
| `items` | `T[]` | - | Kontrollü öğeler, güncel sırasıyla. onItemsChange ile birlikte kullanın. |
| `defaultItems` | `T[]` | `[]` | Kontrolsüz kullanımda başlangıç öğeleri. |
| `onItemsChange` | `(items: T[]) => void` | - | Sürükleme bırakıldığında bir kez, klavyeyle her taşımada ve geri almada yeni sırayla çağrılır. Sıra kalıcı olacaksa burada kaydedin. |
| `renderItem` | `(item: T, index: number) => React.ReactNode` | - | children verilmediğinde hazır düzenin (tutamaç, içerik, geri alma) satır içeriği. |
| `getItemLabel` (zorunlu) | `(item: T) => string` | - | Taşıma duyurularında ve tutamaç etiketlerinde ekran okuyucunun okuduğu ad. |
| `children` | `React.ReactNode \| ((items: T[]) => React.ReactNode)` | - | Parçalardan kendi düzeniniz. Fonksiyon verirseniz öğeleri güncel sırasıyla alır. |
| `label` | `string` | `"Sıralanabilir öğeler"` | Listenin erişilebilir adı. |
| `disabled` | `boolean` | `false` | Sürüklemeyi, klavyeyle taşımayı ve geri almayı kapatır. |
| `showUndo` | `boolean` | `true` | Hazır düzende listenin altında geri alma butonunu gösterir. |
| `className` | `string` | - | Köke eklenen sınıflar. |
| `itemClassName` | `string` | - | Her satıra eklenen sınıflar. |

### `SortableStackItem`

| Prop | Tip | Varsayılan | Açıklama |
| --- | --- | --- | --- |
| `id` (zorunlu) | `string` | - | Köke verilen öğenin id'si. React key olarak da kullanın. |
| `children` | `React.ReactNode` | - | Satırın içeriği: tutamaç ve SortableStackItemContent. |

### Diğer parçalar

| Parça | Ne yapar |
| --- | --- |
| `SortableStackGroup` | Sıralanan satırları taşır. Başlık ve geri alma butonu dışında kalır. |
| `SortableStackHandle` | Sürüklemeyi başlatan tutamaç; ghost `icon-sm` Button. |
| `SortableStackItemContent` | Satırın kalan genişliğini alır. |
| `SortableStackUndo` | Son taşımadan önceki sırayı geri getirir; bir şey taşınana kadar devre dışıdır. |
| `useSortableStack` | Parçaların paylaştığı bağlama erişim. |
