Skip to content

Commit

Permalink
feat [ANDROAPP-6794]: Add vertical tabs (#354)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Pajuelo Cabezas <[email protected]>
  • Loading branch information
Balcan authored Jan 30, 2025
1 parent dc88216 commit 2ad3e3f
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 0 deletions.
2 changes: 2 additions & 0 deletions common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import org.hisp.dhis.common.screens.Groups
import org.hisp.dhis.common.screens.NoComponentSelectedScreen
import org.hisp.dhis.common.screens.VerticalTabsScreen
import org.hisp.dhis.common.screens.actionInputs.ActionInputsScreen
import org.hisp.dhis.common.screens.basicTextInputs.BasicTextInputsScreen
import org.hisp.dhis.common.screens.bottomSheets.BottomSheetsScreen
Expand Down Expand Up @@ -142,6 +143,7 @@ fun Main(
onLocationRequest?.invoke(locationQuery, locationCallback)
}
Groups.TABLE -> TableScreen()
Groups.VERTICAL_TABS -> VerticalTabsScreen()
}
} else {
NoComponentSelectedScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ enum class Groups(val label: String) {
NO_GROUP_SELECTED("No group selected"),
LOCATION_SEARCH_BAR("Location Search Bar"),
TABLE("Table"),
VERTICAL_TABS("Vertical Tabs"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.hisp.dhis.common.screens

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import org.hisp.dhis.mobile.ui.designsystem.component.VerticalTabs
import org.hisp.dhis.mobile.ui.designsystem.component.model.Tab
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing

@Composable
fun VerticalTabsScreen() {
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"),
),
onSectionSelected = {
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.hisp.dhis.mobile.ui.designsystem

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import org.hisp.dhis.mobile.ui.designsystem.component.VerticalTabs
import org.hisp.dhis.mobile.ui.designsystem.component.model.Tab
import org.hisp.dhis.mobile.ui.designsystem.theme.DHIS2Theme
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing
import org.junit.Rule
import org.junit.Test

class VerticalTabsSnapshotTest {
@get:Rule
val paparazzi = paparazzi()

@Test
fun launchVerticalTabsTest() {
paparazzi.snapshot {
DHIS2Theme {
Box(
Modifier
.fillMaxSize()
.padding(Spacing.Spacing16),
) {
VerticalTabs(
modifier = Modifier
.fillMaxSize(),
initialSelectedTabIndex = 1,
tabs = listOf(
Tab(id = "1", label = "Tab 1"),
Tab(id = "2", label = "Tab 2"),
Tab(id = "3", label = "Tab 3"),
Tab(id = "3", label = "Tab 4"),
Tab(id = "3", label = "Tab 5"),
),
onSectionSelected = {
},
)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package org.hisp.dhis.mobile.ui.designsystem.component

import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
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.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import org.hisp.dhis.mobile.ui.designsystem.component.model.Tab
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing

@Composable
fun VerticalTabs(
modifier: Modifier = Modifier,
tabs: List<Tab>,
initialSelectedTabIndex: Int = 0,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
backgroundShape: Shape = MaterialTheme.shapes.large,
defaultTabHeight: Dp = 48.dp,
defaultIndicatorWidth: Dp = 6.dp,
onSectionSelected: (String) -> Unit,
) {
var selectedSection by remember { mutableStateOf(initialSelectedTabIndex) }
val indicatorVerticalOffset by animateDpAsState(
targetValue = defaultTabHeight * selectedSection,
label = "",
)

Box(
modifier = modifier
.background(
color = backgroundColor,
shape = backgroundShape,
)
.clip(backgroundShape),
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
itemsIndexed(tabs) { index, tab ->
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = Modifier
.fillMaxWidth()
.height(defaultTabHeight)
.clickable(
onClick = {
selectedSection = index
onSectionSelected(tab.id)
},
role = Role.Tab,
interactionSource = interactionSource,
indication = ripple(
color = MaterialTheme.colorScheme.primary,
),
),
) {
Text(
modifier = Modifier.align(Alignment.CenterStart)
.padding(horizontal = Spacing.Spacing16),
text = tab.label,
color = if (selectedSection == index) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurface
},
style = MaterialTheme.typography.titleSmall,
)
}
}
}
Spacer(
Modifier
.align(Alignment.TopEnd)
.offset(x = defaultIndicatorWidth / 2, y = indicatorVerticalOffset)
.requiredHeight(defaultTabHeight)
.requiredWidth(defaultIndicatorWidth)
.background(
color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(defaultIndicatorWidth / 2),
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.hisp.dhis.mobile.ui.designsystem.component.model

data class Tab(
val id: String,
val label: String,
)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2ad3e3f

Please sign in to comment.