Skip to content

Commit 36c9534

Browse files
committed
chore: ensure CodeButton sizing remains consistent across states
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent b26a367 commit 36c9534

27 files changed

Lines changed: 159 additions & 63 deletions
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.getcode.util
2+
3+
import androidx.compose.foundation.layout.PaddingValues
4+
import androidx.compose.foundation.layout.calculateStartPadding
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.ui.platform.LocalLayoutDirection
7+
import androidx.compose.ui.unit.Dp
8+
import androidx.compose.ui.unit.dp
9+
10+
fun PaddingValues.calculateVerticalPadding() = calculateTopPadding() + calculateBottomPadding()
11+
12+
@Composable
13+
fun PaddingValues.calculateStartPadding(): Dp {
14+
val ldr = LocalLayoutDirection.current
15+
return calculateLeftPadding(ldr)
16+
}
17+
18+
@Composable
19+
fun PaddingValues.calculateEndPadding(): Dp {
20+
val ldr = LocalLayoutDirection.current
21+
return calculateRightPadding(ldr)
22+
}
23+
24+
@Composable
25+
fun PaddingValues.calculateHorizontalPadding(): Dp {
26+
val ldr = LocalLayoutDirection.current
27+
return calculateLeftPadding(ldr) + calculateRightPadding(ldr)
28+
}
29+
30+
@Composable
31+
fun PaddingValues.minus(
32+
start: Dp = 0.dp,
33+
top: Dp = 0.dp,
34+
end: Dp = 0.dp,
35+
bottom: Dp = 0.dp,
36+
): PaddingValues {
37+
return PaddingValues(
38+
start = calculateStartPadding() - start,
39+
top = calculateTopPadding() - top,
40+
end = calculateEndPadding() - end,
41+
bottom = calculateBottomPadding() - bottom,
42+
)
43+
}
44+
45+
@Composable
46+
fun PaddingValues.plus(
47+
start: Dp = 0.dp,
48+
top: Dp = 0.dp,
49+
end: Dp = 0.dp,
50+
bottom: Dp = 0.dp,
51+
): PaddingValues {
52+
return PaddingValues(
53+
start = calculateStartPadding() + start,
54+
top = calculateTopPadding() + top,
55+
end = calculateEndPadding() + end,
56+
bottom = calculateBottomPadding() + bottom,
57+
)
58+
}

app/src/main/java/com/getcode/view/components/BottomBarView.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ fun BottomBarView(
6464
}
6565
Column(verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2)) {
6666
CodeButton(
67+
modifier = Modifier.fillMaxWidth(),
6768
onClick = {
6869
bottomBarMessage.onPositive()
6970
onClose(BottomBarManager.BottomBarActionType.Positive)
@@ -77,6 +78,7 @@ fun BottomBarView(
7778
text = bottomBarMessage.positiveText
7879
)
7980
CodeButton(
81+
modifier = Modifier.fillMaxWidth(),
8082
onClick = {
8183
bottomBarMessage.onNegative()
8284
onClose(BottomBarManager.BottomBarActionType.Negative)

app/src/main/java/com/getcode/view/components/CodeButton.kt

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,24 @@ import androidx.compose.material.ripple.RippleAlpha
1010
import androidx.compose.material.ripple.RippleTheme
1111
import androidx.compose.runtime.Composable
1212
import androidx.compose.runtime.CompositionLocalProvider
13+
import androidx.compose.runtime.derivedStateOf
14+
import androidx.compose.runtime.getValue
1315
import androidx.compose.runtime.remember
1416
import androidx.compose.ui.Alignment
1517
import androidx.compose.ui.Modifier
1618
import androidx.compose.ui.graphics.Color
1719
import androidx.compose.ui.graphics.Shape
20+
import androidx.compose.ui.graphics.takeOrElse
21+
import androidx.compose.ui.platform.LocalLayoutDirection
1822
import androidx.compose.ui.res.painterResource
23+
import androidx.compose.ui.unit.Dp
1924
import androidx.compose.ui.unit.dp
2025
import com.getcode.R
2126
import com.getcode.theme.*
27+
import com.getcode.util.calculateHorizontalPadding
28+
import com.getcode.util.calculateVerticalPadding
29+
import com.getcode.util.minus
30+
import com.getcode.util.plus
2231

2332
enum class ButtonState {
2433
Bordered,
@@ -27,6 +36,7 @@ enum class ButtonState {
2736
Subtle
2837
}
2938

39+
@OptIn(ExperimentalMaterialApi::class)
3040
@Composable
3141
fun CodeButton(
3242
modifier: Modifier = Modifier,
@@ -38,75 +48,66 @@ fun CodeButton(
3848
enabled: Boolean = true,
3949
buttonState: ButtonState = ButtonState.Bordered,
4050
isPaddedVertical: Boolean = true,
41-
textColor: Color? = null,
42-
isMaxWidth: Boolean = true,
51+
textColor: Color = Color.Unspecified,
4352
shape: Shape = CodeTheme.shapes.small,
4453
) {
45-
val isEnabledC = enabled && !isLoading && !isSuccess
54+
val isEnabled by remember(enabled, isLoading, isSuccess) {
55+
derivedStateOf { enabled && !isLoading && !isSuccess }
56+
}
57+
val isSuccessful by remember(isSuccess, isTextSuccess) {
58+
derivedStateOf { isSuccess || isTextSuccess }
59+
}
4660
val colors = getButtonColors(buttonState, textColor)
47-
val border = getButtonBorder(buttonState, isEnabledC)
61+
val border = getButtonBorder(buttonState, isEnabled)
4862
val ripple = getRipple(
4963
buttonState = buttonState,
50-
contentColor = colors.contentColor(enabled = isEnabledC).value
64+
contentColor = colors.contentColor(enabled = isEnabled).value
5165
)
5266

53-
CompositionLocalProvider(LocalRippleTheme provides ripple) {
67+
CompositionLocalProvider(
68+
LocalMinimumInteractiveComponentEnforcement provides false,
69+
LocalRippleTheme provides ripple
70+
) {
5471
Button(
55-
onClick = { onClick() },
56-
modifier = modifier
57-
.let { if (isMaxWidth) it.fillMaxWidth() else it },
72+
onClick = onClick,
73+
modifier = modifier,
5874
colors = colors,
5975
border = border,
60-
enabled = isEnabledC,
76+
enabled = isEnabled,
6177
elevation = elevation(
6278
defaultElevation = 0.dp,
6379
pressedElevation = 0.dp
6480
),
6581
shape = shape,
82+
contentPadding = ButtonDefaults.ContentPadding.plus(
83+
top = if (isPaddedVertical) CodeTheme.dimens.grid.x3 else 0.dp,
84+
bottom = if (isPaddedVertical) CodeTheme.dimens.grid.x3 else 0.dp,
85+
)
6686
) {
67-
Box {
68-
Text(
69-
text = " ",
70-
style = CodeTheme.typography.button,
71-
modifier = Modifier.padding(
72-
vertical = if (isPaddedVertical) CodeTheme.dimens.grid.x3 else 0.dp,
73-
),
74-
)
87+
when {
88+
isLoading -> {
89+
CodeCircularProgressIndicator(
90+
strokeWidth = CodeTheme.dimens.thickBorder,
91+
color = White,
92+
modifier = Modifier
93+
.size(CodeTheme.dimens.grid.x3)
94+
)
95+
}
7596

76-
Row {
77-
if (isLoading) {
78-
CodeCircularProgressIndicator(
79-
strokeWidth = CodeTheme.dimens.thickBorder,
80-
color = White,
81-
modifier = Modifier
82-
.padding(vertical = if (isPaddedVertical) CodeTheme.dimens.grid.x3 else 0.dp)
83-
.size(CodeTheme.dimens.grid.x4)
84-
.align(Alignment.CenterVertically)
85-
)
86-
} else {
87-
if (isSuccess || isTextSuccess) {
88-
Image(
89-
painter = painterResource(id = R.drawable.ic_check),
90-
contentDescription = "",
91-
modifier = Modifier
92-
.padding(
93-
horizontal = CodeTheme.dimens.grid.x1,
94-
vertical = if (isPaddedVertical) CodeTheme.dimens.grid.x3 else 0.dp
95-
)
96-
.align(Alignment.CenterVertically)
97-
)
98-
}
99-
if (!isSuccess) {
100-
Text(
101-
text = text,
102-
style = CodeTheme.typography.button,
103-
modifier = Modifier.padding(
104-
vertical = if (isPaddedVertical) CodeTheme.dimens.grid.x3 else 0.dp,
105-
horizontal = CodeTheme.dimens.grid.x2,
106-
)
107-
)
108-
}
109-
}
97+
isSuccessful -> {
98+
Icon(
99+
modifier = Modifier.requiredSize(CodeTheme.dimens.grid.x3),
100+
painter = painterResource(id = R.drawable.ic_check),
101+
tint = Color.Unspecified,
102+
contentDescription = "",
103+
)
104+
}
105+
106+
else -> {
107+
Text(
108+
text = text,
109+
style = CodeTheme.typography.button,
110+
)
110111
}
111112
}
112113
}
@@ -144,32 +145,35 @@ fun getRipple(
144145
@Composable
145146
fun getButtonColors(
146147
buttonState: ButtonState = ButtonState.Bordered,
147-
textColor: Color? = null,
148+
textColor: Color = Color.Unspecified,
148149
): ButtonColors {
149150
return when (buttonState) {
150151
ButtonState.Filled -> ButtonDefaults.buttonColors(
151152
backgroundColor = White,
152-
contentColor = textColor ?: Color(0XFF121212),
153+
contentColor = textColor.takeOrElse { Color(0XFF121212) },
153154
disabledBackgroundColor = White10,
154155
disabledContentColor = White10,
155156
)
157+
156158
ButtonState.Bordered ->
157159
ButtonDefaults.outlinedButtonColors(
158160
backgroundColor = Brand,
159161
disabledContentColor = Color.LightGray,
160-
contentColor = textColor ?: Color.LightGray,
162+
contentColor = textColor.takeOrElse { Color.LightGray }
161163
)
164+
162165
ButtonState.Filled10 ->
163166
ButtonDefaults.outlinedButtonColors(
164167
backgroundColor = White10,
165168
disabledContentColor = White50,
166-
contentColor = textColor ?: White50,
169+
contentColor = textColor.takeOrElse { White50 },
167170
)
171+
168172
ButtonState.Subtle ->
169173
ButtonDefaults.outlinedButtonColors(
170174
backgroundColor = Transparent,
171175
disabledContentColor = Transparent,
172-
contentColor = textColor ?: BrandLight,
176+
contentColor = textColor.takeOrElse { BrandLight },
173177
)
174178
}
175179
}
@@ -181,7 +185,7 @@ fun getButtonBorder(buttonState: ButtonState, isEnabled: Boolean = true): Border
181185
if (buttonState == ButtonState.Bordered && isEnabled) {
182186
BorderStroke(border, White50)
183187
} else {
184-
BorderStroke(border, Color.Transparent)
188+
null
185189
}
186190
}
187191
}

app/src/main/java/com/getcode/view/login/AccessKey.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ fun AccessKey(
160160
.align(Alignment.BottomCenter)
161161
.measured { buttonHeight = it.height }) {
162162
CodeButton(
163-
modifier = Modifier,
163+
modifier = Modifier.fillMaxWidth(),
164164
onClick = {
165165
onExportClick()
166166
},
@@ -172,7 +172,7 @@ fun AccessKey(
172172
)
173173

174174
CodeButton(
175-
modifier = Modifier,
175+
modifier = Modifier.fillMaxWidth(),
176176
onClick = {
177177
BottomBarManager.showMessage(
178178
BottomBarManager.BottomBarMessage(

app/src/main/java/com/getcode/view/login/CameraPermission.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fun CameraPermission(navigator: CodeNavigator = LocalCodeNavigator.current) {
9292
text = stringResource(R.string.action_allowCameraAccess),
9393
buttonState = ButtonState.Filled,
9494
modifier = Modifier
95+
.fillMaxWidth()
9596
.padding(horizontal = CodeTheme.dimens.inset)
9697
.constrainAs(button) {
9798
linkTo(button.bottom, parent.bottom, bias = 1.0F)

app/src/main/java/com/getcode/view/login/InviteCode.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ fun InviteCode(
7676
.focusRequester(focusRequester)
7777
.padding(top = CodeTheme.dimens.grid.x1)
7878
.height(CodeTheme.dimens.grid.x12)
79-
.border(width = CodeTheme.dimens.border, color = BrandLight, shape = CodeTheme.shapes.extraSmall)
79+
.border(
80+
width = CodeTheme.dimens.border,
81+
color = BrandLight,
82+
shape = CodeTheme.shapes.extraSmall
83+
)
8084
.background(White05),
8185
value = dataState.inviteCode,
8286
textStyle = CodeTheme.typography.subtitle1,
@@ -121,6 +125,7 @@ fun InviteCode(
121125

122126
CodeButton(
123127
modifier = Modifier
128+
.fillMaxWidth()
124129
.constrainAs(buttonAction) {
125130
bottom.linkTo(parent.bottom)
126131
},

app/src/main/java/com/getcode/view/login/LoginHome.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ fun LoginHome() {
8686

8787
CodeButton(
8888
Modifier
89+
.fillMaxWidth()
8990
.constrainAs(buttonCreate) {
9091
top.linkTo(logo.bottom) //possibly remove!!
9192
bottom.linkTo(buttonLogin.top)
@@ -99,6 +100,7 @@ fun LoginHome() {
99100
)
100101
CodeButton(
101102
Modifier
103+
.fillMaxWidth()
102104
.constrainAs(buttonLogin) {
103105
top.linkTo(buttonCreate.bottom)
104106
}

app/src/main/java/com/getcode/view/login/NotificationPermission.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ fun NotificationPermission(navigator: CodeNavigator = LocalCodeNavigator.current
7373
text = stringResource(R.string.action_allowPushNotifications),
7474
buttonState = ButtonState.Filled,
7575
modifier = Modifier
76+
.fillMaxWidth()
7677
.padding(horizontal = CodeTheme.dimens.inset)
7778
.constrainAs(button) {
7879
start.linkTo(parent.start)
@@ -83,6 +84,7 @@ fun NotificationPermission(navigator: CodeNavigator = LocalCodeNavigator.current
8384

8485
CodeButton(
8586
modifier = Modifier
87+
.fillMaxWidth()
8688
.padding(bottom = CodeTheme.dimens.grid.x2)
8789
.padding(horizontal = CodeTheme.dimens.inset)
8890
.constrainAs(buttonSkip) {

app/src/main/java/com/getcode/view/login/PhoneConfirm.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ fun PhoneConfirm(
177177
}
178178

179179
CodeButton(
180+
modifier = Modifier.fillMaxWidth(),
180181
onClick = {
181182
viewModel.onSubmit()
182183
},

app/src/main/java/com/getcode/view/login/PhoneVerify.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Spacer
1515
import androidx.compose.foundation.layout.WindowInsets
1616
import androidx.compose.foundation.layout.fillMaxHeight
1717
import androidx.compose.foundation.layout.fillMaxSize
18+
import androidx.compose.foundation.layout.fillMaxWidth
1819
import androidx.compose.foundation.layout.height
1920
import androidx.compose.foundation.layout.imePadding
2021
import androidx.compose.foundation.layout.navigationBars
@@ -128,6 +129,7 @@ internal fun PhoneVerify(
128129

129130

130131
CodeButton(
132+
modifier = Modifier.fillMaxWidth(),
131133
onClick = {
132134
viewModel.onSubmit(navigator, context.getActivity())
133135
},

0 commit comments

Comments
 (0)