diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt index 4c4fc1046..da09ea305 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt @@ -21,10 +21,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import org.hisp.dhis.common.screens.BadgesScreen import org.hisp.dhis.common.screens.BottomSheetHeaderScreen import org.hisp.dhis.common.screens.ButtonBlockScreen import org.hisp.dhis.common.screens.ButtonScreen import org.hisp.dhis.common.screens.CheckboxScreen +import org.hisp.dhis.common.screens.ChipsScreen import org.hisp.dhis.common.screens.Components import org.hisp.dhis.common.screens.FormShellsScreen import org.hisp.dhis.common.screens.FormsComponentsScreen @@ -133,6 +135,8 @@ fun Main() { Components.INPUT_INTEGER -> InputIntegerScreen() Components.INPUT_NUMBER -> InputNumberScreen() Components.INPUT_LETTER -> InputLetterScreen() + Components.CHIPS -> ChipsScreen() + Components.BADGES -> BadgesScreen() Components.SWITCH -> SwitchScreen() Components.INPUT_RADIO_BUTTON -> InputRadioButtonScreen() } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/BadgesScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/BadgesScreen.kt new file mode 100644 index 000000000..a9506b575 --- /dev/null +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/BadgesScreen.kt @@ -0,0 +1,18 @@ +package org.hisp.dhis.common.screens + +import androidx.compose.runtime.Composable +import org.hisp.dhis.mobile.ui.designsystem.component.Badge +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer +import org.hisp.dhis.mobile.ui.designsystem.component.ErrorBadge + +@Composable +fun BadgesScreen() { + ColumnComponentContainer(title = "Badges") { + Badge() + Badge(text = "32") + } + ColumnComponentContainer(title = "Error badges") { + ErrorBadge() + ErrorBadge(text = "32") + } +} diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/ChipsScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/ChipsScreen.kt new file mode 100644 index 000000000..fa2a6a6ea --- /dev/null +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/ChipsScreen.kt @@ -0,0 +1,17 @@ +package org.hisp.dhis.common.screens + +import androidx.compose.runtime.Composable +import org.hisp.dhis.mobile.ui.designsystem.component.Chip +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer + +@Composable +fun ChipsScreen() { + ColumnComponentContainer(title = "Filter Chips") { + Chip(label = "Label", selected = true) + Chip(label = "Label", selected = false) + } + ColumnComponentContainer(title = "With badges") { + Chip(label = "Label", selected = true, badge = "3") + Chip(label = "Label", selected = false, badge = "3") + } +} diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt index e2a7f4a24..bacc51319 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Components.kt @@ -25,6 +25,8 @@ enum class Components(val label: String) { LEGEND_DESCRIPTION("Legend description"), INPUT("Input"), BUTTON_BLOCK("Button block"), + CHIPS("Chips"), + BADGES("Badges"), ICON_CARDS("Icon Cards"), INPUT_RADIO_BUTTON("Input Radio Button"), SWITCH("Switch"), diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Badge.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Badge.kt new file mode 100644 index 000000000..aa15828c8 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Badge.kt @@ -0,0 +1,49 @@ +package org.hisp.dhis.mobile.ui.designsystem.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor + +@Composable +fun Badge( + modifier: Modifier = Modifier, + text: String? = null, + color: Color = SurfaceColor.Primary, + textColor: Color = TextColor.OnPrimary, +) { + Box( + modifier + .defaultMinSize(Spacing.Spacing6, Spacing.Spacing6) + .background(color, CircleShape), + ) { + text?.let { + Text( + modifier = Modifier + .padding(horizontal = Spacing.Spacing4) + .padding(bottom = Spacing.Spacing1), + text = it, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.labelSmall.copy(color = textColor), + ) + } + } +} + +@Composable +fun ErrorBadge( + modifier: Modifier = Modifier, + text: String? = null, +) { + Badge(modifier, text, SurfaceColor.Error, TextColor.OnError) +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Chip.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Chip.kt new file mode 100644 index 000000000..4cd6a4c03 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/Chip.kt @@ -0,0 +1,75 @@ +package org.hisp.dhis.mobile.ui.designsystem.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Done +import androidx.compose.material.ripple.LocalRippleTheme +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip +import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.unit.IntOffset +import org.hisp.dhis.mobile.ui.designsystem.theme.Ripple +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Chip( + modifier: Modifier = Modifier, + label: String, + selected: Boolean = false, + onSelected: ((Boolean) -> Unit)? = null, + badge: String? = null, +) { + Box(modifier = modifier) { + CompositionLocalProvider(LocalRippleTheme provides Ripple.CustomDHISRippleTheme) { + var isSelected by remember { mutableStateOf(selected) } + + FilterChip( + onClick = { + isSelected = !isSelected + onSelected?.invoke(isSelected) + }, + label = { Text(label) }, + selected = isSelected, + colors = FilterChipDefaults.filterChipColors( + containerColor = SurfaceColor.SurfaceBright, + selectedContainerColor = SurfaceColor.Container, + ), + leadingIcon = if (isSelected) { + { + Icon( + imageVector = Icons.Filled.Done, + contentDescription = "Done icon", + modifier = Modifier.size(FilterChipDefaults.IconSize), + ) + } + } else { + null + }, + ) + } + badge?.let { + var offset by remember { mutableStateOf(IntOffset(0, 0)) } + Badge( + modifier = Modifier + .align(Alignment.TopEnd) + .onSizeChanged { offset = IntOffset(it.width / 3, it.height / 3) } + .offset { offset }, + text = badge, + ) + } + } +}