taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

NewMenuComponents.kt (6094B)


      1 /**
      2  * Metrolist Project (C) 2026
      3  * Licensed under GPL-3.0 | See git history for contributors
      4  */
      5 
      6 package net.taler.wallet.compose
      7 
      8 import androidx.compose.animation.animateContentSize
      9 import androidx.compose.foundation.clickable
     10 import androidx.compose.foundation.layout.Arrangement
     11 import androidx.compose.foundation.layout.Column
     12 import androidx.compose.foundation.layout.Row
     13 import androidx.compose.foundation.layout.Spacer
     14 import androidx.compose.foundation.layout.fillMaxWidth
     15 import androidx.compose.foundation.layout.height
     16 import androidx.compose.foundation.layout.padding
     17 import androidx.compose.foundation.layout.width
     18 import androidx.compose.foundation.shape.RoundedCornerShape
     19 import androidx.compose.material3.Card
     20 import androidx.compose.material3.CardColors
     21 import androidx.compose.material3.CardDefaults
     22 import androidx.compose.material3.HorizontalDivider
     23 import androidx.compose.material3.ListItem
     24 import androidx.compose.material3.MaterialTheme
     25 import androidx.compose.material3.ProvideTextStyle
     26 import androidx.compose.material3.Text
     27 import androidx.compose.runtime.Composable
     28 import androidx.compose.ui.Alignment
     29 import androidx.compose.ui.Modifier
     30 import androidx.compose.ui.text.font.FontWeight
     31 import androidx.compose.ui.unit.dp
     32 import androidx.compose.ui.unit.sp
     33 
     34 // Enhanced Menu Item - Material 3 Expressive Design
     35 @Composable
     36 fun NewMenuItem(
     37     modifier: Modifier = Modifier,
     38     headlineContent: @Composable () -> Unit,
     39     leadingContent: @Composable (() -> Unit)? = null,
     40     trailingContent: @Composable (() -> Unit)? = null,
     41     supportingContent: @Composable (() -> Unit)? = null,
     42     onClick: (() -> Unit)? = null,
     43     enabled: Boolean = true,
     44 ) {
     45     ListItem(
     46         headlineContent = headlineContent,
     47         leadingContent = leadingContent,
     48         trailingContent = trailingContent,
     49         supportingContent = supportingContent,
     50         modifier = modifier
     51             .clickable(enabled = enabled) { onClick?.invoke() }
     52             .padding(horizontal = 4.dp),
     53         tonalElevation = 0.dp
     54     )
     55 }
     56 
     57 // Enhanced Menu Section Header - Material 3 Expressive Design
     58 @Composable
     59 fun NewMenuSectionHeader(
     60     modifier: Modifier = Modifier,
     61     text: String,
     62 ) {
     63     Text(
     64         text = text,
     65         style = MaterialTheme.typography.titleMedium.copy(
     66             fontWeight = FontWeight.SemiBold,
     67             fontSize = 16.sp
     68         ),
     69         color = MaterialTheme.colorScheme.primary,
     70         modifier = modifier.padding(horizontal = 20.dp, vertical = 12.dp)
     71     )
     72 }
     73 
     74 // Enhanced Menu Content - Material 3 Expressive Design
     75 @Composable
     76 fun NewMenuContent(
     77     modifier: Modifier = Modifier,
     78     headerContent: @Composable (() -> Unit)? = null,
     79     actionGrid: @Composable (() -> Unit)? = null,
     80     menuItems: @Composable (() -> Unit)? = null,
     81 ) {
     82     Column(
     83         modifier = modifier,
     84         verticalArrangement = Arrangement.spacedBy(8.dp)
     85     ) {
     86         // Header
     87         headerContent?.invoke()
     88 
     89         // Action Grid
     90         actionGrid?.invoke()
     91 
     92         // Divider if both header and actions exist
     93         if (headerContent != null && actionGrid != null) {
     94             HorizontalDivider(
     95                 modifier = Modifier.padding(vertical = 16.dp),
     96                 color = MaterialTheme.colorScheme.outlineVariant
     97             )
     98         }
     99 
    100         // Menu Items
    101         menuItems?.invoke()
    102     }
    103 }
    104 
    105 @Composable
    106 fun Material3MenuGroup(
    107     items: List<Material3MenuItemData>,
    108 ) {
    109     Column(
    110         modifier = Modifier.fillMaxWidth(),
    111         verticalArrangement = Arrangement.spacedBy(4.dp)
    112     ) {
    113         items.forEachIndexed { index, item ->
    114             val shape = when {
    115                 items.size == 1 -> RoundedCornerShape(24.dp)
    116                 index == 0 -> RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp, bottomStart = 6.dp, bottomEnd = 6.dp)
    117                 index == items.size - 1 -> RoundedCornerShape(topStart = 6.dp, topEnd = 6.dp, bottomStart = 24.dp, bottomEnd = 24.dp)
    118                 else -> RoundedCornerShape(6.dp)
    119             }
    120 
    121             Card(
    122                 modifier = Modifier
    123                     .fillMaxWidth()
    124                     .animateContentSize(),
    125                 shape = shape,
    126                 colors = item.cardColors ?: CardDefaults.cardColors(
    127                     containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
    128                 ),
    129                 elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
    130             ) {
    131                 Material3MenuItemRow(item = item)
    132             }
    133         }
    134     }
    135 }
    136 
    137 @Composable
    138 private fun Material3MenuItemRow(
    139     item: Material3MenuItemData,
    140 ) {
    141     Row(
    142         modifier = Modifier
    143             .fillMaxWidth()
    144             .clickable(
    145                 enabled = item.onClick != null,
    146                 onClick = { item.onClick?.invoke() }
    147             )
    148             .padding(horizontal = 20.dp, vertical = 16.dp),
    149         verticalAlignment = Alignment.CenterVertically
    150     ) {
    151         item.icon?.let { icon ->
    152             icon()
    153             Spacer(modifier = Modifier.width(16.dp))
    154         }
    155 
    156         Column(
    157             modifier = Modifier.weight(1f)
    158         ) {
    159             ProvideTextStyle(MaterialTheme.typography.titleMedium) {
    160                 item.title()
    161             }
    162 
    163             item.description?.let { desc ->
    164                 Spacer(modifier = Modifier.height(2.dp))
    165                 ProvideTextStyle(
    166                     MaterialTheme.typography.bodyMedium.copy(
    167                         color = MaterialTheme.colorScheme.onSurfaceVariant
    168                     )
    169                 ) {
    170                     desc()
    171                 }
    172             }
    173         }
    174         item.trailingContent?.let { trailing ->
    175             Spacer(modifier = Modifier.width(8.dp))
    176             trailing()
    177         }
    178     }
    179 }
    180 
    181 data class Material3MenuItemData(
    182     val icon: (@Composable () -> Unit)? = null,
    183     val title: @Composable () -> Unit,
    184     val description: (@Composable () -> Unit)? = null,
    185     val onClick: (() -> Unit)? = null,
    186     val cardColors: CardColors? = null,
    187     val trailingContent: (@Composable () -> Unit)? = null,
    188 )