xfy f618d09458 Reformat code style across shared module
Apply consistent formatting: import ordering, line wrapping,
indentation, and XML normalization. No functional changes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 17:57:18 +08:00

133 lines
4.3 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 > 此值触发,展开时 progress < (1-此值) 触发 */
const val COLLAPSE_THRESHOLD = 0.25f
/** 滑动偏移插值阈值abs(offsetFraction) > 此值时启用插值 */
const val OFFSET_FRACTION_THRESHOLD = 0.01f
/** 行内 vertical padding (dp) */
const val ROW_PADDING_DP = 6
/** 日历网格水平 padding (dp) */
const val HORIZONTAL_PADDING_DP = 16
/** BottomCard 拖拽手势范围最小值 (dp),防止行数少时 dragRange 过小 */
const val DRAG_RANGE_MIN_DP = 100
/** fling 速度阈值 (dp/s),超过此速度按方向直接折叠/展开,不受 progress 阈值限制 */
const val FLING_VELOCITY_THRESHOLD_DP = 800
/** 日历与 BottomCard 之间的间距 (dp):展开时 */
const val CARD_GAP_EXPANDED_DP = 24
/** 日历与 BottomCard 之间的间距 (dp):折叠时 */
const val CARD_GAP_COLLAPSED_DP = 12
/** 线性插值 */
fun lerp(start: Float, end: Float, fraction: Float): Float = start + (end - start) * fraction
/**
* 计算月份在日历网格中需要的行数4/5/6
*
* @param year 年份
* @param month 月份1-12
* @return 网格行数
*/
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 页码计算该页月份的行数。
*
* @param page 分页器页码
* @param today 今天的日期,用于确定起始月份
* @return 网格行数
*/
fun calculateWeeksCountForPage(page: Int, today: LocalDate): Int {
val initialYear = today.year
@Suppress("DEPRECATION") // monthNumber 无替代 APIkotlinx-datetime 尚未提供新接口
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)
}
/**
* 页码转年月。
*
* 中心页 (Int.MAX_VALUE/2) 对应起始月份,向左递减、向右递增,
* 自动处理跨年12月→1月
*
* @param page 分页器页码
* @param initialYear 起始年份(中心页对应的年份)
* @param initialMonth 起始月份中心页对应的月份1-12
* @return Pair(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)
}
/**
* 年月转页码。
*
* [pageToYearMonth] 的逆运算,用于点击跨月日期时定位目标页。
*
* @param year 目标年份
* @param month 目标月份1-12
* @param initialYear 起始年份
* @param initialMonth 起始月份
* @return 分页器页码
*/
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)
}
/**
* 获取日期所在周的周一。
*
* ISO 8601 周从周一开始。周一返回自身,其他日期回退到该周周一。
*
* @receiver 目标日期
* @return 该日期所在周的周一
*/
fun LocalDate.toWeekMonday(): LocalDate {
val dayOfWeekOrdinal = dayOfWeek.ordinal
return minus(DatePeriod(days = dayOfWeekOrdinal))
}
/**
* 根据 pager 页码计算该页对应的周周一日期。
*
* 中心页对应参考周一,向左/右每页偏移一周。用于 WeekPager 的单周视图渲染。
*
* @param page 分页器页码
* @param initial 参考周一日期(中心页对应的周一)
* @return 该页周一的 LocalDate
*/
fun pageToWeekMonday(page: Int, initial: LocalDate): LocalDate {
val offset = page - START_PAGE
return initial.plus(DatePeriod(days = offset * 7))
}