diff --git a/app/src/main/java/me/rerere/rikkahub/ui/components/ui/Tag.kt b/app/src/main/java/me/rerere/rikkahub/ui/components/ui/Tag.kt index 125169f0a..463c14447 100644 --- a/app/src/main/java/me/rerere/rikkahub/ui/components/ui/Tag.kt +++ b/app/src/main/java/me/rerere/rikkahub/ui/components/ui/Tag.kt @@ -1,7 +1,11 @@ package me.rerere.rikkahub.ui.components.ui +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,11 +16,16 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import me.rerere.rikkahub.ui.hooks.HapticPattern +import me.rerere.rikkahub.ui.hooks.rememberPremiumHaptics import me.rerere.rikkahub.ui.theme.extendColors enum class TagType { @@ -34,6 +43,20 @@ fun Tag( onClick: (() -> Unit)? = null, children: @Composable RowScope.() -> Unit ) { + val haptics = rememberPremiumHaptics() + val interactionSource = remember { MutableInteractionSource() } + val isPressed by interactionSource.collectIsPressedAsState() + + // Physics: "Round/Clicky" spec for small elements + val scale by animateFloatAsState( + targetValue = if (isPressed && onClick != null) 0.85f else 1f, + animationSpec = spring( + dampingRatio = 0.6f, + stiffness = 300f + ), + label = "tag_scale" + ) + val background = when (type) { TagType.SUCCESS -> MaterialTheme.extendColors.green2 TagType.ERROR -> MaterialTheme.extendColors.red2 @@ -51,11 +74,21 @@ fun Tag( ProvideTextStyle(MaterialTheme.typography.labelSmall.copy(color = textColor)) { Row( modifier = modifier + .graphicsLayer { + scaleX = scale + scaleY = scale + } .clip(me.rerere.rikkahub.ui.theme.AppShapes.Tag) .background(background) .let { if (onClick != null) { - it.clickable { onClick() } + it.clickable( + interactionSource = interactionSource, + indication = null // We use scale instead of ripple for that clean fidget feel + ) { + haptics.perform(HapticPattern.Pop) + onClick() + } } else { it } @@ -87,8 +120,8 @@ private fun TagPreview() { Tag(type = TagType.INFO) { Text("测试") } - Tag(type = TagType.DEFAULT) { - Text("测试") + Tag(type = TagType.DEFAULT, onClick = {}) { + Text("Clickable") } } }