Pantrix UI

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

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

ParametreTürVarsayılanNe olduğu
onFiles(List<PantrixFile>) -> Unitbölgenin aldığı dosyalar, en az bir tane, geldikleri sırayla ona verilir.
modifierModifierModifierbölgenin modifier'ı, yardım metniyle birlikte. Bölge kendisine verilen genişliği doldurur.
acceptList<PantrixFileType>listOf(PantrixFileType.Any)bölgenin aldığı dosya türleri. Platformun seçicisi süzebiliyorsa seçici yalnızca bunları gösterir.
multipleBooleanfalsebölgenin bir seferde birden fazla dosya alıp almadığı.
maxSizeLong?nullbö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.
labelString?nullbölgenin platformun sözleri yerine ilk söylediği.
supportingTextString?nullbölgenin altında bir satır: ne aldığı ve ne büyüklükte, ya da neyin yanlış olduğu.
enabledBooleantruebölgenin bir şey alıp almadığı: bir basış ya da bir bırakma. Almayan bölge soluk çizilir.
errorBooleanfalsebö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.
pickerFilePickerrememberFilePicker()bir basışın açtığı: platformun kendi seçicisi ya da bir testinki.
interactionSourceMutableInteractionSource?nullbölgenin etkileşimleri: basışı, üstüne gelinmesi ve odağı.
styleFileDropStyle?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.
colorsFileDropColors?nullbu bölgenin kendi renkleri: verdikleri, gerisi temanın.
tagKeyString?nullbölgenin otomasyon anahtarı: FileDrop_<tagKey> olur.

FileDropStyle

PantrixFileDrop'un geometrisi: bölge, kesik kenarı ve içinde duranlar.

ParametreTürVarsayılanNe olduğu
shapeShapeSpecShapeSpec.UnspecifiedBölgenin köşeleri: markanın şekil ölçeğinden bir basamak ya da sabit bir şekil.
minHeightDpDp.UnspecifiedBölgenin en az yüksekliği. Daha büyük bir yazı onu yükseltir.
paddingDpDp.UnspecifiedBölgenin kenarı ile içinde duranlar arasındaki boşluk.
borderWidthDpDp.UnspecifiedKesik kenarın kalınlığı.
dashLengthDpDp.UnspecifiedKenarın her bir kesiğinin uzunluğu.
dashGapDpDp.Unspecifiedİki kesik arasındaki boşluk.
iconSizeDpDp.UnspecifiedBölgenin sözlerinin üstündeki ikonun genişliği ve yüksekliği.
gapDpDp.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.

ParametreTürVarsayılanNe olduğu
containerColorColor.UnspecifiedBölgenin dolgusu. Temanın surface'ı.
contentColorColor.UnspecifiedBölgenin ilk satırı ve ikonu. Temanın textPrimary'si ya da container olarak verilen bir rol dolgusunun üstünde durana verdiği renk.
actionColorColor.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.
borderColorColor.UnspecifiedKesik 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ı.
overContainerColorColor.UnspecifiedÜstünde bir şey sürüklenirken bölgenin dolgusu. Birincil rolün container'ı.
overContentColorColor.UnspecifiedO sırada içinde yazılan. Birincil rolün onContainer'ı.
overBorderColorColor.UnspecifiedO sıradaki kenar. Birincil rolün color'ı.
errorColorColor.UnspecifiedHata gösteren bir bölgenin kenarı ve yardım metni. Tehlike rolünün color'ı.
supportingColorColor.UnspecifiedBölgenin altındaki yardım metni. Temanın textSecondary'si.

Erişilebilirlik