Skip to content

Commit

Permalink
feat(catalog): add support for transition motions & base for future s…
Browse files Browse the repository at this point in the history
…park animation utils (#1444)
  • Loading branch information
soulcramer authored Jan 28, 2025
1 parent a393ee8 commit c50c65e
Show file tree
Hide file tree
Showing 17 changed files with 574 additions and 146 deletions.
1 change: 1 addition & 0 deletions catalog/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appCompat)
implementation(libs.androidx.navigation.compose)
implementation(libs.material.motion)

implementation(libs.androidx.datastore)
implementation(libs.kotlinx.serialization.json)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,15 +260,18 @@ internal fun ComponentActivity.CatalogApp(
CatalogHomeScreen.Examples -> ComponentsScreen(
components = components,
contentPadding = innerPadding,
navigationMode = theme.navigationMode,
)

CatalogHomeScreen.Configurator -> ConfiguratorComponentsScreen(
components = components,
contentPadding = innerPadding,
navigationMode = theme.navigationMode,
)

CatalogHomeScreen.Icons -> IconDemoScreen(
contentPadding = innerPadding,
navigationMode = theme.navigationMode,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.adevinta.spark.SparkTheme
import com.adevinta.spark.catalog.R
import com.adevinta.spark.catalog.examples.component.ComponentItem
import com.adevinta.spark.catalog.model.Component
import com.adevinta.spark.catalog.themes.NavigationMode
import com.adevinta.spark.catalog.ui.navigation.NavHostSpark
import com.adevinta.spark.components.text.Text
import com.adevinta.spark.tokens.Layout
import kotlinx.serialization.Serializable
Expand All @@ -57,13 +58,15 @@ public fun ConfiguratorComponentsScreen(
modifier: Modifier = Modifier,
components: List<Component>,
contentPadding: PaddingValues,
navigationMode: NavigationMode,
) {
val navController = rememberNavController()

NavHost(
NavHostSpark(
modifier = modifier,
navController = navController,
startDestination = ConfiguratorsList,
navigationMode = navigationMode,
builder = {
configuratorsDestination(
navController = navController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package com.adevinta.spark.catalog.datastore.theme
import com.adevinta.spark.catalog.themes.BrandMode
import com.adevinta.spark.catalog.themes.ColorMode
import com.adevinta.spark.catalog.themes.FontScaleMode
import com.adevinta.spark.catalog.themes.NavigationMode
import com.adevinta.spark.catalog.themes.TextDirection
import com.adevinta.spark.catalog.themes.ThemeMode
import com.adevinta.spark.catalog.themes.UserMode
Expand All @@ -40,6 +41,7 @@ internal data class ThemeProperties(
val fontScaleMode: FontScaleMode,
val colorBlindNessType: ColorBlindNessType,
val colorBlindNessSeverity: Float,
val navigationMode: NavigationMode,
val textDirection: TextDirection,
val highlightSparkComponents: Boolean,
val highlightSparkTokens: Boolean,
Expand All @@ -55,6 +57,7 @@ internal data class ThemeProperties(
fontScaleMode = FontScaleMode.System,
colorBlindNessType = ColorBlindNessType.None,
colorBlindNessSeverity = 0.5f,
navigationMode = NavigationMode.Default,
highlightSparkComponents = false,
highlightSparkTokens = false,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ private fun ThemeProperties.toTheme(): Theme = Theme(
colorMode = colorMode,
brandMode = brandMode,
fontScaleMode = fontScaleMode,
textDirection = textDirection,
colorBlindNessType = colorBlindNessType,
colorBlindNessSeverity = colorBlindNessSeverity,
navigationMode = navigationMode,
textDirection = textDirection,
highlightSparkComponents = highlightSparkComponents,
highlightSparkTokens = highlightSparkTokens,
)
Expand All @@ -93,6 +94,7 @@ private fun Theme.toDataStoreThemeProperties(): ThemeProperties = ThemePropertie
fontScaleMode = fontScaleMode,
colorBlindNessType = colorBlindNessType,
colorBlindNessSeverity = colorBlindNessSeverity,
navigationMode = navigationMode,
textDirection = textDirection,
highlightSparkComponents = highlightSparkComponents,
highlightSparkTokens = highlightSparkTokens,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.adevinta.spark.SparkTheme
import com.adevinta.spark.catalog.R
import com.adevinta.spark.catalog.examples.component.ComponentItem
import com.adevinta.spark.catalog.model.Component
import com.adevinta.spark.catalog.themes.NavigationMode
import com.adevinta.spark.catalog.ui.navigation.NavHostSpark
import com.adevinta.spark.components.text.Text
import com.adevinta.spark.tokens.Layout

Expand All @@ -53,13 +54,15 @@ public fun ComponentsScreen(
modifier: Modifier = Modifier,
components: List<Component>,
contentPadding: PaddingValues,
navigationMode: NavigationMode,
) {
val navController = rememberNavController()

NavHost(
NavHostSpark(
modifier = modifier,
navController = navController,
startDestination = ExamplesList,
navigationMode = navigationMode,
builder = {
examplesDestination(
navController = navController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,40 @@
*/
package com.adevinta.spark.catalog.icons

import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.adevinta.spark.catalog.themes.NavigationMode
import com.adevinta.spark.catalog.ui.navigation.NavHostSpark
import kotlinx.serialization.Serializable

@Serializable
public object IconsList

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
public fun IconDemoScreen(
modifier: Modifier = Modifier,
contentPadding: PaddingValues,
navigationMode: NavigationMode,
) {
val navController = rememberNavController()

NavHost(
modifier = modifier,
navController = navController,
startDestination = IconsList,
builder = {
iconsDemoDestination(
navController = navController,
contentPadding = contentPadding,
)
},
)
SharedTransitionLayout {
val navController = rememberNavController()
NavHostSpark(
modifier = modifier,
navController = navController,
startDestination = IconsList,
navigationMode = navigationMode,
builder = {
iconsDemoDestination(
navController = navController,
contentPadding = contentPadding,
this@SharedTransitionLayout,
)
},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
*/
package com.adevinta.spark.catalog.icons

import android.R.attr.key
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
Expand Down Expand Up @@ -52,80 +56,94 @@ import com.adevinta.spark.icons.SparkIcons
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
internal fun IconExampleScreen(icon: SparkIcon, name: String, isAnimated: Boolean) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterVertically),
) {
var atEnd by remember { mutableStateOf(false) }
var isRunning by remember { mutableStateOf(false) }
internal fun IconExampleScreen(
icon: SparkIcon,
name: String,
isAnimated: Boolean,
sharedTransitionScope: SharedTransitionScope,
animatedContentScope: AnimatedContentScope,
) {
with(sharedTransitionScope) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterVertically),
) {
var atEnd by remember { mutableStateOf(false) }
var isRunning by remember { mutableStateOf(false) }

// This is necessary just if you want to run the animation when the
// component is displayed. Otherwise, you can remove it.
LaunchedEffect(icon, isRunning) {
while (isRunning) {
delay(3.seconds) // set here your delay between animations
atEnd = !atEnd
// This is necessary just if you want to run the animation when the
// component is displayed. Otherwise, you can remove it.
LaunchedEffect(icon, isRunning) {
while (isRunning) {
delay(3.seconds) // set here your delay between animations
atEnd = !atEnd
}
}
if (isAnimated) {
SwitchLabelled(
checked = isRunning,
onCheckedChange = { isRunning = !isRunning },
) {
Text(text = "Animate indefinitely")
}
}

Icon(
sparkIcon = icon,
contentDescription = name,
modifier = Modifier
.size(128.dp)
.sharedElement(
state = sharedTransitionScope.rememberSharedContentState(key = "icon-$name"),
animatedVisibilityScope = animatedContentScope,
),
atEnd = atEnd,
)
IconButtonFilled(
icon = icon,
contentDescription = name,
onClick = {
atEnd = !atEnd
},
atEnd = atEnd,
)
ButtonFilled(onClick = { atEnd = !atEnd }, text = name, icon = icon, atEnd = atEnd)
TagFilled(text = name, leadingIcon = icon, atEnd = atEnd)
ChipTinted(
onClick = { atEnd = !atEnd },
) {
Text(text = name)
Icon(sparkIcon = icon, contentDescription = name, atEnd = atEnd)
}
}
if (isAnimated) {
SwitchLabelled(
checked = isRunning,
onCheckedChange = { isRunning = !isRunning },
checked = atEnd,
onCheckedChange = { atEnd = !atEnd },
icons = SwitchIcons(checked = icon, unchecked = SparkIcons.Close),
) {
Text(text = "Animate indefinitely")
Text(text = name)
}
}

Icon(
sparkIcon = icon,
contentDescription = name,
modifier = Modifier.size(128.dp),
atEnd = atEnd,
)
IconButtonFilled(
icon = icon,
contentDescription = name,
onClick = {
atEnd = !atEnd
},
atEnd = atEnd,
)
ButtonFilled(onClick = { atEnd = !atEnd }, text = name, icon = icon, atEnd = atEnd)
TagFilled(text = name, leadingIcon = icon, atEnd = atEnd)
ChipTinted(
onClick = { atEnd = !atEnd },
) {
Text(text = name)
Icon(sparkIcon = icon, contentDescription = name, atEnd = atEnd)
}
SwitchLabelled(
checked = atEnd,
onCheckedChange = { atEnd = !atEnd },
icons = SwitchIcons(checked = icon, unchecked = SparkIcons.Close),
) {
Text(text = name)
}

val tabs = mutableListOf(
Pair("Home", null),
Pair(name, icon),
)
var selectedIndex by remember { mutableIntStateOf(1) }
TabGroup(
selectedTabIndex = selectedIndex,
) {
tabs.forEachIndexed { index, pair ->
Tab(
selected = selectedIndex == index,
onClick = { selectedIndex = index },
icon = pair.second,
text = pair.first,
)
val tabs = mutableListOf(
Pair("Home", null),
Pair(name, icon),
)
var selectedIndex by remember { mutableIntStateOf(1) }
TabGroup(
selectedTabIndex = selectedIndex,
) {
tabs.forEachIndexed { index, pair ->
Tab(
selected = selectedIndex == index,
onClick = { selectedIndex = index },
icon = pair.second,
text = pair.first,
)
}
}
}
}
Expand Down
Loading

0 comments on commit c50c65e

Please sign in to comment.