Skip to content

Commit

Permalink
fix: [ANDROAPP-6794] Tabs review comments (#357)
Browse files Browse the repository at this point in the history
* fix two pane layout issues

Signed-off-by: Pablo Pajuelo Cabezas <[email protected]>

* fix tabs review comments

Signed-off-by: Pablo Pajuelo Cabezas <[email protected]>

* fix tabs review comments

Signed-off-by: Pablo Pajuelo Cabezas <[email protected]>

* remove icon padding

Signed-off-by: Pablo Pajuelo Cabezas <[email protected]>

---------

Signed-off-by: Pablo Pajuelo Cabezas <[email protected]>
  • Loading branch information
Balcan authored Feb 6, 2025
1 parent 017b2ba commit d1ad140
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -1,53 +1,88 @@
package org.hisp.dhis.common.screens.layouts

import androidx.compose.animation.core.InfiniteRepeatableSpec
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import org.hisp.dhis.common.screens.Groups
import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer
import org.hisp.dhis.mobile.ui.designsystem.component.ColumnScreenContainer
import org.hisp.dhis.mobile.ui.designsystem.component.layout.TwoPaneConfig
import org.hisp.dhis.mobile.ui.designsystem.component.layout.TwoPaneLayout

@Composable
fun TwoPaneLayoutScreen() {
val infiniteTransition = rememberInfiniteTransition()
ColumnScreenContainer(Groups.TWO_PANE_LAYOUT.label) {
ColumnComponentContainer(
"Primary pane 70% of the screen",
) {
TwoPaneLayout(
modifier = Modifier.fillMaxWidth().height(300.dp),
paneConfig = TwoPaneConfig.Weight(0.7f),
primaryPane = {
PrimaryPane()
},
secondaryPane = {
SecondaryPane()
},
)
}
ColumnComponentContainer(
"Primary pane fixed 200 dp width",
) {
TwoPaneLayout(
modifier = Modifier.fillMaxWidth().height(300.dp),
paneConfig = TwoPaneConfig.PrimaryPaneFixedSize(300.dp),
primaryPane = {
PrimaryPane()
},
secondaryPane = {
SecondaryPane()
},
)
}
ColumnComponentContainer(
"Secondary pane fixed 100 dp width",
) {
TwoPaneLayout(
modifier = Modifier.fillMaxWidth().height(300.dp),
paneConfig = TwoPaneConfig.SecondaryPaneFixedSize(150.dp),
primaryPane = {
PrimaryPane()
},
secondaryPane = {
SecondaryPane()
},
)
}
}
}

val weight by infiniteTransition.animateFloat(
initialValue = 0.1f,
targetValue = 0.9f,
animationSpec = InfiniteRepeatableSpec(
animation = tween(2000),
repeatMode = RepeatMode.Reverse,
),
)
@Composable
private fun PrimaryPane() {
Box(
modifier = Modifier.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceContainerHighest),
contentAlignment = Alignment.Center,
) {
Text(text = "Primary pane")
}
}

TwoPaneLayout(
modifier = Modifier.fillMaxSize(),
paneConfig = TwoPaneConfig.Weight(weight),
primaryPane = {
Box(
modifier = Modifier.fillMaxSize().background(Color.Red),
contentAlignment = Alignment.Center,
) {
Text(text = "Primary pane")
}
},
secondaryPane = {
Box(
modifier = Modifier.fillMaxSize().background(Color.Green),
contentAlignment = Alignment.Center,
) {
Text(text = "Secondary pane")
}
},
)
@Composable
private fun SecondaryPane() {
Box(
modifier = Modifier.fillMaxSize()
.background(MaterialTheme.colorScheme.surfaceContainerHigh),
contentAlignment = Alignment.Center,
) {
Text(text = "Secondary pane")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ fun TabsScreen() {
}

enum class Tabs(val label: String) {
VERTICAL_TONAL_TABS_LABEL_ONLY("Tonal label only"),
VERTICAL_TONAL_TABS_ICON_ONLY("Tonal icon only"),
VERTICAL_TONAL_TABS_ICON_LABEL("Tonal icon and label"),
VERTICAL_PRIMARY_TABS_LABEL_ONLY("Primary label only"),
VERTICAL_PRIMARY_TABS_ICON_ONLY("Primary icon only"),
VERTICAL_PRIMARY_TABS_ICON_LABEL("Primary icon and label"),
VERTICAL_TONAL_TABS_LABEL_ONLY("Vertical tabs: Tonal label only"),
VERTICAL_TONAL_TABS_ICON_ONLY("Vertical tabs: Tonal icon only"),
VERTICAL_TONAL_TABS_ICON_LABEL("Vertical tabs: Tonal icon and label"),
VERTICAL_PRIMARY_TABS_LABEL_ONLY("Vertical tabs: Primary label only"),
VERTICAL_PRIMARY_TABS_ICON_ONLY("Vertical tabs: Primary icon only"),
VERTICAL_PRIMARY_TABS_ICON_LABEL("Vertical tabs: Primary icon and label"),
NO_COMPONENT_SELECTED("No component selected"),
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
package org.hisp.dhis.common.screens.tabs

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Bookmarks
import androidx.compose.material.icons.filled.Event
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.outlined.Bookmarks
import androidx.compose.material.icons.outlined.Event
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.hisp.dhis.mobile.ui.designsystem.component.ColumnScreenContainer
import org.hisp.dhis.mobile.ui.designsystem.component.VerticalTabs
import org.hisp.dhis.mobile.ui.designsystem.component.model.IconData
import org.hisp.dhis.mobile.ui.designsystem.component.model.Tab
import org.hisp.dhis.mobile.ui.designsystem.component.model.TabColorStyle
import org.hisp.dhis.mobile.ui.designsystem.component.model.TabStyle
Expand All @@ -15,17 +31,50 @@ fun VerticalTabsScreen(
tabStyle: TabStyle,
tabColorStyle: TabColorStyle,
) {
VerticalTabs(
modifier = Modifier.fillMaxSize()
.padding(Spacing.Spacing16),
tabs = listOf(
Tab(id = "1", label = "Tab 1"),
Tab(id = "2", label = "Tab 2"),
Tab(id = "3", label = "Tab 3"),
),
tabStyle = tabStyle,
tabColorStyle = tabColorStyle,
onSectionSelected = {
},
)
val modifier = if (tabStyle is TabStyle.IconOnly) {
Modifier.width(100.dp)
} else {
Modifier.fillMaxSize()
}

ColumnScreenContainer(scrollable = false) {
VerticalTabs(
modifier = modifier
.padding(Spacing.Spacing16),
tabs = buildList {
repeat(drawableOutlinedList.size) { index ->
add(
Tab(
id = "${index + 1}",
label = "Tab ${index + 1}",
iconData = getIcon(index),
),
)
}
},
tabStyle = tabStyle,
tabColorStyle = tabColorStyle,
onSectionSelected = {
},
contentPadding = PaddingValues(vertical = Spacing.Spacing12),
)
}
}

private val drawableOutlinedList = listOf(
Icons.Outlined.Home,
Icons.Outlined.Bookmarks,
Icons.Outlined.Event,
Icons.Outlined.AccountCircle,
Icons.Outlined.Settings,
)

private val drawableFilledList = listOf(
Icons.Filled.Home,
Icons.Filled.Bookmarks,
Icons.Filled.Event,
Icons.Filled.AccountCircle,
Icons.Filled.Settings,
)

private fun getIcon(index: Int) = IconData(drawableOutlinedList[index], drawableFilledList[index])
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ fun ColumnScreenContainer(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(Spacing.Spacing16),
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
scrollable: Boolean = true,
content: @Composable (() -> Unit),
) {
Column(
Expand All @@ -47,7 +48,7 @@ fun ColumnScreenContainer(
modifier = modifier
.fillMaxSize().background(Color.White, Shape.LargeTop)
.padding(horizontal = Spacing.Spacing16)
.verticalScroll(rememberScrollState()),
.then(if (scrollable)Modifier.verticalScroll(rememberScrollState())else Modifier),
) {
title?.let {
Title(title, modifier = Modifier.padding(top = Spacing.Spacing16))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -17,14 +18,14 @@ import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Circle
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -34,6 +35,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
Expand All @@ -49,12 +52,22 @@ fun VerticalTabs(
tabStyle: TabStyle = TabStyle.LabelOnly,
tabColorStyle: TabColorStyle = TabColorStyle.Tonal,
initialSelectedTabIndex: Int = 0,
backgroundShape: Shape = MaterialTheme.shapes.large,
backgroundShape: Shape = MaterialTheme.shapes.medium,
onSectionSelected: (String) -> Unit,
contentPadding: PaddingValues = PaddingValues(),
) {
val density = LocalDensity.current
var selectedSection by remember { mutableStateOf(initialSelectedTabIndex) }

val scrollState = rememberLazyListState()
val scrollOffset by remember {
derivedStateOf {
VerticalTabsDefaults.tabHeight * scrollState.firstVisibleItemIndex + with(density) { scrollState.firstVisibleItemScrollOffset.toDp() }
}
}

val indicatorVerticalOffset by animateDpAsState(
targetValue = VerticalTabsDefaults.tabHeight * selectedSection,
targetValue = VerticalTabsDefaults.tabHeight * selectedSection - scrollOffset + contentPadding.calculateTopPadding(),
label = "",
)

Expand All @@ -67,12 +80,15 @@ fun VerticalTabs(
.clip(backgroundShape),
) {
LazyColumn(
state = scrollState,
modifier = Modifier.fillMaxSize(),
contentPadding = contentPadding,
) {
itemsIndexed(tabs) { index, tab ->
VerticalTab(
label = tab.label,
selected = index == selectedSection,
iconImage = tab.icon(index == selectedSection),
tabStyle = tabStyle,
tabColorStyle = tabColorStyle,
defaultVerticalTabHeight = VerticalTabsDefaults.tabHeight,
Expand Down Expand Up @@ -100,6 +116,7 @@ internal fun VerticalTab(
selected: Boolean,
tabStyle: TabStyle,
tabColorStyle: TabColorStyle,
iconImage: ImageVector?,
defaultVerticalTabHeight: Dp,
onTabClick: () -> Unit,
) {
Expand All @@ -113,21 +130,33 @@ internal fun VerticalTab(
role = Role.Tab,
interactionSource = interactionSource,
indication = ripple(
color = MaterialTheme.colorScheme.primary,
color = if (tabColorStyle is TabColorStyle.Primary) {
MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.16f)
} else {
MaterialTheme.colorScheme.primary
},
),
)
.padding(horizontal = Spacing.Spacing16, vertical = Spacing.Spacing5),
.padding(
horizontal = if (tabStyle is TabStyle.IconOnly) {
Spacing.Spacing5
} else {
Spacing.Spacing16
},
vertical = Spacing.Spacing5,
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = spacedBy(Spacing.Spacing2),
horizontalArrangement = spacedBy(Spacing.Spacing4),
) {
when (tabStyle) {
TabStyle.IconAndLabel -> {
Icon(
modifier = Modifier.padding(Spacing.Spacing6),
imageVector = Icons.Filled.Circle,
contentDescription = "",
tint = VerticalTabsDefaults.textColor(tabColorStyle, selected),
)
iconImage?.let {
Icon(
imageVector = iconImage,
contentDescription = "",
tint = VerticalTabsDefaults.textColor(tabColorStyle, selected),
)
}
Text(
text = label,
color = VerticalTabsDefaults.textColor(tabColorStyle, selected),
Expand All @@ -140,12 +169,13 @@ internal fun VerticalTab(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.padding(Spacing.Spacing6),
imageVector = Icons.Filled.Circle,
contentDescription = "",
tint = VerticalTabsDefaults.textColor(tabColorStyle, selected),
)
iconImage?.let {
Icon(
imageVector = iconImage,
contentDescription = "",
tint = VerticalTabsDefaults.textColor(tabColorStyle, selected),
)
}
}

TabStyle.LabelOnly ->
Expand Down
Loading

0 comments on commit d1ad140

Please sign in to comment.