From ee2d7be37e4cd90855ca9c47063b18054178bf4e Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 30 Jan 2025 14:04:22 +0100 Subject: [PATCH] feat [ANDROAPP-6793]: Add two pane layout (#353) Signed-off-by: Pablo Pajuelo Cabezas --- .../kotlin/org/hisp/dhis/common/App.kt | 2 + .../org/hisp/dhis/common/screens/Groups.kt | 1 + .../screens/layouts/TwoPaneLayoutScreen.kt | 53 ++++++++++++++++ .../component/layout/TwoPaneConfig.kt | 9 +++ .../component/layout/TwoPaneLayout.kt | 61 +++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 common/src/commonMain/kotlin/org/hisp/dhis/common/screens/layouts/TwoPaneLayoutScreen.kt create mode 100644 designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneConfig.kt create mode 100644 designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneLayout.kt 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 93cdce393..e6a1fe6db 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt @@ -19,6 +19,7 @@ import org.hisp.dhis.common.screens.basicTextInputs.BasicTextInputsScreen import org.hisp.dhis.common.screens.bottomSheets.BottomSheetsScreen import org.hisp.dhis.common.screens.buttons.ButtonsScreen import org.hisp.dhis.common.screens.cards.CardsScreen +import org.hisp.dhis.common.screens.layouts.TwoPaneLayoutScreen import org.hisp.dhis.common.screens.location.LocationSearchBarScreen import org.hisp.dhis.common.screens.menu.MenuScreen import org.hisp.dhis.common.screens.others.BadgesScreen @@ -144,6 +145,7 @@ fun Main( } Groups.TABLE -> TableScreen() Groups.TABS -> TabsScreen() + Groups.TWO_PANE_LAYOUT -> TwoPaneLayoutScreen() } } else { NoComponentSelectedScreen( diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt index c3398430a..768ac25f8 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt @@ -24,4 +24,5 @@ enum class Groups(val label: String) { LOCATION_SEARCH_BAR("Location Search Bar"), TABLE("Table"), TABS("Tabs"), + TWO_PANE_LAYOUT("Two pane layout"), } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/layouts/TwoPaneLayoutScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/layouts/TwoPaneLayoutScreen.kt new file mode 100644 index 000000000..aace5510e --- /dev/null +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/layouts/TwoPaneLayoutScreen.kt @@ -0,0 +1,53 @@ +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.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 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() + + val weight by infiniteTransition.animateFloat( + initialValue = 0.1f, + targetValue = 0.9f, + animationSpec = InfiniteRepeatableSpec( + animation = tween(2000), + repeatMode = RepeatMode.Reverse, + ), + ) + + 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") + } + }, + ) +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneConfig.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneConfig.kt new file mode 100644 index 000000000..cea760791 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneConfig.kt @@ -0,0 +1,9 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.layout + +import androidx.compose.ui.unit.Dp + +sealed class TwoPaneConfig { + data class Weight(val primaryPaneWeight: Float) : TwoPaneConfig() + data class PrimaryPaneFixedSize(val size: Dp) : TwoPaneConfig() + data class SecondaryPaneFixedSize(val size: Dp) : TwoPaneConfig() +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneLayout.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneLayout.kt new file mode 100644 index 000000000..1b2f219b1 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/layout/TwoPaneLayout.kt @@ -0,0 +1,61 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.layout + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +/** + * Two pane screen + * Divides the screen in two parts for extended device screens + * @param modifier: Modifier for styling + * @param paneConfig: Pane configuration + * @param primaryPane: Primary pane content + * @param secondaryPane: Secondary pane content + * */ +@Composable +fun TwoPaneLayout( + modifier: Modifier = Modifier, + paneConfig: TwoPaneConfig, + primaryPane: @Composable () -> Unit, + secondaryPane: @Composable () -> Unit, +) { + Row(modifier = modifier) { + val (primaryPaneModifier, secondaryPaneModifier) = getPaneModifier(paneConfig) + + Box( + modifier = primaryPaneModifier, + ) { + secondaryPane() + } + Box( + modifier = secondaryPaneModifier, + ) { + primaryPane() + } + } +} + +private fun RowScope.getPaneModifier(paneConfig: TwoPaneConfig): Pair = + when (paneConfig) { + is TwoPaneConfig.PrimaryPaneFixedSize -> + Pair( + Modifier.fillMaxHeight().width(paneConfig.size), + Modifier.fillMaxHeight().weight(1f), + ) + + is TwoPaneConfig.SecondaryPaneFixedSize -> + Pair( + Modifier.fillMaxHeight().weight(1f), + Modifier.fillMaxHeight().width(paneConfig.size), + ) + + is TwoPaneConfig.Weight -> + Pair( + Modifier.fillMaxHeight().weight(paneConfig.primaryPaneWeight), + Modifier.fillMaxHeight().weight(1f - paneConfig.primaryPaneWeight), + ) + }