Kalıplar
Veri dışa aktarma
Kayıtları dosyaya aktarırken kapsamı seçmek, küçük ve büyük dışa aktarmayı ayırmak ve dosyayı adlandırmak.
Dışa aktarma, kullanıcının bir listeyi dosya olarak alıp başka bir araçta kullanmasını sağlar. Bu kalıp kapsam seçimini, küçük ve büyük dosyaların farkını ve dosya biçimini sabitler.
Ne zaman kullanılır
Bölüm başlığı “Ne zaman kullanılır”- Kullanıcı tablodaki kayıtları elektronik tabloda ya da raporda kullanmak istediğinde.
- Kapsam birden çok olabiliyorsa: filtrelenmiş kayıtlar ya da tüm kayıtlar.
- Yalnızca birkaç satırı kopyalamak için dışa aktarma yerine seçim ve Toplu işlemler yeterlidir.
Canlı örnek
Bölüm başlığı “Canlı örnek”Kapsamı seçin, sonra “CSV olarak indir”e basın. 120 kayıt hemen iner: düğme yükleniyor, sonra başarı işaretini gösterir. 12.000 kayıt arka planda hazırlanır: bir ilerleme çubuğu dolar, bitince “Dosya hazır” bildirimi “İndir” eylemiyle gelir ve bir indirme bağlantısı çıkar. Örnek bu süreleri zamanlayıcıyla (yaklaşık 1500 ms) taklit eder. Başarısız dışa aktarma için Storybook’ta “Failed” senaryosu bulunur: çubuk kırmızıya döner, tehlike bildirimi “Tekrar dene” sunar.
import { Button, Inline, Link, ProgressBar, Radio, RadioGroup, SplitButton, Stack, Text, useToast } from '@bigbang/ui';import { useEffect, useRef, useState } from 'react';import { type Lang, LangProvider } from '../lang';
const copy = { tr: { scope: 'Kapsam', filtered: 'Filtrelenmiş 120 kayıt', all: 'Tüm 12.000 kayıt', csv: 'CSV olarak indir', excel: 'Excel olarak indir', pdf: 'PDF olarak indir', menu: 'Diğer dışa aktarma biçimleri', preparingLabel: 'Dosya hazırlanıyor', doneLabel: 'İndirildi', preparing: 'Dosya hazırlanıyor', ready: 'Dosya hazır', download: 'Dosyayı indir', downloaded: (name: string) => `${name} indirildi`, progress: 'Dışa aktarma ilerlemesi', failedStatus: 'Dışa aktarma başarısız', toastDownload: 'İndir', toastFailed: 'Dışa aktarma başarısız', toastFailedText: 'Dosya hazırlanamadı. Tekrar deneyin.', retry: 'Tekrar dene', base: 'kayit', }, en: { scope: 'Scope', filtered: 'Filtered 120 records', all: 'All 12,000 records', csv: 'Download as CSV', excel: 'Download as Excel', pdf: 'Download as PDF', menu: 'Other export formats', preparingLabel: 'File is being prepared', doneLabel: 'Downloaded', preparing: 'Preparing the file', ready: 'File ready', download: 'Download the file', downloaded: (name: string) => `${name} downloaded`, progress: 'Export progress', failedStatus: 'Export failed', toastDownload: 'Download', toastFailed: 'Export failed', toastFailedText: 'The file could not be prepared. Try again.', retry: 'Try again', base: 'records', },};
type Format = 'csv' | 'xlsx' | 'pdf';type Phase = 'idle' | 'small' | 'small-done' | 'preparing' | 'ready' | 'downloaded' | 'failed';
const SMALL_MS = 800;const STEP_MS = 300;const STEPS = 5;// With `fail`, the background export stops at this step.const FAIL_STEP = 3;const SUCCESS_MS = 2000;// Up to 10.000 rows download directly; above that the file is prepared in the background.const DIRECT_LIMIT = 10000;const rowCount = { filtered: 120, all: 12000 } as const;
/** * Export in two sizes. A small export (up to 10.000 rows) shows loading on the button, then the success mark and a status line. * A large export runs in the background: a progress bar fills while the file is prepared, then a toast offers the download. * `fail` makes the background export fail, to show the danger toast with a retry action. */export function DataExportDemo({ lang = 'tr', fail = false }: { lang?: Lang; fail?: boolean }) { return ( <LangProvider lang={lang}> <ExportFlow lang={lang} fail={fail} /> </LangProvider> );}
function ExportFlow({ lang, fail }: { lang: Lang; fail: boolean }) { const t = copy[lang]; const toast = useToast(); const [scope, setScope] = useState<keyof typeof rowCount>('filtered'); const [phase, setPhase] = useState<Phase>('idle'); const [fileName, setFileName] = useState(''); const [progress, setProgress] = useState(0); const timersRef = useRef<number[]>([]);
useEffect(() => () => timersRef.current.forEach((timer) => window.clearTimeout(timer)), []);
const later = (action: () => void, ms: number) => { timersRef.current.push(window.setTimeout(action, ms)); };
const run = (format: Format) => { timersRef.current.forEach((timer) => window.clearTimeout(timer)); timersRef.current = []; const name = `${t.base}-2026-10-28.${format}`; setFileName(name); setProgress(0); if (rowCount[scope] <= DIRECT_LIMIT) { setPhase('small'); later(() => setPhase('small-done'), SMALL_MS); later(() => setPhase('idle'), SMALL_MS + SUCCESS_MS); return; } setPhase('preparing'); for (let step = 1; step <= STEPS; step += 1) { later(() => { if (fail && step === FAIL_STEP) { setPhase('failed'); toast.show({ title: t.toastFailed, description: t.toastFailedText, tone: 'danger', action: { label: t.retry, onClick: () => run(format) } }); return; } setProgress((step / STEPS) * 100); if (step === STEPS) { setPhase('ready'); toast.show({ title: t.ready, description: name, tone: 'success', action: { label: t.toastDownload, onClick: () => setPhase('downloaded') } }); } }, step * STEP_MS); } };
const status = phase === 'preparing' ? t.preparing : phase === 'ready' ? t.ready : phase === 'small-done' || phase === 'downloaded' ? t.downloaded(fileName) : phase === 'failed' ? t.failedStatus : '';
return ( <Stack gap="lg" className="max-w-md"> <RadioGroup label={t.scope} value={scope} onValueChange={(next) => setScope(next === 'all' ? 'all' : 'filtered')}> <Radio value="filtered" label={t.filtered} /> <Radio value="all" label={t.all} /> </RadioGroup> <Inline gap="md" align="center" wrap> {phase === 'small' ? ( <Button loading loadingLabel={t.preparingLabel}> {t.csv} </Button> ) : phase === 'small-done' ? ( <Button success successLabel={t.doneLabel}> {t.csv} </Button> ) : ( <SplitButton menuLabel={t.menu} onClick={() => run('csv')} actions={[ { label: t.excel, onSelect: () => run('xlsx') }, { label: t.pdf, onSelect: () => run('pdf') }, ]} > {t.csv} </SplitButton> )} {phase === 'ready' ? ( <Link href="#download" onClick={() => setPhase('downloaded')}> {t.download} </Link> ) : null} </Inline> {phase === 'preparing' || phase === 'failed' ? ( <div> <ProgressBar label={t.progress} value={progress} tone={phase === 'failed' ? 'danger' : 'brand'} size="sm" /> </div> ) : null} <Text size="sm" tone="subtle" role="status"> {status} </Text> </Stack> );}Kurallar
Bölüm başlığı “Kurallar”| Kural | Değer |
|---|---|
| Ana düğme | “CSV olarak indir”; yanındaki menüde “Excel olarak indir”, “PDF olarak indir”. |
| Kapsam | Radyo grubu: “Filtrelenmiş 120 kayıt” ya da “Tüm 12.000 kayıt”. Sayılar görünür. |
| Doğrudan indirme | En fazla 10.000 satır. Düğme yükleniyor, ardından başarı işareti (2000 ms) gösterir. |
| Arka plan | 10.000 satırın üstü arka planda hazırlanır; ProgressBar ilerlemeyi gösterir. Bitince “İndir” eylemli bir Toast ve indirme bağlantısı gelir. Hata olursa tehlike tonlu bir Toast “Tekrar dene” sunar. |
| Filtre, sıra, sütun | Dışa aktarma geçerli filtreleri, sıralamayı ve görünür sütunları izler. |
| Dosya adı | <kayit-turu>-<YYYY-MM-DD>.csv, örneğin kayit-2026-10-28.csv. |
| Gizli alanlar | Gizlenmiş ya da maskelenmiş alanlar dosyada da maskeli kalır. |
| Dosya biçimi | CSV, UTF-8 ve BOM ile; tarihler ISO 8601. |
Davranış
Bölüm başlığı “Davranış”- Kullanıcı kapsamı seçer. Varsayılan kapsam filtrelenmiş kayıtlardır.
- Kullanıcı ana düğmeye ya da menüdeki bir biçime basar.
- Kayıt sayısı 10.000 ya da altındaysa düğme yükleniyor durumuna geçer, dosya iner ve düğme 2000 ms başarı işaretini gösterir. Durum metni dosya adını söyler.
- Sayı 10.000’in üstündeyse iş arka planda başlar. Durum “Dosya hazırlanıyor” olur ve ilerleme çubuğu dolar; kullanıcı sayfada çalışmaya devam eder.
- Dosya hazır olunca durum “Dosya hazır” olur, “İndir” eylemli bir bildirim çıkar ve “Dosyayı indir” bağlantısı kalır.
- Dosya hazırlanamazsa tehlike tonlu bir bildirim hatayı söyler ve “Tekrar dene” sunar; tehlike bildirimi kapatılana dek kalır.
Dışa aktarma sırasında filtre ya da sıralama değişirse, çalışan iş başladığı andaki ayarlarla sürer.
Erişilebilirlik
Bölüm başlığı “Erişilebilirlik”- Menü düğmesi yalnızca bir ok gösterir; adı “Diğer dışa aktarma biçimleri”dir.
- Kapsam,
<fieldset>ve açıklayıcı göstergeyle bir radyo grubudur; okun tuşları seçeneği değiştirir. - Durum metni nazik bir durum bölgesindedir. “Dosya hazırlanıyor” ve “Dosya hazır” odak taşınmadan duyurulur (WCAG 4.1.3).
- Düğmenin yükleniyor ve başarı durumu ekran okuyucuya metinle bildirilir.
- İndirme bağlantısı gerçek bir bağlantıdır; klavye ile erişilir.
İçerik
Bölüm başlığı “İçerik”- Düğmeler fiille başlar: “CSV olarak indir”, “Excel olarak indir”.
- Durum metni kısa ve geçmiş ya da süren zamandadır: “Dosya hazırlanıyor”, “Dosya hazır”.
- Dosya adı kayıt türünü ve tarihi içerir; kişisel veri içermez.
- Kapsam sayıları binlik ayraçla yazılır: “12.000”.
Hareket
Bölüm başlığı “Hareket”| Öğe | Hareket | Preset |
|---|---|---|
| Düğme yükleniyor | 1000 ms doğrusal dönen gösterge | Spinner bileşeni |
| Başarı işareti | 0,6 ölçekten 1’e 240 ms, 2000 ms görünür kalır | motion.success |
| Menü | 150 ms açılma, 110 ms kapanma | motion.popover |
Hareketi azaltma tercihinde ölçek ve kaymalar kalkar; başarı işareti hareketsiz gösterilir.
Kullanılan bileşenler
Bölüm başlığı “Kullanılan bileşenler”SplitButton, RadioGroup, ProgressBar, Toast, Button, Link, Text. İlgili kalıplar: Filtreleme, Toplu işlemler.