1package com.peachpasswords.ui.components23import androidx.compose.animation.core.LinearEasing4import androidx.compose.animation.core.animateFloatAsState5import androidx.compose.animation.core.tween6import androidx.compose.foundation.background7import androidx.compose.foundation.clickable8import androidx.compose.foundation.layout.Box9import androidx.compose.foundation.layout.size10import androidx.compose.foundation.shape.RoundedCornerShape11import androidx.compose.material.icons.Icons12import androidx.compose.material.icons.rounded.Check13import androidx.compose.material.icons.rounded.ContentCopy14import androidx.compose.material3.Icon15import androidx.compose.runtime.Composable16import androidx.compose.runtime.getValue17import androidx.compose.ui.Alignment18import androidx.compose.ui.Modifier19import androidx.compose.ui.draw.clip20import androidx.compose.ui.draw.drawBehind21import androidx.compose.ui.geometry.Offset22import androidx.compose.ui.geometry.Size23import androidx.compose.ui.graphics.Brush24import androidx.compose.ui.graphics.Color25import androidx.compose.ui.unit.dp26import com.peachpasswords.ui.theme.LocalPeachAccent27import com.peachpasswords.ui.theme.LocalReduceMotion28import com.peachpasswords.ui.theme.peachColors2930313233343536373839@Composable40fun CopyButton(copied: Boolean, onCopy: () -> Unit) {41 val accent = LocalPeachAccent.current42 val reduceMotion = LocalReduceMotion.current43 val animate = copied && !reduceMotion44 val drain by animateFloatAsState(45 targetValue = if (copied) 1f else 0f,46 animationSpec = if (animate) tween(1400, easing = LinearEasing) else tween(0),47 label = "copy-drain",48 )49 val sheen by animateFloatAsState(50 targetValue = if (copied) 1f else 0f,51 animationSpec = if (animate) tween(600, easing = LinearEasing) else tween(0),52 label = "copy-sheen",53 )54 Box(55 Modifier56 .size(36.dp)57 .clip(RoundedCornerShape(6.dp))58 .background(peachColors.S2)59 .clickable(onClick = onCopy)60 .drawBehind {61 if (animate && sheen < 1f) {62 val sweepX = (sheen * 2f - 0.5f) * size.width63 val band = size.width * 0.5f64 drawRect(65 brush = Brush.linearGradient(66 colors = listOf(Color.Transparent, accent.copy(alpha = 0.35f), Color.Transparent),67 start = Offset(sweepX - band, 0f),68 end = Offset(sweepX + band, size.height),69 ),70 )71 }72 if (drain > 0f) {73 val h = 2.dp.toPx()74 drawRect(75 color = accent,76 topLeft = Offset(0f, size.height - h),77 size = Size(size.width * drain, h),78 )79 }80 },81 contentAlignment = Alignment.Center,82 ) {83 Icon(84 if (copied) Icons.Rounded.Check else Icons.Rounded.ContentCopy,85 contentDescription = "copy",86 tint = if (copied) accent else peachColors.T1,87 modifier = Modifier.size(15.dp),88 )89 }90}