package com.suno.android.common_ui.components import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Tab import androidx.compose.material3.TabPosition import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterStart import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import com.suno.android.common_ui.theme.ExtendedTheme import com.suno.android.common_ui.theme.SunoTheme import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.launch import com.suno.android.common_res.R as CommonResR @Composable fun PillTab( modifier: Modifier = Modifier, ) { Column( modifier .fillMaxSize() .padding(5.dp) .background( color = ExtendedTheme.colors.backgroundInvert, RoundedCornerShape(20.dp), ), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) {} } fun Modifier.myTabIndicatorOffset( currentTabPosition: TabPosition, ): Modifier = composed { val currentTabWidth by animateDpAsState( targetValue = currentTabPosition.width, animationSpec = tween(durationMillis = 150, easing = FastOutSlowInEasing), ) val indicatorOffset by animateDpAsState( targetValue = currentTabPosition.left, animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing), ) wrapContentSize(CenterStart) .offset(x = indicatorOffset) .width(currentTabWidth) } @Composable fun PillTabRow( pagerState: androidx.compose.foundation.pager.PagerState, modifier: Modifier = Modifier, ) { val titles = listOf( stringResource(CommonResR.string.discover), stringResource(CommonResR.string.library), ) val coroutineScope = rememberCoroutineScope() val selectedTabIndex: State = remember { derivedStateOf { pagerState.currentPage } } val indicator = @Composable { tabPositions: List -> if (selectedTabIndex.value < tabPositions.size) { PillTab( Modifier.myTabIndicatorOffset(tabPositions[selectedTabIndex.value]), ) } } Box { TabRow( selectedTabIndex = selectedTabIndex.value, indicator = indicator, containerColor = ExtendedTheme.colors.backgroundPrimary, contentColor = Color.White, divider = {}, // we don't want any divider modifier = Modifier .padding(5.dp) .height(45.dp) .clip( shape = RoundedCornerShape(20.dp), ), ) { titles.forEachIndexed { index, title -> Tab( text = { Text(title, fontWeight = FontWeight.Bold) }, selected = selectedTabIndex.value == index, modifier = Modifier .clip(shape = RoundedCornerShape(20.dp)) .padding(bottom = 3.dp) .zIndex(2f), onClick = { coroutineScope.launch { pagerState.animateScrollToPage(index) } }, ) } } } } class DisabledInteractionSource : MutableInteractionSource { override val interactions: Flow = emptyFlow() override suspend fun emit( interaction: Interaction, ) {} override fun tryEmit( interaction: Interaction, ): Boolean = true } @Composable @Preview private fun PillTabRow_Preview() { SunoTheme { val pagerState = rememberPagerState(pageCount = { 2 }) PillTabRow(pagerState = pagerState) } }