Replace button-triggered collapse with vertical drag gesture
ViewModel now exposes onDrag/onDragEnd so BottomCard drives the collapse animation via detectVerticalDragGestures instead of a discrete collapse() call. Drag maps to 0–1 progress over 200dp; on release the animatable snaps to the nearest end with spring. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
35cbcaf430
commit
b734e26645
@ -1,6 +1,7 @@
|
|||||||
package plus.rua.project
|
package plus.rua.project
|
||||||
|
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@ -42,33 +43,29 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
|
|||||||
selectedDate = date
|
selectedDate = date
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collapse() {
|
fun onDrag(delta: Float) {
|
||||||
if (isCollapsed) return
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
_collapseAnimatable.animateTo(
|
val new = (_collapseAnimatable.value + delta).coerceIn(0f, 1f)
|
||||||
targetValue = 1f,
|
_collapseAnimatable.snapTo(new)
|
||||||
animationSpec = androidx.compose.animation.core.spring(
|
|
||||||
dampingRatio = 0.8f,
|
|
||||||
stiffness = 400f
|
|
||||||
)
|
|
||||||
)
|
|
||||||
isCollapsed = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expand() {
|
fun onDragEnd() {
|
||||||
if (!isCollapsed) return
|
|
||||||
isCollapsed = false
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
|
if (_collapseAnimatable.value > 0.5f) {
|
||||||
|
_collapseAnimatable.animateTo(
|
||||||
|
targetValue = 1f,
|
||||||
|
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||||
|
)
|
||||||
|
isCollapsed = true
|
||||||
|
} else {
|
||||||
_collapseAnimatable.animateTo(
|
_collapseAnimatable.animateTo(
|
||||||
targetValue = 0f,
|
targetValue = 0f,
|
||||||
animationSpec = androidx.compose.animation.core.spring(
|
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||||
dampingRatio = 0.8f,
|
|
||||||
stiffness = 400f
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getIsoWeekNumber(date: LocalDate): Int {
|
fun getIsoWeekNumber(date: LocalDate): Int {
|
||||||
val jan4 = LocalDate(date.year, 1, 4)
|
val jan4 = LocalDate(date.year, 1, 4)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package plus.rua.project.ui
|
package plus.rua.project.ui
|
||||||
|
|
||||||
import androidx.compose.animation.core.animate
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.detectVerticalDragGestures
|
import androidx.compose.foundation.gestures.detectVerticalDragGestures
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -12,14 +11,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import plus.rua.project.CalendarViewModel
|
import plus.rua.project.CalendarViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -27,16 +25,20 @@ fun BottomCard(
|
|||||||
viewModel: CalendarViewModel,
|
viewModel: CalendarViewModel,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val density = LocalDensity.current
|
||||||
|
val dragRange = with(density) { 200.dp.toPx() }
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.pointerInput(Unit) {
|
.pointerInput(viewModel.isCollapsed) {
|
||||||
detectVerticalDragGestures { _, dragAmount ->
|
if (viewModel.isCollapsed) return@pointerInput
|
||||||
if (dragAmount < 0 && !viewModel.isCollapsed) {
|
detectVerticalDragGestures(
|
||||||
viewModel.collapse()
|
onDragEnd = { viewModel.onDragEnd() },
|
||||||
}
|
onDragCancel = { viewModel.onDragEnd() }
|
||||||
|
) { _, dragAmount ->
|
||||||
|
val delta = -dragAmount / dragRange
|
||||||
|
viewModel.onDrag(delta)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user