Extract calendar utilities and use derivedStateOf for reactive state
Move shared constants and helper functions into CalendarUtils.kt, replace manual state synchronization with derivedStateOf in CalendarViewModel and UI composables. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3612efb665
commit
ddc852a667
@ -16,6 +16,7 @@ import kotlinx.datetime.number
|
|||||||
import kotlinx.datetime.plus
|
import kotlinx.datetime.plus
|
||||||
import kotlinx.datetime.todayIn
|
import kotlinx.datetime.todayIn
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
|
import plus.rua.project.ui.COLLAPSE_THRESHOLD
|
||||||
|
|
||||||
data class CalendarDay(
|
data class CalendarDay(
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
@ -56,11 +57,11 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拖拽超过 50% 时自动折叠到周视图,否则回弹到月视图
|
// 拖拽超过阈值时自动折叠到周视图,否则回弹到月视图
|
||||||
fun onDragEnd() {
|
fun onDragEnd() {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val current = _collapseAnimatable.value
|
val current = _collapseAnimatable.value
|
||||||
if (current > 0.5f) {
|
if (current > COLLAPSE_THRESHOLD) {
|
||||||
_collapseAnimatable.animateTo(
|
_collapseAnimatable.animateTo(
|
||||||
targetValue = 1f,
|
targetValue = 1f,
|
||||||
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||||
@ -83,11 +84,11 @@ class CalendarViewModel(private val coroutineScope: CoroutineScope) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下拉超过 50% 时自动展开到月视图,否则回弹到周视图
|
// 下拉超过阈值时自动展开到月视图,否则回弹到周视图
|
||||||
fun onExpandDragEnd() {
|
fun onExpandDragEnd() {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
val current = _collapseAnimatable.value
|
val current = _collapseAnimatable.value
|
||||||
if (current < 0.5f) {
|
if (current < COLLAPSE_THRESHOLD) {
|
||||||
_collapseAnimatable.animateTo(
|
_collapseAnimatable.animateTo(
|
||||||
targetValue = 0f,
|
targetValue = 0f,
|
||||||
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
animationSpec = spring(dampingRatio = 0.8f, stiffness = 400f)
|
||||||
|
|||||||
@ -32,7 +32,7 @@ fun BottomCard(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val dragRange = with(density) { 200.dp.toPx() }
|
val dragRange = with(density) { DRAG_RANGE_DP.dp.toPx() }
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|||||||
@ -111,7 +111,7 @@ fun CalendarMonthPage(
|
|||||||
else Modifier
|
else Modifier
|
||||||
)
|
)
|
||||||
.offset(y = yOffsetDp)
|
.offset(y = yOffsetDp)
|
||||||
.padding(vertical = 4.dp)
|
.padding(vertical = ROW_PADDING_DP.dp)
|
||||||
.then(
|
.then(
|
||||||
if (weekIndex == 0 && rowHeightPx == 0) {
|
if (weekIndex == 0 && rowHeightPx == 0) {
|
||||||
Modifier.onSizeChanged { size ->
|
Modifier.onSizeChanged { size ->
|
||||||
|
|||||||
@ -31,10 +31,6 @@ import kotlin.math.abs
|
|||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
import plus.rua.project.CalendarViewModel
|
import plus.rua.project.CalendarViewModel
|
||||||
|
|
||||||
private const val START_PAGE = Int.MAX_VALUE / 2
|
|
||||||
private const val ROW_PADDING_DP = 4
|
|
||||||
private const val HORIZONTAL_PADDING_DP = 16
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日历主界面,包含月/周视图切换和折叠动画。
|
* 日历主界面,包含月/周视图切换和折叠动画。
|
||||||
*
|
*
|
||||||
@ -50,8 +46,8 @@ fun CalendarMonthView(
|
|||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val viewModel = remember { CalendarViewModel(coroutineScope) }
|
val viewModel = remember { CalendarViewModel(coroutineScope) }
|
||||||
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
|
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
|
||||||
var currentYear by remember { mutableIntStateOf(viewModel.currentYear) }
|
val currentYear by remember { derivedStateOf { viewModel.selectedDate.year } }
|
||||||
var currentMonth by remember { mutableIntStateOf(viewModel.currentMonth) }
|
val currentMonth by remember { derivedStateOf { viewModel.selectedDate.month.number } }
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
|
|
||||||
var monthHeaderHeightPx by remember { mutableIntStateOf(0) }
|
var monthHeaderHeightPx by remember { mutableIntStateOf(0) }
|
||||||
@ -66,24 +62,19 @@ fun CalendarMonthView(
|
|||||||
val headerHeightPx = monthHeaderHeightPx + weekdayHeaderHeightPx
|
val headerHeightPx = monthHeaderHeightPx + weekdayHeaderHeightPx
|
||||||
val rowPaddingPx = with(density) { ROW_PADDING_DP.dp.toPx() }.toInt()
|
val rowPaddingPx = with(density) { ROW_PADDING_DP.dp.toPx() }.toInt()
|
||||||
|
|
||||||
// 滑动偏移插值行数
|
val interpolatedWeeks by remember {
|
||||||
// 以 currentPage 为基准页,offsetFraction 表示基准页与可视区域左边缘的偏移:
|
derivedStateOf {
|
||||||
// offsetFraction > 0:基准页偏右,可视区域露出下一页(page+1)
|
val fraction = pagerState.currentPageOffsetFraction
|
||||||
// offsetFraction < 0:基准页偏左,可视区域露出上一页(page-1)
|
if (abs(fraction) > OFFSET_FRACTION_THRESHOLD) {
|
||||||
// 过渡进度 = abs(offsetFraction),目标页 = page ± 1。
|
val cp = pagerState.currentPage
|
||||||
// 当 currentPage 跳变(如从 Jul 跳到 Aug),基准页行数也随之跳变,
|
val baseWeeks = calculateWeeksCountForPage(cp, today)
|
||||||
// 但 abs(offsetFraction) 同时从 ~0.5 降到 ~0.5(连续),所以插值结果连续:
|
val targetPage = cp + if (fraction > 0) 1 else -1
|
||||||
// 跳变前: cp=Jul(5行), off=+0.49 → base=5, target=Aug(6), lerp(5,6,0.49)=5.49
|
val targetWeeks = calculateWeeksCountForPage(targetPage, today)
|
||||||
// 跳变后: cp=Aug(6行), off=-0.47 → base=6, target=Jul(5), lerp(6,5,0.47)=5.47 ← 连续!
|
lerp(baseWeeks.toFloat(), targetWeeks.toFloat(), abs(fraction))
|
||||||
val offsetFraction by remember { derivedStateOf { pagerState.currentPageOffsetFraction } }
|
} else {
|
||||||
val interpolatedWeeks = if (abs(offsetFraction) > 0.01f) {
|
calculateWeeksCountForPage(pagerState.currentPage, today).toFloat()
|
||||||
val cp = pagerState.currentPage
|
}
|
||||||
val baseWeeks = calculateWeeksCountForPage(cp, today)
|
}
|
||||||
val targetPage = cp + if (offsetFraction > 0) 1 else -1
|
|
||||||
val targetWeeks = calculateWeeksCountForPage(targetPage, today)
|
|
||||||
lerp(baseWeeks.toFloat(), targetWeeks.toFloat(), abs(offsetFraction))
|
|
||||||
} else {
|
|
||||||
calculateWeeksCountForPage(pagerState.currentPage, today).toFloat()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预估行高:DayCell aspectRatio=1,宽度 = (screenWidth - horizontalPadding) / 7
|
// 预估行高:DayCell aspectRatio=1,宽度 = (screenWidth - horizontalPadding) / 7
|
||||||
@ -100,14 +91,18 @@ fun CalendarMonthView(
|
|||||||
// gridH = rowH × (1 + (weeks-1) × (1-p))
|
// gridH = rowH × (1 + (weeks-1) × (1-p))
|
||||||
val effectiveWeeks = interpolatedWeeks
|
val effectiveWeeks = interpolatedWeeks
|
||||||
|
|
||||||
val gridHeightPx = if (effectiveRowHeightPx > 0) {
|
val gridHeightPx by remember {
|
||||||
val rowH = effectiveRowHeightPx.toFloat()
|
derivedStateOf {
|
||||||
if (p > 0.01f) {
|
if (effectiveRowHeightPx > 0) {
|
||||||
(rowH * (1 + (effectiveWeeks - 1) * (1f - p))).toInt()
|
val rowH = effectiveRowHeightPx.toFloat()
|
||||||
} else {
|
if (p > OFFSET_FRACTION_THRESHOLD) {
|
||||||
(rowH * effectiveWeeks).toInt()
|
(rowH * (1 + (effectiveWeeks - 1) * (1f - p))).toInt()
|
||||||
|
} else {
|
||||||
|
(rowH * effectiveWeeks).toInt()
|
||||||
|
}
|
||||||
|
} else 0
|
||||||
}
|
}
|
||||||
} else 0
|
}
|
||||||
|
|
||||||
val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx
|
val calendarAreaHeightPx = headerHeightPx + gridHeightPx + rowPaddingPx
|
||||||
val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0
|
val cardHeightPx = if (screenHeightPx > 0 && calendarAreaHeightPx > 0) screenHeightPx - calendarAreaHeightPx else 0
|
||||||
@ -154,8 +149,6 @@ fun CalendarMonthView(
|
|||||||
val weekSunday = weekMonday.plus(DatePeriod(days = 6))
|
val weekSunday = weekMonday.plus(DatePeriod(days = 6))
|
||||||
val date = if (today in weekMonday..weekSunday) today else weekMonday
|
val date = if (today in weekMonday..weekSunday) today else weekMonday
|
||||||
viewModel.selectDate(date)
|
viewModel.selectDate(date)
|
||||||
currentYear = date.year
|
|
||||||
currentMonth = date.month.number
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -167,14 +160,12 @@ fun CalendarMonthView(
|
|||||||
val date = if (year == today.year && today.month.number == month) today
|
val date = if (year == today.year && today.month.number == month) today
|
||||||
else LocalDate(year, month, 1)
|
else LocalDate(year, month, 1)
|
||||||
viewModel.selectDate(date)
|
viewModel.selectDate(date)
|
||||||
currentYear = year
|
|
||||||
currentMonth = month
|
|
||||||
},
|
},
|
||||||
collapseProgress = viewModel.collapseProgress,
|
collapseProgress = viewModel.collapseProgress,
|
||||||
rowHeightPx = rowHeightPx,
|
rowHeightPx = rowHeightPx,
|
||||||
effectiveWeeks = effectiveWeeks,
|
effectiveWeeks = effectiveWeeks,
|
||||||
onRowHeightMeasured = { h ->
|
onRowHeightMeasured = { h ->
|
||||||
if (h > 0 && rowHeightPx == 0) rowHeightPx = h
|
if (h > 0) rowHeightPx = h
|
||||||
},
|
},
|
||||||
pagerState = pagerState,
|
pagerState = pagerState,
|
||||||
modifier = pagerModifier
|
modifier = pagerModifier
|
||||||
@ -193,15 +184,3 @@ fun CalendarMonthView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lerp(start: Float, end: Float, fraction: Float): Float = start + (end - start) * fraction
|
|
||||||
|
|
||||||
private fun calculateWeeksCountForPage(page: Int, today: LocalDate): Int {
|
|
||||||
val initialYear = today.year
|
|
||||||
val initialMonth = today.month.number
|
|
||||||
val offset = page - START_PAGE
|
|
||||||
val totalMonths = initialYear * 12 + (initialMonth - 1) + offset
|
|
||||||
val year = totalMonths / 12
|
|
||||||
val month = totalMonths % 12 + 1
|
|
||||||
return calculateWeeksCount(year, month)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -11,14 +11,9 @@ import androidx.compose.runtime.snapshotFlow
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.datetime.DatePeriod
|
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.datetime.minus
|
|
||||||
import kotlinx.datetime.number
|
import kotlinx.datetime.number
|
||||||
|
|
||||||
/** 无限分页中心页,用于 HorizontalPager 的起始位置 */
|
|
||||||
private const val START_PAGE = Int.MAX_VALUE / 2
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 月度日历分页器,HorizontalPager 实现无限左右滑动切换月份。
|
* 月度日历分页器,HorizontalPager 实现无限左右滑动切换月份。
|
||||||
*
|
*
|
||||||
@ -43,13 +38,14 @@ fun CalendarPager(
|
|||||||
pagerState: PagerState,
|
pagerState: PagerState,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val initialYearMonth = remember { today.toYearMonth() }
|
val initialYear = remember { today.year }
|
||||||
|
val initialMonth = remember { today.month.number }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
// Sync settled page to onMonthChanged (skip initial emission to preserve "today" selection)
|
// Sync settled page to onMonthChanged (skip initial emission to preserve "today" selection)
|
||||||
LaunchedEffect(pagerState) {
|
LaunchedEffect(pagerState) {
|
||||||
snapshotFlow { pagerState.settledPage }.drop(1).collect { page ->
|
snapshotFlow { pagerState.settledPage }.drop(1).collect { page ->
|
||||||
val yearMonth = pageToYearMonth(page, initialYearMonth)
|
val yearMonth = pageToYearMonth(page, initialYear, initialMonth)
|
||||||
onMonthChanged(yearMonth.first, yearMonth.second)
|
onMonthChanged(yearMonth.first, yearMonth.second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,7 +56,7 @@ fun CalendarPager(
|
|||||||
flingBehavior = PagerDefaults.flingBehavior(state = pagerState),
|
flingBehavior = PagerDefaults.flingBehavior(state = pagerState),
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) { page ->
|
) { page ->
|
||||||
val (year, month) = pageToYearMonth(page, initialYearMonth)
|
val (year, month) = pageToYearMonth(page, initialYear, initialMonth)
|
||||||
CalendarMonthPage(
|
CalendarMonthPage(
|
||||||
year = year,
|
year = year,
|
||||||
month = month,
|
month = month,
|
||||||
@ -69,9 +65,10 @@ fun CalendarPager(
|
|||||||
onDateClick = { date ->
|
onDateClick = { date ->
|
||||||
onDateClick(date)
|
onDateClick(date)
|
||||||
// If clicking a date in a different month, scroll to that page
|
// If clicking a date in a different month, scroll to that page
|
||||||
val clickedYearMonth = date.toYearMonth()
|
val clickedYear = date.year
|
||||||
if (clickedYearMonth != pageToYearMonth(page, initialYearMonth)) {
|
val clickedMonth = date.month.number
|
||||||
val targetPage = yearMonthToPage(clickedYearMonth, initialYearMonth)
|
if (clickedYear != year || clickedMonth != month) {
|
||||||
|
val targetPage = yearMonthToPage(clickedYear, clickedMonth, initialYear, initialMonth)
|
||||||
if (targetPage != pagerState.currentPage) {
|
if (targetPage != pagerState.currentPage) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
pagerState.animateScrollToPage(targetPage)
|
pagerState.animateScrollToPage(targetPage)
|
||||||
@ -86,28 +83,3 @@ fun CalendarPager(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LocalDate.toYearMonth(): Pair<Int, Int> = Pair(year, month.number)
|
|
||||||
|
|
||||||
// 页码→年月:偏移量 + 初始月份的绝对月数,再拆分回年月
|
|
||||||
private fun pageToYearMonth(page: Int, initial: Pair<Int, Int>): Pair<Int, Int> {
|
|
||||||
val offset = page - START_PAGE
|
|
||||||
val totalMonths = initial.first * 12 + (initial.second - 1) + offset
|
|
||||||
return Pair(totalMonths / 12, totalMonths % 12 + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 年月→页码:目标与初始的绝对月数差 + 起始页
|
|
||||||
private fun yearMonthToPage(yearMonth: Pair<Int, Int>, initial: Pair<Int, Int>): Int {
|
|
||||||
val targetTotal = yearMonth.first * 12 + (yearMonth.second - 1)
|
|
||||||
val initialTotal = initial.first * 12 + (initial.second - 1)
|
|
||||||
return START_PAGE + (targetTotal - initialTotal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算月份在日历网格中需要的行数(4/5/6)
|
|
||||||
internal fun calculateWeeksCount(year: Int, month: Int): Int {
|
|
||||||
val firstOfMonth = LocalDate(year, month, 1)
|
|
||||||
val offset = firstOfMonth.dayOfWeek.ordinal
|
|
||||||
val nextMonth = if (month == 12) LocalDate(year + 1, 1, 1) else LocalDate(year, month + 1, 1)
|
|
||||||
val daysInMonth = nextMonth.minus(DatePeriod(days = 1)).day
|
|
||||||
return ((offset + daysInMonth - 1) / 7) + 1
|
|
||||||
}
|
|
||||||
|
|||||||
@ -0,0 +1,86 @@
|
|||||||
|
package plus.rua.project.ui
|
||||||
|
|
||||||
|
import kotlinx.datetime.DatePeriod
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
import kotlinx.datetime.minus
|
||||||
|
import kotlinx.datetime.number
|
||||||
|
import kotlinx.datetime.plus
|
||||||
|
|
||||||
|
/** 无限分页中心页,用于 HorizontalPager 的起始位置 */
|
||||||
|
const val START_PAGE = Int.MAX_VALUE / 2
|
||||||
|
|
||||||
|
/** 折叠判定阈值:progress > 此值时折叠,< 此值时展开 */
|
||||||
|
const val COLLAPSE_THRESHOLD = 0.5f
|
||||||
|
|
||||||
|
/** 滑动偏移插值阈值:abs(offsetFraction) > 此值时启用插值 */
|
||||||
|
const val OFFSET_FRACTION_THRESHOLD = 0.01f
|
||||||
|
|
||||||
|
/** 行内 vertical padding (dp) */
|
||||||
|
const val ROW_PADDING_DP = 4
|
||||||
|
|
||||||
|
/** 日历网格水平 padding (dp) */
|
||||||
|
const val HORIZONTAL_PADDING_DP = 16
|
||||||
|
|
||||||
|
/** BottomCard 拖拽手势范围 (dp) */
|
||||||
|
const val DRAG_RANGE_DP = 200
|
||||||
|
|
||||||
|
/** 线性插值 */
|
||||||
|
fun lerp(start: Float, end: Float, fraction: Float): Float = start + (end - start) * fraction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算月份在日历网格中需要的行数(4/5/6)。
|
||||||
|
*/
|
||||||
|
fun calculateWeeksCount(year: Int, month: Int): Int {
|
||||||
|
val firstOfMonth = LocalDate(year, month, 1)
|
||||||
|
val offset = firstOfMonth.dayOfWeek.ordinal
|
||||||
|
val nextMonth = if (month == 12) LocalDate(year + 1, 1, 1) else LocalDate(year, month + 1, 1)
|
||||||
|
val daysInMonth = nextMonth.minus(DatePeriod(days = 1)).day
|
||||||
|
return ((offset + daysInMonth - 1) / 7) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 pager 页码计算该页月份的行数。
|
||||||
|
*/
|
||||||
|
fun calculateWeeksCountForPage(page: Int, today: LocalDate): Int {
|
||||||
|
val initialYear = today.year
|
||||||
|
val initialMonth = today.month.number
|
||||||
|
val offset = page - START_PAGE
|
||||||
|
val totalMonths = initialYear * 12 + (initialMonth - 1) + offset
|
||||||
|
val year = totalMonths / 12
|
||||||
|
val month = totalMonths % 12 + 1
|
||||||
|
return calculateWeeksCount(year, month)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页码转年月。
|
||||||
|
*/
|
||||||
|
fun pageToYearMonth(page: Int, initialYear: Int, initialMonth: Int): Pair<Int, Int> {
|
||||||
|
val offset = page - START_PAGE
|
||||||
|
val totalMonths = initialYear * 12 + (initialMonth - 1) + offset
|
||||||
|
return Pair(totalMonths / 12, totalMonths % 12 + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 年月转页码。
|
||||||
|
*/
|
||||||
|
fun yearMonthToPage(year: Int, month: Int, initialYear: Int, initialMonth: Int): Int {
|
||||||
|
val targetTotal = year * 12 + (month - 1)
|
||||||
|
val initialTotal = initialYear * 12 + (initialMonth - 1)
|
||||||
|
return START_PAGE + (targetTotal - initialTotal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日期所在周的周一。
|
||||||
|
*/
|
||||||
|
fun LocalDate.toWeekMonday(): LocalDate {
|
||||||
|
val dayOfWeekOrdinal = dayOfWeek.ordinal
|
||||||
|
return minus(DatePeriod(days = dayOfWeekOrdinal))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 pager 页码计算该页对应的周周一日期。
|
||||||
|
*/
|
||||||
|
fun pageToWeekMonday(page: Int, initial: LocalDate): LocalDate {
|
||||||
|
val offset = page - START_PAGE
|
||||||
|
return initial.plus(DatePeriod(days = offset * 7))
|
||||||
|
}
|
||||||
@ -9,20 +9,14 @@ import androidx.compose.foundation.pager.rememberPagerState
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.datetime.DatePeriod
|
import kotlinx.datetime.DatePeriod
|
||||||
import kotlinx.datetime.LocalDate
|
import kotlinx.datetime.LocalDate
|
||||||
import kotlinx.datetime.minus
|
|
||||||
import kotlinx.datetime.plus
|
import kotlinx.datetime.plus
|
||||||
|
|
||||||
/** 无限分页中心页,用于 HorizontalPager 的起始位置 */
|
|
||||||
private const val START_PAGE = Int.MAX_VALUE / 2
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 周视图分页器,折叠状态下显示选中日期所在周,支持左右滑动切换周。
|
* 周视图分页器,折叠状态下显示选中日期所在周,支持左右滑动切换周。
|
||||||
*
|
*
|
||||||
@ -79,13 +73,3 @@ fun WeekPager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LocalDate.toWeekMonday(): LocalDate {
|
|
||||||
val dayOfWeekOrdinal = dayOfWeek.ordinal // Monday=0 ... Sunday=6
|
|
||||||
return minus(DatePeriod(days = dayOfWeekOrdinal))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pageToWeekMonday(page: Int, initial: LocalDate): LocalDate {
|
|
||||||
val offset = page - START_PAGE
|
|
||||||
return initial.plus(DatePeriod(days = offset * 7))
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user