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 25 additions and 1 deletions
Showing only changes of commit 1e9581528e - Show all commits

View File

@@ -66,6 +66,25 @@ internal class MonthMorphScope(
internal val LocalMonthMorph = compositionLocalOf<MonthMorphScope?> { null }
/**
* True while the grid is mid-morph, for content that has to stop clipping to let
* the travelling pieces through.
*
* A mark's two homes are in *different rows*: the dot for the 20th sits a third
* of the way down the compact grid, its bar most of the way down the expanded
* one. Clipped to the row it is arriving at, a mark spends the first half of its
* journey outside those bounds and is simply not drawn — so it appears from
* nowhere, part-way through, already near its destination. Rendering in place
* rather than in an overlay is what subjects them to that clip, and is worth
* keeping; the clip is what has to yield, and only while pieces are in flight.
*/
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
internal fun morphInFlight(): Boolean {
val morph = LocalMonthMorph.current ?: return false
return morph.shared.isTransitionActive
}
/**
* 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.

View File

@@ -1647,6 +1647,7 @@ private fun MonthWeekRow(
val dark = isSystemInDarkTheme()
val laneCount = (week.spans.maxOfOrNull { it.lane } ?: -1) + 1
val shownLanes = laneCount.coerceAtMost(MAX_EVENT_ROWS)
val morphing = morphInFlight()
Row(modifier) {
// Optional calendar-week gutter, sized so the seven day columns below
@@ -1720,11 +1721,15 @@ private fun MonthWeekRow(
// Breathing room between the day number (and today's circle) and the
// first event row.
Spacer(Modifier.height(DAY_NUMBER_GAP))
// Clipped at rest so a bar can't spill into the row below, but not
// while a morph is in flight: a mark travels between two different
// rows, and clipping it to the one it is arriving at hides it for
// the whole first half of the journey. See [morphInFlight].
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.clipToBounds(),
.then(if (morphing) Modifier else Modifier.clipToBounds()),
) {
// Spanning bars on their shared lanes.
week.spans.filter { it.lane < shownLanes }.forEach { span ->