Skip to content

Commit

Permalink
fix: [ANDROAPP-6744] update bottomsheet
Browse files Browse the repository at this point in the history
  • Loading branch information
xavimolloy committed Jan 24, 2025
1 parent f5c1071 commit f23f386
Showing 1 changed file with 215 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,9 @@ fun BottomSheetHeader(
* @param subtitle: subTitle to be shown.
* @param description: PopUp description.
* @param searchQuery: Search query to be displayed in the search bar.
* @param showTopSectionDivider: whether to show the top divider or not.
* @param showBottomSectionDivider: whether to show the bottom divider or not.
* @param windowInsets: The insets to use for the bottom sheet shell.
* @param bottomPadding The lower padding for the bottom sheet shell.
* @param showSectionDivider: whether to show the divider or not.
* @param icon: the icon to be shown.
* @param buttonBlock: Space for the lower buttons, use together with BottomSheetShellDefaults
* button block padding to ensure a correct style is displayed.
* @param buttonBlock: Space for the lower buttons.
* @param content: to be shown under the header.
* @param contentScrollState: Pass custom scroll state when content is
* scrollable. For example, pass configure it when using `LazyColumn` to `Modifier.verticalScroll`
Expand All @@ -158,17 +154,15 @@ fun BottomSheetHeader(
* @param scrollableContainerMaxHeight: Max size for scrollable content.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Deprecated("Use the new BottomSheetShell with the new parameters")
@Composable
fun BottomSheetShell(
content: @Composable (() -> Unit)?,
title: String? = null,
subtitle: String? = null,
description: String? = null,
searchQuery: String? = null,
showTopSectionDivider: Boolean = true,
showBottomSectionDivider: Boolean = true,
windowInsets: @Composable () -> WindowInsets = { BottomSheetDefaults.windowInsets },
bottomPadding: Dp = Spacing0,
showSectionDivider: Boolean = true,
contentScrollState: ScrollableState = rememberScrollState(),
icon: @Composable (() -> Unit)? = null,
buttonBlock: @Composable (() -> Unit)? = null,
Expand Down Expand Up @@ -203,7 +197,6 @@ fun BottomSheetShell(
ModalBottomSheet(
modifier = modifier,
containerColor = Color.Transparent,
contentWindowInsets = windowInsets,
onDismissRequest = {
onDismiss()
},
Expand Down Expand Up @@ -242,6 +235,7 @@ fun BottomSheetShell(
Column(
modifier = Modifier
.weight(1f, fill = false)
.background(SurfaceColor.SurfaceBright, Shape.ExtraLargeTop)
.padding(top = Spacing24),
) {
val hasSearch =
Expand Down Expand Up @@ -276,7 +270,183 @@ fun BottomSheetShell(
}

if (showHeader || hasSearch) {
if (showTopSectionDivider) {
if (showSectionDivider) {
HorizontalDivider(
modifier = Modifier.fillMaxWidth()
.padding(top = Spacing24, start = Spacing24, end = Spacing24, bottom = Spacing8),
color = TextColor.OnDisabledSurface,
thickness = Border.Thin,
)
} else {
Spacer(Modifier.requiredHeight(Spacing24))
}
}

content?.let {
val scrollModifier = if ((contentScrollState as? ScrollState) != null) {
Modifier.verticalScroll(contentScrollState)
} else {
Modifier
}
Column(
Modifier
.then(scrollColumnShadow),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
modifier = Modifier
.padding(horizontal = Spacing24)
.heightIn(scrollableContainerMinHeight, scrollableContainerMaxHeight)
.then(scrollModifier),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = spacedBy(Spacing8),
) {
content.invoke()
}
if (showSectionDivider && !canScrollForward) {
HorizontalDivider(
modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing24),
color = TextColor.OnDisabledSurface,
thickness = Border.Thin,
)
}
}
}
}
buttonBlock?.let {
buttonBlock.invoke()
}
}
}
}

/**
* DHIS2 BottomSheetShell. Wraps compose · [ModalBottomSheet].
* desktop version to be implemented
* @param uiState UI data class of type [BottomSheetShellUIState] with all the values for the ui elements used in the component.
* @param windowInsets: The insets to use for the bottom sheet shell.
* @param icon: the icon to be shown.
* @param buttonBlock: Space for the lower buttons, use together with BottomSheetShellDefaults
* button block padding to ensure a correct style is displayed.
* @param content: to be shown under the header.
* @param contentScrollState: Pass custom scroll state when content is
* scrollable. For example, pass configure it when using `LazyColumn` to `Modifier.verticalScroll`
* for content.
* @param onSearchQueryChanged: Callback when search query is changed.
* @param onSearch: Callback when search action is triggered.
* @param onDismiss: gives access to the onDismiss event.
* @param modifier allows a modifier to be passed externally.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetShell(
uiState: BottomSheetShellUIState,
modifier: Modifier = Modifier,
content: @Composable (() -> Unit)?,
windowInsets: @Composable () -> WindowInsets = { BottomSheetDefaults.windowInsets },
contentScrollState: ScrollableState = rememberScrollState(),
icon: @Composable (() -> Unit)? = null,
buttonBlock: @Composable (() -> Unit)? = null,
onSearchQueryChanged: ((String) -> Unit)? = null,
onSearch: ((String) -> Unit)? = null,
onDismiss: () -> Unit,
) {
val sheetState = rememberModalBottomSheetState(true)
val scope = rememberCoroutineScope()
val keyboardState by keyboardAsState()

var isKeyboardOpen by remember { mutableStateOf(false) }
val showHeader by remember {
derivedStateOf {
if (uiState.animateHeaderOnKeyboardAppearance) {
!uiState.title.isNullOrBlank() && !isKeyboardOpen
} else {
!uiState.title.isNullOrBlank()
}
}
}

LaunchedEffect(keyboardState) {
isKeyboardOpen = keyboardState == Keyboard.Opened
}

ModalBottomSheet(
modifier = modifier,
containerColor = Color.Transparent,
contentWindowInsets = windowInsets,
onDismissRequest = {
onDismiss()
},
sheetState = sheetState,
dragHandle = {
Box(
modifier = Modifier.padding(top = Spacing.Spacing72),
) {
BottomSheetIconButton(
icon = {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = "Button",
tint = SurfaceColor.SurfaceBright,
)
},
modifier = Modifier.padding(bottom = Spacing.Spacing4),
) {
scope.launch {
onDismiss()
}
}
}
},
) {
val canScrollForward by derivedStateOf { contentScrollState.canScrollForward }

Column(
modifier = Modifier.padding(bottom = Spacing0).background(SurfaceColor.SurfaceBright, Shape.ExtraLargeTop),
) {
val scrollColumnShadow = if (canScrollForward) {
Modifier.innerShadow(blur = 32.dp)
} else {
Modifier
}
Column(
modifier = Modifier
.weight(1f, fill = false)
.padding(top = Spacing24),
) {
val hasSearch =
uiState.searchQuery != null && onSearchQueryChanged != null && onSearch != null
AnimatedVisibility(
visible = showHeader,
) {
BottomSheetHeader(
title = uiState.title!!,
subTitle = uiState.subtitle,
description = uiState.description,
icon = icon,
hasSearch = hasSearch,
headerTextAlignment = uiState.headerTextAlignment,
modifier = Modifier
.padding(vertical = Spacing0)
.align(Alignment.CenterHorizontally),
)
}

if (showHeader && hasSearch) {
Spacer(Modifier.requiredHeight(16.dp))
}

if (hasSearch) {
SearchBar(
modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing24),
text = uiState.searchQuery!!,
onQueryChange = onSearchQueryChanged!!,
onSearch = onSearch!!,
)
}

if (showHeader || hasSearch) {
if (uiState.showTopSectionDivider) {
HorizontalDivider(
modifier = Modifier.fillMaxWidth()
.padding(top = Spacing24, start = Spacing24, end = Spacing24, bottom = Spacing0),
Expand All @@ -302,15 +472,15 @@ fun BottomSheetShell(
Column(
modifier = Modifier
.padding(horizontal = Spacing24)
.heightIn(scrollableContainerMinHeight, scrollableContainerMaxHeight)
.heightIn(uiState.scrollableContainerMinHeight, uiState.scrollableContainerMaxHeight)
.then(scrollModifier),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = spacedBy(Spacing8),
) {
content.invoke()
Spacer(Modifier.requiredHeight(Spacing8))
}
if (showBottomSectionDivider && !canScrollForward) {
if (uiState.showBottomSectionDivider && !canScrollForward) {
HorizontalDivider(
modifier = Modifier.fillMaxWidth().padding(start = Spacing24, end = Spacing24, bottom = Spacing0, top = Spacing0),
color = TextColor.OnDisabledSurface,
Expand All @@ -324,11 +494,41 @@ fun BottomSheetShell(
buttonBlock?.let {
buttonBlock.invoke()
}
Spacer(Modifier.requiredHeight(bottomPadding))
Spacer(Modifier.requiredHeight(uiState.bottomPadding))
}
}
}

/**
* Data class representing the UI state for the BottomSheetShell component.
*
* @property title The title to be displayed in the bottom sheet header.
* @property subtitle The subtitle to be displayed in the bottom sheet header.
* @property description The description to be displayed in the bottom sheet header.
* @property searchQuery The search query to be displayed in the search bar.
* @property showTopSectionDivider Whether to show the top section divider.
* @property showBottomSectionDivider Whether to show the bottom section divider.
* @property bottomPadding The lower padding for the bottom sheet shell.
* @property headerTextAlignment The alignment for the header text.
* @property scrollableContainerMinHeight The minimum height for the scrollable content container.
* @property scrollableContainerMaxHeight The maximum height for the scrollable content container.
* @property animateHeaderOnKeyboardAppearance Whether to animate the header when the keyboard appears.
*/

data class BottomSheetShellUIState(
val title: String? = null,
val subtitle: String? = null,
val description: String? = null,
val searchQuery: String? = null,
val showTopSectionDivider: Boolean = true,
val showBottomSectionDivider: Boolean = true,
val bottomPadding: Dp = Spacing0,
val headerTextAlignment: TextAlign = TextAlign.Center,
val scrollableContainerMinHeight: Dp = Spacing0,
val scrollableContainerMaxHeight: Dp = InternalSizeValues.Size386,
val animateHeaderOnKeyboardAppearance: Boolean = true,
)

/**
* Provides default values and configurations for the BottomSheet component.
*/
Expand Down

0 comments on commit f23f386

Please sign in to comment.