feat(month): month view style picker + Split pull-to-expand (#38, #53) #90

Merged
makiolaj merged 36 commits from feat/month-view-style into release/v2.16.0 2026-07-20 21:25:50 +00:00
2 changed files with 2 additions and 28 deletions
Showing only changes of commit 01a6c8bab8 - Show all commits

View File

@@ -1,18 +1,13 @@
package de.jeanlucmakiola.calendula.ui.month package de.jeanlucmakiola.calendula.ui.month
import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.BoundsTransform
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
/** /**
@@ -61,22 +56,6 @@ internal class MonthMorphScope(
internal val LocalMonthMorph = compositionLocalOf<MonthMorphScope?> { null } internal val LocalMonthMorph = compositionLocalOf<MonthMorphScope?> { null }
/**
* How fast a tagged piece travels between its two homes.
*
* The *fast* spatial spec, not the default one the month/week/day page slide
* uses. Paging is a spring under one whole page and wants room to settle; this is
* a direct answer to a drag the user has already committed to, and at the default
* pace the grid felt like it was catching up rather than responding. It is also
* a hundred-odd pieces moving at once, where a long settle reads as lag.
*/
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
internal fun rememberMorphBoundsTransform(): BoundsTransform {
val spec = MaterialTheme.motionScheme.fastSpatialSpec<Rect>()
return remember(spec) { BoundsTransform { _, _ -> spec } }
}
/** /**
* Tag content that is *the same thing* on both sides — a background pill, a day * Tag content that is *the same thing* on both sides — a background pill, a day
* number — so it animates between its two positions and sizes. * number — so it animates between its two positions and sizes.
@@ -89,7 +68,6 @@ internal fun Modifier.morphElement(key: MonthMorphKey): Modifier {
this@morphElement.sharedElement( this@morphElement.sharedElement(
sharedContentState = rememberSharedContentState(key), sharedContentState = rememberSharedContentState(key),
animatedVisibilityScope = morph.visibility, animatedVisibilityScope = morph.visibility,
boundsTransform = rememberMorphBoundsTransform(),
) )
} }
} }
@@ -114,7 +92,6 @@ internal fun Modifier.morphBounds(key: MonthMorphKey): Modifier {
animatedVisibilityScope = morph.visibility, animatedVisibilityScope = morph.visibility,
enter = fadeIn(), enter = fadeIn(),
exit = fadeOut(), exit = fadeOut(),
boundsTransform = rememberMorphBoundsTransform(),
resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds, resizeMode = SharedTransitionScope.ResizeMode.RemeasureToBounds,
) )
} }

View File

@@ -47,7 +47,6 @@ import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.DrawerValue import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -909,7 +908,7 @@ private val MONTH_EXPAND_THRESHOLD = 48.dp
* screen to explain why. [rememberSaveable] carries it across a rotation, which * screen to explain why. [rememberSaveable] carries it across a rotation, which
* is as long as it should live. * is as long as it should live.
*/ */
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
private fun SplitMonthContent( private fun SplitMonthContent(
state: MonthUiState, state: MonthUiState,
@@ -924,9 +923,7 @@ private fun SplitMonthContent(
onEventClick: (EventInstance) -> Unit, onEventClick: (EventInstance) -> Unit,
onCreateEvent: (LocalDate) -> Unit, onCreateEvent: (LocalDate) -> Unit,
) { ) {
// The fast effects spec, matching the morph's fast spatial one — the page val fadeSpec = rememberCalendarFadeSpec()
// slide's default pace made the swap trail behind a gesture already committed.
val fadeSpec = MaterialTheme.motionScheme.fastEffectsSpec<Float>()
val reduceMotion = rememberReduceMotion() val reduceMotion = rememberReduceMotion()
var expanded by rememberSaveable { mutableStateOf(false) } var expanded by rememberSaveable { mutableStateOf(false) }
// Back collapses before it does anything else. Expanding replaces the whole // Back collapses before it does anything else. Expanding replaces the whole