fix: add card elevation and interaction feedback to Attribute/Talent cards (Closes #8)
Use CardDefaults.cardElevation() with default=2dp, pressed=6dp, hovered=4dp on both Attribute and Talent cards. Switch from Modifier.clickable to Card(onClick=...) for proper Material 3 interaction ripple and pressed elevation animation. Extract card content into private composable functions to avoid duplication between onClick and non-onClick card variants. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,10 @@ package org.shahondin1624.lib.components.charactermodel.attributespage
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -13,6 +13,7 @@ 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.ColorFilter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -36,66 +37,87 @@ fun Attribute(
|
||||
onEdit: (() -> Unit)? = null,
|
||||
) {
|
||||
val textColor = contrastTextColor(attribute.type.color)
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.testTag(TestTags.attributeCard(attribute.type.name))
|
||||
.then(
|
||||
if (onEdit != null) Modifier.clickable { onEdit() }
|
||||
else Modifier
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
val cardModifier = Modifier.testTag(TestTags.attributeCard(attribute.type.name))
|
||||
val elevation = CardDefaults.cardElevation(
|
||||
defaultElevation = 2.dp,
|
||||
pressedElevation = 6.dp,
|
||||
hoveredElevation = 4.dp
|
||||
)
|
||||
if (onEdit != null) {
|
||||
Card(
|
||||
onClick = onEdit,
|
||||
modifier = cardModifier,
|
||||
elevation = elevation
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = SMALL_PADDING)
|
||||
.background(attribute.type.color, shape = RoundedCornerShape(4.dp))
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = attribute.type.name,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = SMALL_PADDING),
|
||||
textAlign = TextAlign.Center,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = attribute.value.toString(),
|
||||
modifier = Modifier
|
||||
.padding(end = SMALL_PADDING),
|
||||
textAlign = TextAlign.Center,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
val result = attribute.test()
|
||||
onRoll(result)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(end = SMALL_PADDING)
|
||||
.testTag(TestTags.rollButton(attribute.type.name))
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(Res.drawable.dice),
|
||||
contentDescription = "Roll dice",
|
||||
colorFilter = ColorFilter.tint(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
AttributeCardContent(attribute, textColor, onRoll)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
modifier = cardModifier,
|
||||
elevation = elevation
|
||||
) {
|
||||
AttributeCardContent(attribute, textColor, onRoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AttributeCardContent(
|
||||
attribute: Attribute,
|
||||
textColor: Color,
|
||||
onRoll: (DiceRoll) -> Unit
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = SMALL_PADDING)
|
||||
.background(attribute.type.color, shape = RoundedCornerShape(4.dp))
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = attribute.type.name,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = SMALL_PADDING),
|
||||
textAlign = TextAlign.Center,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = attribute.value.toString(),
|
||||
modifier = Modifier
|
||||
.padding(end = SMALL_PADDING),
|
||||
textAlign = TextAlign.Center,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
val result = attribute.test()
|
||||
onRoll(result)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(end = SMALL_PADDING)
|
||||
.testTag(TestTags.rollButton(attribute.type.name))
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(Res.drawable.dice),
|
||||
contentDescription = "Roll dice",
|
||||
colorFilter = ColorFilter.tint(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.shahondin1624.lib.components.charactermodel.attributespage
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -11,6 +10,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -18,6 +18,7 @@ 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.ColorFilter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -43,80 +44,102 @@ fun Talent(
|
||||
onEdit: (() -> Unit)? = null,
|
||||
) {
|
||||
val textColor = contrastTextColor(talent.attribute.color)
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.testTag(TestTags.talentCard(talent.name))
|
||||
.then(
|
||||
if (onEdit != null) Modifier.clickable { onEdit() }
|
||||
else Modifier
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
val cardModifier = Modifier.testTag(TestTags.talentCard(talent.name))
|
||||
val elevation = CardDefaults.cardElevation(
|
||||
defaultElevation = 2.dp,
|
||||
pressedElevation = 6.dp,
|
||||
hoveredElevation = 4.dp
|
||||
)
|
||||
if (onEdit != null) {
|
||||
Card(
|
||||
onClick = onEdit,
|
||||
modifier = cardModifier,
|
||||
elevation = elevation
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = SMALL_PADDING)
|
||||
.background(
|
||||
talent.attribute.color,
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = talent.name,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = SMALL_PADDING),
|
||||
textAlign = TextAlign.Start,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = attributes.getAttributeByType(talent.attribute).value.toString(),
|
||||
modifier = Modifier
|
||||
.widthIn(min = 24.dp)
|
||||
.padding(horizontal = SMALL_PADDING),
|
||||
textAlign = TextAlign.End,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = talent.value.toString(),
|
||||
modifier = Modifier
|
||||
.widthIn(min = 24.dp)
|
||||
.padding(end = SMALL_PADDING),
|
||||
textAlign = TextAlign.End,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
val result = talent.test(modifiers = emptyList(), attributes = attributes)
|
||||
onRoll(result)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(end = SMALL_PADDING)
|
||||
.testTag(TestTags.rollButton(talent.name))
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(Res.drawable.dice),
|
||||
contentDescription = "Roll dice",
|
||||
colorFilter = ColorFilter.tint(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
TalentCardContent(talent, attributes, textColor, onRoll)
|
||||
}
|
||||
} else {
|
||||
Card(
|
||||
modifier = cardModifier,
|
||||
elevation = elevation
|
||||
) {
|
||||
TalentCardContent(talent, attributes, textColor, onRoll)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TalentCardContent(
|
||||
talent: TalentDefinition,
|
||||
attributes: Attributes,
|
||||
textColor: Color,
|
||||
onRoll: (DiceRoll) -> Unit
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = SMALL_PADDING)
|
||||
.background(
|
||||
talent.attribute.color,
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = talent.name,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = SMALL_PADDING),
|
||||
textAlign = TextAlign.Start,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = attributes.getAttributeByType(talent.attribute).value.toString(),
|
||||
modifier = Modifier
|
||||
.widthIn(min = 24.dp)
|
||||
.padding(horizontal = SMALL_PADDING),
|
||||
textAlign = TextAlign.End,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = talent.value.toString(),
|
||||
modifier = Modifier
|
||||
.widthIn(min = 24.dp)
|
||||
.padding(end = SMALL_PADDING),
|
||||
textAlign = TextAlign.End,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
val result = talent.test(modifiers = emptyList(), attributes = attributes)
|
||||
onRoll(result)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(end = SMALL_PADDING)
|
||||
.testTag(TestTags.rollButton(talent.name))
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(Res.drawable.dice),
|
||||
contentDescription = "Roll dice",
|
||||
colorFilter = ColorFilter.tint(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user