Coverage Summary for Class: CategoryPickerGridKt (com.georgeci.moneysurfer.feature.category.picker)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| CategoryPickerGridKt |
0%
(0/6)
|
0%
(0/20)
|
0%
(0/56)
|
0%
(0/553)
|
| CategoryPickerGridKt$CategoryPickerGrid$1$1$2$1$1 |
0%
(0/1)
|
|
0%
(0/1)
|
0%
(0/8)
|
| CategoryPickerGridKt$CategoryPickerGrid$lambda$0$0$$inlined$items$default$1 |
0%
(0/1)
|
|
| CategoryPickerGridKt$CategoryPickerGrid$lambda$0$0$$inlined$items$default$2 |
0%
(0/1)
|
|
| CategoryPickerGridKt$CategoryPickerGrid$lambda$0$0$$inlined$items$default$3 |
0%
(0/1)
|
|
| CategoryPickerGridKt$CategoryPickerGrid$lambda$0$0$$inlined$items$default$4 |
0%
(0/1)
|
|
| CategoryPickerGridKt$CategoryPickerGrid$lambda$0$0$$inlined$items$default$5 |
0%
(0/1)
|
|
| Total |
0%
(0/12)
|
0%
(0/20)
|
0%
(0/57)
|
0%
(0/561)
|
package com.georgeci.moneysurfer.feature.category.picker
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.CircleShape
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.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.georgeci.moneysurfer.domain.primitives.CategoryId
import com.georgeci.moneysurfer.uikit.components.SurferCategoryPalette
import com.georgeci.moneysurfer.uikit.components.category.SurferCategoryCell
import com.georgeci.moneysurfer.uikit.icons.SurferIcons
import com.georgeci.moneysurfer.uikit.modifier.surferDashedBorder
import com.georgeci.moneysurfer.uikit.semantics.SurferSemantics
import com.georgeci.moneysurfer.uikit.theme.AppTheme
import moneysurfer.feature.category.generated.resources.Res
import moneysurfer.feature.category.generated.resources.category_picker_new
import org.jetbrains.compose.resources.stringResource
/** Column count the artboard specifies for the flat grid. */
private const val GridColumns = 4
private val GridMinHeight = 200.dp
private val GridMaxHeight = 420.dp
private val GridSpacing = 10.dp
private val BubbleSize = 44.dp
private val CellRadius = 14.dp
private val NewGlyphSize = 20.dp
private val CellVerticalPadding = 8.dp
private val CellLabelSpacing = 6.dp
/**
* Variant A of the picker: a flat 4-column grid of every category of the selected type, parents
* and children mixed with no hierarchy — the fast path for someone who already knows the name.
* The last cell always creates a new category.
*/
@Composable
internal fun CategoryPickerGrid(
items: List<CategoryPickerItem>,
onSelect: (CategoryId) -> Unit,
onCreateNew: () -> Unit,
modifier: Modifier = Modifier,
) {
LazyVerticalGrid(
columns = GridCells.Fixed(GridColumns),
modifier = modifier
.fillMaxWidth()
.heightIn(min = GridMinHeight, max = GridMaxHeight),
contentPadding = PaddingValues(
horizontal = SheetBodyPadding,
vertical = CellVerticalPadding,
),
horizontalArrangement = Arrangement.spacedBy(GridSpacing),
verticalArrangement = Arrangement.spacedBy(GridSpacing),
) {
items(items, key = { it.id.value }) { item ->
val visual = SurferCategoryPalette.visualFor(
id = item.id.value,
iconKey = item.iconKey,
hue = item.hue,
systemKind = item.systemKind,
)
SurferCategoryCell(
name = item.name,
icon = visual.icon,
tint = visual.tint,
selected = item.selected,
onClick = { onSelect(item.id) },
)
}
item(key = "create-new") {
NewCategoryCell(onClick = onCreateNew)
}
}
}
/** The trailing "New" tile — a dashed outline circle, so it never reads as a real category. */
@Composable
private fun NewCategoryCell(
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val outline = AppTheme.materialColors.outline
Column(
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(CellRadius))
.clickable(onClick = onClick)
.padding(vertical = CellVerticalPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(CellLabelSpacing),
) {
Box(
modifier = Modifier
.size(BubbleSize)
.surferDashedBorder(color = outline, shape = CircleShape),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = SurferIcons.Add,
contentDescription = SurferSemantics.Decorative,
tint = AppTheme.materialColors.primary,
modifier = Modifier.size(NewGlyphSize),
)
}
Text(
text = stringResource(Res.string.category_picker_new),
style = AppTheme.typography.labelMedium,
color = AppTheme.materialColors.primary,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}