Skip to content

Commit

Permalink
Merge pull request #615 from zsoltk/material-nav
Browse files Browse the repository at this point in the history
Material navigation helpers
  • Loading branch information
zsoltk authored Oct 17, 2023
2 parents bc6c38c + 04b9c61 commit 595f6a1
Show file tree
Hide file tree
Showing 69 changed files with 1,268 additions and 107 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

- [#611](https://github.com/bumble-tech/appyx/pull/611) – Lower position and rotation animation default round-off thresholds

### Added

- [#615](https://github.com/bumble-tech/appyx/pull/615) – Material navigation helpers

---

## 2.0.0-alpha08
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.bumble.appyx.components.spotlight.ui.fader

import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.property.impl.Alpha
import com.bumble.appyx.interactions.core.ui.state.MutableUiStateSpecs

@MutableUiStateSpecs
class TargetUiState(
val alpha: Alpha.Target,
) {
constructor(base: TargetUiState) : this(
alpha = base.alpha,
)

fun toMutableState(
uiContext: UiContext,
): MutableUiState =
MutableUiState(
uiContext = uiContext,
alpha = Alpha(uiContext, alpha),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import com.bumble.appyx.interactions.core.ui.context.TransitionBounds
import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.gesture.GestureFactory
import com.bumble.appyx.interactions.core.ui.gesture.GestureSettleConfig
import com.bumble.appyx.mapState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.StateFlow

open class Spotlight<InteractionTarget : Any>(
scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main),
Expand All @@ -36,4 +38,10 @@ open class Spotlight<InteractionTarget : Any>(
gestureSettleConfig = gestureSettleConfig,
disableAnimations = disableAnimations,
isDebug = isDebug
)
) {
val activeIndex: StateFlow<Float> = model.output
.mapState(scope) { it.currentTargetState.activeIndex }

val activeElement: StateFlow<InteractionTarget?> = model.output
.mapState(scope) { it.currentTargetState.activeElement }
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class SpotlightModel<InteractionTarget : Any>(

fun hasNext(): Boolean =
activeIndex <= positions.lastIndex - 1

val activeElement: InteractionTarget? =
positions.getOrNull(activeIndex.toInt())?.elements?.firstNotNullOf { it.key.interactionTarget }
}

override val initialState: State<InteractionTarget> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,29 @@ class SpotlightModelTest {
)
}

@Test
fun GIVEN_empty_spotlight_WHEN_updated_with_element_THEN_provides_correct_activeElement() {
val spotlight = SpotlightModel(
items = listOf(),
savedStateMap = null
)

val newElements = listOf(Child1, Child2)
val newActiveIndex = 1f

spotlight.operation(
UpdateElements(
items = newElements,
initialActiveIndex = newActiveIndex
)
)

assertEquals(
expected = Child2,
actual = spotlight.output.value.currentTargetState.activeElement,
)
}

@Test
fun GIVEN_spotlight_WHEN_updated_with_elements_different_size_THEN_state_contains_element() {
val spotlight = SpotlightModel(
Expand Down
1 change: 1 addition & 0 deletions appyx-interactions/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ appyx {

dependencies {
val composeBom = platform(libs.compose.bom)
api(libs.compose.material3)

api(project(":appyx-interactions:appyx-interactions"))
api(libs.compose.ui.test.junit4)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,69 @@ val appyx_yellow1 = Color(0xFFFFC629)
val appyx_yellow2 = Color(0xFFFFE54A)
val appyx_dark = Color(0xFF1F2126)
val appyx_bright = Color(0xFFFFFFFF)


val md_theme_light_primary = Color(0xFF785A00)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDF9A)
val md_theme_light_onPrimaryContainer = Color(0xFF251A00)
val md_theme_light_secondary = Color(0xFF6B5D3F)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFF4E0BB)
val md_theme_light_onSecondaryContainer = Color(0xFF241A04)
val md_theme_light_tertiary = Color(0xFF496547)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFCBEBC5)
val md_theme_light_onTertiaryContainer = Color(0xFF062109)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1E1B16)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1E1B16)
val md_theme_light_surfaceVariant = Color(0xFFEDE1CF)
val md_theme_light_onSurfaceVariant = Color(0xFF4D4639)
val md_theme_light_outline = Color(0xFF7F7667)
val md_theme_light_inverseOnSurface = Color(0xFFF7F0E7)
val md_theme_light_inverseSurface = Color(0xFF33302A)
val md_theme_light_inversePrimary = Color(0xFFF6BE1F)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF785A00)
val md_theme_light_outlineVariant = Color(0xFFD0C5B4)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFFF6BE1F)
val md_theme_dark_onPrimary = Color(0xFF3F2E00)
val md_theme_dark_primaryContainer = Color(0xFF5A4300)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDF9A)
val md_theme_dark_secondary = Color(0xFFD7C4A0)
val md_theme_dark_onSecondary = Color(0xFF3A2F15)
val md_theme_dark_secondaryContainer = Color(0xFF52452A)
val md_theme_dark_onSecondaryContainer = Color(0xFFF4E0BB)
val md_theme_dark_tertiary = Color(0xFFAFCFAA)
val md_theme_dark_onTertiary = Color(0xFF1C361D)
val md_theme_dark_tertiaryContainer = Color(0xFF324D31)
val md_theme_dark_onTertiaryContainer = Color(0xFFCBEBC5)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1E1B16)
val md_theme_dark_onBackground = Color(0xFFE9E1D9)
val md_theme_dark_surface = Color(0xFF1E1B16)
val md_theme_dark_onSurface = Color(0xFFE9E1D9)
val md_theme_dark_surfaceVariant = Color(0xFF4D4639)
val md_theme_dark_onSurfaceVariant = Color(0xFFD0C5B4)
val md_theme_dark_outline = Color(0xFF999080)
val md_theme_dark_inverseOnSurface = Color(0xFF1E1B16)
val md_theme_dark_inverseSurface = Color(0xFFE9E1D9)
val md_theme_dark_inversePrimary = Color(0xFF785A00)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFF6BE1F)
val md_theme_dark_outlineVariant = Color(0xFF4D4639)
val md_theme_dark_scrim = Color(0xFF000000)


val seed = Color(0xFFFFC629)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.bumble.appyx.interactions.theme

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

val Shapes = Shapes(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,90 @@
package com.bumble.appyx.interactions.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable

private val DarkColorPalette = darkColors(
primary = appyx_yellow1,
primaryVariant = appyx_yellow2,
secondary = appyx_yellow2,
background = appyx_dark,
surface = appyx_dark,
onPrimary = appyx_bright,
onSecondary = appyx_bright,
onBackground = appyx_bright,
onSurface = appyx_bright,

private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)

private val LightColorPalette = lightColors(
primary = appyx_yellow1,
primaryVariant = appyx_yellow2,
secondary = appyx_yellow2,
background = appyx_bright,
surface = appyx_bright,
onPrimary = appyx_dark,
onSecondary = appyx_dark,
onBackground = appyx_dark,
onSurface = appyx_dark,

private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)

@Composable
fun AppyxTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit)
{
val colors = if (darkTheme) {
DarkColorPalette
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
LightColors
} else {
LightColorPalette
DarkColors
}

MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
colorScheme = colors,
content = content
)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.bumble.appyx.interactions.theme

import androidx.compose.material.Typography
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

// Set of Material typography styles to start with
val Typography = Typography(
body1 = TextStyle(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ open class BaseAppyxComponent<InteractionTarget : Any, ModelState : Any>(
override fun updateContext(uiContext: UiContext) {
if (this.uiContext != uiContext) {
this.uiContext = uiContext
AppyxLogger.d("AppyxComponent", "new uiContext supplied: $uiContext")
AppyxLogger.d("AppyxComponent", "${this::class.simpleName} – UiContext update: $uiContext")
_visualisation = visualisation(uiContext).also {
onVisualisationReady(it)
}
Expand All @@ -176,6 +176,9 @@ open class BaseAppyxComponent<InteractionTarget : Any, ModelState : Any>(

override fun updateBounds(transitionBounds: TransitionBounds) {
if (transitionBounds != this.transitionBounds) {
with (transitionBounds) {
AppyxLogger.d("AppyxComponent", "${this::class.simpleName} – Bounds update: ${widthPx}x${heightPx}")
}
this.transitionBounds = transitionBounds
_gestureFactory = gestureFactory(transitionBounds)
_visualisation?.updateBounds(transitionBounds)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,23 @@ package com.bumble.appyx.navigation.integration
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.bumble.appyx.navigation.integration.ScreenSize.WindowSizeClass.COMPACT
import com.bumble.appyx.navigation.integration.ScreenSize.WindowSizeClass.EXPANDED
import com.bumble.appyx.navigation.integration.ScreenSize.WindowSizeClass.MEDIUM

data class ScreenSize(val widthDp: Dp, val heightDp: Dp)
data class ScreenSize(val widthDp: Dp, val heightDp: Dp) {

enum class WindowSizeClass {
COMPACT, MEDIUM, EXPANDED
}

val windowSizeClass: WindowSizeClass =
when {
widthDp < 600.dp -> COMPACT
widthDp < 840.dp -> MEDIUM
else -> EXPANDED
}
}

@Suppress("CompositionLocalAllowlist")
val LocalScreenSize = compositionLocalOf { ScreenSize(0.dp, 0.dp) }
5 changes: 4 additions & 1 deletion demos/appyx-interactions/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ dependencies {
implementation(project(":appyx-components:stable:spotlight:spotlight"))
implementation(project(":appyx-components:experimental:cards:android"))
implementation(project(":appyx-components:experimental:modal:modal"))
implementation(project(":appyx-components:experimental:promoter:android"))
implementation(project(":appyx-components:experimental:puzzle15:android"))
implementation(project(":appyx-components:internal:test-drive:android"))
implementation(project(":utils:material3"))

implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.lifecycle.java8)
implementation(libs.compose.ui.tooling)
implementation(libs.compose.ui.ui)

implementation(libs.compose.material.icons.extended)
implementation(libs.google.material)
implementation(libs.compose.material3)
}
Loading

0 comments on commit 595f6a1

Please sign in to comment.