design(calendar): месячная сетка 1:1 по живой теме

_calendar-month-view-mobile.css: месяц — белая карточка r12 с рамкой
и паддингом 12; дни недели — круги 40 #F5F5F5 (16/500, были пилюли);
ячейки — карточки #FDFDFD r4 с тенью, высота 120 (были gridline 66),
зазор 4; номер дня 14/500 справа-сверху; «сегодня» — зелёная плашка
40×24 r4 с белым числом (был круг); события — пилюли #ECF9DE с рамкой
#70B62B r4, текст 10/12, до 3 в ячейке; вне месяца — серые.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
b-dev-mobile
2026-07-09 21:20:26 +00:00
parent bc9a31a026
commit 65805a15af
@@ -392,54 +392,58 @@ fun CalendarMonthGrid(
) { ) {
val days = CalendarRepository.monthGridDays(month) val days = CalendarRepository.monthGridDays(month)
val today = LocalDate.now() val today = LocalDate.now()
// Живая тема (_calendar-month-view-mobile.css): месяц — белая карточка r12 с рамкой,
// внутри дни недели — круги 40 (#F5F5F5, 16/500) и ячейки-карточки #FDFDFD r4
// с тенью, зазор 4.
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
verticalArrangement = Arrangement.spacedBy(6.dp), .fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(Color.White)
.border(1.dp, F7Colors.Border, RoundedCornerShape(12.dp))
.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
// Заголовок дней недели — серые пилюли (по макету «Календарь · Месяц»)
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
weekDayLabels.forEach { label -> weekDayLabels.forEach { label ->
Box( Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) {
modifier = Modifier Box(
.weight(1f) modifier = Modifier
.clip(RoundedCornerShape(100.dp)) .size(40.dp)
.background(F7Colors.Grey2) .clip(CircleShape)
.padding(vertical = 6.dp), .background(F7Colors.Grey2),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Text( Text(
text = label, text = label,
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = F7Colors.TextSecondary, color = F7Colors.TextPrimary,
) )
}
} }
} }
} }
// Гридлайн-сетка без скруглений (по макету) days.chunked(7).forEach { week ->
Column( Row(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.fillMaxWidth() horizontalArrangement = Arrangement.spacedBy(4.dp),
.border(0.5.dp, F7Colors.Grey3), ) {
) { week.forEach { day ->
days.chunked(7).forEach { week -> val inMonth = day.month == month.month
Row(modifier = Modifier.fillMaxWidth()) { val dayEvents = eventsByDay[day].orEmpty()
week.forEach { day -> CalendarDayCell(
val inMonth = day.month == month.month day = day.dayOfMonth,
val dayEvents = eventsByDay[day].orEmpty() inMonth = inMonth,
CalendarDayCell( isToday = day == today,
day = day.dayOfMonth, selected = day == selectedDay,
inMonth = inMonth, events = dayEvents,
isToday = day == today, onClick = { onSelectDay(day) },
selected = day == selectedDay, modifier = Modifier.weight(1f),
events = dayEvents, )
onClick = { onSelectDay(day) },
modifier = Modifier.weight(1f),
)
}
} }
} }
} }
@@ -456,65 +460,79 @@ private fun CalendarDayCell(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val cellBg = when { // Живая тема: ячейка — карточка #FDFDFD r4 с тонкой тенью, высота 120, паддинг 4;
isToday -> F7Colors.Green10 // номер дня 14/500 СПРАВА-сверху (чужой месяц — серый); «сегодня» — зелёная плашка
selected -> F7Colors.PrimaryLight // 40×24 r4 с белым числом; выбранный день подсвечиваем #ECF9DE (наша адаптация тапа).
else -> F7Colors.Surface
}
Column( Column(
modifier = modifier modifier = modifier
.heightIn(min = 66.dp) .height(120.dp)
.border(0.5.dp, F7Colors.Grey3) .shadow(
.background(cellBg) elevation = 1.dp,
shape = RoundedCornerShape(4.dp),
spotColor = F7Colors.Border,
)
.clip(RoundedCornerShape(4.dp))
.background(if (selected) F7Colors.PrimaryLight else Color(0xFFFDFDFD))
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 4.dp, vertical = 4.dp), .padding(4.dp),
verticalArrangement = Arrangement.spacedBy(2.dp), verticalArrangement = Arrangement.spacedBy(2.dp),
) { ) {
// Номер дня сверху-слева; сегодня — в зелёном круге Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Box( if (isToday) {
modifier = Modifier Box(
.size(22.dp) modifier = Modifier
.then(if (isToday) Modifier.clip(CircleShape).background(F7Colors.Primary) else Modifier), .width(40.dp)
contentAlignment = Alignment.Center, .height(24.dp)
) { .clip(RoundedCornerShape(4.dp))
Text( .background(F7Colors.Primary),
text = day.toString(), contentAlignment = Alignment.Center,
style = MaterialTheme.typography.bodySmall, ) {
fontWeight = if (isToday) FontWeight.SemiBold else FontWeight.Normal, Text(
color = when { text = day.toString(),
isToday -> F7Colors.TextOnPrimary style = MaterialTheme.typography.bodyMedium,
!inMonth -> F7Colors.TextMuted fontWeight = FontWeight.Medium,
else -> F7Colors.TextPrimary color = Color.White,
}, )
) }
} } else {
if (inMonth) {
events.take(2).forEach { event ->
CalendarDayEventPill(event.summary.ifBlank { "Событие" })
}
if (events.size > 2) {
Text( Text(
"+${events.size - 2}", text = day.toString(),
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.bodyMedium,
color = F7Colors.Primary,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = if (inMonth) F7Colors.TextPrimary else F7Colors.TextSecondary,
) )
} }
} }
events.take(3).forEach { event ->
CalendarDayEventPill(event.summary.ifBlank { "Событие" }, muted = !inMonth)
}
if (events.size > 3) {
Text(
"+${events.size - 3}",
style = MaterialTheme.typography.labelSmall,
color = F7Colors.Primary,
fontWeight = FontWeight.Medium,
)
}
} }
} }
@Composable @Composable
private fun CalendarDayEventPill(text: String) { private fun CalendarDayEventPill(text: String, muted: Boolean = false) {
// Живая тема: пилюля события — фон #ECF9DE, рамка #70B62B, r4, текст 10/12;
// вне текущего месяца — серые с белым текстом.
Text( Text(
text = text, text = text,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.background(F7Colors.PrimaryLight) .background(if (muted) F7Colors.TextSecondary else F7Colors.PrimaryLight)
.padding(horizontal = 4.dp, vertical = 1.dp), .border(1.dp, if (muted) F7Colors.TextSecondary else F7Colors.Primary, RoundedCornerShape(4.dp))
style = MaterialTheme.typography.labelSmall, .padding(horizontal = 4.dp, vertical = 2.dp),
color = F7Colors.PrimaryDark, fontSize = 10.sp,
lineHeight = 12.sp,
fontWeight = FontWeight.Medium,
color = if (muted) Color.White else F7Colors.TextPrimary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )