From cc530d7ebfd629f3d28a30d9342dd2630a1bb40e Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 3 Jan 2025 15:30:11 +0100 Subject: [PATCH] design : introduce CounterAtom and ListItemContent.Counter --- .../designsystem/atomic/atoms/CounterAtom.kt | 83 +++++++++++++++++++ .../components/list/ListItemContent.kt | 7 ++ 2 files changed, 90 insertions(+) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt new file mode 100644 index 0000000000..c84906bbad --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/CounterAtom.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.libraries.designsystem.atomic.atoms + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement.spacedBy +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.toDp +import io.element.android.libraries.designsystem.theme.components.Text + +private const val MAX_COUNT = 99 +private const val MAX_COUNT_STRING = "+$MAX_COUNT" + +/** + * A counter atom that displays a number in a circle. + * Figma link : https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=2805-2649&m=dev + * + * @param count The number to display. If the number is greater than [MAX_COUNT], the counter will display [MAX_COUNT_STRING]. + * If the number is less than 1, the counter will not be displayed. + * @param modifier The modifier to apply to this layout. + */ +@Composable +fun CounterAtom( + count: Int, + modifier: Modifier = Modifier, +) { + if (count < 1) return + val countAsText = when (count) { + in 0..MAX_COUNT -> count.toString() + else -> MAX_COUNT_STRING + } + val textStyle = ElementTheme.typography.fontBodyMdMedium + val textMeasurer = rememberTextMeasurer() + // Measure the maximum count string size + val textLayoutResult = textMeasurer.measure( + text = MAX_COUNT_STRING, + style = textStyle + ) + val textSize = textLayoutResult.size + val squareSize = maxOf(textSize.width, textSize.height) + Box( + modifier = modifier + .size(squareSize.toDp() + 1.dp) + .clip(CircleShape) + .background(ElementTheme.colors.iconSuccessPrimary) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = countAsText, + style = textStyle, + color = Color.White, + ) + } +} + +@PreviewsDayNight +@Composable +internal fun CounterAtomPreview() = ElementPreview { + Column(verticalArrangement = spacedBy(2.dp)) { + CounterAtom(count = 0) + CounterAtom(count = 4) + CounterAtom(count = 99) + CounterAtom(count = 100) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt index 67d464a946..d2fb1259fc 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.atomic.atoms.CounterAtom import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.ListItem @@ -91,6 +92,9 @@ sealed interface ListItemContent { /** Displays a badge. */ data object Badge : ListItemContent + /** Displays a counter. */ + data class Counter(val count: Int) : ListItemContent + @Composable fun View() { when (this) { @@ -125,6 +129,9 @@ sealed interface ListItemContent { ) { RedIndicatorAtom() } + is Counter -> { + CounterAtom(count = count) + } is Custom -> content() } }