Skip to content

Commit

Permalink
Merge pull request #208 from Infomaniak/generic-button
Browse files Browse the repository at this point in the history
feat: Add a generic button component
  • Loading branch information
LunarX authored Nov 27, 2024
2 parents 630a114 + 80d2b52 commit 14cdf38
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,11 @@ package com.infomaniak.swisstransfer.ui.components
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
Expand All @@ -37,7 +33,6 @@ import androidx.compose.ui.unit.dp
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.images.AppImages.AppIcons
import com.infomaniak.swisstransfer.ui.images.icons.Add
import com.infomaniak.swisstransfer.ui.theme.CustomShapes
import com.infomaniak.swisstransfer.ui.theme.Dimens
import com.infomaniak.swisstransfer.ui.theme.Margin
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme
Expand Down Expand Up @@ -108,43 +103,15 @@ private fun CoreButton(
onClick: () -> Unit,
imageVector: ImageVector?,
) {
val isEnabled by remember(progress) { derivedStateOf { enabled() && !showIndeterminateProgress() && progress == null } }
val buttonColors = style.buttonColors()

Button(
modifier = modifier.height(buttonSize.height),
colors = buttonColors,
shape = CustomShapes.MEDIUM,
enabled = isEnabled,
onClick = onClick,
SwissTransferButton(
modifier.height(buttonSize.height),
style,
enabled,
showIndeterminateProgress,
progress,
onClick,
) {
when {
progress != null -> {
val (progressColor, progressModifier) = getProgressSpecs(buttonColors)
KeepButtonSize(imageVector, titleRes) {
CircularProgressIndicator(modifier = progressModifier, color = progressColor, progress = progress)
}
}
showIndeterminateProgress() -> {
val (progressColor, progressModifier) = getProgressSpecs(buttonColors)
KeepButtonSize(imageVector, titleRes) {
CircularProgressIndicator(modifier = progressModifier, color = progressColor)
}
}
else -> {
ButtonTextContent(imageVector, titleRes)
}
}
}
}

@Composable
fun KeepButtonSize(imageVector: ImageVector?, titleRes: Int, content: @Composable () -> Unit) {
Box(contentAlignment = Alignment.Center) {
Row(modifier = Modifier.alpha(0f)) {
ButtonTextContent(imageVector, titleRes)
}
content()
ButtonTextContent(imageVector, titleRes)
}
}

Expand All @@ -157,42 +124,6 @@ private fun ButtonTextContent(imageVector: ImageVector?, titleRes: Int) {
Text(text = stringResource(id = titleRes), style = SwissTransferTheme.typography.bodyMedium)
}

@Composable
private fun getProgressSpecs(buttonColors: ButtonColors): Pair<Color, Modifier> {
val progressColor = buttonColors.disabledContentColor
val progressModifier = Modifier
.fillMaxHeight(0.8f)
.aspectRatio(1f)
return Pair(progressColor, progressModifier)
}

enum class ButtonType(val buttonColors: @Composable () -> ButtonColors) {
PRIMARY({
ButtonDefaults.buttonColors(
containerColor = SwissTransferTheme.materialColors.primary,
contentColor = SwissTransferTheme.materialColors.onPrimary,
)
}),
SECONDARY({
ButtonDefaults.buttonColors(
containerColor = SwissTransferTheme.colors.tertiaryButtonBackground,
contentColor = SwissTransferTheme.materialColors.primary,
)
}),
TERTIARY({
ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = SwissTransferTheme.materialColors.primary,
)
}),
ERROR({
ButtonDefaults.buttonColors(
containerColor = SwissTransferTheme.materialColors.error,
contentColor = SwissTransferTheme.materialColors.onError,
)
}),
}

private enum class ButtonSize(val height: Dp) {
LARGE(Dimens.LargeButtonHeight),
SMALL(40.dp),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Infomaniak SwissTransfer - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.swisstransfer.ui.components

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.infomaniak.swisstransfer.ui.theme.CustomShapes
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme
import com.infomaniak.swisstransfer.ui.utils.PreviewLightAndDark


/**
* Most basic and customizable button component.
*
* Only needed for exceptional edge cases where [LargeButton] or [SmallButton] are not enough and we need more control over the
* button component.
*
* Specifying a progress has the priority over specifying showIndeterminateProgress.
*/
@Composable
fun SwissTransferButton(
modifier: Modifier = Modifier,
style: ButtonType = ButtonType.PRIMARY,
enabled: () -> Boolean = { true },
showIndeterminateProgress: () -> Boolean = { false },
progress: (() -> Float)? = null,
onClick: () -> Unit,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable () -> Unit,
) {
val isEnabled by remember(progress) { derivedStateOf { enabled() && !showIndeterminateProgress() && progress == null } }
val buttonColors = style.buttonColors()

Button(
modifier = modifier,
colors = buttonColors,
shape = CustomShapes.MEDIUM,
enabled = isEnabled,
contentPadding = contentPadding,
onClick = onClick,
) {
when {
progress != null -> {
val (progressColor, progressModifier) = getProgressSpecs(buttonColors)
KeepButtonSize(content) {
CircularProgressIndicator(modifier = progressModifier, color = progressColor, progress = progress)
}
}
showIndeterminateProgress() -> {
val (progressColor, progressModifier) = getProgressSpecs(buttonColors)
KeepButtonSize(content) {
CircularProgressIndicator(modifier = progressModifier, color = progressColor)
}
}
else -> content()
}
}
}

@Composable
private fun KeepButtonSize(targetSizeContent: @Composable () -> Unit, content: @Composable () -> Unit) {
Box(contentAlignment = Alignment.Center) {
Row(modifier = Modifier.alpha(0f)) {
targetSizeContent()
}
content()
}
}

@Composable
private fun getProgressSpecs(buttonColors: ButtonColors): Pair<Color, Modifier> {
val progressColor = buttonColors.disabledContentColor
val progressModifier = Modifier
.fillMaxHeight(0.8f)
.aspectRatio(1f)
return Pair(progressColor, progressModifier)
}

enum class ButtonType(val buttonColors: @Composable () -> ButtonColors) {
PRIMARY({
ButtonDefaults.buttonColors(
containerColor = SwissTransferTheme.materialColors.primary,
contentColor = SwissTransferTheme.materialColors.onPrimary,
)
}),
SECONDARY({
ButtonDefaults.buttonColors(
containerColor = SwissTransferTheme.colors.tertiaryButtonBackground,
contentColor = SwissTransferTheme.materialColors.primary,
)
}),
TERTIARY({
ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = SwissTransferTheme.materialColors.primary,
)
}),
ERROR({
ButtonDefaults.buttonColors(
containerColor = SwissTransferTheme.materialColors.error,
contentColor = SwissTransferTheme.materialColors.onError,
)
}),
}


@PreviewLightAndDark
@Composable
private fun Preview() {
SwissTransferTheme {
Surface {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
ButtonType.entries.forEach { buttonType ->
Row {
SwissTransferButton(
modifier = Modifier.height(40.dp),
style = buttonType,
onClick = {},
content = { Text("Click me!") },
)

Spacer(modifier = Modifier.width(12.dp))

SwissTransferButton(
modifier = Modifier.height(40.dp),
style = buttonType,
onClick = {},
progress = { 0.8f },
content = { Text("Click me!") },
)
}
}
}
}
}
}

0 comments on commit 14cdf38

Please sign in to comment.