diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt index c889e863df..e8cf99da89 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow +import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextAlign @@ -27,7 +28,6 @@ import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEachIndexed import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign @@ -108,47 +108,121 @@ fun TangemSegmentedPicker( segmentHeight = segmentHeight.value, separatorWidth = SEPARATOR_WIDTH, ) - Row(verticalAlignment = Alignment.CenterVertically) { - items.fastForEachIndexed { index, item -> - Segment( - item = item, - index = index, - isFixed = isFixed, - minSegmentWidth = minSegmentWidth, - selectedIndex = selectedIndex, - onClick = { onClick(item) }, - modifier = Modifier - .onGloballyPositioned { - with(density) { - itemsWidths[index] = it.size.width.toDp() - segmentHeight.value = it.size.height.toDp().coerceAtLeast(segmentHeight.value) - } - }, - ) - - if (index != items.lastIndex) { - val selected by selectedIndex - val alpha by animateFloatAsState( - targetValue = if (selected == index || selected == index + 1) 0f else 1f, - animationSpec = tween(durationMillis = 300), - label = "separatorAlpha", + SegmentsRow( + isFixed = isFixed, + minSegmentWidth = minSegmentWidth, + segmentCount = items.size, + content = { + items.fastForEachIndexed { index, item -> + Segment( + item = item, + index = index, + minSegmentWidth = minSegmentWidth, + selectedIndex = selectedIndex, + onClick = { onClick(item) }, + modifier = Modifier + .onGloballyPositioned { + with(density) { + itemsWidths[index] = it.size.width.toDp() + segmentHeight.value = it.size.height.toDp().coerceAtLeast(segmentHeight.value) + } + }, ) - Box( - Modifier - .alpha(alpha) - .width(SEPARATOR_WIDTH) - .height(20.dp) - .background( - color = TangemTheme.colors2.border.neutral.tertiary.copy(alpha = 0.1f), - ), - ) + if (index != items.lastIndex) { + Separator(index = index, selectedIndex = selectedIndex) + } + } + }, + ) + } +} + +/** + * Lays out segments (with the inter-segment separators interleaved) in a single row. + * + * - When [isFixed] is `true` and the parent width is bounded, every segment is sized to at least its + * content width, and the remaining free space is distributed equally between segments — so the row + * fills the full width without clipping any segment's text. + * - Otherwise segments wrap their content. + * + * Children must be supplied as `segment, separator, segment, separator, … , segment` (even indices are + * segments, odd indices are separators). + */ +@Composable +private fun SegmentsRow( + isFixed: Boolean, + minSegmentWidth: Dp, + segmentCount: Int, + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + Layout(modifier = modifier, content = content) { measurables, constraints -> + val segMeasurables = measurables.filterIndexed { i, _ -> i % 2 == 0 } + val sepMeasurables = measurables.filterIndexed { i, _ -> i % 2 == 1 } + + val sepPlaceables = sepMeasurables.map { it.measure(constraints.copy(minWidth = 0)) } + val totalSeparators = sepPlaceables.sumOf { it.width } + + val minPx = if (minSegmentWidth != Dp.Unspecified) minSegmentWidth.roundToPx() else 0 + val baseWidths = segMeasurables.map { maxOf(it.maxIntrinsicWidth(constraints.maxHeight), minPx) } + + val widths = if (isFixed && constraints.hasBoundedWidth) { + val leftover = constraints.maxWidth - baseWidths.sum() - totalSeparators + if (leftover > 0) { + val extra = leftover / segmentCount + val remainder = leftover % segmentCount + baseWidths.mapIndexed { i, w -> w + extra + if (i < remainder) 1 else 0 } + } else { + baseWidths + } + } else { + baseWidths + } + + val segPlaceables = segMeasurables.mapIndexed { i, m -> + m.measure(constraints.copy(minWidth = widths[i], maxWidth = widths[i])) + } + + val layoutWidth = (widths.sum() + totalSeparators) + .coerceIn(constraints.minWidth, constraints.maxWidth) + val layoutHeight = (segPlaceables + sepPlaceables).maxOf { it.height } + .coerceIn(constraints.minHeight, constraints.maxHeight) + + layout(layoutWidth, layoutHeight) { + var x = 0 + segPlaceables.forEachIndexed { i, seg -> + seg.placeRelative(x, (layoutHeight - seg.height) / 2) + x += seg.width + sepPlaceables.getOrNull(i)?.let { sep -> + sep.placeRelative(x, (layoutHeight - sep.height) / 2) + x += sep.width } } } } } +@Composable +private fun Separator(index: Int, selectedIndex: MutableState) { + val selected by selectedIndex + val alpha by animateFloatAsState( + targetValue = if (selected == index || selected == index + 1) 0f else 1f, + animationSpec = tween(durationMillis = 300), + label = "separatorAlpha", + ) + + Box( + Modifier + .alpha(alpha) + .width(SEPARATOR_WIDTH) + .height(20.dp) + .background( + color = TangemTheme.colors2.border.neutral.tertiary.copy(alpha = 0.1f), + ), + ) +} + private val SEPARATOR_WIDTH = 0.5.dp @Composable @@ -199,10 +273,9 @@ private fun SegmentSelection( } @Composable -private fun RowScope.Segment( +private fun Segment( item: TangemSegmentUM, index: Int, - isFixed: Boolean, selectedIndex: MutableState, onClick: () -> Unit, modifier: Modifier = Modifier, @@ -211,9 +284,6 @@ private fun RowScope.Segment( Box( modifier = modifier .defaultMinSize(minWidth = minSegmentWidth) - .conditional(isFixed) { - weight(1f) - } .clickable( indication = null, interactionSource = remember { MutableInteractionSource() }, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/ContainerWithDivider.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/ContainerWithDivider.kt deleted file mode 100644 index 31b88692e3..0000000000 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/components/ContainerWithDivider.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.features.feed.ui.components - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.HorizontalDivider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.res.TangemTheme - -@Composable -internal fun ContainerWithDivider( - modifier: Modifier = Modifier, - showDivider: Boolean = false, - paddingValues: PaddingValues = PaddingValues(start = TangemTheme.dimens2.x3), - content: @Composable () -> Unit, -) { - Box(modifier = modifier) { - content() - if (showDivider) { - HorizontalDivider( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(paddingValues), - color = TangemTheme.colors2.graphic.neutral.quaternary, - thickness = 1.dp, - ) - } - } -} \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/LinksBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/LinksBlock.kt index ff70bd80a1..78b45dd66f 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/LinksBlock.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/LinksBlock.kt @@ -27,7 +27,6 @@ import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.utils.PreviewShimmerContainer import com.tangem.features.feed.impl.R -import com.tangem.features.feed.ui.components.ContainerWithDivider import com.tangem.features.feed.ui.market.detailed.state.LinksUM import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -105,7 +104,6 @@ private fun LinksBlockV2(state: LinksUM, modifier: Modifier = Modifier) { title = stringResourceSafe(id = R.string.markets_token_details_blockchain_site), links = state.blockchainSite, onLinkClick = state.onLinkClick, - lastBlock = true, ) } } @@ -159,44 +157,38 @@ private fun SubBlockV2( links: ImmutableList, onLinkClick: (LinksUM.Link) -> Unit, modifier: Modifier = Modifier, - lastBlock: Boolean = false, ) { if (links.isEmpty()) return - ContainerWithDivider( - modifier = modifier, - showDivider = !lastBlock, + Column( + modifier = modifier.padding(vertical = TangemTheme.dimens2.x2), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), ) { - Column( - modifier = Modifier.padding(vertical = TangemTheme.dimens2.x2), + Text( + modifier = Modifier.padding(start = 10.dp, top = TangemTheme.dimens2.x4), + text = title, + style = TangemTheme.typography2.headingSemibold20, + color = TangemTheme.colors2.text.neutral.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + FlowRow( + modifier = Modifier.padding(vertical = TangemTheme.dimens2.x2, horizontal = 3.dp), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), ) { - Text( - modifier = Modifier.padding(start = 10.dp, top = TangemTheme.dimens2.x4), - text = title, - style = TangemTheme.typography2.headingSemibold20, - color = TangemTheme.colors2.text.neutral.primary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - FlowRow( - modifier = Modifier.padding(vertical = TangemTheme.dimens2.x2, horizontal = 3.dp), - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), - ) { - links.fastForEach { link -> - SecondaryTangemButton( - onClick = { onLinkClick(link) }, - text = stringReference(link.title), - iconPosition = TangemButtonIconPosition.Start, - tangemIconUM = TangemIconUM.Icon( - iconRes = link.iconRes, - tintReference = { TangemTheme.colors2.graphic.neutral.primary }, - ), - size = TangemButtonSize.X9, - shape = TangemButtonShape.Rounded, - ) - } + links.fastForEach { link -> + SecondaryTangemButton( + onClick = { onLinkClick(link) }, + text = stringReference(link.title), + iconPosition = TangemButtonIconPosition.Start, + tangemIconUM = TangemIconUM.Icon( + iconRes = link.iconRes, + tintReference = { TangemTheme.colors2.graphic.neutral.primary }, + ), + size = TangemButtonSize.X9, + shape = TangemButtonShape.Rounded, + ) } } } @@ -237,7 +229,7 @@ private fun LinksBlockPlaceholderV2(modifier: Modifier = Modifier) { Column(modifier = modifier) { SubBlockPlaceholderV2() SubBlockPlaceholderV2() - SubBlockPlaceholderV2(lastBlock = true) + SubBlockPlaceholderV2() } } @@ -269,30 +261,25 @@ private fun SubBlockPlaceholderV1(modifier: Modifier = Modifier, lastBlock: Bool } @Composable -private fun SubBlockPlaceholderV2(modifier: Modifier = Modifier, lastBlock: Boolean = false) { - ContainerWithDivider( - modifier = modifier, - showDivider = !lastBlock, +private fun SubBlockPlaceholderV2(modifier: Modifier = Modifier) { + Column( + modifier = modifier.padding(TangemTheme.dimens2.x2), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), ) { - Column( - modifier = Modifier.padding(TangemTheme.dimens2.x2), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), + TextShimmer( + modifier = Modifier.width(56.dp), + style = TangemTheme.typography2.bodySemibold16, + radius = TangemTheme.dimens2.x25, + ) + Row( + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), ) { - TextShimmer( - modifier = Modifier.width(56.dp), - style = TangemTheme.typography2.bodySemibold16, - radius = TangemTheme.dimens2.x25, - ) - Row( - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), - ) { - repeat(times = 3) { - ChipShimmer( - modifier = Modifier - .height(36.dp) - .weight(1f), - ) - } + repeat(times = 3) { + ChipShimmer( + modifier = Modifier + .height(36.dp) + .weight(1f), + ) } } }