File Drop
Uygulamaya dosya verilen yer: basılınca platformun seçicisini açan, üstüne platformun dosyalarından, başka bir uygulamadan ya da başka bir pencereden bırakılan dosyaları alan, kesik kenarlı bir bölge.
import com.pantrixui.components.elements.filedrop.PantrixFileDrop
- Basmak her yerde çalışır. Bölgeye basmak (ya da odaktayken Enter veya Boşluk) platformun seçicisini açar: Android'de belgeler, iOS'ta Dosyalar, masaüstünde sistemin penceresi, web'de tarayıcınınki. Yalnızca görsel alınıyorsa Android ve iOS fotoğraf seçicisini açar. Hiçbiri izin istemez.
- Bırakmak işaretçinin işidir. Masaüstünde ve web'de dosyalar bölgenin üstüne sürüklenip bırakılır; Android'de ve iPad'de yandaki bir uygulamadan da. Sürüklerken bölge birincil rolün rengine döner ve "Eklemek için bırakın" der. Yazısı da buna göredir: masaüstünde ve web'de önce bırakmayı, telefonda önce seçmeyi söyler.
- Eleme.
acceptalınan türleri,maxSizeen büyük boyutu,multiplebirden fazla dosyayı belirler. Uymayan dosyalar nedenleriyle (tür, boyut, sayı)onRejected'a gider, gerisionFiles'a; ne söyleneceği uygulamanındır. - Bölge durum tutmaz. Alınan dosyaları uygulama tutar ve bölgenin altına
PantrixFileItemsatırları olarak dizer: ad (uzunsa ortası kısaltılır), dile göre boyut, kaldırma düğmesi, isteğe bağlı ilerleme ve hata. - Okumak.
PantrixFileadını, boyutunu ve türünü taşır; baytlarınıreadBytes()istendiğinde okur. Platformun kendi dosyası da oradadır: Android'deuri, iOS'taurl, masaüstündefile, web'dedomFile. - Platform. Bölge telefonda 140dp, masaüstünde ve web'de 120dp yüksekliğindedir; satırlar telefonda 56dp, işaretçinin altında 44dp.
Örnekler
Ekler: türler, boyut ve satırlar
Bir formun ekleri: PDF ya da görsel, birden fazla, her biri en çok 10 MB. Alınan her dosya bir satır olur ve gönderiliyormuş gibi dolan bir çubuk alır; kaldırma düğmesi satırı siler. Alınmayanlar nedenleriyle yardım metninde yazılır.
val attachments = remember { mutableStateListOf<PantrixFile>() }
val sent = remember { mutableStateMapOf<PantrixFile, Float>() }
var refused by remember { mutableStateOf(emptyList<RejectedFile>()) }
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixFileDrop(
onFiles = { files ->
attachments += files
refused = emptyList()
},
accept = listOf(PantrixFileType.Pdf, PantrixFileType.Images),
multiple = true,
maxSize = TEN_MEGABYTES,
onRejected = { refused = it },
supportingText = if (refused.isEmpty()) "PDF ya da görsel, her biri en çok 10 MB" else reasonOf(refused),
error = refused.isNotEmpty(),
tagKey = "attachments"
)
attachments.forEach { file ->
key(file) {
SendingLater(file, sent)
PantrixFileItem(
name = file.name,
size = file.size,
onRemove = {
attachments -= file
sent -= file
},
progress = sent[file]?.takeIf { it < 1f },
tagKey = file.name
)
}
}
}
@Composable
private fun SendingLater(
file: PantrixFile,
sent: MutableMap<PantrixFile, Float>
) {
LaunchedEffect(file) {
if (file in sent) return@LaunchedEffect
repeat(STEPS + 1) { step ->
sent[file] = step / STEPS.toFloat()
delay(STEP_MILLIS)
}
}
}
private fun reasonOf(refused: List<RejectedFile>): String = refused.groupBy { it.reason }.entries.joinToString(" · ") {
val names = it.value.joinToString { rejected -> rejected.file.name }
when (it.key) {
FileRejection.Type -> "Bu tür alınmıyor: $names"
FileRejection.Size -> "Çok büyük: $names"
FileRejection.Count -> "Yalnızca bir dosya alınır: $names"
}
}
private const val TEN_MEGABYTES = 10_000_000L
private const val STEPS = 10
private const val STEP_MILLIS = 150L
Tek bir görsel ve ret nedeni
Tek bir görsel, en çok 2 MB: bir profil resmi. Telefonda fotoğraf seçicisi açılır. Seçilen resim okunur ve gösterilir; daha büyüğü ya da görsel olmayan bir dosya reddedilir ve nedeni bölgenin altında hata olarak yazılır.
var avatar by remember { mutableStateOf<PantrixFile?>(null) }
var refused by remember { mutableStateOf<RejectedFile?>(null) }
var picture by remember { mutableStateOf<ImageBitmap?>(null) }
LaunchedEffect(avatar) {
// Baytlar ancak istenince okunur: burada, bölgenin işi bittikten sonra.
picture = avatar?.let { file -> runCatching { file.readBytes().decodeToImageBitmap() }.getOrNull() }
}
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixFileDrop(
onFiles = { files ->
avatar = files.single()
refused = null
},
accept = listOf(PantrixFileType.Images),
maxSize = TWO_MEGABYTES,
onRejected = { refused = it.first() },
label = "Profil resmi seçin",
supportingText = refused?.let { reasonOf(listOf(it)) } ?: "PNG, JPEG ya da WebP, en çok 2 MB",
error = refused != null,
tagKey = "avatar"
)
picture?.let {
PantrixImage(bitmap = it, contentDescription = "Seçilen resim", modifier = Modifier.size(PICTURE_SIZE))
}
avatar?.let { PantrixFileItem(name = it.name, size = it.size, onRemove = { avatar = null }, tagKey = "avatar") }
}
private fun reasonOf(refused: List<RejectedFile>): String = refused.groupBy { it.reason }.entries.joinToString(" · ") {
val names = it.value.joinToString { rejected -> rejected.file.name }
when (it.key) {
FileRejection.Type -> "Bu tür alınmıyor: $names"
FileRejection.Size -> "Çok büyük: $names"
FileRejection.Count -> "Yalnızca bir dosya alınır: $names"
}
}
private val PICTURE_SIZE = 96.dp
private const val TWO_MEGABYTES = 2_000_000L
Kendi sözleri, renkleri ve devre dışı
Bölgenin kendi sözleri ve renkleri: ilk satırı çağıran yazar, dolgusu ve kenarı uygulamanın kendi rengidir. Anahtar kapalıyken bölge hiçbir şey almaz: basılmaz, üstüne bırakılan alınmaz, soluk çizilir.
var open by rememberSaveable { mutableStateOf(true) }
var taken by remember { mutableStateOf(0) }
val own = ownColors()
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixSwitchButton(checked = open, onCheckedChange = { open = it }, label = "Bölge dosya alıyor", tagKey = "accepting")
PantrixFileDrop(
onFiles = { taken += it.size },
multiple = true,
label = "Raporlarınızı buraya bırakın",
enabled = open,
colors = FileDropColors(container = own.container, border = own.color, action = own.color),
tagKey = "reports"
)
PantrixText(
text = "Alınan dosya: $taken",
color = PantrixTheme.colors.textSecondary,
tagKey = "taken"
)
}
Kendi stiliyle alçak bir alan ve sık satırlar
Kendi stiliyle: FileDropStyle bırakma alanının yüksekliğini, kesik çizgisini ve köşesini, FileItemStyle dosya satırının yüksekliğini ve ilerleme çizgisini verir. Verilmeyen her alan platformun ön ayarıdır.
val files = remember { mutableStateListOf<PantrixFile>() }
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.xs)) {
PantrixFileDrop(
onFiles = { files += it },
multiple = true,
style = FileDropStyle(minHeight = 72.dp, shape = ShapeRole.Large, dashLength = 4.dp, dashGap = 4.dp),
tagKey = "low"
)
files.forEach { file ->
key(file) {
PantrixFileItem(
name = file.name,
size = file.size,
onRemove = { files -= file },
style = FileItemStyle(minHeight = 40.dp, verticalPadding = 4.dp, progressHeight = 2.dp),
tagKey = file.name
)
}
}
}
API
onFiles
Uygulamaya dosya verilen yer: basılınca platformun seçicisini açan, üstüne platformun dosyalarından, başka bir uygulamadan ya da başka bir pencereden bırakılan dosyaları alan, kesik kenarlı bir bölge.
| Parametre | Tür | Varsayılan | Ne olduğu |
|---|---|---|---|
onFiles | (List<PantrixFile>) -> Unit | bölgenin aldığı dosyalar, en az bir tane, geldikleri sırayla ona verilir. | |
modifier | Modifier | Modifier | bölgenin modifier'ı, yardım metniyle birlikte. Bölge kendisine verilen genişliği doldurur. |
accept | List<PantrixFileType> | listOf(PantrixFileType.Any) | bölgenin aldığı dosya türleri. Platformun seçicisi süzebiliyorsa seçici yalnızca bunları gösterir. |
multiple | Boolean | false | bölgenin bir seferde birden fazla dosya alıp almadığı. |
maxSize | Long? | null | bölgenin aldığı bir dosyanın en çok kaç bayt olabileceği ya da her boyut için null. |
onRejected | (List<RejectedFile>) -> Unit | {} | bölgenin almadığı dosyalar, her biri nedeniyle, en az bir tane, ona verilir. |
label | String? | null | bölgenin platformun sözleri yerine ilk söylediği. |
supportingText | String? | null | bölgenin altında bir satır: ne aldığı ve ne büyüklükte, ya da neyin yanlış olduğu. |
enabled | Boolean | true | bölgenin bir şey alıp almadığı: bir basış ya da bir bırakma. Almayan bölge soluk çizilir. |
error | Boolean | false | bölgenin, kendisine verilenin doğru olmadığını gösterip göstermediği: kenarı ve yardım metni tehlike renginde. Ekran okuyucu yardım metnini hata olarak duyar. |
picker | FilePicker | rememberFilePicker() | bir basışın açtığı: platformun kendi seçicisi ya da bir testinki. |
interactionSource | MutableInteractionSource? | null | bölgenin etkileşimleri: basışı, üstüne gelinmesi ve odağı. |
style | FileDropStyle? | null | Bu çağrının kendi stili: çevresindeki her stili ezer, yoğunluk ona da uygulanır. Yalnızca verdiği alanlar değişir ve her platformda değişir. |
colors | FileDropColors? | null | bu bölgenin kendi renkleri: verdikleri, gerisi temanın. |
tagKey | String? | null | bölgenin otomasyon anahtarı: FileDrop_<tagKey> olur. |
FileDropStyle
PantrixFileDrop'un geometrisi: bölge, kesik kenarı ve içinde duranlar.
| Parametre | Tür | Varsayılan | Ne olduğu |
|---|---|---|---|
shape | ShapeSpec | ShapeSpec.Unspecified | Bölgenin köşeleri: markanın şekil ölçeğinden bir basamak ya da sabit bir şekil. |
minHeight | Dp | Dp.Unspecified | Bölgenin en az yüksekliği. Daha büyük bir yazı onu yükseltir. |
padding | Dp | Dp.Unspecified | Bölgenin kenarı ile içinde duranlar arasındaki boşluk. |
borderWidth | Dp | Dp.Unspecified | Kesik kenarın kalınlığı. |
dashLength | Dp | Dp.Unspecified | Kenarın her bir kesiğinin uzunluğu. |
dashGap | Dp | Dp.Unspecified | İki kesik arasındaki boşluk. |
iconSize | Dp | Dp.Unspecified | Bölgenin sözlerinin üstündeki ikonun genişliği ve yüksekliği. |
gap | Dp | Dp.Unspecified | İkon ile sözler arasında ve iki söz satırı arasında. |
FileDropColors
Bir PantrixFileDrop'un temanın renkleri yerine boyandığı renkler: verilen renk kullanılır, verilmeyen temanın kalır.
| Parametre | Tür | Varsayılan | Ne olduğu |
|---|---|---|---|
container | Color | Color.Unspecified | Bölgenin dolgusu. Temanın surface'ı. |
content | Color | Color.Unspecified | Bölgenin ilk satırı ve ikonu. Temanın textPrimary'si ya da container olarak verilen bir rol dolgusunun üstünde durana verdiği renk. |
action | Color | Color.Unspecified | İkinci satır, seçiciyi açan basış, bir bağlantı gibi. Temanın kendi zeminlerinde birincil rolün color'ı, başka bir dolguda content. |
border | Color | Color.Unspecified | Kesik kenar. Temanın kendi zeminlerinde border'ı, işaretçinin altında borderHover'ı; başka bir dolguda, bir dolgu üstündeki çizgi gibi content'in bir payı. |
overContainer | Color | Color.Unspecified | Üstünde bir şey sürüklenirken bölgenin dolgusu. Birincil rolün container'ı. |
overContent | Color | Color.Unspecified | O sırada içinde yazılan. Birincil rolün onContainer'ı. |
overBorder | Color | Color.Unspecified | O sıradaki kenar. Birincil rolün color'ı. |
error | Color | Color.Unspecified | Hata gösteren bir bölgenin kenarı ve yardım metni. Tehlike rolünün color'ı. |
supporting | Color | Color.Unspecified | Bölgenin altındaki yardım metni. Temanın textSecondary'si. |
Erişilebilirlik
- Ekran okuyucu. Bütün bölge tek bir düğmedir. Kaç dosya alındığı ve kaç dosyanın alınmadığı kibarca duyurulur;
errorile yardım metni hata olarak okunur.