Coverage Summary for Class: SurferPickerRowKt (com.georgeci.moneysurfer.uikit.components)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| SurferPickerRowKt |
100%
(1/1)
|
62.5%
(5/8)
|
48.1%
(25/52)
|
100%
(73/73)
|
92.2%
(677/734)
|
package com.georgeci.moneysurfer.uikit.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.georgeci.moneysurfer.uikit.atom.SurferCard
import com.georgeci.moneysurfer.uikit.icons.SurferIcons
import com.georgeci.moneysurfer.uikit.preview.SurferComponentPreview
import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
import com.georgeci.moneysurfer.uikit.theme.AppTheme
import com.georgeci.moneysurfer.uikit.theme.SurferContainerStyle
/**
* Row used for fields that open a picker sheet (account, category, recurring frequency, …).
*
* Layout: leading squircle tile, two-line label/value column, optional supporting line, trailing
* chevron. Wraps [SurferCard] so the row inherits the active [SurferContainerStyle] —
* `Card` adds a 2dp shadow that lifts to 8dp when [selected], `Filled` paints
* `surfaceContainer`, `Outlined` keeps the 1dp border. That way the picker matches every other
* surface in the screen instead of hard-coding its own treatment.
*/
@Composable
fun SurferPickerRow(
label: String,
value: String,
icon: ImageVector,
onClick: () -> Unit,
modifier: Modifier = Modifier,
selected: Boolean = false,
iconBackground: Color = AppTheme.materialColors.primaryContainer,
iconTint: Color = AppTheme.materialColors.onPrimaryContainer,
supporting: String? = null,
) {
SurferPickerRow(
label = label,
value = value,
onClick = onClick,
modifier = modifier,
selected = selected,
supporting = supporting,
leading = {
SurferPickerRowIcon(
icon = icon,
background = iconBackground,
tint = iconTint,
)
},
)
}
/**
* Slot-based variant — caller supplies the leading composable (e.g. a category bubble) instead
* of an [ImageVector]. Use when the leading visual is more than a single tinted icon.
*/
@Composable
fun SurferPickerRow(
label: String,
value: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
selected: Boolean = false,
supporting: String? = null,
leading: @Composable () -> Unit,
) {
SurferCard(modifier = modifier.fillMaxWidth(), selected = selected, onClick = onClick) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(AppTheme.spacing.medium),
) {
leading()
Column(modifier = Modifier.weight(1f)) {
Text(
text = label,
style = AppTheme.typography.bodySmall,
color = AppTheme.materialColors.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = value,
style = AppTheme.typography.titleSmall,
color = AppTheme.materialColors.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
if (!supporting.isNullOrBlank()) {
Text(
text = supporting,
style = AppTheme.typography.bodySmall,
color = AppTheme.materialColors.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
Icon(
imageVector = SurferIcons.DropDown,
contentDescription = SurferSemantics.Decorative,
tint = AppTheme.materialColors.onSurfaceVariant,
modifier = Modifier.size(18.dp),
)
}
}
}
@Composable
private fun SurferPickerRowIcon(
icon: ImageVector,
background: Color,
tint: Color,
) {
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(percent = 36))
.background(background),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = icon,
contentDescription = SurferSemantics.Decorative,
tint = tint,
modifier = Modifier.size(20.dp),
)
}
}
@Preview
@Composable
private fun SurferPickerRowCardPreview() {
SurferComponentPreview(containerStyle = SurferContainerStyle.Card) {
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
SurferPickerRow(
label = "From account",
value = "Everyday · €2,480.32",
icon = SurferIcons.CreditCard,
onClick = {},
)
SurferPickerRow(
label = "Category",
value = "Coffee",
supporting = "Subcategory of Dining",
icon = SurferIcons.Category,
onClick = {},
)
SurferPickerRow(
label = "Selected",
value = "Savings",
icon = SurferIcons.Savings,
selected = true,
onClick = {},
)
}
}
}
@Preview
@Composable
private fun SurferPickerRowFilledPreview() {
SurferComponentPreview(containerStyle = SurferContainerStyle.Filled) {
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
SurferPickerRow(
label = "From account",
value = "Everyday · €2,480.32",
icon = SurferIcons.CreditCard,
onClick = {},
)
SurferPickerRow(
label = "Category",
value = "Coffee",
icon = SurferIcons.Category,
onClick = {},
)
}
}
}
@Preview
@Composable
private fun SurferPickerRowOutlinedPreview() {
SurferComponentPreview(containerStyle = SurferContainerStyle.Outlined) {
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
SurferPickerRow(
label = "From account",
value = "Everyday · €2,480.32",
icon = SurferIcons.CreditCard,
onClick = {},
)
SurferPickerRow(
label = "Category",
value = "Coffee",
icon = SurferIcons.Category,
onClick = {},
)
}
}
}