-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI: Build Button component for TAJ (#545)
### TL;DR Introduced a new reusable Button component and added hex color code utilities to the design system. ### What changed? - Created a new `Button` component in the Taj design system with support for theming, animations, and disabled states - Added utility functions to convert hex color codes to Compose Color objects - Refactored the theme selection screen to use the new Button component - Improved color transition animations with FastOutSlowInEasing - Added proper content alpha handling for enabled/disabled states ### How to test? 1. Navigate to the theme selection screen 2. Verify the "Let's #KRAIL" button appears with proper styling 3. Test button interactions and color transitions when selecting different themes 4. Verify button responds appropriately to enabled/disabled states 5. Test with various hex color codes to ensure proper color conversion ### Why make this change? This change establishes a consistent button implementation across the app, reducing code duplication and ensuring a uniform user experience. The new button component handles common use cases like theming, animations, and accessibility, making it easier to maintain and modify button behavior app-wide.
- Loading branch information
1 parent
a0a39bc
commit b0930e5
Showing
3 changed files
with
162 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
taj/src/commonMain/kotlin/xyz/ksharma/krail/taj/ColorsExt.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package xyz.ksharma.krail.taj | ||
|
||
import androidx.compose.ui.graphics.Color | ||
|
||
fun String.hexToComposeColor(): Color { | ||
require(isValidHexColorCode()) { | ||
"Invalid hex color code: $this. Hex color codes must be in the format #RRGGBB or #AARRGGBB." | ||
} | ||
|
||
// Remove the leading '#' if present | ||
val hex = removePrefix("#") | ||
|
||
// Parse the hex value | ||
return when (hex.length) { | ||
6 -> { | ||
// If the string is in the format RRGGBB, add full opacity (FF) at the start | ||
val r = hex.substring(0, 2).toInt(16) | ||
val g = hex.substring(2, 4).toInt(16) | ||
val b = hex.substring(4, 6).toInt(16) | ||
Color(red = r / 255f, green = g / 255f, blue = b / 255f) | ||
} | ||
8 -> { | ||
// If the string is in the format AARRGGBB | ||
val a = hex.substring(0, 2).toInt(16) | ||
val r = hex.substring(2, 4).toInt(16) | ||
val g = hex.substring(4, 6).toInt(16) | ||
val b = hex.substring(6, 8).toInt(16) | ||
Color(alpha = a / 255f, red = r / 255f, green = g / 255f, blue = b / 255f) | ||
} | ||
else -> throw IllegalArgumentException("Invalid hex color format. Use #RRGGBB or #AARRGGBB.") | ||
} | ||
} | ||
|
||
/** | ||
* Checks if a string is a valid hexadecimal color code. | ||
* | ||
* This function uses a regular expression to validate the format of the | ||
* provided string. A valid hex color code must start with "#" followed by | ||
* either 6 or 8 hexadecimal digits (0-9, A-F, a-f). | ||
* | ||
* @return true if the string is a valid hex color code, false otherwise. | ||
*/ | ||
private fun String.isValidHexColorCode(): Boolean { | ||
// Regular expression to match #RRGGBB or #RRGGBBAA hex color codes | ||
val hexColorRegex = Regex("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})\$") | ||
return hexColorRegex.matches(this) | ||
} |
105 changes: 105 additions & 0 deletions
105
taj/src/commonMain/kotlin/xyz/ksharma/krail/taj/components/Button.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package xyz.ksharma.krail.taj.components | ||
|
||
import androidx.compose.animation.animateColorAsState | ||
import androidx.compose.animation.core.FastOutSlowInEasing | ||
import androidx.compose.animation.core.tween | ||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.CompositionLocalProvider | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.saveable.rememberSaveable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.semantics.Role | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.unit.dp | ||
import xyz.ksharma.krail.taj.LocalContentAlpha | ||
import xyz.ksharma.krail.taj.LocalContentColor | ||
import xyz.ksharma.krail.taj.LocalOnContentColor | ||
import xyz.ksharma.krail.taj.LocalTextStyle | ||
import xyz.ksharma.krail.taj.LocalThemeColor | ||
import xyz.ksharma.krail.taj.hexToComposeColor | ||
import xyz.ksharma.krail.taj.theme.KrailTheme | ||
import xyz.ksharma.krail.taj.theme.getForegroundColor | ||
import xyz.ksharma.krail.taj.tokens.ContentAlphaTokens.DisabledContentAlpha | ||
import xyz.ksharma.krail.taj.tokens.ContentAlphaTokens.EnabledContentAlpha | ||
|
||
/** | ||
* Button component for Taj | ||
* | ||
* @param label The text to be displayed on the button. | ||
* @param modifier The modifier to be applied to the button. | ||
* @param themeColor The background color of the button. If null, a default color is used. | ||
* @param enabled Whether the button is enabled or not. Defaults to true. | ||
* @param onClick The callback to be invoked when the button is clicked. | ||
*/ | ||
@Composable | ||
fun Button( | ||
label: String, | ||
modifier: Modifier = Modifier, | ||
themeColor: Color? = null, // TODO - concrete type for theme | ||
enabled: Boolean = true, | ||
onClick: () -> Unit = {}, | ||
) { | ||
val contentAlphaProvider = | ||
rememberSaveable(enabled) { if (enabled) EnabledContentAlpha else DisabledContentAlpha } | ||
|
||
val hexContentColor by LocalThemeColor.current | ||
val contentColor by remember(themeColor, hexContentColor) { | ||
mutableStateOf(themeColor ?: hexContentColor.hexToComposeColor()) | ||
} | ||
val onContentColor by animateColorAsState( | ||
targetValue = getForegroundColor(contentColor), | ||
label = "buttonTextColor", | ||
animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing), | ||
) | ||
|
||
CompositionLocalProvider( | ||
LocalContentAlpha provides contentAlphaProvider, | ||
LocalTextStyle provides KrailTheme.typography.titleMedium, | ||
LocalContentColor provides contentColor, | ||
LocalOnContentColor provides onContentColor, | ||
) { | ||
val contentAlpha = LocalContentAlpha.current | ||
|
||
Text( | ||
text = label, | ||
textAlign = TextAlign.Center, | ||
color = LocalOnContentColor.current.copy(alpha = contentAlpha), | ||
style = LocalTextStyle.current, | ||
modifier = modifier | ||
.fillMaxWidth() | ||
.padding(horizontal = 24.dp) // TODO - tokens for padding | ||
.clip(RoundedCornerShape(50)) | ||
.background(color = LocalContentColor.current.copy(alpha = contentAlpha)) | ||
.clickable( | ||
role = Role.Button, | ||
interactionSource = remember { MutableInteractionSource() }, | ||
enabled = enabled, | ||
indication = null, | ||
onClick = onClick, | ||
) | ||
.padding(10.dp), // TODO - tokens for padding | ||
) | ||
} | ||
} | ||
|
||
/** TODO | ||
* Button | ||
* Kind | ||
* - Cta button - text only | ||
* - Button - text with background color | ||
* | ||
* Sizing | ||
* - Compact - a small button with minimal padding horizontally | ||
* - Default - a button with default padding horizontally | ||
* - Large - a full width button. | ||
*/ |