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 c62bdb5b9..66c039502 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt @@ -29,6 +29,7 @@ import org.hisp.dhis.common.screens.others.ProgressScreen import org.hisp.dhis.common.screens.others.SearchBarScreen import org.hisp.dhis.common.screens.others.SectionScreen import org.hisp.dhis.common.screens.others.TagsScreen +import org.hisp.dhis.common.screens.others.TopBarScreen import org.hisp.dhis.common.screens.parameter.ParameterSelectorScreen import org.hisp.dhis.common.screens.toggleableInputs.ToggleableInputsScreen import org.hisp.dhis.mobile.ui.designsystem.component.DropdownItem @@ -101,6 +102,7 @@ fun Main( Groups.SEARCH_BAR -> SearchBarScreen() Groups.NAVIGATION_BAR -> NavigationBarScreen() Groups.NO_GROUP_SELECTED -> NoComponentSelectedScreen() + Groups.TOP_BAR -> TopBarScreen() } } 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 403da1712..821607631 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 @@ -18,5 +18,6 @@ enum class Groups(val label: String) { INDICATOR("Indicators"), PARAMETER_SELECTOR("Parameter selector"), NAVIGATION_BAR("Navigation Bar"), + TOP_BAR("Top Bar"), NO_GROUP_SELECTED("No group selected"), } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/others/TopBarScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/others/TopBarScreen.kt new file mode 100644 index 000000000..10064b7b8 --- /dev/null +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/others/TopBarScreen.kt @@ -0,0 +1,167 @@ +package org.hisp.dhis.common.screens.others + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.ArrowBack +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.FileDownload +import androidx.compose.material.icons.outlined.Menu +import androidx.compose.material.icons.outlined.Share +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +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.IconButton +import org.hisp.dhis.mobile.ui.designsystem.component.TopBar +import org.hisp.dhis.mobile.ui.designsystem.component.TopBarAction +import org.hisp.dhis.mobile.ui.designsystem.component.TopBarData +import org.hisp.dhis.mobile.ui.designsystem.component.TopBarType +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopBarScreen() { + val defaultTopBarData = TopBarData( + type = TopBarType.DEFAULT, + title = "Title", + navigationIcon = { + IconButton( + onClick = { }, + icon = { + Icon( + imageVector = Icons.Outlined.Menu, + contentDescription = "Menu Button", + ) + }, + ) + }, + primaryAction = TopBarAction( + icon = Icons.Outlined.Share, + onClick = { }, + ), + secondaryAction = TopBarAction( + icon = Icons.Outlined.FileDownload, + onClick = { }, + ), + dropdownMenu = { + DropdownMenu( + expanded = false, + onDismissRequest = { }, + ) { + DropdownMenuItem( + text = { Text("Action 1") }, + onClick = {}, + leadingIcon = { + IconButton( + onClick = { }, + icon = { + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = "Edit Button", + ) + }, + ) + }, + ) + } + }, + color = SurfaceColor.PrimaryContainer, + ) + + ColumnScreenContainer( + title = Groups.TOP_BAR.label, + ) { + ColumnComponentContainer("Default") { + TopBar( + topBarData = defaultTopBarData, + ) + } + + ColumnComponentContainer("Back") { + val backTopBarData = defaultTopBarData + .copy( + navigationIcon = { + IconButton( + onClick = { }, + icon = { + Icon( + imageVector = Icons.AutoMirrored.Outlined.ArrowBack, + contentDescription = "Back Button", + ) + }, + ) + }, + primaryAction = TopBarAction( + icon = Icons.Outlined.Delete, + onClick = {}, + ), + secondaryAction = null, + ) + TopBar( + topBarData = backTopBarData, + ) + } + + ColumnComponentContainer("Without Icons") { + val backTopBarData = defaultTopBarData + .copy( + navigationIcon = { + IconButton( + onClick = { }, + icon = { + Icon( + imageVector = Icons.AutoMirrored.Outlined.ArrowBack, + contentDescription = "Back Button", + ) + }, + ) + }, + primaryAction = null, + secondaryAction = null, + dropdownMenu = null, + ) + TopBar( + topBarData = backTopBarData, + ) + } + + ColumnComponentContainer("Centered") { + val centeredTopBarData = defaultTopBarData + .copy( + type = TopBarType.CENTERED, + primaryAction = null, + secondaryAction = null, + dropdownMenu = { + DropdownMenu( + expanded = false, + onDismissRequest = { }, + ) { + DropdownMenuItem( + text = { Text("Action 1") }, + onClick = {}, + leadingIcon = { + IconButton( + onClick = { }, + icon = { + Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = "Edit Button", + ) + }, + ) + }, + ) + } + }, + ) + + TopBar( + topBarData = centeredTopBarData, + ) + } + } +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/TopBar.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/TopBar.kt new file mode 100644 index 000000000..acfab2043 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/TopBar.kt @@ -0,0 +1,155 @@ +package org.hisp.dhis.mobile.ui.designsystem.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +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.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.style.TextOverflow + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TopBar( + modifier: Modifier = Modifier, + topBarData: TopBarData, +) { + var showMenu by remember { mutableStateOf(false) } + + if (topBarData.type == TopBarType.DEFAULT) { + TopAppBar( + modifier = modifier, + title = { + Text(text = topBarData.title) + }, + navigationIcon = { + topBarData.navigationIcon() + }, + actions = { + if (topBarData.primaryAction != null) { + IconButton( + onClick = { topBarData.primaryAction.onClick() }, + icon = { + Icon( + imageVector = topBarData.primaryAction.icon, + contentDescription = "Primary Action", + ) + }, + ) + } + if (topBarData.secondaryAction != null) { + IconButton( + onClick = { topBarData.secondaryAction.onClick() }, + icon = { + Icon( + imageVector = topBarData.secondaryAction.icon, + contentDescription = "Primary Action", + ) + }, + ) + } + + if (topBarData.dropdownMenu != null) { + IconButton( + onClick = { showMenu = !showMenu }, + icon = { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "More", + ) + }, + ) + topBarData.dropdownMenu() + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = topBarData.color, + ), + ) + } else { + CenterAlignedTopAppBar( + modifier = modifier, + title = { + Text( + text = topBarData.title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + navigationIcon = { + topBarData.navigationIcon() + }, + actions = { + if (topBarData.primaryAction != null) { + IconButton( + onClick = { topBarData.primaryAction.onClick() }, + icon = { + Icon( + imageVector = topBarData.primaryAction.icon, + contentDescription = "Primary Action", + ) + }, + ) + } + if (topBarData.secondaryAction != null) { + IconButton( + onClick = { topBarData.secondaryAction.onClick() }, + icon = { + Icon( + imageVector = topBarData.secondaryAction.icon, + contentDescription = "Primary Action", + ) + }, + ) + } + + if (topBarData.dropdownMenu != null) { + IconButton( + onClick = { showMenu = !showMenu }, + icon = { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = "More", + ) + }, + ) + topBarData.dropdownMenu() + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = topBarData.color, + ), + ) + } +} + +@ExperimentalMaterial3Api +data class TopBarData( + val type: TopBarType = TopBarType.DEFAULT, + val title: String, + val navigationIcon: @Composable () -> Unit, + val primaryAction: TopBarAction? = null, + val secondaryAction: TopBarAction? = null, + val dropdownMenu: (@Composable () -> Unit)? = null, + val color: Color, +) + +data class TopBarAction( + val icon: ImageVector, + val onClick: () -> Unit, +) + +enum class TopBarType { + DEFAULT, + CENTERED, +}