feat(filters): чипы растворяются под кнопкой-категорией (fade left edge)

Кнопка-категории теперь ПОВЕРХ чипов (Box-оверлей), чипы идут во всю ширину с
отступом слева под кнопку. У левого края — затухание прозрачности (Modifier.
fadingStartEdge: graphicsLayer Offscreen + horizontalGradient BlendMode.DstIn), так
чипы при прокрутке влево красиво уплывают под кнопку и растворяются, а не обрезаются.
FilterChips/GenreSelector получили параметр contentPadding. Экраны Радио и Чарты.
This commit is contained in:
nk
2026-06-07 17:33:35 +03:00
parent 78282e97ca
commit 87dca7a6df
3 changed files with 60 additions and 20 deletions

View File

@@ -46,6 +46,7 @@ import com.radiola.domain.model.StatPoint
import com.radiola.domain.model.TrackStats import com.radiola.domain.model.TrackStats
import com.radiola.ui.components.CategoryPicker import com.radiola.ui.components.CategoryPicker
import com.radiola.ui.components.EmptyState import com.radiola.ui.components.EmptyState
import com.radiola.ui.components.fadingStartEdge
import com.radiola.ui.components.PopularityChart import com.radiola.ui.components.PopularityChart
import com.radiola.ui.components.crossfadeModel import com.radiola.ui.components.crossfadeModel
import com.radiola.ui.components.serviceLogoRes import com.radiola.ui.components.serviceLogoRes
@@ -102,19 +103,23 @@ fun ChartsScreen(
// Фильтр по жанру (если бэкенд уже накопил жанры) // Фильтр по жанру (если бэкенд уже накопил жанры)
if (genres.isNotEmpty()) { if (genres.isNotEmpty()) {
Spacer(Modifier.height(10.dp)) Spacer(Modifier.height(10.dp))
Row(verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.fillMaxWidth().height(44.dp)) {
GenreSelector(
genres = genres,
selected = selectedGenre,
onSelect = viewModel::selectGenre,
contentPadding = PaddingValues(start = 70.dp, end = 20.dp),
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center)
.fadingStartEdge(62.dp)
)
CategoryPicker( CategoryPicker(
title = "Стиль музыки", title = "Стиль музыки",
items = genres, items = genres,
selected = selectedGenre, selected = selectedGenre,
onSelect = viewModel::selectGenre, onSelect = viewModel::selectGenre,
modifier = Modifier.padding(start = 20.dp) modifier = Modifier.align(Alignment.CenterStart).padding(start = 20.dp)
)
GenreSelector(
genres = genres,
selected = selectedGenre,
onSelect = viewModel::selectGenre,
modifier = Modifier.weight(1f)
) )
} }
} }
@@ -225,12 +230,13 @@ private fun GenreSelector(
genres: List<String>, genres: List<String>,
selected: String?, selected: String?,
onSelect: (String?) -> Unit, onSelect: (String?) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(horizontal = 20.dp)
) { ) {
LazyRow( LazyRow(
modifier = modifier, modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(9.dp), horizontalArrangement = Arrangement.spacedBy(9.dp),
contentPadding = PaddingValues(horizontal = 20.dp) contentPadding = contentPadding
) { ) {
item { item {
PeriodChip( PeriodChip(

View File

@@ -20,18 +20,45 @@ import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.radiola.ui.theme.Motion import com.radiola.ui.theme.Motion
import com.radiola.ui.theme.RadiolaTheme import com.radiola.ui.theme.RadiolaTheme
/**
* Плавное затухание содержимого у ЛЕВОГО края (прозрачность) на ширину [width].
* Чипы, заезжая под кнопку-«категории», красиво растворяются, а не обрезаются.
*/
fun Modifier.fadingStartEdge(width: Dp): Modifier = this
.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
.drawWithContent {
drawContent()
val w = width.toPx().coerceAtMost(size.width)
drawRect(
brush = Brush.horizontalGradient(
colorStops = arrayOf(
0f to Color.Transparent,
(w / size.width) to Color.Black
)
),
blendMode = BlendMode.DstIn
)
}
@Composable @Composable
fun FilterChips( fun FilterChips(
tags: List<String>, tags: List<String>,
selectedTag: String?, selectedTag: String?,
onTagSelected: (String?) -> Unit, onTagSelected: (String?) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp)
) { ) {
val listState = rememberLazyListState() val listState = rememberLazyListState()
// Доводим выбранный чип в зону видимости (важно при свайп-переключении). // Доводим выбранный чип в зону видимости (важно при свайп-переключении).
@@ -43,7 +70,7 @@ fun FilterChips(
modifier = modifier, modifier = modifier,
state = listState, state = listState,
horizontalArrangement = Arrangement.spacedBy(9.dp), horizontalArrangement = Arrangement.spacedBy(9.dp),
contentPadding = PaddingValues(horizontal = 16.dp) contentPadding = contentPadding
) { ) {
item { item {
Chip(label = "Все", selected = selectedTag == null) { onTagSelected(null) } Chip(label = "Все", selected = selectedTag == null) { onTagSelected(null) }

View File

@@ -178,19 +178,26 @@ fun StationsScreen(
) )
.padding(top = 2.dp, bottom = 12.dp) .padding(top = 2.dp, bottom = 12.dp)
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.fillMaxWidth().height(44.dp)) {
// Чипы во всю ширину, но с отступом слева под кнопку; у левого
// края — затухание прозрачности (чипы «уплывают» под кнопку).
FilterChips(
tags = tags,
selectedTag = selectedTag,
onTagSelected = viewModel::onTagSelected,
contentPadding = PaddingValues(start = 66.dp, end = 16.dp),
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center)
.fadingStartEdge(60.dp)
)
// Кнопка-категории — поверх чипов, слева.
CategoryPicker( CategoryPicker(
title = "Категории", title = "Категории",
items = tags, items = tags,
selected = selectedTag, selected = selectedTag,
onSelect = viewModel::onTagSelected, onSelect = viewModel::onTagSelected,
modifier = Modifier.padding(start = 16.dp) modifier = Modifier.align(Alignment.CenterStart).padding(start = 16.dp)
)
FilterChips(
tags = tags,
selectedTag = selectedTag,
onTagSelected = viewModel::onTagSelected,
modifier = Modifier.weight(1f)
) )
} }
} }