[Compound] Implement Snackbars based on designs (#1054)
* Make `InternalButton` internal instead of private so it can be customised. Also, change the `ButtonColors.contentColor` for text buttons to `LocalContentColor.current` by default. * Add temporary color for Snackbar action label * Implement `Snackbar` component based on Compound * Propagate changes to all other components * Use right Preview annotation config * Move `ButtonVisuals` to their own file * Update screenshots * Make previews internal * Update screenshots * Set a custom token for contentColor in AppBars * Change 'Label' to 'Action' in the previews * Add changelog * Update screenshots --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
acf29036a6
commit
9e1ff513e4
49 changed files with 441 additions and 147 deletions
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.components.button
|
||||
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
|
||||
/**
|
||||
* A sealed class that represents the different visual styles that a button can have.
|
||||
*/
|
||||
sealed interface ButtonVisuals {
|
||||
|
||||
val action: () -> Unit
|
||||
|
||||
/**
|
||||
* Creates a [Button] composable based on the visual state.
|
||||
*/
|
||||
@Composable
|
||||
fun Composable()
|
||||
|
||||
data class Text(val text: String, override val action: () -> Unit) : ButtonVisuals {
|
||||
@Composable
|
||||
override fun Composable() {
|
||||
TextButton(text = text, onClick = action)
|
||||
}
|
||||
}
|
||||
data class Icon(val iconSource: IconSource, override val action: () -> Unit) : ButtonVisuals {
|
||||
@Composable
|
||||
override fun Composable() {
|
||||
IconButton(onClick = action) {
|
||||
Icon(iconSource.getPainter(), iconSource.contentDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ object PreviewGroup {
|
|||
const val Preferences = "Preferences"
|
||||
const val Progress = "Progress Indicators"
|
||||
const val Search = "Search views"
|
||||
const val Snackbars = "Snackbars"
|
||||
const val Sliders = "Sliders"
|
||||
const val Text = "Text"
|
||||
const val TextFields = "TextFields"
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ private fun ContentToPreview() {
|
|||
WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) {
|
||||
OutlinedButton(
|
||||
text = "A Button with rulers on it!",
|
||||
buttonSize = ButtonSize.Medium,
|
||||
size = ButtonSize.Medium,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,19 +72,19 @@ internal fun SimpleAlertDialogContent(
|
|||
// Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f
|
||||
TextButton(
|
||||
text = thirdButtonText,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
size = ButtonSize.Medium,
|
||||
onClick = onThirdButtonClicked,
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
text = cancelText,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
size = ButtonSize.Medium,
|
||||
onClick = onCancelClicked,
|
||||
)
|
||||
if (submitText != null) {
|
||||
Button(
|
||||
text = submitText,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
size = ButtonSize.Medium,
|
||||
onClick = onSubmitClicked,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import androidx.compose.foundation.progressSemantics
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -41,6 +42,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.isSpecified
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
|
|
@ -60,10 +62,19 @@ fun Button(
|
|||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
buttonSize: ButtonSize = ButtonSize.Large,
|
||||
size: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) = ButtonInternal(text, onClick, ButtonStyle.Filled, modifier, enabled, buttonSize, showProgress, leadingIcon)
|
||||
) = ButtonInternal(
|
||||
text = text,
|
||||
onClick = onClick,
|
||||
style = ButtonStyle.Filled,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
size = size,
|
||||
showProgress = showProgress,
|
||||
leadingIcon = leadingIcon
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun OutlinedButton(
|
||||
|
|
@ -71,10 +82,19 @@ fun OutlinedButton(
|
|||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
buttonSize: ButtonSize = ButtonSize.Large,
|
||||
size: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) = ButtonInternal(text, onClick, ButtonStyle.Outlined, modifier, enabled, buttonSize, showProgress, leadingIcon)
|
||||
) = ButtonInternal(
|
||||
text = text,
|
||||
onClick = onClick,
|
||||
style = ButtonStyle.Outlined,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
size = size,
|
||||
showProgress = showProgress,
|
||||
leadingIcon = leadingIcon
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun TextButton(
|
||||
|
|
@ -82,17 +102,27 @@ fun TextButton(
|
|||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
buttonSize: ButtonSize = ButtonSize.Large,
|
||||
size: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) = ButtonInternal(text, onClick, ButtonStyle.Text, modifier, enabled, buttonSize, showProgress, leadingIcon)
|
||||
) = ButtonInternal(
|
||||
text = text,
|
||||
onClick = onClick,
|
||||
style = ButtonStyle.Text,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
size = size,
|
||||
showProgress = showProgress,
|
||||
leadingIcon = leadingIcon
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun ButtonInternal(
|
||||
internal fun ButtonInternal(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
style: ButtonStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
colors: ButtonColors = style.getColors(),
|
||||
enabled: Boolean = true,
|
||||
size: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
|
|
@ -123,21 +153,6 @@ private fun ButtonInternal(
|
|||
ButtonStyle.Text -> RectangleShape
|
||||
}
|
||||
|
||||
val colors = when (style) {
|
||||
ButtonStyle.Filled -> ButtonDefaults.buttonColors(
|
||||
containerColor = ElementTheme.materialColors.primary,
|
||||
contentColor = ElementTheme.materialColors.onPrimary,
|
||||
disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled,
|
||||
disabledContentColor = ElementTheme.colors.textOnSolidPrimary
|
||||
)
|
||||
ButtonStyle.Outlined, ButtonStyle.Text -> ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = ElementTheme.materialColors.primary,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
disabledContentColor = ElementTheme.colors.textDisabled,
|
||||
)
|
||||
}
|
||||
|
||||
val border = when (style) {
|
||||
ButtonStyle.Filled, ButtonStyle.Text -> null
|
||||
ButtonStyle.Outlined -> BorderStroke(
|
||||
|
|
@ -202,8 +217,10 @@ private fun ButtonInternal(
|
|||
}
|
||||
|
||||
sealed interface IconSource {
|
||||
data class Resource(val id: Int) : IconSource
|
||||
data class Vector(val vector: ImageVector) : IconSource
|
||||
val contentDescription: String?
|
||||
|
||||
data class Resource(val id: Int, override val contentDescription: String? = null) : IconSource
|
||||
data class Vector(val vector: ImageVector, override val contentDescription: String? = null) : IconSource
|
||||
|
||||
@Composable
|
||||
fun getPainter(): Painter = when (this) {
|
||||
|
|
@ -216,16 +233,38 @@ enum class ButtonSize {
|
|||
Medium, Large
|
||||
}
|
||||
|
||||
private enum class ButtonStyle {
|
||||
Filled, Outlined, Text
|
||||
internal enum class ButtonStyle {
|
||||
Filled, Outlined, Text;
|
||||
|
||||
@Composable
|
||||
fun getColors(): ButtonColors = when (this) {
|
||||
Filled -> ButtonDefaults.buttonColors(
|
||||
containerColor = ElementTheme.materialColors.primary,
|
||||
contentColor = ElementTheme.materialColors.onPrimary,
|
||||
disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled,
|
||||
disabledContentColor = ElementTheme.colors.textOnSolidPrimary
|
||||
)
|
||||
Outlined -> ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = ElementTheme.materialColors.primary,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
disabledContentColor = ElementTheme.colors.textDisabled,
|
||||
)
|
||||
Text -> ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = if (LocalContentColor.current.isSpecified) LocalContentColor.current else ElementTheme.materialColors.primary,
|
||||
disabledContainerColor = Color.Transparent,
|
||||
disabledContentColor = ElementTheme.colors.textDisabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun FilledButtonMediumPreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Filled,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
style = ButtonStyle.Filled,
|
||||
size = ButtonSize.Medium,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +272,8 @@ internal fun FilledButtonMediumPreview() {
|
|||
@Composable
|
||||
internal fun FilledButtonLargePreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Filled,
|
||||
buttonSize = ButtonSize.Large,
|
||||
style = ButtonStyle.Filled,
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -242,8 +281,8 @@ internal fun FilledButtonLargePreview() {
|
|||
@Composable
|
||||
internal fun OutlinedButtonMediumPreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Outlined,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
style = ButtonStyle.Outlined,
|
||||
size = ButtonSize.Medium,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -251,8 +290,8 @@ internal fun OutlinedButtonMediumPreview() {
|
|||
@Composable
|
||||
internal fun OutlinedButtonLargePreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Outlined,
|
||||
buttonSize = ButtonSize.Large,
|
||||
style = ButtonStyle.Outlined,
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -260,8 +299,8 @@ internal fun OutlinedButtonLargePreview() {
|
|||
@Composable
|
||||
internal fun TextButtonMediumPreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Text,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
style = ButtonStyle.Text,
|
||||
size = ButtonSize.Medium,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -269,15 +308,15 @@ internal fun TextButtonMediumPreview() {
|
|||
@Composable
|
||||
internal fun TextButtonLargePreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Text,
|
||||
buttonSize = ButtonSize.Large,
|
||||
style = ButtonStyle.Text,
|
||||
size = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonCombinationPreview(
|
||||
buttonStyle: ButtonStyle,
|
||||
buttonSize: ButtonSize,
|
||||
style: ButtonStyle,
|
||||
size: ButtonSize,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ElementThemedPreview {
|
||||
|
|
@ -290,24 +329,24 @@ private fun ButtonCombinationPreview(
|
|||
// Normal
|
||||
ButtonRowPreview(
|
||||
modifier = Modifier.then(modifier),
|
||||
buttonStyle = buttonStyle,
|
||||
buttonSize = buttonSize,
|
||||
style = style,
|
||||
size = size,
|
||||
)
|
||||
|
||||
// With icon
|
||||
ButtonRowPreview(
|
||||
modifier = Modifier.then(modifier),
|
||||
leadingIcon = IconSource.Vector(Icons.Outlined.Share),
|
||||
buttonStyle = buttonStyle,
|
||||
buttonSize = buttonSize,
|
||||
style = style,
|
||||
size = size,
|
||||
)
|
||||
|
||||
// With progress
|
||||
ButtonRowPreview(
|
||||
modifier = Modifier.then(modifier),
|
||||
showProgress = true,
|
||||
buttonStyle = buttonStyle,
|
||||
buttonSize = buttonSize,
|
||||
style = style,
|
||||
size = size,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -315,8 +354,8 @@ private fun ButtonCombinationPreview(
|
|||
|
||||
@Composable
|
||||
private fun ButtonRowPreview(
|
||||
buttonStyle: ButtonStyle,
|
||||
buttonSize: ButtonSize,
|
||||
style: ButtonStyle,
|
||||
size: ButtonSize,
|
||||
modifier: Modifier = Modifier,
|
||||
leadingIcon: IconSource? = null,
|
||||
showProgress: Boolean = false,
|
||||
|
|
@ -326,8 +365,8 @@ private fun ButtonRowPreview(
|
|||
text = "A button",
|
||||
showProgress = showProgress,
|
||||
onClick = {},
|
||||
style = buttonStyle,
|
||||
size = buttonSize,
|
||||
style = style,
|
||||
size = size,
|
||||
leadingIcon = leadingIcon,
|
||||
modifier = Modifier.then(modifier),
|
||||
)
|
||||
|
|
@ -336,8 +375,8 @@ private fun ButtonRowPreview(
|
|||
showProgress = showProgress,
|
||||
enabled = false,
|
||||
onClick = {},
|
||||
style = buttonStyle,
|
||||
size = buttonSize,
|
||||
style = style,
|
||||
size = size,
|
||||
leadingIcon = leadingIcon,
|
||||
modifier = Modifier.then(modifier),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components
|
|||
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.TopAppBarColors
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -43,7 +49,11 @@ fun MediumTopAppBar(
|
|||
title = title,
|
||||
modifier = modifier,
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
actions = {
|
||||
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) {
|
||||
actions()
|
||||
}
|
||||
},
|
||||
windowInsets = windowInsets,
|
||||
colors = colors,
|
||||
scrollBehavior = scrollBehavior,
|
||||
|
|
@ -58,5 +68,14 @@ internal fun MediumTopAppBarPreview() =
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
MediumTopAppBar(title = { Text(text = "Title") })
|
||||
MediumTopAppBar(
|
||||
title = { Text(text = "Title") },
|
||||
navigationIcon = { BackButton(onClick = {}) },
|
||||
actions = {
|
||||
TextButton(text = "Action", onClick = {})
|
||||
IconButton(onClick = {}) {
|
||||
Icon(imageVector = Icons.Default.Share, contentDescription = null)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.designsystem.theme.components
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.SnackbarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.theme.SnackBarLabelColorDark
|
||||
import io.element.android.libraries.theme.SnackBarLabelColorLight
|
||||
|
||||
@Composable
|
||||
fun Snackbar(
|
||||
message: String,
|
||||
modifier: Modifier = Modifier,
|
||||
action: ButtonVisuals? = null,
|
||||
dismissAction: ButtonVisuals? = null,
|
||||
actionOnNewLine: Boolean = false,
|
||||
shape: Shape = RoundedCornerShape(8.dp),
|
||||
containerColor: Color = SnackbarDefaults.color,
|
||||
contentColor: Color = ElementTheme.materialColors.inverseOnSurface,
|
||||
actionContentColor: Color = actionContentColor(),
|
||||
dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor,
|
||||
) {
|
||||
Snackbar(
|
||||
modifier = modifier,
|
||||
action = action?.let { @Composable { it.Composable() } },
|
||||
dismissAction = dismissAction?.let { @Composable { it.Composable() } },
|
||||
actionOnNewLine = actionOnNewLine,
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
actionContentColor = actionContentColor,
|
||||
dismissActionContentColor = dismissActionContentColor,
|
||||
content = { Text(text = message) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Snackbar(
|
||||
modifier: Modifier = Modifier,
|
||||
action: @Composable (() -> Unit)? = null,
|
||||
dismissAction: @Composable (() -> Unit)? = null,
|
||||
actionOnNewLine: Boolean = false,
|
||||
shape: Shape = RoundedCornerShape(8.dp),
|
||||
containerColor: Color = SnackbarDefaults.color,
|
||||
contentColor: Color = ElementTheme.materialColors.inverseOnSurface,
|
||||
actionContentColor: Color = actionContentColor(),
|
||||
dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
androidx.compose.material3.Snackbar(
|
||||
modifier = modifier,
|
||||
action = action,
|
||||
dismissAction = dismissAction,
|
||||
actionOnNewLine = actionOnNewLine,
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor,
|
||||
actionContentColor = actionContentColor,
|
||||
dismissActionContentColor = dismissActionContentColor,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO this color is temporary, an `inverse` version should be added to the semantic colors instead
|
||||
@Composable
|
||||
private fun actionContentColor(): Color {
|
||||
return if (ElementTheme.isLightTheme) {
|
||||
SnackBarLabelColorLight
|
||||
} else {
|
||||
SnackBarLabelColorDark
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Snackbar", group = PreviewGroup.Snackbars)
|
||||
@Composable
|
||||
internal fun SnackbarPreview() {
|
||||
ElementThemedPreview {
|
||||
Snackbar(message = "Snackbar supporting text")
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Snackbar with action", group = PreviewGroup.Snackbars)
|
||||
@Composable
|
||||
internal fun SnackbarWithActionPreview() {
|
||||
ElementThemedPreview {
|
||||
Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Snackbar with action and close button", group = PreviewGroup.Snackbars)
|
||||
@Composable
|
||||
internal fun SnackbarWithActionAndCloseButtonPreview() {
|
||||
ElementThemedPreview {
|
||||
Snackbar(
|
||||
message = "Snackbar supporting text",
|
||||
action = ButtonVisuals.Text("Action", {}),
|
||||
dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Snackbar with action on new line", group = PreviewGroup.Snackbars)
|
||||
@Composable
|
||||
internal fun SnackbarWithActionOnNewLinePreview() {
|
||||
ElementThemedPreview {
|
||||
Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}), actionOnNewLine = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Snackbar with action and close button on new line", group = PreviewGroup.Snackbars)
|
||||
@Composable
|
||||
internal fun SnackbarWithActionOnNewLineAndCloseButtonPreview() {
|
||||
ElementThemedPreview {
|
||||
Snackbar(
|
||||
message = "Snackbar supporting text",
|
||||
action = ButtonVisuals.Text("Action", {}),
|
||||
dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {}),
|
||||
actionOnNewLine = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components
|
|||
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.TopAppBarColors
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -43,7 +49,11 @@ fun TopAppBar(
|
|||
title = title,
|
||||
modifier = modifier,
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
actions = {
|
||||
CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) {
|
||||
actions()
|
||||
}
|
||||
},
|
||||
windowInsets = windowInsets,
|
||||
colors = colors,
|
||||
scrollBehavior = scrollBehavior,
|
||||
|
|
@ -58,5 +68,14 @@ internal fun TopAppBarPreview() =
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
TopAppBar(title = { Text(text = "Title") })
|
||||
TopAppBar(
|
||||
title = { Text(text = "Title") },
|
||||
navigationIcon = { BackButton(onClick = {}) },
|
||||
actions = {
|
||||
TextButton(text = "Action", onClick = {})
|
||||
IconButton(onClick = {}) {
|
||||
Icon(imageVector = Icons.Default.Share, contentDescription = null)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package io.element.android.libraries.designsystem.utils
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -25,7 +27,11 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.libraries.designsystem.components.button.ButtonVisuals
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
import io.element.android.libraries.designsystem.theme.components.Snackbar
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
|
@ -65,6 +71,19 @@ fun SnackbarDispatcher.collectSnackbarMessageAsState(): State<SnackbarMessage?>
|
|||
return snackbarMessage.collectAsState(initial = null)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) {
|
||||
androidx.compose.material3.SnackbarHost(hostState, modifier) { data ->
|
||||
Snackbar(
|
||||
message = data.visuals.message,
|
||||
action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) },
|
||||
dismissAction = if (data.visuals.withDismissAction) {
|
||||
ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), data::dismiss)
|
||||
} else null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostState {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package io.element.android.libraries.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens
|
||||
import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens
|
||||
|
||||
// =================================================================================================
|
||||
// IMPORTANT!
|
||||
|
|
@ -26,3 +28,6 @@ import androidx.compose.ui.graphics.Color
|
|||
// =================================================================================================
|
||||
|
||||
val LinkColor = Color(0xFF0086E6)
|
||||
|
||||
val SnackBarLabelColorLight = LightDesignTokens.colorGray700
|
||||
val SnackBarLabelColorDark = DarkDesignTokens.colorGray700
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue