From af8422c7745be093e3fb4bb369124385cbd8dd17 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 25 Dec 2025 04:04:20 +0000 Subject: [PATCH] ui: glow-up Tag component with physics and haptics - Implemented `animateFloatAsState` with `spring(dampingRatio = 0.6f, stiffness = 300f)` for tactile scale effect on press (target 0.85f). - Added `rememberPremiumHaptics` and triggered `HapticPattern.Pop` on click. - Removed standard ripple indication in favor of physics-based motion. - Updated `Tag` component to support interaction state via `MutableInteractionSource`. --- .../rerere/rikkahub/ui/components/ui/Tag.kt | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) 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") } } }