@@ -54,6 +54,15 @@ internal sealed interface MonthMorphKey {
|
|||||||
*/
|
*/
|
||||||
data class Overflow(val date: LocalDate) : MonthMorphKey
|
data class Overflow(val date: LocalDate) : MonthMorphKey
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selected day's outline. The expanded grid draws no outline — once the
|
||||||
|
* cells carry real event bars it is one mark too many — so this pairs the
|
||||||
|
* compact grid's outline with an *invisible* stand-in over the same cell
|
||||||
|
* there. Without one it had nowhere to travel and could only fade in place
|
||||||
|
* while everything around it moved.
|
||||||
|
*/
|
||||||
|
data class Selection(val date: LocalDate) : MonthMorphKey
|
||||||
|
|
||||||
/** The grab handle, which slides from the pane's seam to the foot of the grid. */
|
/** The grab handle, which slides from the pane's seam to the foot of the grid. */
|
||||||
data object Handle : MonthMorphKey
|
data object Handle : MonthMorphKey
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,6 +591,8 @@ internal fun MonthGrid(
|
|||||||
state: MonthUiState.Success,
|
state: MonthUiState.Success,
|
||||||
showWeekNumbers: Boolean,
|
showWeekNumbers: Boolean,
|
||||||
onOpenDay: (LocalDate) -> Unit,
|
onOpenDay: (LocalDate) -> Unit,
|
||||||
|
/** See [MonthWeekRow]'s `selected`: an anchor for the morph, never a mark. */
|
||||||
|
selected: LocalDate? = null,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -609,6 +611,7 @@ internal fun MonthGrid(
|
|||||||
inMonth = { it.month == month.month && it.year == month.year },
|
inMonth = { it.month == month.month && it.year == month.year },
|
||||||
showWeekNumbers = showWeekNumbers,
|
showWeekNumbers = showWeekNumbers,
|
||||||
onOpenDay = onOpenDay,
|
onOpenDay = onOpenDay,
|
||||||
|
selected = selected,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
@@ -1003,6 +1006,7 @@ private fun SplitMonthBody(
|
|||||||
if (expanded) {
|
if (expanded) {
|
||||||
SplitMonthExpanded(
|
SplitMonthExpanded(
|
||||||
state = state,
|
state = state,
|
||||||
|
selected = selected,
|
||||||
slideDir = slideDir,
|
slideDir = slideDir,
|
||||||
showWeekNumbers = showWeekNumbers,
|
showWeekNumbers = showWeekNumbers,
|
||||||
swipeModifier = swipeModifier,
|
swipeModifier = swipeModifier,
|
||||||
@@ -1109,6 +1113,8 @@ private fun SplitMonthCollapsed(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SplitMonthExpanded(
|
private fun SplitMonthExpanded(
|
||||||
state: MonthUiState.Success,
|
state: MonthUiState.Success,
|
||||||
|
/** Marked nowhere here; it only anchors the outline's morph. */
|
||||||
|
selected: LocalDate,
|
||||||
slideDir: Int,
|
slideDir: Int,
|
||||||
showWeekNumbers: Boolean,
|
showWeekNumbers: Boolean,
|
||||||
swipeModifier: Modifier,
|
swipeModifier: Modifier,
|
||||||
@@ -1123,18 +1129,19 @@ private fun SplitMonthExpanded(
|
|||||||
// of it, and the handle is the obvious thing to reach for on the way back.
|
// of it, and the handle is the obvious thing to reach for on the way back.
|
||||||
Column(Modifier.fillMaxSize().then(swipeModifier)) {
|
Column(Modifier.fillMaxSize().then(swipeModifier)) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = state,
|
targetState = state to selected,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
contentKey = { it.month },
|
contentKey = { (s, _) -> s.month },
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
calendarSlideTransition(slideDir, slideSpec, fadeSpec, reduceMotion)
|
calendarSlideTransition(slideDir, slideSpec, fadeSpec, reduceMotion)
|
||||||
},
|
},
|
||||||
label = "split-expanded-month-transition",
|
label = "split-expanded-month-transition",
|
||||||
) { s ->
|
) { (s, sel) ->
|
||||||
MonthGrid(
|
MonthGrid(
|
||||||
state = s,
|
state = s,
|
||||||
showWeekNumbers = showWeekNumbers,
|
showWeekNumbers = showWeekNumbers,
|
||||||
onOpenDay = onPickDay,
|
onOpenDay = onPickDay,
|
||||||
|
selected = sel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SplitExpandHandle(expanded = true, onToggle = onCollapse)
|
SplitExpandHandle(expanded = true, onToggle = onCollapse)
|
||||||
@@ -1350,12 +1357,21 @@ private fun SplitDayCell(
|
|||||||
// is laid out where the collapsed cell actually is and simply fades in,
|
// is laid out where the collapsed cell actually is and simply fades in,
|
||||||
// which is the only size it is ever true at.
|
// which is the only size it is ever true at.
|
||||||
//
|
//
|
||||||
// It can stay plain content now that nothing renders into an overlay above
|
// Tagged only while it is the selected day, and paired with an invisible
|
||||||
// the tree — see [morphElement]. It sits above its own pill by ordinary
|
// stand-in over the same cell in the expanded grid, so it travels with its
|
||||||
// z-order and fades with the page it belongs to, in step with the rest.
|
// cell instead of fading in place. Tagged unconditionally it would put a
|
||||||
|
// key on all 42 cells against the one partner the expanded grid offers,
|
||||||
|
// and the other 41 would fly in from the layout origin.
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.then(
|
||||||
|
if (isSelected) {
|
||||||
|
Modifier.morphBounds(MonthMorphKey.Selection(date))
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
},
|
||||||
|
)
|
||||||
.drawBehind {
|
.drawBehind {
|
||||||
val alpha = selection.value
|
val alpha = selection.value
|
||||||
if (alpha <= 0f) return@drawBehind
|
if (alpha <= 0f) return@drawBehind
|
||||||
@@ -1647,6 +1663,13 @@ private fun MonthWeekRow(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
blankOutside: Boolean = false,
|
blankOutside: Boolean = false,
|
||||||
labelMonthOnFirst: Boolean = false,
|
labelMonthOnFirst: Boolean = false,
|
||||||
|
/**
|
||||||
|
* The selected day, when there is one. Draws *nothing* — it only places the
|
||||||
|
* invisible counterpart the compact grid's outline morphs against. Null for
|
||||||
|
* every style but the split style's expanded form, which composes no extra
|
||||||
|
* layer at all.
|
||||||
|
*/
|
||||||
|
selected: LocalDate? = null,
|
||||||
) {
|
) {
|
||||||
val dark = isSystemInDarkTheme()
|
val dark = isSystemInDarkTheme()
|
||||||
val laneCount = (week.spans.maxOfOrNull { it.lane } ?: -1) + 1
|
val laneCount = (week.spans.maxOfOrNull { it.lane } ?: -1) + 1
|
||||||
@@ -1846,6 +1869,29 @@ private fun MonthWeekRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invisible stand-in for the compact grid's selection outline, so it
|
||||||
|
// has somewhere to travel to and from. Its own layer rather than a
|
||||||
|
// child of the cell pill: that pill is itself a tagged piece, and a
|
||||||
|
// tag nested inside a tag travels twice. Padded to match the outline's
|
||||||
|
// own bounds over there, or it would arrive at the wrong size.
|
||||||
|
if (selected != null) {
|
||||||
|
Row(Modifier.matchParentSize()) {
|
||||||
|
week.days.forEach { d ->
|
||||||
|
if (d == selected && inMonth(d)) {
|
||||||
|
Box(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.padding(horizontal = CELL_GAP, vertical = 1.dp)
|
||||||
|
.morphBounds(MonthMorphKey.Selection(d)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Spacer(Modifier.weight(1f).fillMaxHeight())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tap layer: in month view a tap on any day opens that day. Padded and
|
// Tap layer: in month view a tap on any day opens that day. Padded and
|
||||||
// clipped to the background pill so the ripple matches it. A blanked
|
// clipped to the background pill so the ripple matches it. A blanked
|
||||||
// cell isn't part of this month, so it takes no taps either.
|
// cell isn't part of this month, so it takes no taps either.
|
||||||
|
|||||||
Reference in New Issue
Block a user