CardEditFields.kt

4.0 KB · raw

1package com.peachpasswords.ui.vault23import androidx.compose.foundation.clickable4import androidx.compose.foundation.layout.Arrangement5import androidx.compose.foundation.layout.Row6import androidx.compose.foundation.layout.padding7import androidx.compose.foundation.shape.RoundedCornerShape8import androidx.compose.material3.Text9import androidx.compose.runtime.Composable10import androidx.compose.ui.Modifier11import androidx.compose.ui.draw.clip12import androidx.compose.ui.text.input.KeyboardType13import androidx.compose.ui.text.input.PasswordVisualTransformation14import androidx.compose.ui.text.input.VisualTransformation15import androidx.compose.ui.unit.dp16import androidx.compose.ui.unit.sp17import com.peachpasswords.ui.theme.LocalPeachAccent18import com.peachpasswords.ui.theme.peachColors19import com.peachpasswords.ui.theme.PeachType2021/**22 * D2 — EntryEdit CARD field block. Self-contained: drop a single call to23 * [CardEditFields] in place of the login fields when `f.type == "card"`.24 *25 * Mirrors the existing private `Field` underline style in EntryEditScreen.kt26 * (1.dp T3 border, mono 13sp, accent cursor). Includes its own label+box27 * primitive (`CardField`) so it does not depend on that file's private members.28 *29 * Network is detected live from the card number; CVC maxLength + PAN grouping30 * follow the detected network (Amex 15/4, others 16/3).31 */32@Composable33fun CardEditFields(34    cardNickname: String,35    cardNumber: String,36    cardholderName: String,37    expiry: String,38    cvc: String,39    showCvc: Boolean,40    network: String,41    onCardNickname: (String) -> Unit,42    onCardNumber: (String) -> Unit,43    onCardholder: (String) -> Unit,44    onExpiry: (String) -> Unit,45    onCvc: (String) -> Unit,46    onToggleShowCvc: () -> Unit,47) {48    PeachField(49        label = "nickname",50        value = cardNickname,51        onChange = onCardNickname,52        mono = false,53    )5455    // CARD NUMBER — auto-formatted, network label beside the label.56    PeachField(57        label = "card number",58        value = cardNumber,59        onChange = { raw -> onCardNumber(CardFieldUtils.formatPan(raw)) },60        keyboardType = KeyboardType.Number,61        labelTrailing = {62            val net = CardFieldUtils.networkLabel(network)63            if (net.isNotEmpty()) {64                Text(net, color = peachColors.T2, fontFamily = PeachType.Mono, fontSize = 9.sp)65            }66        },67    )6869    // CARDHOLDER — Inter body, not mono.70    PeachField(71        label = "cardholder",72        value = cardholderName,73        onChange = onCardholder,74        mono = false,75    )7677    // EXPIRY — browser-style split MM / YYYY fields.78    Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {79        PeachField(80            label = "expiry mm",81            value = CardFieldUtils.expiryMonth(expiry),82            onChange = { raw -> onExpiry(CardFieldUtils.combineExpiry(raw, CardFieldUtils.expiryYear(expiry))) },83            keyboardType = KeyboardType.Number,84            modifier = Modifier.weight(1f),85        )86        PeachField(87            label = "expiry year",88            value = CardFieldUtils.expiryYear(expiry),89            onChange = { raw -> onExpiry(CardFieldUtils.combineExpiry(CardFieldUtils.expiryMonth(expiry), raw)) },90            keyboardType = KeyboardType.Number,91            modifier = Modifier.weight(1f),92        )93    }9495    // CVC — masked by default, eye toggle, maxLength by network.96    val cvcMax = CardFieldUtils.cvcMaxDigits(network)97    PeachField(98        label = "cvc",99        value = cvc,100        onChange = { raw -> onCvc(raw.filter { it.isDigit() }.take(cvcMax)) },101        keyboardType = KeyboardType.NumberPassword,102        visualTransformation = if (showCvc) VisualTransformation.None else PasswordVisualTransformation(),103        trailing = {104            Text(105                if (showCvc) "hide" else "show",106                color = LocalPeachAccent.current, fontFamily = PeachType.Mono, fontSize = 9.sp,107                modifier = Modifier.clip(RoundedCornerShape(6.dp)).clickable(onClick = onToggleShowCvc).padding(6.dp),108            )109        },110    )111}