Pantrix UI

File Item

Uygulamanın aldığı bir dosya, bir satırda: ikonu, adı, boyutu ya da onunla ilgili neyin yanlış gittiği, ne kadarının gönderildiği ve onu kaldıran düğme. Satırlar bir PantrixFileDrop'un altında, her biri uygulamanın tuttuğu bir dosya için durur.

import com.pantrixui.components.elements.filedrop.PantrixFileItem

Ö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 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

name

Uygulamanın aldığı bir dosya, bir satırda: ikonu, adı, boyutu ya da onunla ilgili neyin yanlış gittiği, ne kadarının gönderildiği ve onu kaldıran düğme. Satırlar bir PantrixFileDrop'un altında, her biri uygulamanın tuttuğu bir dosya için durur.

ParametreTürVarsayılanNe olduğu
nameStringdosyanın adı.
modifierModifierModifiersatırın modifier'ı. Satır kendisine verilen genişliği doldurur.
sizeLong?nulldosyanın kaç bayt olduğu ya da hiçbir şey söylememek için null.
onRemove(() -> Unit)?nullkaldırma düğmesinin yaptığı ya da düğmesiz bir satır için null.
progressFloat?nulldosyanın ne kadarının gönderildiği, sıfırdan bire, ya da çubuk olmaması için null.
errorString?nulldosyayla ilgili neyin yanlış gittiği, boyutunun yerinde, ya da null.
iconImageVectorPantrixIcons.Fileadın önünde duran: varsayılan olarak bir dosya.
enabledBooleantruekaldırma düğmesine basılıp basılamayacağı. Basılamayan satır soluk çizilir.
styleFileItemStyle?nullBu ç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.
tagKeyString?nullsatırın otomasyon anahtarı: FileItem_<tagKey> olur, düğmesininki de onun içinde IconButton_remove. Yalnızca anahtar verildiğinde etiketlenir.

FileItemStyle

PantrixFileItem'ın geometrisi: uygulamanın aldığı bir dosya için ikonu, adı ve boyutu ve onu kaldıran düğmesiyle bir satır.

ParametreTürVarsayılanNe olduğu
shapeShapeSpecShapeSpec.UnspecifiedSatırın köşeleri.
minHeightDpDp.UnspecifiedSatırın en az yüksekliği. Daha büyük bir yazı, bir ilerleme çubuğu ya da bir hata onu yükseltir.
horizontalPaddingDpDp.UnspecifiedSatırın yanları ile tuttukları arasındaki boşluk.
verticalPaddingDpDp.UnspecifiedTuttuklarının üstündeki ve altındaki boşluk.
borderWidthDpDp.UnspecifiedSatırın kenarının kalınlığı.
iconSizeDpDp.UnspecifiedDosyanın ikonunun genişliği ve yüksekliği.
gapDpDp.Unspecifiedİkon, sözler ve düğme arasında; sözler ile ilerleme çubuğu arasında.
progressHeightDpDp.UnspecifiedSözlerin altındaki ilerleme çubuğunun yüksekliği.

Erişilebilirlik