Update :core-designsystem module based on Figma file

pull/485/head
Nick Rout 2 years ago
parent 68dc1d517a
commit c12fe1fbe5

@ -38,7 +38,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaDropdownMenuButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaDropdownMenuButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilledButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilterChip import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilterChip
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBar import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBar
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBarItem import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBarItem
@ -46,7 +46,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOutli
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTab import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTab
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTabRow import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTabRow
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTextButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTextButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaViewToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaViewToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
@ -77,7 +77,7 @@ fun NiaCatalog() {
item { Text("Buttons", Modifier.padding(top = 16.dp)) } item { Text("Buttons", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(onClick = {}) { NiaButton(onClick = {}) {
Text(text = "Enabled") Text(text = "Enabled")
} }
NiaOutlinedButton(onClick = {}) { NiaOutlinedButton(onClick = {}) {
@ -91,7 +91,7 @@ fun NiaCatalog() {
item { Text("Disabled buttons", Modifier.padding(top = 16.dp)) } item { Text("Disabled buttons", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton( NiaButton(
onClick = {}, onClick = {},
enabled = false enabled = false
) { ) {
@ -114,7 +114,7 @@ fun NiaCatalog() {
item { Text("Buttons with leading icons", Modifier.padding(top = 16.dp)) } item { Text("Buttons with leading icons", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton( NiaButton(
onClick = {}, onClick = {},
text = { Text(text = "Enabled") }, text = { Text(text = "Enabled") },
leadingIcon = { leadingIcon = {
@ -140,7 +140,7 @@ fun NiaCatalog() {
item { Text("Disabled buttons with leading icons", Modifier.padding(top = 16.dp)) } item { Text("Disabled buttons with leading icons", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton( NiaButton(
onClick = {}, onClick = {},
enabled = false, enabled = false,
text = { Text(text = "Disabled") }, text = { Text(text = "Disabled") },
@ -166,251 +166,24 @@ fun NiaCatalog() {
) )
} }
} }
item { Text("Buttons with trailing icons", Modifier.padding(top = 16.dp)) } item { Text("Dropdown menus", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton( NiaDropdownMenuButton(
onClick = {}, text = { Text("Enabled") },
text = { Text(text = "Enabled") }, items = listOf("Item 1", "Item 2", "Item 3"),
trailingIcon = { onItemClick = {},
Icon(imageVector = NiaIcons.Add, contentDescription = null) itemText = { item -> Text(item) }
} )
) NiaDropdownMenuButton(
NiaOutlinedButton( text = { Text("Disabled") },
onClick = {}, items = listOf("Item 1", "Item 2", "Item 3"),
text = { Text(text = "Enabled") }, onItemClick = {},
trailingIcon = { itemText = { item -> Text(item) },
Icon(imageVector = NiaIcons.Add, contentDescription = null) enabled = false
}
)
NiaTextButton(
onClick = {},
text = { Text(text = "Enabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
}
}
item { Text("Disabled buttons with trailing icons", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
enabled = false,
text = { Text(text = "Disabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaOutlinedButton(
onClick = {},
enabled = false,
text = { Text(text = "Disabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaTextButton(
onClick = {},
enabled = false,
text = { Text(text = "Disabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
}
}
item { Text("Small buttons", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
small = true
) {
Text(text = "Enabled")
}
NiaOutlinedButton(
onClick = {},
small = true
) {
Text(text = "Enabled")
}
NiaTextButton(
onClick = {},
small = true
) {
Text(text = "Enabled")
}
}
}
item { Text("Disabled small buttons", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
enabled = false,
small = true
) {
Text(text = "Disabled")
}
NiaOutlinedButton(
onClick = {},
enabled = false,
small = true
) {
Text(text = "Disabled")
}
NiaTextButton(
onClick = {},
enabled = false,
small = true
) {
Text(text = "Disabled")
}
}
}
item { Text("Small buttons with leading icons", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
small = true,
text = { Text(text = "Enabled") },
leadingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaOutlinedButton(
onClick = {},
small = true,
text = { Text(text = "Enabled") },
leadingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaTextButton(
onClick = {},
small = true,
text = { Text(text = "Enabled") },
leadingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
}
}
item {
Text(
"Disabled small buttons with leading icons",
Modifier.padding(top = 16.dp)
)
}
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
enabled = false,
small = true,
text = { Text(text = "Disabled") },
leadingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaOutlinedButton(
onClick = {},
enabled = false,
small = true,
text = { Text(text = "Disabled") },
leadingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaTextButton(
onClick = {},
enabled = false,
small = true,
text = { Text(text = "Disabled") },
leadingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
}
}
item { Text("Small buttons with trailing icons", Modifier.padding(top = 16.dp)) }
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
small = true,
text = { Text(text = "Enabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaOutlinedButton(
onClick = {},
small = true,
text = { Text(text = "Enabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaTextButton(
onClick = {},
small = true,
text = { Text(text = "Enabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
}
}
item {
Text(
"Disabled small buttons with trailing icons",
Modifier.padding(top = 16.dp)
)
}
item {
FlowRow(mainAxisSpacing = 16.dp) {
NiaFilledButton(
onClick = {},
enabled = false,
small = true,
text = { Text(text = "Disabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaOutlinedButton(
onClick = {},
enabled = false,
small = true,
text = { Text(text = "Disabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
)
NiaTextButton(
onClick = {},
enabled = false,
small = true,
text = { Text(text = "Disabled") },
trailingIcon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null)
}
) )
} }
} }
item { Text("Dropdown menu", Modifier.padding(top = 16.dp)) }
item {
NiaDropdownMenuButton(
text = { Text("Newest first") },
items = listOf("Item 1", "Item 2", "Item 3"),
onItemClick = {},
itemText = { item -> Text(item) }
)
}
item { Text("Chips", Modifier.padding(top = 16.dp)) } item { Text("Chips", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
@ -418,28 +191,33 @@ fun NiaCatalog() {
NiaFilterChip( NiaFilterChip(
selected = firstChecked, selected = firstChecked,
onSelectedChange = { checked -> firstChecked = checked }, onSelectedChange = { checked -> firstChecked = checked },
label = { Text(text = "Enabled".uppercase()) } label = { Text(text = "Enabled") }
) )
var secondChecked by remember { mutableStateOf(true) } var secondChecked by remember { mutableStateOf(true) }
NiaFilterChip( NiaFilterChip(
selected = secondChecked, selected = secondChecked,
onSelectedChange = { checked -> secondChecked = checked }, onSelectedChange = { checked -> secondChecked = checked },
label = { Text(text = "Enabled".uppercase()) } label = { Text(text = "Enabled") }
) )
var thirdChecked by remember { mutableStateOf(true) }
NiaFilterChip( NiaFilterChip(
selected = thirdChecked, selected = false,
onSelectedChange = { checked -> thirdChecked = checked }, onSelectedChange = {},
enabled = false, enabled = false,
label = { Text(text = "Disabled".uppercase()) } label = { Text(text = "Disabled") }
)
NiaFilterChip(
selected = true,
onSelectedChange = {},
enabled = false,
label = { Text(text = "Disabled") }
) )
} }
} }
item { Text("Toggle buttons", Modifier.padding(top = 16.dp)) } item { Text("Icon buttons", Modifier.padding(top = 16.dp)) }
item { item {
FlowRow(mainAxisSpacing = 16.dp) { FlowRow(mainAxisSpacing = 16.dp) {
var firstChecked by remember { mutableStateOf(false) } var firstChecked by remember { mutableStateOf(false) }
NiaToggleButton( NiaIconToggleButton(
checked = firstChecked, checked = firstChecked,
onCheckedChange = { checked -> firstChecked = checked }, onCheckedChange = { checked -> firstChecked = checked },
icon = { icon = {
@ -456,7 +234,7 @@ fun NiaCatalog() {
} }
) )
var secondChecked by remember { mutableStateOf(true) } var secondChecked by remember { mutableStateOf(true) }
NiaToggleButton( NiaIconToggleButton(
checked = secondChecked, checked = secondChecked,
onCheckedChange = { checked -> secondChecked = checked }, onCheckedChange = { checked -> secondChecked = checked },
icon = { icon = {
@ -472,27 +250,39 @@ fun NiaCatalog() {
) )
} }
) )
var thirdChecked by remember { mutableStateOf(false) } NiaIconToggleButton(
NiaToggleButton( checked = false,
checked = thirdChecked, onCheckedChange = {},
onCheckedChange = { checked -> thirdChecked = checked },
icon = { icon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null) Icon(
painter = painterResource(id = NiaIcons.BookmarkBorder),
contentDescription = null
)
}, },
checkedIcon = { checkedIcon = {
Icon(imageVector = NiaIcons.Check, contentDescription = null) Icon(
} painter = painterResource(id = NiaIcons.Bookmark),
contentDescription = null
)
},
enabled = false
) )
var fourthChecked by remember { mutableStateOf(true) } NiaIconToggleButton(
NiaToggleButton( checked = true,
checked = fourthChecked, onCheckedChange = {},
onCheckedChange = { checked -> fourthChecked = checked },
icon = { icon = {
Icon(imageVector = NiaIcons.Add, contentDescription = null) Icon(
painter = painterResource(id = NiaIcons.BookmarkBorder),
contentDescription = null
)
}, },
checkedIcon = { checkedIcon = {
Icon(imageVector = NiaIcons.Check, contentDescription = null) Icon(
} painter = painterResource(id = NiaIcons.Bookmark),
contentDescription = null
)
},
enabled = false
) )
} }
} }
@ -513,6 +303,13 @@ fun NiaCatalog() {
compactText = { Text(text = "Compact view") }, compactText = { Text(text = "Compact view") },
expandedText = { Text(text = "Expanded view") } expandedText = { Text(text = "Expanded view") }
) )
NiaViewToggleButton(
expanded = false,
onExpandedChange = {},
compactText = { Text(text = "Disabled") },
expandedText = { Text(text = "Disabled") },
enabled = false
)
} }
} }
item { Text("Tags", Modifier.padding(top = 16.dp)) } item { Text("Tags", Modifier.padding(top = 16.dp)) }
@ -524,7 +321,7 @@ fun NiaCatalog() {
NiaTopicTag( NiaTopicTag(
expanded = expandedTopicId == "Topic 1", expanded = expandedTopicId == "Topic 1",
followed = firstFollowed, followed = firstFollowed,
onDropMenuToggle = { show -> onDropdownMenuToggle = { show ->
expandedTopicId = if (show) "Topic 1" else null expandedTopicId = if (show) "Topic 1" else null
}, },
onFollowClick = { firstFollowed = true }, onFollowClick = { firstFollowed = true },
@ -539,7 +336,7 @@ fun NiaCatalog() {
NiaTopicTag( NiaTopicTag(
expanded = expandedTopicId == "Topic 2", expanded = expandedTopicId == "Topic 2",
followed = secondFollowed, followed = secondFollowed,
onDropMenuToggle = { show -> onDropdownMenuToggle = { show ->
expandedTopicId = if (show) "Topic 2" else null expandedTopicId = if (show) "Topic 2" else null
}, },
onFollowClick = { secondFollowed = true }, onFollowClick = { secondFollowed = true },
@ -550,6 +347,16 @@ fun NiaCatalog() {
unFollowText = { Text(text = "Unfollow") }, unFollowText = { Text(text = "Unfollow") },
browseText = { Text(text = "Browse topic") } browseText = { Text(text = "Browse topic") }
) )
NiaTopicTag(
expanded = false,
followed = false,
onDropdownMenuToggle = {},
onFollowClick = {},
onUnfollowClick = {},
onBrowseClick = {},
text = { Text(text = "Disabled".uppercase()) },
enabled = false
)
} }
} }
item { Text("Tabs", Modifier.padding(top = 16.dp)) } item { Text("Tabs", Modifier.padding(top = 16.dp)) }

@ -32,7 +32,6 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.GradientColo
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightAndroidBackgroundTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightAndroidBackgroundTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightAndroidColorScheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightAndroidColorScheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightDefaultColorScheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightDefaultColorScheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LightDefaultGradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalBackgroundTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalBackgroundTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalGradientColors
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
@ -63,7 +62,11 @@ class ThemeTest {
) { ) {
val colorScheme = LightDefaultColorScheme val colorScheme = LightDefaultColorScheme
assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme) assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme)
val gradientColors = LightDefaultGradientColors val gradientColors = GradientColors(
top = colorScheme.inverseOnSurface,
bottom = colorScheme.primaryContainer,
container = colorScheme.surface
)
assertEquals(gradientColors, LocalGradientColors.current) assertEquals(gradientColors, LocalGradientColors.current)
val backgroundTheme = BackgroundTheme( val backgroundTheme = BackgroundTheme(
color = colorScheme.surface, color = colorScheme.surface,
@ -84,7 +87,11 @@ class ThemeTest {
) { ) {
val colorScheme = DarkDefaultColorScheme val colorScheme = DarkDefaultColorScheme
assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme) assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme)
val gradientColors = GradientColors() val gradientColors = GradientColors(
top = colorScheme.inverseOnSurface,
bottom = colorScheme.primaryContainer,
container = colorScheme.surface
)
assertEquals(gradientColors, LocalGradientColors.current) assertEquals(gradientColors, LocalGradientColors.current)
val backgroundTheme = BackgroundTheme( val backgroundTheme = BackgroundTheme(
color = colorScheme.surface, color = colorScheme.surface,
@ -111,7 +118,11 @@ class ThemeTest {
val gradientColors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val gradientColors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
GradientColors() GradientColors()
} else { } else {
LightDefaultGradientColors GradientColors(
top = colorScheme.inverseOnSurface,
bottom = colorScheme.primaryContainer,
container = colorScheme.surface
)
} }
assertEquals(gradientColors, LocalGradientColors.current) assertEquals(gradientColors, LocalGradientColors.current)
val backgroundTheme = BackgroundTheme( val backgroundTheme = BackgroundTheme(
@ -136,7 +147,15 @@ class ThemeTest {
DarkDefaultColorScheme DarkDefaultColorScheme
} }
assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme) assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme)
val gradientColors = GradientColors() val gradientColors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
GradientColors()
} else {
GradientColors(
top = colorScheme.inverseOnSurface,
bottom = colorScheme.primaryContainer,
container = colorScheme.surface
)
}
assertEquals(gradientColors, LocalGradientColors.current) assertEquals(gradientColors, LocalGradientColors.current)
val backgroundTheme = BackgroundTheme( val backgroundTheme = BackgroundTheme(
color = colorScheme.surface, color = colorScheme.surface,
@ -192,7 +211,15 @@ class ThemeTest {
) { ) {
val colorScheme = LightAndroidColorScheme val colorScheme = LightAndroidColorScheme
assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme) assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme)
val gradientColors = GradientColors() val gradientColors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
GradientColors()
} else {
GradientColors(
top = colorScheme.inverseOnSurface,
bottom = colorScheme.primaryContainer,
container = colorScheme.surface
)
}
assertEquals(gradientColors, LocalGradientColors.current) assertEquals(gradientColors, LocalGradientColors.current)
val backgroundTheme = LightAndroidBackgroundTheme val backgroundTheme = LightAndroidBackgroundTheme
assertEquals(backgroundTheme, LocalBackgroundTheme.current) assertEquals(backgroundTheme, LocalBackgroundTheme.current)
@ -209,7 +236,15 @@ class ThemeTest {
) { ) {
val colorScheme = DarkAndroidColorScheme val colorScheme = DarkAndroidColorScheme
assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme) assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme)
val gradientColors = GradientColors() val gradientColors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
GradientColors()
} else {
GradientColors(
top = colorScheme.inverseOnSurface,
bottom = colorScheme.primaryContainer,
container = colorScheme.surface
)
}
assertEquals(gradientColors, LocalGradientColors.current) assertEquals(gradientColors, LocalGradientColors.current)
val backgroundTheme = DarkAndroidBackgroundTheme val backgroundTheme = DarkAndroidBackgroundTheme
assertEquals(backgroundTheme, LocalBackgroundTheme.current) assertEquals(backgroundTheme, LocalBackgroundTheme.current)
@ -249,6 +284,8 @@ class ThemeTest {
assertEquals(expectedColorScheme.onSurface, actualColorScheme.onSurface) assertEquals(expectedColorScheme.onSurface, actualColorScheme.onSurface)
assertEquals(expectedColorScheme.surfaceVariant, actualColorScheme.surfaceVariant) assertEquals(expectedColorScheme.surfaceVariant, actualColorScheme.surfaceVariant)
assertEquals(expectedColorScheme.onSurfaceVariant, actualColorScheme.onSurfaceVariant) assertEquals(expectedColorScheme.onSurfaceVariant, actualColorScheme.onSurfaceVariant)
assertEquals(expectedColorScheme.inverseSurface, actualColorScheme.inverseSurface)
assertEquals(expectedColorScheme.inverseOnSurface, actualColorScheme.inverseOnSurface)
assertEquals(expectedColorScheme.outline, actualColorScheme.outline) assertEquals(expectedColorScheme.outline, actualColorScheme.outline)
} }
} }

@ -41,7 +41,7 @@ import kotlin.math.tan
/** /**
* The main background for the app. * The main background for the app.
* Uses [LocalBackgroundTheme] to set the color and tonal elevation of a [Box]. * Uses [LocalBackgroundTheme] to set the color and tonal elevation of a [Surface].
* *
* @param modifier Modifier to be applied to the background. * @param modifier Modifier to be applied to the background.
* @param content The background content. * @param content The background content.
@ -66,23 +66,28 @@ fun NiaBackground(
/** /**
* A gradient background for select screens. Uses [LocalBackgroundTheme] to set the gradient colors * A gradient background for select screens. Uses [LocalBackgroundTheme] to set the gradient colors
* of a [Box]. * of a [Box] within a [Surface].
* *
* @param modifier Modifier to be applied to the background. * @param modifier Modifier to be applied to the background.
* @param topColor The top gradient color to be rendered. * @param topColor The top gradient color to be rendered.
* @param bottomColor The bottom gradient color to be rendered. * @param bottomColor The bottom gradient color to be rendered.
* @param containerColor The container color over which the gradient will be rendered.
* @param content The background content. * @param content The background content.
*/ */
@Composable @Composable
fun NiaGradientBackground( fun NiaGradientBackground(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
topColor: Color = LocalGradientColors.current.primary, topColor: Color = LocalGradientColors.current.top,
bottomColor: Color = LocalGradientColors.current.secondary, bottomColor: Color = LocalGradientColors.current.bottom,
containerColor: Color = LocalGradientColors.current.container,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val currentTopColor by rememberUpdatedState(topColor) val currentTopColor by rememberUpdatedState(topColor)
val currentBottomColor by rememberUpdatedState(bottomColor) val currentBottomColor by rememberUpdatedState(bottomColor)
NiaBackground(modifier) { Surface(
color = if (containerColor == Color.Unspecified) Color.Transparent else containerColor,
modifier = modifier.fillMaxSize()
) {
Box( Box(
Modifier Modifier
.fillMaxSize() .fillMaxSize()

@ -20,20 +20,15 @@ import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
/** /**
@ -43,38 +38,27 @@ import androidx.compose.ui.unit.dp
* @param modifier Modifier to be applied to the button. * @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be * @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services. * clickable and will appear disabled to accessibility services.
* @param small Whether or not the size of the button should be small or regular.
* @param colors [ButtonColors] that will be used to resolve the container and content color for
* this button in different states. See [NiaButtonDefaults.filledButtonColors].
* @param contentPadding The spacing values to apply internally between the container and the * @param contentPadding The spacing values to apply internally between the container and the
* content. See [NiaButtonDefaults.buttonContentPadding]. * content.
* @param content The button content. * @param content The button content.
*/ */
@Composable @Composable
fun NiaFilledButton( fun NiaButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
small: Boolean = false, contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
colors: ButtonColors = NiaButtonDefaults.filledButtonColors(),
contentPadding: PaddingValues = NiaButtonDefaults.buttonContentPadding(small = small),
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
Button( Button(
onClick = onClick, onClick = onClick,
modifier = if (small) { modifier = modifier,
modifier.heightIn(min = NiaButtonDefaults.SmallButtonHeight)
} else {
modifier
},
enabled = enabled, enabled = enabled,
colors = colors, colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.onBackground
),
contentPadding = contentPadding, contentPadding = contentPadding,
content = { content = content
ProvideTextStyle(value = MaterialTheme.typography.labelSmall) {
content()
}
}
) )
} }
@ -85,40 +69,30 @@ fun NiaFilledButton(
* @param modifier Modifier to be applied to the button. * @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be * @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services. * clickable and will appear disabled to accessibility services.
* @param small Whether or not the size of the button should be small or regular.
* @param colors [ButtonColors] that will be used to resolve the container and content color for
* this button in different states. See [NiaButtonDefaults.filledButtonColors].
* @param text The button text label content. * @param text The button text label content.
* @param leadingIcon The button leading icon content. Pass `null` here for no leading icon. * @param leadingIcon The button leading icon content. Pass `null` here for no leading icon.
* @param trailingIcon The button trailing icon content. Pass `null` here for no trailing icon.
*/ */
@Composable @Composable
fun NiaFilledButton( fun NiaButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
small: Boolean = false,
colors: ButtonColors = NiaButtonDefaults.filledButtonColors(),
text: @Composable () -> Unit, text: @Composable () -> Unit,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null
trailingIcon: @Composable (() -> Unit)? = null
) { ) {
NiaFilledButton( NiaButton(
onClick = onClick, onClick = onClick,
modifier = modifier, modifier = modifier,
enabled = enabled, enabled = enabled,
small = small, contentPadding = if (leadingIcon != null) {
colors = colors, ButtonDefaults.ButtonWithIconContentPadding
contentPadding = NiaButtonDefaults.buttonContentPadding( } else {
small = small, ButtonDefaults.ContentPadding
leadingIcon = leadingIcon != null, }
trailingIcon = trailingIcon != null
)
) { ) {
NiaButtonContent( NiaButtonContent(
text = text, text = text,
leadingIcon = leadingIcon, leadingIcon = leadingIcon
trailingIcon = trailingIcon
) )
} }
} }
@ -130,12 +104,8 @@ fun NiaFilledButton(
* @param modifier Modifier to be applied to the button. * @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be * @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services. * clickable and will appear disabled to accessibility services.
* @param small Whether or not the size of the button should be small or regular.
* @param border Border to draw around the button. Pass `null` here for no border.
* @param colors [ButtonColors] that will be used to resolve the container and content color for
* this button in different states. See [NiaButtonDefaults.outlinedButtonColors].
* @param contentPadding The spacing values to apply internally between the container and the * @param contentPadding The spacing values to apply internally between the container and the
* content. See [NiaButtonDefaults.buttonContentPadding]. * content.
* @param content The button content. * @param content The button content.
*/ */
@Composable @Composable
@ -143,28 +113,28 @@ fun NiaOutlinedButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
small: Boolean = false, contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
border: BorderStroke? = NiaButtonDefaults.outlinedButtonBorder(enabled = enabled),
colors: ButtonColors = NiaButtonDefaults.outlinedButtonColors(),
contentPadding: PaddingValues = NiaButtonDefaults.buttonContentPadding(small = small),
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
OutlinedButton( OutlinedButton(
onClick = onClick, onClick = onClick,
modifier = if (small) { modifier = modifier,
modifier.heightIn(min = NiaButtonDefaults.SmallButtonHeight)
} else {
modifier
},
enabled = enabled, enabled = enabled,
border = border, colors = ButtonDefaults.outlinedButtonColors(
colors = colors, contentColor = MaterialTheme.colorScheme.onBackground
contentPadding = contentPadding, ),
content = { border = BorderStroke(
ProvideTextStyle(value = MaterialTheme.typography.labelSmall) { width = NiaButtonDefaults.OutlinedButtonBorderWidth,
content() color = if (enabled) {
MaterialTheme.colorScheme.outline
} else {
MaterialTheme.colorScheme.onSurface.copy(
alpha = NiaButtonDefaults.DisabledOutlinedButtonBorderAlpha
)
} }
} ),
contentPadding = contentPadding,
content = content
) )
} }
@ -175,43 +145,30 @@ fun NiaOutlinedButton(
* @param modifier Modifier to be applied to the button. * @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be * @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services. * clickable and will appear disabled to accessibility services.
* @param small Whether or not the size of the button should be small or regular.
* @param border Border to draw around the button. Pass `null` here for no border.
* @param colors [ButtonColors] that will be used to resolve the container and content color for
* this button in different states. See [NiaButtonDefaults.outlinedButtonColors].
* @param text The button text label content. * @param text The button text label content.
* @param leadingIcon The button leading icon content. Pass `null` here for no leading icon. * @param leadingIcon The button leading icon content. Pass `null` here for no leading icon.
* @param trailingIcon The button trailing icon content. Pass `null` here for no trailing icon.
*/ */
@Composable @Composable
fun NiaOutlinedButton( fun NiaOutlinedButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
small: Boolean = false,
border: BorderStroke? = NiaButtonDefaults.outlinedButtonBorder(enabled = enabled),
colors: ButtonColors = NiaButtonDefaults.outlinedButtonColors(),
text: @Composable () -> Unit, text: @Composable () -> Unit,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null
trailingIcon: @Composable (() -> Unit)? = null
) { ) {
NiaOutlinedButton( NiaOutlinedButton(
onClick = onClick, onClick = onClick,
modifier = modifier, modifier = modifier,
enabled = enabled, enabled = enabled,
small = small, contentPadding = if (leadingIcon != null) {
border = border, ButtonDefaults.ButtonWithIconContentPadding
colors = colors, } else {
contentPadding = NiaButtonDefaults.buttonContentPadding( ButtonDefaults.ContentPadding
small = small, }
leadingIcon = leadingIcon != null,
trailingIcon = trailingIcon != null
)
) { ) {
NiaButtonContent( NiaButtonContent(
text = text, text = text,
leadingIcon = leadingIcon, leadingIcon = leadingIcon
trailingIcon = trailingIcon
) )
} }
} }
@ -223,11 +180,6 @@ fun NiaOutlinedButton(
* @param modifier Modifier to be applied to the button. * @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be * @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services. * clickable and will appear disabled to accessibility services.
* @param small Whether or not the size of the button should be small or regular.
* @param colors [ButtonColors] that will be used to resolve the container and content color for
* this button in different states. See [NiaButtonDefaults.textButtonColors].
* @param contentPadding The spacing values to apply internally between the container and the
* content. See [NiaButtonDefaults.buttonContentPadding].
* @param content The button content. * @param content The button content.
*/ */
@Composable @Composable
@ -235,26 +187,16 @@ fun NiaTextButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
small: Boolean = false,
colors: ButtonColors = NiaButtonDefaults.textButtonColors(),
contentPadding: PaddingValues = NiaButtonDefaults.buttonContentPadding(small = small),
content: @Composable RowScope.() -> Unit content: @Composable RowScope.() -> Unit
) { ) {
TextButton( TextButton(
onClick = onClick, onClick = onClick,
modifier = if (small) { modifier = modifier,
modifier.heightIn(min = NiaButtonDefaults.SmallButtonHeight)
} else {
modifier
},
enabled = enabled, enabled = enabled,
colors = colors, colors = ButtonDefaults.textButtonColors(
contentPadding = contentPadding, contentColor = MaterialTheme.colorScheme.onBackground
content = { ),
ProvideTextStyle(value = MaterialTheme.typography.labelSmall) { content = content
content()
}
}
) )
} }
@ -265,60 +207,42 @@ fun NiaTextButton(
* @param modifier Modifier to be applied to the button. * @param modifier Modifier to be applied to the button.
* @param enabled Controls the enabled state of the button. When `false`, this button will not be * @param enabled Controls the enabled state of the button. When `false`, this button will not be
* clickable and will appear disabled to accessibility services. * clickable and will appear disabled to accessibility services.
* @param small Whether or not the size of the button should be small or regular.
* @param colors [ButtonColors] that will be used to resolve the container and content color for
* this button in different states. See [NiaButtonDefaults.textButtonColors].
* @param text The button text label content. * @param text The button text label content.
* @param leadingIcon The button leading icon content. Pass `null` here for no leading icon. * @param leadingIcon The button leading icon content. Pass `null` here for no leading icon.
* @param trailingIcon The button trailing icon content. Pass `null` here for no trailing icon.
*/ */
@Composable @Composable
fun NiaTextButton( fun NiaTextButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
small: Boolean = false,
colors: ButtonColors = NiaButtonDefaults.textButtonColors(),
text: @Composable () -> Unit, text: @Composable () -> Unit,
leadingIcon: @Composable (() -> Unit)? = null, leadingIcon: @Composable (() -> Unit)? = null
trailingIcon: @Composable (() -> Unit)? = null
) { ) {
NiaTextButton( NiaTextButton(
onClick = onClick, onClick = onClick,
modifier = modifier, modifier = modifier,
enabled = enabled, enabled = enabled
small = small,
colors = colors,
contentPadding = NiaButtonDefaults.buttonContentPadding(
small = small,
leadingIcon = leadingIcon != null,
trailingIcon = trailingIcon != null
)
) { ) {
NiaButtonContent( NiaButtonContent(
text = text, text = text,
leadingIcon = leadingIcon, leadingIcon = leadingIcon
trailingIcon = trailingIcon
) )
} }
} }
/** /**
* Internal Now in Android button content layout for arranging the text label, leading icon and * Internal Now in Android button content layout for arranging the text label and leading icon.
* trailing icon.
* *
* @param text The button text label content. * @param text The button text label content.
* @param leadingIcon The button leading icon content. Pass `null` here for no leading icon. * @param leadingIcon The button leading icon content. Pass `null` here for no leading icon.
* @param trailingIcon The button trailing icon content. Pass `null` here for no trailing icon.
*/ */
@Composable @Composable
private fun RowScope.NiaButtonContent( private fun NiaButtonContent(
text: @Composable () -> Unit, text: @Composable () -> Unit,
leadingIcon: @Composable (() -> Unit)?, leadingIcon: @Composable (() -> Unit)?
trailingIcon: @Composable (() -> Unit)?
) { ) {
if (leadingIcon != null) { if (leadingIcon != null) {
Box(Modifier.sizeIn(maxHeight = NiaButtonDefaults.ButtonIconSize)) { Box(Modifier.sizeIn(maxHeight = ButtonDefaults.IconSize)) {
leadingIcon() leadingIcon()
} }
} }
@ -326,12 +250,7 @@ private fun RowScope.NiaButtonContent(
Modifier Modifier
.padding( .padding(
start = if (leadingIcon != null) { start = if (leadingIcon != null) {
NiaButtonDefaults.ButtonContentSpacing ButtonDefaults.IconSpacing
} else {
0.dp
},
end = if (trailingIcon != null) {
NiaButtonDefaults.ButtonContentSpacing
} else { } else {
0.dp 0.dp
} }
@ -339,104 +258,16 @@ private fun RowScope.NiaButtonContent(
) { ) {
text() text()
} }
if (trailingIcon != null) {
Box(Modifier.sizeIn(maxHeight = NiaButtonDefaults.ButtonIconSize)) {
trailingIcon()
}
}
} }
/** /**
* Now in Android button default values. * Now in Android button default values.
*/ */
object NiaButtonDefaults { object NiaButtonDefaults {
val SmallButtonHeight = 32.dp // TODO: File bug
const val DisabledButtonContainerAlpha = 0.12f // OutlinedButton border color doesn't respect disabled state by default
const val DisabledButtonContentAlpha = 0.38f const val DisabledOutlinedButtonBorderAlpha = 0.12f
val ButtonHorizontalPadding = 24.dp // TODO: File bug
val ButtonHorizontalIconPadding = 16.dp // OutlinedButton default border width isn't exposed via ButtonDefaults
val ButtonVerticalPadding = 8.dp val OutlinedButtonBorderWidth = 1.dp
val SmallButtonHorizontalPadding = 16.dp
val SmallButtonHorizontalIconPadding = 12.dp
val SmallButtonVerticalPadding = 7.dp
val ButtonContentSpacing = 8.dp
val ButtonIconSize = 18.dp
fun buttonContentPadding(
small: Boolean,
leadingIcon: Boolean = false,
trailingIcon: Boolean = false
): PaddingValues {
return PaddingValues(
start = when {
small && leadingIcon -> SmallButtonHorizontalIconPadding
small -> SmallButtonHorizontalPadding
leadingIcon -> ButtonHorizontalIconPadding
else -> ButtonHorizontalPadding
},
top = if (small) SmallButtonVerticalPadding else ButtonVerticalPadding,
end = when {
small && trailingIcon -> SmallButtonHorizontalIconPadding
small -> SmallButtonHorizontalPadding
trailingIcon -> ButtonHorizontalIconPadding
else -> ButtonHorizontalPadding
},
bottom = if (small) SmallButtonVerticalPadding else ButtonVerticalPadding
)
}
@Composable
fun filledButtonColors(
containerColor: Color = MaterialTheme.colorScheme.onBackground,
contentColor: Color = MaterialTheme.colorScheme.onPrimary,
disabledContainerColor: Color = MaterialTheme.colorScheme.onBackground.copy(
alpha = DisabledButtonContainerAlpha
),
disabledContentColor: Color = MaterialTheme.colorScheme.onBackground.copy(
alpha = DisabledButtonContentAlpha
)
) = ButtonDefaults.buttonColors(
containerColor = containerColor,
contentColor = contentColor,
disabledContainerColor = disabledContainerColor,
disabledContentColor = disabledContentColor
)
@Composable
fun outlinedButtonBorder(
enabled: Boolean,
width: Dp = 1.dp,
color: Color = MaterialTheme.colorScheme.onBackground,
disabledColor: Color = MaterialTheme.colorScheme.onBackground.copy(
alpha = DisabledButtonContainerAlpha
)
): BorderStroke = BorderStroke(
width = width,
color = if (enabled) color else disabledColor
)
@Composable
fun outlinedButtonColors(
containerColor: Color = Color.Transparent,
contentColor: Color = MaterialTheme.colorScheme.onBackground,
disabledContainerColor: Color = Color.Transparent,
disabledContentColor: Color = MaterialTheme.colorScheme.onBackground.copy(
alpha = DisabledButtonContentAlpha
)
) = ButtonDefaults.outlinedButtonColors(
containerColor = containerColor,
contentColor = contentColor,
disabledContainerColor = disabledContainerColor,
disabledContentColor = disabledContentColor
)
@Composable
fun textButtonColors(
containerColor: Color = Color.Transparent,
contentColor: Color = MaterialTheme.colorScheme.onBackground,
disabledContainerColor: Color = Color.Transparent,
disabledContentColor: Color = MaterialTheme.colorScheme.onBackground.copy(
alpha = DisabledButtonContentAlpha
)
) = ButtonDefaults.textButtonColors(
containerColor = containerColor,
contentColor = contentColor,
disabledContainerColor = disabledContainerColor,
disabledContentColor = disabledContentColor
)
} }

@ -58,11 +58,15 @@ fun NiaFilterChip(
}, },
modifier = modifier, modifier = modifier,
enabled = enabled, enabled = enabled,
trailingIcon = { leadingIcon = if (selected) {
Icon( {
imageVector = NiaIcons.Check, Icon(
contentDescription = null imageVector = NiaIcons.Check,
) contentDescription = null
)
}
} else {
null
}, },
shape = CircleShape, shape = CircleShape,
border = FilterChipDefaults.filterChipBorder( border = FilterChipDefaults.filterChipBorder(
@ -74,11 +78,9 @@ fun NiaFilterChip(
disabledSelectedBorderColor = MaterialTheme.colorScheme.onBackground.copy( disabledSelectedBorderColor = MaterialTheme.colorScheme.onBackground.copy(
alpha = NiaChipDefaults.DisabledChipContentAlpha alpha = NiaChipDefaults.DisabledChipContentAlpha
), ),
borderWidth = NiaChipDefaults.ChipBorderWidth,
selectedBorderWidth = NiaChipDefaults.ChipBorderWidth selectedBorderWidth = NiaChipDefaults.ChipBorderWidth
), ),
colors = FilterChipDefaults.filterChipColors( colors = FilterChipDefaults.filterChipColors(
containerColor = Color.Transparent,
labelColor = MaterialTheme.colorScheme.onBackground, labelColor = MaterialTheme.colorScheme.onBackground,
iconColor = MaterialTheme.colorScheme.onBackground, iconColor = MaterialTheme.colorScheme.onBackground,
disabledContainerColor = if (selected) { disabledContainerColor = if (selected) {
@ -105,6 +107,8 @@ fun NiaFilterChip(
* Now in Android chip default values. * Now in Android chip default values.
*/ */
object NiaChipDefaults { object NiaChipDefaults {
// TODO: File bug
// FilterChip default values aren't exposed via FilterChipDefaults
const val DisabledChipContainerAlpha = 0.12f const val DisabledChipContainerAlpha = 0.12f
const val DisabledChipContentAlpha = 0.38f const val DisabledChipContentAlpha = 0.38f
val ChipBorderWidth = 1.dp val ChipBorderWidth = 1.dp

@ -16,16 +16,25 @@
package com.google.samples.apps.nowinandroid.core.designsystem.component package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
/** /**
@ -56,17 +65,34 @@ fun <T> NiaDropdownMenuButton(
) { ) {
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
Box(modifier = modifier) { Box(modifier = modifier) {
NiaOutlinedButton( OutlinedButton(
onClick = { expanded = true }, onClick = { expanded = true },
enabled = enabled, enabled = enabled,
text = text, colors = ButtonDefaults.outlinedButtonColors(
trailingIcon = { contentColor = MaterialTheme.colorScheme.onBackground
Icon( ),
imageVector = if (expanded) NiaIcons.ArrowDropUp else NiaIcons.ArrowDropDown, border = BorderStroke(
contentDescription = null width = NiaDropdownMenuDefaults.DropdownMenuButtonBorderWidth,
) color = if (enabled) {
} MaterialTheme.colorScheme.outline
) } else {
MaterialTheme.colorScheme.onSurface.copy(
alpha = NiaDropdownMenuDefaults.DisabledDropdownMenuButtonBorderAlpha
)
}
),
contentPadding = NiaDropdownMenuDefaults.DropdownMenuButtonContentPadding
) {
NiaDropdownMenuButtonContent(
text = text,
trailingIcon = {
Icon(
imageVector = if (expanded) NiaIcons.ArrowDropUp else NiaIcons.ArrowDropDown,
contentDescription = null
)
}
)
}
NiaDropdownMenu( NiaDropdownMenu(
expanded = expanded, expanded = expanded,
onDismissRequest = { expanded = false }, onDismissRequest = { expanded = false },
@ -80,6 +106,39 @@ fun <T> NiaDropdownMenuButton(
} }
} }
/**
* Internal Now in Android dropdown menu button content layout for arranging the text label and
* trailing icon.
*
* @param text The button text label content.
* @param trailingIcon The button trailing icon content. Pass `null` here for no trailing icon.
*/
@Composable
private fun NiaDropdownMenuButtonContent(
text: @Composable () -> Unit,
trailingIcon: @Composable (() -> Unit)?,
) {
Box(
Modifier
.padding(
end = if (trailingIcon != null) {
ButtonDefaults.IconSpacing
} else {
0.dp
}
)
) {
ProvideTextStyle(value = MaterialTheme.typography.labelSmall) {
text()
}
}
if (trailingIcon != null) {
Box(Modifier.sizeIn(maxHeight = ButtonDefaults.IconSize)) {
trailingIcon()
}
}
}
/** /**
* Now in Android dropdown menu with item content slots. Wraps Material 3 [DropdownMenu] and * Now in Android dropdown menu with item content slots. Wraps Material 3 [DropdownMenu] and
* [DropdownMenuItem]. * [DropdownMenuItem].
@ -130,3 +189,24 @@ fun <T> NiaDropdownMenu(
} }
} }
} }
/**
* Now in Android dropdown menu default values.
*/
object NiaDropdownMenuDefaults {
// TODO: File bug
// OutlinedButton border color doesn't respect disabled state by default
const val DisabledDropdownMenuButtonBorderAlpha = 0.12f
// TODO: File bug
// OutlinedButton default border width isn't exposed via ButtonDefaults
val DropdownMenuButtonBorderWidth = 1.dp
// TODO: File bug
// Various default button padding values aren't exposed via ButtonDefaults
val DropdownMenuButtonContentPadding =
PaddingValues(
start = 24.dp,
top = 8.dp,
end = 16.dp,
bottom = 8.dp
)
}

@ -0,0 +1,80 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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
*
* https://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 com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion
import androidx.compose.ui.unit.dp
/**
* Now in Android toggle button with icon and checked icon content slots. Wraps Material 3
* [IconButton].
*
* @param checked Whether the toggle button is currently checked.
* @param onCheckedChange Called when the user clicks the toggle button and toggles checked.
* @param modifier Modifier to be applied to the toggle button.
* @param enabled Controls the enabled state of the toggle button. When `false`, this toggle button
* will not be clickable and will appear disabled to accessibility services.
* @param icon The icon content to show when unchecked.
* @param checkedIcon The icon content to show when checked.
*/
@Composable
fun NiaIconToggleButton(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
icon: @Composable () -> Unit,
checkedIcon: @Composable () -> Unit = icon
) {
// TODO: File bug
// Can't use regular IconToggleButton as it doesn't include a shape (appears square)
FilledIconToggleButton(
checked = checked,
onCheckedChange = onCheckedChange,
modifier = modifier,
enabled = enabled,
colors = IconButtonDefaults.iconToggleButtonColors(
checkedContainerColor = MaterialTheme.colorScheme.primaryContainer,
checkedContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
disabledContainerColor = if (checked) {
MaterialTheme.colorScheme.onBackground.copy(
alpha = NiaIconButtonDefaults.DisabledIconButtonContainerAlpha
)
} else {
Color.Transparent
}
)
) {
if (checked) checkedIcon() else icon()
}
}
/**
* Now in Android icon button default values.
*/
object NiaIconButtonDefaults {
// TODO: File bug
// IconToggleButton disabled container alpha not exposed by IconButtonDefaults
const val DisabledIconButtonContainerAlpha = 0.12f
}

@ -17,24 +17,26 @@
package com.google.samples.apps.nowinandroid.core.designsystem.component package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.contentColorFor import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.google.samples.apps.nowinandroid.core.designsystem.R import com.google.samples.apps.nowinandroid.core.designsystem.R
@Composable @Composable
fun NiaTopicTag( fun NiaTopicTag(
modifier: Modifier = Modifier,
expanded: Boolean = false, expanded: Boolean = false,
followed: Boolean, followed: Boolean,
onDropMenuToggle: (show: Boolean) -> Unit = {}, onDropdownMenuToggle: (show: Boolean) -> Unit = {},
onFollowClick: () -> Unit, onFollowClick: () -> Unit,
onUnfollowClick: () -> Unit, onUnfollowClick: () -> Unit,
onBrowseClick: () -> Unit, onBrowseClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
text: @Composable () -> Unit, text: @Composable () -> Unit,
followText: @Composable () -> Unit = { Text(stringResource(R.string.follow)) }, followText: @Composable () -> Unit = { Text(stringResource(R.string.follow)) },
@ -46,28 +48,28 @@ fun NiaTopicTag(
val containerColor = if (followed) { val containerColor = if (followed) {
MaterialTheme.colorScheme.primaryContainer MaterialTheme.colorScheme.primaryContainer
} else { } else {
MaterialTheme.colorScheme.surfaceVariant MaterialTheme.colorScheme.surfaceVariant.copy(
alpha = NiaTagDefaults.UnfollowedTopicTagContainerAlpha
)
} }
NiaTextButton( TextButton(
onClick = { onDropMenuToggle(true) }, onClick = { onDropdownMenuToggle(true) },
enabled = enabled, enabled = enabled,
small = true, colors = ButtonDefaults.textButtonColors(
colors = NiaButtonDefaults.textButtonColors(
containerColor = containerColor, containerColor = containerColor,
contentColor = contentColorFor(backgroundColor = containerColor), contentColor = contentColorFor(backgroundColor = containerColor),
disabledContainerColor = if (followed) { disabledContainerColor = MaterialTheme.colorScheme.onSurface.copy(
MaterialTheme.colorScheme.onBackground.copy( alpha = NiaTagDefaults.DisabledTopicTagContainerAlpha
alpha = NiaButtonDefaults.DisabledButtonContentAlpha )
) )
} else { ) {
Color.Transparent ProvideTextStyle(value = MaterialTheme.typography.labelSmall) {
} text()
), }
text = text }
)
NiaDropdownMenu( NiaDropdownMenu(
expanded = expanded, expanded = expanded,
onDismissRequest = { onDropMenuToggle(false) }, onDismissRequest = { onDropdownMenuToggle(false) },
items = if (followed) listOf(UNFOLLOW, BROWSE) else listOf(FOLLOW, BROWSE), items = if (followed) listOf(UNFOLLOW, BROWSE) else listOf(FOLLOW, BROWSE),
onItemClick = { item -> onItemClick = { item ->
when (item) { when (item) {
@ -87,6 +89,16 @@ fun NiaTopicTag(
} }
} }
/**
* Now in Android tag default values.
*/
object NiaTagDefaults {
const val UnfollowedTopicTagContainerAlpha = 0.5f
// TODO: File bug
// Button disabled container alpha value not exposed by ButtonDefaults
const val DisabledTopicTagContainerAlpha = 0.12f
}
private const val FOLLOW = 1 private const val FOLLOW = 1
private const val UNFOLLOW = 2 private const val UNFOLLOW = 2
private const val BROWSE = 3 private const val BROWSE = 3

@ -1,107 +0,0 @@
/*
* Copyright 2022 The Android Open Source Project
*
* 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
*
* https://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 com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
/**
* Now in Android toggle button with icon and checked icon content slots. Wraps Material 3
* [IconButton].
*
* @param checked Whether the toggle button is currently checked.
* @param onCheckedChange Called when the user clicks the toggle button and toggles checked.
* @param modifier Modifier to be applied to the toggle button.
* @param enabled Controls the enabled state of the toggle button. When `false`, this toggle button
* will not be clickable and will appear disabled to accessibility services.
* @param icon The icon content to show when unchecked.
* @param checkedIcon The icon content to show when checked.
* @param size The size of the toggle button.
* @param iconSize The size of the icon.
* @param backgroundColor The background color when unchecked.
* @param checkedBackgroundColor The background color when checked.
* @param iconColor The icon color when unchecked.
* @param iconColor The icon color when checked.
*/
@Composable
fun NiaToggleButton(
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
icon: @Composable () -> Unit,
checkedIcon: @Composable () -> Unit = icon,
size: Dp = NiaToggleButtonDefaults.ToggleButtonSize,
iconSize: Dp = NiaToggleButtonDefaults.ToggleButtonIconSize,
backgroundColor: Color = Color.Transparent,
checkedBackgroundColor: Color = MaterialTheme.colorScheme.primaryContainer,
iconColor: Color = contentColorFor(backgroundColor),
checkedIconColor: Color = contentColorFor(checkedBackgroundColor)
) {
val radius = with(LocalDensity.current) { (size / 2).toPx() }
IconButton(
onClick = { onCheckedChange(!checked) },
modifier = modifier
.size(size)
.toggleable(value = checked, enabled = enabled, role = Role.Button, onValueChange = {
onCheckedChange(!checked)
})
.drawBehind {
drawCircle(
color = if (checked) checkedBackgroundColor else backgroundColor,
radius = radius
)
},
enabled = enabled,
content = {
Box(
modifier = Modifier.sizeIn(
maxWidth = iconSize,
maxHeight = iconSize
)
) {
val contentColor = if (checked) checkedIconColor else iconColor
CompositionLocalProvider(LocalContentColor provides contentColor) {
if (checked) checkedIcon() else icon()
}
}
}
)
}
/**
* Now in Android toggle button default values.
*/
object NiaToggleButtonDefaults {
val ToggleButtonSize = 40.dp
val ToggleButtonIconSize = 18.dp
}

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
@file:OptIn(ExperimentalMaterial3Api::class)
package com.google.samples.apps.nowinandroid.core.designsystem.component package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.annotation.StringRes import androidx.annotation.StringRes

@ -16,9 +16,18 @@
package com.google.samples.apps.nowinandroid.core.designsystem.component package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
/** /**
@ -42,16 +51,71 @@ fun NiaViewToggleButton(
compactText: @Composable () -> Unit, compactText: @Composable () -> Unit,
expandedText: @Composable () -> Unit expandedText: @Composable () -> Unit
) { ) {
NiaTextButton( TextButton(
onClick = { onExpandedChange(!expanded) }, onClick = { onExpandedChange(!expanded) },
modifier = modifier, modifier = modifier,
enabled = enabled, enabled = enabled,
text = if (expanded) expandedText else compactText, colors = ButtonDefaults.textButtonColors(
trailingIcon = { contentColor = MaterialTheme.colorScheme.onBackground
Icon( ),
imageVector = if (expanded) NiaIcons.ViewDay else NiaIcons.ShortText, contentPadding = NiaViewToggleDefaults.ViewToggleButtonContentPadding
contentDescription = null ) {
NiaViewToggleButtonContent(
text = if (expanded) expandedText else compactText,
trailingIcon = {
Icon(
imageVector = if (expanded) NiaIcons.ViewDay else NiaIcons.ShortText,
contentDescription = null
)
}
)
}
}
/**
* Internal Now in Android view toggle button content layout for arranging the text label and
* trailing icon.
*
* @param text The button text label content.
* @param trailingIcon The button trailing icon content. Pass `null` here for no trailing icon.
*/
@Composable
private fun NiaViewToggleButtonContent(
text: @Composable () -> Unit,
trailingIcon: @Composable (() -> Unit)?,
) {
Box(
Modifier
.padding(
end = if (trailingIcon != null) {
ButtonDefaults.IconSpacing
} else {
0.dp
}
) )
) {
ProvideTextStyle(value = MaterialTheme.typography.labelSmall) {
text()
}
}
if (trailingIcon != null) {
Box(Modifier.sizeIn(maxHeight = ButtonDefaults.IconSize)) {
trailingIcon()
} }
) }
}
/**
* Now in Android view toggle default values.
*/
object NiaViewToggleDefaults {
// TODO: File bug
// Various default button padding values aren't exposed via ButtonDefaults
val ViewToggleButtonContentPadding =
PaddingValues(
start = 16.dp,
top = 8.dp,
end = 12.dp,
bottom = 8.dp
)
} }

@ -18,12 +18,12 @@ package com.google.samples.apps.nowinandroid.core.designsystem.icon
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.outlined.AccountCircle import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.ArrowDropUp
import androidx.compose.material.icons.rounded.Check import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.ExpandLess import androidx.compose.material.icons.rounded.ExpandLess
@ -48,8 +48,8 @@ object NiaIcons {
val AccountCircle = Icons.Outlined.AccountCircle val AccountCircle = Icons.Outlined.AccountCircle
val Add = Icons.Rounded.Add val Add = Icons.Rounded.Add
val ArrowBack = Icons.Rounded.ArrowBack val ArrowBack = Icons.Rounded.ArrowBack
val ArrowDropDown = Icons.Rounded.ArrowDropDown val ArrowDropDown = Icons.Default.ArrowDropDown
val ArrowDropUp = Icons.Rounded.ArrowDropUp val ArrowDropUp = Icons.Default.ArrowDropUp
val Bookmark = R.drawable.ic_bookmark val Bookmark = R.drawable.ic_bookmark
val BookmarkBorder = R.drawable.ic_bookmark_border val BookmarkBorder = R.drawable.ic_bookmark_border
val Bookmarks = R.drawable.ic_bookmarks val Bookmarks = R.drawable.ic_bookmarks

@ -21,13 +21,13 @@ import androidx.compose.ui.graphics.Color
/** /**
* Now in Android colors. * Now in Android colors.
*/ */
internal val Blue10 = Color(0xFF001F29) internal val Blue10 = Color(0xFF001F28)
internal val Blue20 = Color(0xFF003544) internal val Blue20 = Color(0xFF003544)
internal val Blue30 = Color(0xFF004D61) internal val Blue30 = Color(0xFF004D61)
internal val Blue40 = Color(0xFF006781) internal val Blue40 = Color(0xFF006780)
internal val Blue80 = Color(0xFF5DD4FB) internal val Blue80 = Color(0xFF5DD5FC)
internal val Blue90 = Color(0xFFB5EAFF) internal val Blue90 = Color(0xFFB8EAFF)
internal val Blue95 = Color(0xFFDCF5FF) internal val Blue95 = Color(0xFFDDF4FF)
internal val DarkGreen10 = Color(0xFF0D1F12) internal val DarkGreen10 = Color(0xFF0D1F12)
internal val DarkGreen20 = Color(0xFF223526) internal val DarkGreen20 = Color(0xFF223526)
internal val DarkGreen30 = Color(0xFF394B3C) internal val DarkGreen30 = Color(0xFF394B3C)
@ -35,10 +35,12 @@ internal val DarkGreen40 = Color(0xFF4F6352)
internal val DarkGreen80 = Color(0xFFB7CCB8) internal val DarkGreen80 = Color(0xFFB7CCB8)
internal val DarkGreen90 = Color(0xFFD3E8D3) internal val DarkGreen90 = Color(0xFFD3E8D3)
internal val DarkGreenGray10 = Color(0xFF1A1C1A) internal val DarkGreenGray10 = Color(0xFF1A1C1A)
internal val DarkGreenGray20 = Color(0xFF2F312E)
internal val DarkGreenGray90 = Color(0xFFE2E3DE) internal val DarkGreenGray90 = Color(0xFFE2E3DE)
internal val DarkGreenGray95 = Color(0xFFF0F1EC) internal val DarkGreenGray95 = Color(0xFFF0F1EC)
internal val DarkGreenGray99 = Color(0xFFFBFDF7) internal val DarkGreenGray99 = Color(0xFFFBFDF7)
internal val DarkPurpleGray10 = Color(0xFF201A1B) internal val DarkPurpleGray10 = Color(0xFF201A1B)
internal val DarkPurpleGray20 = Color(0xFF362F30)
internal val DarkPurpleGray90 = Color(0xFFECDFE0) internal val DarkPurpleGray90 = Color(0xFFECDFE0)
internal val DarkPurpleGray95 = Color(0xFFFAEEEF) internal val DarkPurpleGray95 = Color(0xFFFAEEEF)
internal val DarkPurpleGray99 = Color(0xFFFCFCFC) internal val DarkPurpleGray99 = Color(0xFFFCFCFC)
@ -53,31 +55,31 @@ internal val GreenGray50 = Color(0xFF727971)
internal val GreenGray60 = Color(0xFF8B938A) internal val GreenGray60 = Color(0xFF8B938A)
internal val GreenGray80 = Color(0xFFC1C9BF) internal val GreenGray80 = Color(0xFFC1C9BF)
internal val GreenGray90 = Color(0xFFDDE5DB) internal val GreenGray90 = Color(0xFFDDE5DB)
internal val Orange10 = Color(0xFF390C00) internal val Orange10 = Color(0xFF380D00)
internal val Orange20 = Color(0xFF5D1900) internal val Orange20 = Color(0xFF5B1A00)
internal val Orange30 = Color(0xFF812800) internal val Orange30 = Color(0xFF812800)
internal val Orange40 = Color(0xFFA23F16) internal val Orange40 = Color(0xFFA23F16)
internal val Orange80 = Color(0xFFFFB599) internal val Orange80 = Color(0xFFFFB59B)
internal val Orange90 = Color(0xFFFFDBCE) internal val Orange90 = Color(0xFFFFDBCF)
internal val Orange95 = Color(0xFFFFEDE6) internal val Orange95 = Color(0xFFFFEDE8)
internal val Purple10 = Color(0xFF36003D) internal val Purple10 = Color(0xFF36003C)
internal val Purple20 = Color(0xFF560A5E) internal val Purple20 = Color(0xFF560A5D)
internal val Purple30 = Color(0xFF702776) internal val Purple30 = Color(0xFF702776)
internal val Purple40 = Color(0xFF8C4190) internal val Purple40 = Color(0xFF8B418F)
internal val Purple80 = Color(0xFFFFA8FF) internal val Purple80 = Color(0xFFFFA9FE)
internal val Purple90 = Color(0xFFFFD5FC) internal val Purple90 = Color(0xFFFFD6FA)
internal val Purple95 = Color(0xFFFFEBFB) internal val Purple95 = Color(0xFFFFEBFA)
internal val PurpleGray30 = Color(0xFF4E444C) internal val PurpleGray30 = Color(0xFF4D444C)
internal val PurpleGray50 = Color(0xFF7F747C) internal val PurpleGray50 = Color(0xFF7F747C)
internal val PurpleGray60 = Color(0xFF998D96) internal val PurpleGray60 = Color(0xFF998D96)
internal val PurpleGray80 = Color(0xFFD0C2CC) internal val PurpleGray80 = Color(0xFFD0C3CC)
internal val PurpleGray90 = Color(0xFFEDDEE8) internal val PurpleGray90 = Color(0xFFEDDEE8)
internal val Red10 = Color(0xFF410001) internal val Red10 = Color(0xFF410002)
internal val Red20 = Color(0xFF680003) internal val Red20 = Color(0xFF690005)
internal val Red30 = Color(0xFF930006) internal val Red30 = Color(0xFF93000A)
internal val Red40 = Color(0xFFBA1B1B) internal val Red40 = Color(0xFFBA1A1A)
internal val Red80 = Color(0xFFFFB4A9) internal val Red80 = Color(0xFFFFB4AB)
internal val Red90 = Color(0xFFFFDAD4) internal val Red90 = Color(0xFFFFDAD6)
internal val Teal10 = Color(0xFF001F26) internal val Teal10 = Color(0xFF001F26)
internal val Teal20 = Color(0xFF02363F) internal val Teal20 = Color(0xFF02363F)
internal val Teal30 = Color(0xFF214D56) internal val Teal30 = Color(0xFF214D56)

@ -25,10 +25,9 @@ import androidx.compose.ui.graphics.Color
*/ */
@Immutable @Immutable
data class GradientColors( data class GradientColors(
val primary: Color = Color.Unspecified, val top: Color = Color.Unspecified,
val secondary: Color = Color.Unspecified, val bottom: Color = Color.Unspecified,
val tertiary: Color = Color.Unspecified, val container: Color = Color.Unspecified
val neutral: Color = Color.Unspecified
) )
/** /**

@ -58,6 +58,8 @@ val LightDefaultColorScheme = lightColorScheme(
onSurface = DarkPurpleGray10, onSurface = DarkPurpleGray10,
surfaceVariant = PurpleGray90, surfaceVariant = PurpleGray90,
onSurfaceVariant = PurpleGray30, onSurfaceVariant = PurpleGray30,
inverseSurface = DarkPurpleGray20,
inverseOnSurface = DarkPurpleGray95,
outline = PurpleGray50 outline = PurpleGray50
) )
@ -88,6 +90,8 @@ val DarkDefaultColorScheme = darkColorScheme(
onSurface = DarkPurpleGray90, onSurface = DarkPurpleGray90,
surfaceVariant = PurpleGray30, surfaceVariant = PurpleGray30,
onSurfaceVariant = PurpleGray80, onSurfaceVariant = PurpleGray80,
inverseSurface = DarkPurpleGray90,
inverseOnSurface = DarkPurpleGray10,
outline = PurpleGray60 outline = PurpleGray60
) )
@ -118,6 +122,8 @@ val LightAndroidColorScheme = lightColorScheme(
onSurface = DarkGreenGray10, onSurface = DarkGreenGray10,
surfaceVariant = GreenGray90, surfaceVariant = GreenGray90,
onSurfaceVariant = GreenGray30, onSurfaceVariant = GreenGray30,
inverseSurface = DarkGreenGray20,
inverseOnSurface = DarkGreenGray95,
outline = GreenGray50 outline = GreenGray50
) )
@ -148,19 +154,11 @@ val DarkAndroidColorScheme = darkColorScheme(
onSurface = DarkGreenGray90, onSurface = DarkGreenGray90,
surfaceVariant = GreenGray30, surfaceVariant = GreenGray30,
onSurfaceVariant = GreenGray80, onSurfaceVariant = GreenGray80,
inverseSurface = DarkGreenGray90,
inverseOnSurface = DarkGreenGray10,
outline = GreenGray60 outline = GreenGray60
) )
/**
* Light default gradient colors
*/
val LightDefaultGradientColors = GradientColors(
primary = Purple95,
secondary = Orange95,
tertiary = Blue95,
neutral = DarkPurpleGray95
)
/** /**
* Light Android background theme * Light Android background theme
*/ */
@ -207,22 +205,38 @@ internal fun NiaTheme(
disableDynamicTheming: Boolean, disableDynamicTheming: Boolean,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val colorScheme = if (androidTheme) { // Color scheme
if (darkTheme) DarkAndroidColorScheme else LightAndroidColorScheme val colorScheme = when {
} else if (!disableDynamicTheming && supportsDynamicTheming()) { !disableDynamicTheming -> {
val context = LocalContext.current if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) val context = LocalContext.current
} else { if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme } else {
if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme
}
}
androidTheme -> if (darkTheme) DarkAndroidColorScheme else LightAndroidColorScheme
else -> if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme
} }
// Gradient colors
val defaultGradientColors = GradientColors() val emptyGradientColors = GradientColors()
val gradientColors = if (androidTheme || (!disableDynamicTheming && supportsDynamicTheming())) { val defaultGradientColors = GradientColors(
defaultGradientColors top = colorScheme.inverseOnSurface,
} else { bottom = colorScheme.primaryContainer,
if (darkTheme) defaultGradientColors else LightDefaultGradientColors container = colorScheme.surface
)
val gradientColors = when {
!disableDynamicTheming -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
emptyGradientColors
} else {
defaultGradientColors
}
}
androidTheme -> emptyGradientColors
else -> defaultGradientColors
} }
// Background theme
val defaultBackgroundTheme = BackgroundTheme( val defaultBackgroundTheme = BackgroundTheme(
color = colorScheme.surface, color = colorScheme.surface,
tonalElevation = 2.dp tonalElevation = 2.dp
@ -232,7 +246,7 @@ internal fun NiaTheme(
} else { } else {
defaultBackgroundTheme defaultBackgroundTheme
} }
// Composition locals
CompositionLocalProvider( CompositionLocalProvider(
LocalGradientColors provides gradientColors, LocalGradientColors provides gradientColors,
LocalBackgroundTheme provides backgroundTheme LocalBackgroundTheme provides backgroundTheme

@ -18,98 +18,101 @@ package com.google.samples.apps.nowinandroid.core.designsystem.theme
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
/** /**
* Now in Android typography. * Now in Android typography.
*
* TODO: Add custom font
*/ */
internal val NiaTypography = Typography( internal val NiaTypography = Typography(
displayLarge = TextStyle( displayLarge = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 57.sp, fontSize = 57.sp,
lineHeight = 64.sp, lineHeight = 64.sp,
letterSpacing = (-0.25).sp letterSpacing = (-0.25).sp
), ),
displayMedium = TextStyle( displayMedium = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 45.sp, fontSize = 45.sp,
lineHeight = 52.sp lineHeight = 52.sp,
letterSpacing = 0.sp
), ),
displaySmall = TextStyle( displaySmall = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 36.sp, fontSize = 36.sp,
lineHeight = 44.sp lineHeight = 44.sp,
letterSpacing = 0.sp
), ),
headlineLarge = TextStyle( headlineLarge = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 32.sp, fontSize = 32.sp,
lineHeight = 40.sp lineHeight = 40.sp,
letterSpacing = 0.sp
), ),
headlineMedium = TextStyle( headlineMedium = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 28.sp, fontSize = 28.sp,
lineHeight = 36.sp lineHeight = 36.sp,
letterSpacing = 0.sp
), ),
headlineSmall = TextStyle( headlineSmall = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 24.sp, fontSize = 24.sp,
lineHeight = 32.sp lineHeight = 32.sp,
letterSpacing = 0.sp
), ),
titleLarge = TextStyle( titleLarge = TextStyle(
fontWeight = FontWeight.W700, fontWeight = FontWeight.Bold,
fontSize = 22.sp, fontSize = 22.sp,
lineHeight = 28.sp lineHeight = 28.sp,
letterSpacing = 0.sp
), ),
titleMedium = TextStyle( titleMedium = TextStyle(
fontWeight = FontWeight.W700, fontWeight = FontWeight.Bold,
fontSize = 16.sp, fontSize = 18.sp,
lineHeight = 24.sp, lineHeight = 24.sp,
letterSpacing = 0.1.sp letterSpacing = 0.1.sp
), ),
titleSmall = TextStyle( titleSmall = TextStyle(
fontWeight = FontWeight.W500, fontWeight = FontWeight.Medium,
fontSize = 14.sp, fontSize = 14.sp,
lineHeight = 20.sp, lineHeight = 20.sp,
letterSpacing = 0.1.sp letterSpacing = 0.1.sp
), ),
bodyLarge = TextStyle( bodyLarge = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 16.sp, fontSize = 16.sp,
lineHeight = 24.sp, lineHeight = 24.sp,
letterSpacing = 0.5.sp letterSpacing = 0.5.sp
), ),
bodyMedium = TextStyle( bodyMedium = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 14.sp, fontSize = 14.sp,
lineHeight = 20.sp, lineHeight = 20.sp,
letterSpacing = 0.25.sp letterSpacing = 0.25.sp
), ),
bodySmall = TextStyle( bodySmall = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Normal,
fontSize = 12.sp, fontSize = 12.sp,
lineHeight = 16.sp, lineHeight = 16.sp,
letterSpacing = 0.4.sp letterSpacing = 0.4.sp
), ),
labelLarge = TextStyle( labelLarge = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Medium,
fontSize = 14.sp, fontSize = 14.sp,
lineHeight = 20.sp, lineHeight = 20.sp,
letterSpacing = 0.1.sp letterSpacing = 0.1.sp
), ),
labelMedium = TextStyle( labelMedium = TextStyle(
fontWeight = FontWeight.W400, fontWeight = FontWeight.Medium,
fontSize = 12.sp, fontSize = 12.sp,
lineHeight = 16.sp, lineHeight = 16.sp,
letterSpacing = 0.5.sp letterSpacing = 0.5.sp
), ),
labelSmall = TextStyle( labelSmall = TextStyle(
fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Medium,
fontWeight = FontWeight.W500,
fontSize = 10.sp, fontSize = 10.sp,
lineHeight = 16.sp lineHeight = 16.sp,
letterSpacing = 0.sp
) )
) )

@ -52,7 +52,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.google.samples.apps.nowinandroid.core.designsystem.R as DesignsystemR import com.google.samples.apps.nowinandroid.core.designsystem.R as DesignsystemR
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
@ -155,7 +155,7 @@ fun BookmarkButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
NiaToggleButton( NiaIconToggleButton(
checked = isBookmarked, checked = isBookmarked,
onCheckedChange = { onClick() }, onCheckedChange = { onClick() },
modifier = modifier, modifier = modifier,
@ -232,7 +232,7 @@ fun NewsResourceTopics(
NiaTopicTag( NiaTopicTag(
expanded = expandedTopicId == topic.id, expanded = expandedTopicId == topic.id,
followed = true, // ToDo: Check if topic is followed followed = true, // ToDo: Check if topic is followed
onDropMenuToggle = { show -> onDropdownMenuToggle = { show ->
expandedTopicId = if (show) topic.id else null expandedTopicId = if (show) topic.id else null
}, },
onFollowClick = { }, // ToDo onFollowClick = { }, // ToDo

@ -77,9 +77,9 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilledButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOverlayLoadingWheel import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOverlayLoadingWheel
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
@ -257,7 +257,7 @@ private fun LazyGridScope.onboarding(
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
NiaFilledButton( NiaButton(
onClick = saveFollowedTopics, onClick = saveFollowedTopics,
enabled = onboardingUiState.isDismissable, enabled = onboardingUiState.isDismissable,
modifier = Modifier modifier = Modifier
@ -350,7 +350,7 @@ private fun SingleTopicButton(
.weight(1f), .weight(1f),
color = MaterialTheme.colorScheme.onSurface color = MaterialTheme.colorScheme.onSurface
) )
NiaToggleButton( NiaIconToggleButton(
checked = isSelected, checked = isSelected,
onCheckedChange = { checked -> onClick(topicId, checked) }, onCheckedChange = { checked -> onClick(topicId, checked) },
icon = { icon = {

@ -37,7 +37,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaToggleButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.feature.interests.R.string import com.google.samples.apps.nowinandroid.feature.interests.R.string
@ -69,7 +69,7 @@ fun InterestsItem(
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
InterestContent(name, description) InterestContent(name, description)
} }
NiaToggleButton( NiaIconToggleButton(
checked = following, checked = following,
onCheckedChange = onFollowButtonClick, onCheckedChange = onFollowButtonClick,
icon = { icon = {

@ -80,7 +80,7 @@ class DesignSystemDetector : Detector(), Detector.UastScanner {
// instead of hardcoded names. // instead of hardcoded names.
val METHOD_NAMES = mapOf( val METHOD_NAMES = mapOf(
"MaterialTheme" to "NiaTheme", "MaterialTheme" to "NiaTheme",
"Button" to "NiaFilledButton", "Button" to "NiaButton",
"OutlinedButton" to "NiaOutlinedButton", "OutlinedButton" to "NiaOutlinedButton",
"TextButton" to "NiaTextButton", "TextButton" to "NiaTextButton",
"FilterChip" to "NiaFilterChip", "FilterChip" to "NiaFilterChip",
@ -92,10 +92,10 @@ class DesignSystemDetector : Detector(), Detector.UastScanner {
"NavigationRailItem" to "NiaNavigationRailItem", "NavigationRailItem" to "NiaNavigationRailItem",
"TabRow" to "NiaTabRow", "TabRow" to "NiaTabRow",
"Tab" to "NiaTab", "Tab" to "NiaTab",
"IconToggleButton" to "NiaToggleButton", "IconToggleButton" to "NiaIconToggleButton",
"FilledIconToggleButton" to "NiaToggleButton", "FilledIconToggleButton" to "NiaIconToggleButton",
"FilledTonalIconToggleButton" to "NiaToggleButton", "FilledTonalIconToggleButton" to "NiaIconToggleButton",
"OutlinedIconToggleButton" to "NiaToggleButton", "OutlinedIconToggleButton" to "NiaIconToggleButton",
"CenterAlignedTopAppBar" to "NiaTopAppBar", "CenterAlignedTopAppBar" to "NiaTopAppBar",
"SmallTopAppBar" to "NiaTopAppBar", "SmallTopAppBar" to "NiaTopAppBar",
"MediumTopAppBar" to "NiaTopAppBar", "MediumTopAppBar" to "NiaTopAppBar",

Loading…
Cancel
Save