From f13c4d8179207fe8c160cfd13d5f3b73c67197a7 Mon Sep 17 00:00:00 2001 From: Jan Skrasek Date: Wed, 16 Mar 2022 11:37:06 +0100 Subject: [PATCH] buttons: implement new ButtonLinks variants (color & Text-aligned) --- .../compose/catalog/screens/ButtonScreen.kt | 129 +++++++++++-- .../kiwi/orbit/compose/ui/controls/Button.kt | 17 +- .../orbit/compose/ui/controls/ButtonLink.kt | 170 ++++++++++++++++++ .../compose/ui/controls/ButtonPrimitive.kt | 8 +- 4 files changed, 291 insertions(+), 33 deletions(-) create mode 100644 ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonLink.kt diff --git a/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/ButtonScreen.kt b/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/ButtonScreen.kt index a000f641d..da612f64c 100644 --- a/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/ButtonScreen.kt +++ b/catalog/src/main/java/kiwi/orbit/compose/catalog/screens/ButtonScreen.kt @@ -4,47 +4,114 @@ import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.Tab +import androidx.compose.material.TabRow +import androidx.compose.material.TabRowDefaults +import androidx.compose.material.TabRowDefaults.tabIndicatorOffset import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import kiwi.orbit.compose.catalog.Screen +import com.google.accompanist.insets.navigationBarsPadding +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.HorizontalPager +import com.google.accompanist.pager.rememberPagerState import kiwi.orbit.compose.ui.OrbitTheme import kiwi.orbit.compose.ui.controls.ButtonBundleBasic import kiwi.orbit.compose.ui.controls.ButtonBundleMedium import kiwi.orbit.compose.ui.controls.ButtonBundleTop import kiwi.orbit.compose.ui.controls.ButtonCritical import kiwi.orbit.compose.ui.controls.ButtonCriticalSubtle -import kiwi.orbit.compose.ui.controls.ButtonLink +import kiwi.orbit.compose.ui.controls.ButtonLinkCritical +import kiwi.orbit.compose.ui.controls.ButtonLinkPrimary +import kiwi.orbit.compose.ui.controls.ButtonLinkSecondary import kiwi.orbit.compose.ui.controls.ButtonPrimary import kiwi.orbit.compose.ui.controls.ButtonPrimarySubtle import kiwi.orbit.compose.ui.controls.ButtonPrimitive import kiwi.orbit.compose.ui.controls.ButtonSecondary +import kiwi.orbit.compose.ui.controls.ButtonTextLinkCritical +import kiwi.orbit.compose.ui.controls.ButtonTextLinkPrimary import kiwi.orbit.compose.ui.controls.ButtonToggleContainer +import kiwi.orbit.compose.ui.controls.Card +import kiwi.orbit.compose.ui.controls.Scaffold import kiwi.orbit.compose.ui.controls.Text +import kiwi.orbit.compose.ui.controls.TopAppBar +import kotlinx.coroutines.launch +@OptIn(ExperimentalPagerApi::class) @Composable fun ButtonScreen(onNavigateUp: () -> Unit) { - Screen( - title = "Button", - onNavigateUp = onNavigateUp, + val state = rememberPagerState(0) + val scope = rememberCoroutineScope() + Scaffold( + topBar = { + TopAppBar( + title = { Text("Buttons") }, + onNavigateUp = onNavigateUp, + extraContent = { + TabRow( + modifier = Modifier.navigationBarsPadding(bottom = false), + selectedTabIndex = state.currentPage, + backgroundColor = OrbitTheme.colors.surface.main, + indicator = { tabPositions -> + TabRowDefaults.Indicator( + modifier = Modifier.tabIndicatorOffset(tabPositions[state.currentPage]), + color = OrbitTheme.colors.primary.main, + ) + }, + divider = {}, + ) { + Tab( + selected = state.currentPage == 0, + onClick = { scope.launch { state.animateScrollToPage(0) } }, + text = { Text("Button") }, + ) + Tab( + selected = state.currentPage == 1, + onClick = { scope.launch { state.animateScrollToPage(1) } }, + text = { Text("ButtonLink") }, + ) + } + } + ) + }, ) { contentPadding -> - Box( - Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(contentPadding) - ) { - ButtonScreenInner() + HorizontalPager( + count = 2, + state = state, + modifier = Modifier.padding(top = contentPadding.calculateTopPadding()), + ) { tabIndex -> + Box( + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding( + start = contentPadding.calculateStartPadding(LayoutDirection.Ltr), + end = contentPadding.calculateEndPadding(LayoutDirection.Ltr), + bottom = contentPadding.calculateBottomPadding(), + ) + ) { + when (tabIndex) { + 0 -> ButtonScreenInner() + 1 -> ButtonLinkScreenInner() + } + } } } } @@ -67,7 +134,6 @@ private fun ButtonScreenInner() { ButtonBundleBasic(onClick = {}, maxWidth) { Text("Bundle Basic Button") } ButtonBundleMedium(onClick = {}, maxWidth) { Text("Bundle Medium Button") } ButtonBundleTop(onClick = {}, maxWidth) { Text("Bundle Top Button") } - ButtonLink(onClick = {}, maxWidth) { Text("Link Button") } Text("Manually themed", Modifier.padding(top = 16.dp)) @@ -94,3 +160,40 @@ private fun ButtonScreenInner() { } } } + +@Composable +private fun ButtonLinkScreenInner() { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(16.dp), + ) { + ButtonLinkPrimary(onClick = {}, Modifier.fillMaxWidth()) { Text("Primary ButtonLink") } + ButtonLinkSecondary(onClick = {}, Modifier.fillMaxWidth()) { Text("Secondary ButtonLink") } + ButtonLinkCritical(onClick = {}, Modifier.fillMaxWidth()) { Text("Critical ButtonLink") } + + Card { + Column(Modifier.padding(16.dp)) { + Text( + "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam sit amet magna in magna gravida vehicula. Nam quis nulla. Nam sed tellus id magna elementum tincidunt.", + style = OrbitTheme.typography.bodyNormal + ) + Spacer(Modifier.size(8.dp)) + ButtonTextLinkPrimary("Translate", onClick = {}) + } + } + + Card { + Row(Modifier.padding(16.dp)) { + Text( + text = "Title", + style = OrbitTheme.typography.title3, + modifier = Modifier + .alignByBaseline() + .weight(1f) + ) + Spacer(Modifier.size(8.dp)) + ButtonTextLinkCritical("Delete", onClick = {}, modifier = Modifier.alignByBaseline()) + } + } + } +} diff --git a/ui/src/main/java/kiwi/orbit/compose/ui/controls/Button.kt b/ui/src/main/java/kiwi/orbit/compose/ui/controls/Button.kt index 553a15dda..fb8c62c40 100644 --- a/ui/src/main/java/kiwi/orbit/compose/ui/controls/Button.kt +++ b/ui/src/main/java/kiwi/orbit/compose/ui/controls/Button.kt @@ -129,22 +129,7 @@ public fun ButtonBundleTop( } @Composable -public fun ButtonLink( - onClick: () -> Unit, - modifier: Modifier = Modifier, - content: @Composable RowScope.() -> Unit -) { - ButtonLargePrimitive( - onClick = onClick, - backgroundColor = Color.Transparent, - contentColor = OrbitTheme.colors.primary.main, - modifier = modifier, - content = content, - ) -} - -@Composable -private fun ButtonLargePrimitive( +internal fun ButtonLargePrimitive( onClick: () -> Unit, backgroundColor: Color, modifier: Modifier = Modifier, diff --git a/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonLink.kt b/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonLink.kt new file mode 100644 index 000000000..ce2c06aa2 --- /dev/null +++ b/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonLink.kt @@ -0,0 +1,170 @@ +package kiwi.orbit.compose.ui.controls + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.padding +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.Role +import kiwi.orbit.compose.ui.OrbitTheme +import kiwi.orbit.compose.ui.foundation.LocalContentColor +import kiwi.orbit.compose.ui.layout.expand + +/** + * ButtonLink with common button appearance. + * + * Consider using [ButtonTextLinkPrimary] for alignment with Text. + */ +@Composable +public fun ButtonLinkPrimary( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) { + ButtonLargePrimitive( + onClick = onClick, + backgroundColor = Color.Transparent, + contentColor = OrbitTheme.colors.primary.main, + modifier = modifier, + content = content, + ) +} + +/** + * Text aligning version of ButtonLink. + * + * Suitable for aligning with other text (both horizontally and vertically). + * Touch feedback is drawn 8.dp outside the composable boundaries. + */ +@Composable +public fun ButtonTextLinkPrimary( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ButtonTextLink( + text = text, + onClick = onClick, + modifier = modifier, + color = OrbitTheme.colors.primary.main, + ) +} + +/** + * ButtonLink with common button appearance. + * + * Consider using [ButtonTextLinkSecondary] for alignment with Text. + */ +@Composable +public fun ButtonLinkSecondary( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) { + ButtonLargePrimitive( + onClick = onClick, + backgroundColor = Color.Transparent, + contentColor = OrbitTheme.colors.content.normal, + modifier = modifier, + content = content, + ) +} + +/** + * Text aligning version of ButtonLink. + * + * Suitable for aligning with other text (both horizontally and vertically). + * Touch feedback is drawn 8.dp outside the composable boundaries. + */ +@Composable +public fun ButtonTextLinkSecondary( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ButtonTextLink( + text = text, + onClick = onClick, + modifier = modifier, + color = OrbitTheme.colors.content.normal, + ) +} + +/** + * ButtonLink with common button appearance. + * + * Consider using [ButtonTextLinkCritical] for alignment with Text. + */ +@Composable +public fun ButtonLinkCritical( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) { + ButtonLargePrimitive( + onClick = onClick, + backgroundColor = Color.Transparent, + contentColor = OrbitTheme.colors.critical.main, + modifier = modifier, + content = content, + ) +} + +/** + * Text aligning version of ButtonLink. + * + * Suitable for aligning with other text (both horizontally and vertically). + * Touch feedback is drawn 8.dp outside the composable boundaries. + */ +@Composable +public fun ButtonTextLinkCritical( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ButtonTextLink( + text = text, + onClick = onClick, + modifier = modifier, + color = OrbitTheme.colors.critical.main, + ) +} + +@Composable +private fun ButtonTextLink( + text: String, + onClick: () -> Unit, + modifier: Modifier, + color: Color, +) { + CompositionLocalProvider( + LocalContentColor provides color, + ) { + Text( + text = text, + style = OrbitTheme.typography.bodyNormalMedium, + modifier = modifier + .expand( + horizontal = ButtonDefaults.ButtonSmallHorizontalPadding, + vertical = ButtonDefaults.ButtonSmallVerticalPadding + ) + .clip(OrbitTheme.shapes.normal) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(), + onClick = onClick, + role = Role.Button, + ) + .padding( + horizontal = ButtonDefaults.ButtonSmallHorizontalPadding, + vertical = ButtonDefaults.ButtonSmallVerticalPadding + ) + ) + } +} diff --git a/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonPrimitive.kt b/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonPrimitive.kt index 59ec569c6..460c52542 100644 --- a/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonPrimitive.kt +++ b/ui/src/main/java/kiwi/orbit/compose/ui/controls/ButtonPrimitive.kt @@ -88,10 +88,10 @@ public fun ButtonPrimitive( } public object ButtonDefaults { - private val ButtonHorizontalPadding = 16.dp - private val ButtonVerticalPadding = 12.dp - private val ButtonSmallHorizontalPadding = 8.dp - private val ButtonSmallVerticalPadding = 8.dp + internal val ButtonHorizontalPadding = 16.dp + internal val ButtonVerticalPadding = 12.dp + internal val ButtonSmallHorizontalPadding = 8.dp + internal val ButtonSmallVerticalPadding = 8.dp public val ContentPadding: PaddingValues = PaddingValues( start = ButtonHorizontalPadding,