[Compound] Implement components (Button) (#1021)
* Create `CompoundButton` * Some fixes * Lint fixes * Start replacing existing `Button` usages * Replace button usages * Remove previous Button composable * Rename `CompoundButton` to `Button` * Fix emphasized button being displayed as Text * Fix cancel button in `WaitListView` * Update screenshots * Add shorthand functions for `OutlinedButton` and `TextButton` * Add changelog * Fix wrong size used for emphasized button in dialog * Create a private `ButtonInternal` implementation with the shared logic. - Make `ButtonStyle` private. - Rename `title` to `text`. - Rename `buttonStyle` and `buttonSize` to just `style` and `size`. * Fix several warnings and lint issues. * Update screenshots --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
fdee5d8a76
commit
23982dde47
228 changed files with 805 additions and 950 deletions
|
|
@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp
|
|||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
|
||||
@Composable
|
||||
|
|
@ -59,11 +59,8 @@ internal fun ButtonColumnMoleculeDarkPreview() =
|
|||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
ButtonColumnMolecule {
|
||||
Button(onClick = {}, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(text = "Button")
|
||||
}
|
||||
TextButton(onClick = {}, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(text = "TextButton")
|
||||
}
|
||||
Button(text = "Button", onClick = {}, modifier = Modifier.fillMaxWidth())
|
||||
OutlinedButton(text = "OutlinedButton", onClick = {}, modifier = Modifier.fillMaxWidth())
|
||||
TextButton(text = "TextButton", onClick = {}, modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
|
||||
@Composable
|
||||
|
|
@ -54,11 +53,7 @@ internal fun ButtonRowMoleculeDarkPreview() =
|
|||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
ButtonRowMolecule {
|
||||
TextButton(onClick = { }) {
|
||||
Text("Button 1")
|
||||
}
|
||||
TextButton(onClick = { }) {
|
||||
Text("Button 2")
|
||||
}
|
||||
TextButton(text = "Button 1", onClick = {})
|
||||
TextButton(text = "Button 2", onClick = {})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,9 +129,10 @@ private fun ProgressDialogContent(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.BottomEnd
|
||||
) {
|
||||
TextButton(onClick = onCancelClicked) {
|
||||
Text(stringResource(id = CommonStrings.action_cancel))
|
||||
}
|
||||
TextButton(
|
||||
text = stringResource(id = CommonStrings.action_cancel),
|
||||
onClick = onCancelClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,10 @@ fun AsyncFailure(
|
|||
Text(text = throwable.message ?: stringResource(id = CommonStrings.error_unknown))
|
||||
if (onRetry != null) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Button(onClick = onRetry) {
|
||||
Text(text = stringResource(id = CommonStrings.action_retry))
|
||||
}
|
||||
Button(
|
||||
text = stringResource(id = CommonStrings.action_retry),
|
||||
onClick = onRetry
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* 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.foundation.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.progressSemantics
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonElevation
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
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.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.aliasButtonText
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.ElementButtonDefaults
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
/**
|
||||
* A component that will display a button with an indeterminate circular progressbar.
|
||||
* When [showProgress] is true:
|
||||
* - A circular progressbar is displayed.
|
||||
* - [text] is replaced by [progressText], if defined.
|
||||
* - [onClick] gets disabled.
|
||||
*/
|
||||
@Composable
|
||||
fun ButtonWithProgress(
|
||||
text: String?,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
showProgress: Boolean = false,
|
||||
progressText: String? = text,
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = ElementButtonDefaults.shape,
|
||||
colors: ButtonColors = ElementButtonDefaults.buttonColors(),
|
||||
elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(),
|
||||
border: BorderStroke? = null,
|
||||
contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
if (!showProgress) {
|
||||
onClick()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
contentPadding = contentPadding,
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
if (showProgress) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.progressSemantics()
|
||||
.size(18.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
if (progressText != null) {
|
||||
Spacer(Modifier.width(10.dp))
|
||||
Text(progressText, style = ElementTheme.typography.aliasButtonText)
|
||||
}
|
||||
} else if (text != null) {
|
||||
Text(text, style = ElementTheme.typography.aliasButtonText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun ButtonWithProgressPreview() = ElementThemedPreview {
|
||||
ButtonWithProgress(
|
||||
text = "Button with progress",
|
||||
onClick = {},
|
||||
showProgress = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ import androidx.compose.material3.LocalContentColor
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -38,7 +37,10 @@ import androidx.compose.ui.layout.Layout
|
|||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import kotlin.math.max
|
||||
|
||||
|
|
@ -53,7 +55,6 @@ internal fun SimpleAlertDialogContent(
|
|||
onSubmitClicked: () -> Unit = {},
|
||||
thirdButtonText: String? = null,
|
||||
onThirdButtonClicked: () -> Unit = {},
|
||||
emphasizeSubmitButton: Boolean = false,
|
||||
shape: Shape = AlertDialogDefaults.shape,
|
||||
containerColor: Color = AlertDialogDefaults.containerColor,
|
||||
iconContentColor: Color = AlertDialogDefaults.iconContentColor,
|
||||
|
|
@ -71,30 +72,23 @@ internal fun SimpleAlertDialogContent(
|
|||
if (thirdButtonText != null) {
|
||||
// If there is a 3rd item it should be at the end of the dialog
|
||||
// Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f
|
||||
TextButton(onClick = onThirdButtonClicked) {
|
||||
Text(
|
||||
text = thirdButtonText,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
}
|
||||
}
|
||||
TextButton(onClick = onCancelClicked) {
|
||||
Text(
|
||||
text = cancelText,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
TextButton(
|
||||
text = thirdButtonText,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
onClick = onThirdButtonClicked,
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
text = cancelText,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
onClick = onCancelClicked,
|
||||
)
|
||||
if (submitText != null) {
|
||||
TextButton(onClick = onSubmitClicked) {
|
||||
Text(
|
||||
text = submitText,
|
||||
style = if (emphasizeSubmitButton) {
|
||||
ElementTheme.typography.fontBodyMdMedium
|
||||
} else {
|
||||
ElementTheme.typography.fontBodyMdRegular
|
||||
}
|
||||
)
|
||||
}
|
||||
Button(
|
||||
text = submitText,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
onClick = onSubmitClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,11 +26,9 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.utils.BooleanProvider
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -44,7 +42,6 @@ fun ConfirmationDialog(
|
|||
submitText: String = stringResource(id = CommonStrings.action_ok),
|
||||
cancelText: String = stringResource(id = CommonStrings.action_cancel),
|
||||
thirdButtonText: String? = null,
|
||||
emphasizeSubmitButton: Boolean = false,
|
||||
onCancelClicked: () -> Unit = onDismiss,
|
||||
onThirdButtonClicked: () -> Unit = {},
|
||||
shape: Shape = AlertDialogDefaults.shape,
|
||||
|
|
@ -71,7 +68,6 @@ fun ConfirmationDialog(
|
|||
titleContentColor = titleContentColor,
|
||||
textContentColor = textContentColor,
|
||||
tonalElevation = tonalElevation,
|
||||
emphasizeSubmitButton = emphasizeSubmitButton,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +83,6 @@ private fun ConfirmationDialogContent(
|
|||
title: String? = null,
|
||||
thirdButtonText: String? = null,
|
||||
onThirdButtonClicked: () -> Unit = {},
|
||||
emphasizeSubmitButton: Boolean = false,
|
||||
shape: Shape = AlertDialogDefaults.shape,
|
||||
containerColor: Color = AlertDialogDefaults.containerColor,
|
||||
iconContentColor: Color = AlertDialogDefaults.iconContentColor,
|
||||
|
|
@ -106,7 +101,6 @@ private fun ConfirmationDialogContent(
|
|||
onCancelClicked = onCancelClicked,
|
||||
thirdButtonText = thirdButtonText,
|
||||
onThirdButtonClicked = onThirdButtonClicked,
|
||||
emphasizeSubmitButton = emphasizeSubmitButton,
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
iconContentColor = iconContentColor,
|
||||
|
|
@ -119,7 +113,7 @@ private fun ConfirmationDialogContent(
|
|||
|
||||
@Preview(group = PreviewGroup.Dialogs)
|
||||
@Composable
|
||||
internal fun ConfirmationDialogPreview(@PreviewParameter(BooleanProvider::class) emphasizeSubmitButton: Boolean) =
|
||||
internal fun ConfirmationDialogPreview() =
|
||||
ElementThemedPreview {
|
||||
DialogPreview {
|
||||
ConfirmationDialogContent(
|
||||
|
|
@ -130,7 +124,6 @@ internal fun ConfirmationDialogPreview(@PreviewParameter(BooleanProvider::class)
|
|||
thirdButtonText = "Disable",
|
||||
onSubmitClicked = {},
|
||||
onCancelClicked = {},
|
||||
emphasizeSubmitButton = emphasizeSubmitButton,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package io.element.android.libraries.designsystem.components.dialogs
|
|||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -28,10 +28,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RetryDialog(
|
||||
content: String,
|
||||
|
|
@ -48,44 +47,22 @@ fun RetryDialog(
|
|||
textContentColor: Color = AlertDialogDefaults.textContentColor,
|
||||
tonalElevation: Dp = AlertDialogDefaults.TonalElevation,
|
||||
) {
|
||||
AlertDialog(
|
||||
modifier = modifier,
|
||||
onDismissRequest = onDismiss,
|
||||
title = {
|
||||
Text(
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontHeadingSmRegular,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = content,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onRetry) {
|
||||
Text(
|
||||
text = retryText,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(
|
||||
text = dismissText,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
}
|
||||
},
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
iconContentColor = iconContentColor,
|
||||
titleContentColor = titleContentColor,
|
||||
textContentColor = textContentColor,
|
||||
tonalElevation = tonalElevation,
|
||||
)
|
||||
AlertDialog(modifier = modifier, onDismissRequest = onDismiss) {
|
||||
RetryDialogContent(
|
||||
title = title,
|
||||
content = content,
|
||||
retryText = retryText,
|
||||
dismissText = dismissText,
|
||||
onRetry = onRetry,
|
||||
onDismiss = onDismiss,
|
||||
shape = shape,
|
||||
containerColor = containerColor,
|
||||
iconContentColor = iconContentColor,
|
||||
titleContentColor = titleContentColor,
|
||||
textContentColor = textContentColor,
|
||||
tonalElevation = tonalElevation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ import androidx.compose.ui.unit.Dp
|
|||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.ButtonSize
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
/**
|
||||
* Debug tool to add a vertical and a horizontal ruler on top of the content.
|
||||
|
|
@ -76,8 +76,10 @@ internal fun WithRulerDarkPreview() =
|
|||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) {
|
||||
OutlinedButton(onClick = {}) {
|
||||
Text(text = "A Button with rulers on it!")
|
||||
}
|
||||
OutlinedButton(
|
||||
text = "A Button with rulers on it!",
|
||||
buttonSize = ButtonSize.Medium,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,68 +18,323 @@ package io.element.android.libraries.designsystem.theme.components
|
|||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
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.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonElevation
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
|
||||
// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=U03tOFZz5FSLVUMa-1
|
||||
|
||||
@Composable
|
||||
fun Button(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = ElementButtonDefaults.shape,
|
||||
colors: ButtonColors = ElementButtonDefaults.buttonColors(),
|
||||
elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(),
|
||||
border: BorderStroke? = null,
|
||||
contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable RowScope.() -> Unit
|
||||
buttonSize: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) = ButtonInternal(text, onClick, ButtonStyle.Filled, modifier, enabled, buttonSize, showProgress, leadingIcon)
|
||||
|
||||
@Composable
|
||||
fun OutlinedButton(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
buttonSize: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) = ButtonInternal(text, onClick, ButtonStyle.Outlined, modifier, enabled, buttonSize, showProgress, leadingIcon)
|
||||
|
||||
@Composable
|
||||
fun TextButton(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
buttonSize: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) = ButtonInternal(text, onClick, ButtonStyle.Text, modifier, enabled, buttonSize, showProgress, leadingIcon)
|
||||
|
||||
@Composable
|
||||
private fun ButtonInternal(
|
||||
text: String,
|
||||
onClick: () -> Unit,
|
||||
style: ButtonStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
size: ButtonSize = ButtonSize.Large,
|
||||
showProgress: Boolean = false,
|
||||
leadingIcon: IconSource? = null,
|
||||
) {
|
||||
val minHeight = when (size) {
|
||||
ButtonSize.Medium -> 40.dp
|
||||
ButtonSize.Large -> 48.dp
|
||||
}
|
||||
|
||||
val paddingValues = when (size) {
|
||||
ButtonSize.Medium -> {
|
||||
when (style) {
|
||||
ButtonStyle.Text -> PaddingValues(horizontal = 12.dp, vertical = 10.dp)
|
||||
else -> PaddingValues(horizontal = 16.dp, vertical = 10.dp)
|
||||
}
|
||||
}
|
||||
ButtonSize.Large -> {
|
||||
when (style) {
|
||||
ButtonStyle.Text -> PaddingValues(horizontal = 16.dp, vertical = 13.dp)
|
||||
else -> PaddingValues(horizontal = 24.dp, vertical = 13.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val shape = when (style) {
|
||||
ButtonStyle.Filled, ButtonStyle.Outlined -> RoundedCornerShape(percent = 50)
|
||||
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(
|
||||
width = 1.dp,
|
||||
color = ElementTheme.colors.borderInteractiveSecondary
|
||||
)
|
||||
}
|
||||
|
||||
val textStyle = when (size) {
|
||||
ButtonSize.Medium -> MaterialTheme.typography.labelLarge
|
||||
ButtonSize.Large -> ElementTheme.typography.fontBodyLgMedium
|
||||
}
|
||||
|
||||
androidx.compose.material3.Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
onClick = {
|
||||
if (!showProgress) {
|
||||
onClick()
|
||||
}
|
||||
},
|
||||
modifier = modifier.heightIn(min = minHeight),
|
||||
enabled = enabled,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
elevation = null,
|
||||
border = border,
|
||||
contentPadding = contentPadding,
|
||||
interactionSource = interactionSource,
|
||||
content = content,
|
||||
)
|
||||
contentPadding = paddingValues,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
when {
|
||||
showProgress -> {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.progressSemantics()
|
||||
.size(20.dp),
|
||||
color = LocalContentColor.current,
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
}
|
||||
leadingIcon != null -> {
|
||||
androidx.compose.material.Icon(
|
||||
painter = leadingIcon.getPainter(),
|
||||
contentDescription = null,
|
||||
tint = LocalContentColor.current,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
Text(
|
||||
text = text,
|
||||
style = textStyle,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object ElementButtonDefaults {
|
||||
val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp)
|
||||
val shape: Shape @Composable get() = ButtonDefaults.shape
|
||||
@Composable
|
||||
fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation()
|
||||
sealed interface IconSource {
|
||||
data class Resource(val id: Int) : IconSource
|
||||
data class Vector(val vector: ImageVector) : IconSource
|
||||
|
||||
@Composable
|
||||
fun buttonColors(): ButtonColors = ButtonDefaults.buttonColors()
|
||||
fun getPainter(): Painter = when (this) {
|
||||
is Resource -> painterResource(id)
|
||||
is Vector -> rememberVectorPainter(image = vector)
|
||||
}
|
||||
}
|
||||
|
||||
enum class ButtonSize {
|
||||
Medium, Large
|
||||
}
|
||||
|
||||
private enum class ButtonStyle {
|
||||
Filled, Outlined, Text
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun ButtonPreview() = ElementThemedPreview {
|
||||
Column {
|
||||
Button(onClick = {}, enabled = true) {
|
||||
Text(text = "Click me! - Enabled")
|
||||
}
|
||||
Button(onClick = {}, enabled = false) {
|
||||
Text(text = "Click me! - Disabled")
|
||||
internal fun FilledButtonMediumPreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Filled,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun FilledButtonLargePreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Filled,
|
||||
buttonSize = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun OutlinedButtonMediumPreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Outlined,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun OutlinedButtonLargePreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Outlined,
|
||||
buttonSize = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun TextButtonMediumPreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Text,
|
||||
buttonSize = ButtonSize.Medium,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun TextButtonLargePreview() {
|
||||
ButtonCombinationPreview(
|
||||
buttonStyle = ButtonStyle.Text,
|
||||
buttonSize = ButtonSize.Large,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonCombinationPreview(
|
||||
buttonStyle: ButtonStyle,
|
||||
buttonSize: ButtonSize,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ElementThemedPreview {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.width(IntrinsicSize.Max),
|
||||
) {
|
||||
// Normal
|
||||
ButtonRowPreview(
|
||||
modifier = Modifier.then(modifier),
|
||||
buttonStyle = buttonStyle,
|
||||
buttonSize = buttonSize,
|
||||
)
|
||||
|
||||
// With icon
|
||||
ButtonRowPreview(
|
||||
modifier = Modifier.then(modifier),
|
||||
leadingIcon = IconSource.Vector(Icons.Outlined.Share),
|
||||
buttonStyle = buttonStyle,
|
||||
buttonSize = buttonSize,
|
||||
)
|
||||
|
||||
// With progress
|
||||
ButtonRowPreview(
|
||||
modifier = Modifier.then(modifier),
|
||||
showProgress = true,
|
||||
buttonStyle = buttonStyle,
|
||||
buttonSize = buttonSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ButtonRowPreview(
|
||||
buttonStyle: ButtonStyle,
|
||||
buttonSize: ButtonSize,
|
||||
modifier: Modifier = Modifier,
|
||||
leadingIcon: IconSource? = null,
|
||||
showProgress: Boolean = false,
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)) {
|
||||
ButtonInternal(
|
||||
text = "A button",
|
||||
showProgress = showProgress,
|
||||
onClick = {},
|
||||
style = buttonStyle,
|
||||
size = buttonSize,
|
||||
leadingIcon = leadingIcon,
|
||||
modifier = Modifier.then(modifier),
|
||||
)
|
||||
ButtonInternal(
|
||||
text = "A button",
|
||||
showProgress = showProgress,
|
||||
enabled = false,
|
||||
onClick = {},
|
||||
style = buttonStyle,
|
||||
size = buttonSize,
|
||||
leadingIcon = leadingIcon,
|
||||
modifier = Modifier.then(modifier),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* 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.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
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.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
|
||||
@Composable
|
||||
fun OutlinedButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = ElementOutlinedButtonDefaults.shape,
|
||||
colors: ButtonColors = ElementOutlinedButtonDefaults.buttonColors(),
|
||||
elevation: ButtonElevation? = ElementOutlinedButtonDefaults.buttonElevation(),
|
||||
border: BorderStroke? = ElementOutlinedButtonDefaults.border,
|
||||
contentPadding: PaddingValues = ElementOutlinedButtonDefaults.ContentPadding,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
androidx.compose.material3.Button(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
contentPadding = contentPadding,
|
||||
interactionSource = interactionSource,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
object ElementOutlinedButtonDefaults {
|
||||
val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp)
|
||||
val shape: Shape @Composable get() = ButtonDefaults.outlinedShape
|
||||
val border: BorderStroke @Composable get() = ButtonDefaults.outlinedButtonBorder
|
||||
@Composable
|
||||
fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation()
|
||||
|
||||
@Composable
|
||||
fun buttonColors(): ButtonColors = ButtonDefaults.outlinedButtonColors()
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun OutlinedButtonsPreview() = ElementThemedPreview { ContentToPreview() }
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OutlinedButton(onClick = {}, enabled = true) {
|
||||
Text(text = "Click me! - Enabled")
|
||||
}
|
||||
OutlinedButton(onClick = {}, enabled = false) {
|
||||
Text(text = "Click me! - Disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* 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.BorderStroke
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
|
||||
@Composable
|
||||
fun TextButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
shape: Shape = ButtonDefaults.textShape,
|
||||
colors: ButtonColors = ButtonDefaults.textButtonColors(),
|
||||
elevation: ButtonElevation? = null,
|
||||
border: BorderStroke? = null,
|
||||
contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
content: @Composable RowScope.() -> Unit
|
||||
) {
|
||||
androidx.compose.material3.TextButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
shape = shape,
|
||||
colors = colors,
|
||||
elevation = elevation,
|
||||
border = border,
|
||||
contentPadding = contentPadding,
|
||||
interactionSource = interactionSource,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Buttons)
|
||||
@Composable
|
||||
internal fun TextButtonPreview() = ElementThemedPreview { ContentToPreview() }
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
Column {
|
||||
TextButton(onClick = {}, enabled = true) {
|
||||
Text(text = "Click me! - Enabled")
|
||||
}
|
||||
TextButton(onClick = {}, enabled = false) {
|
||||
Text(text = "Click me! - Disabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,16 +32,13 @@ import io.element.android.libraries.designsystem.theme.components.DropdownMenu
|
|||
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
@Preview(group = PreviewGroup.Menus)
|
||||
@Composable
|
||||
internal fun MenuPreview() {
|
||||
ElementThemedPreview {
|
||||
var isExpanded by remember { mutableStateOf(false) }
|
||||
Button(onClick = { isExpanded = !isExpanded }) {
|
||||
Text("Toggle")
|
||||
}
|
||||
Button(text = "Toggle", onClick = { isExpanded = !isExpanded })
|
||||
DropdownMenu(expanded = isExpanded, onDismissRequest = { isExpanded = false }) {
|
||||
for (i in 0..5) {
|
||||
val leadingIcon: @Composable (() -> Unit)? = if (i in 2..3) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue