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:
+66
-48
@@ -392,41 +392,46 @@ fun CalendarMonthGrid(
|
||||
) {
|
||||
val days = CalendarRepository.monthGridDays(month)
|
||||
val today = LocalDate.now()
|
||||
// Живая тема (_calendar-month-view-mobile.css): месяц — белая карточка r12 с рамкой,
|
||||
// внутри дни недели — круги 40 (#F5F5F5, 16/500) и ячейки-карточки #FDFDFD r4
|
||||
// с тенью, зазор 4.
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = Modifier
|
||||
.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(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
weekDayLabels.forEach { label ->
|
||||
Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clip(RoundedCornerShape(100.dp))
|
||||
.background(F7Colors.Grey2)
|
||||
.padding(vertical = 6.dp),
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(F7Colors.Grey2),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = F7Colors.TextSecondary,
|
||||
color = F7Colors.TextPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Гридлайн-сетка без скруглений (по макету)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.border(0.5.dp, F7Colors.Grey3),
|
||||
) {
|
||||
}
|
||||
days.chunked(7).forEach { week ->
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
week.forEach { day ->
|
||||
val inMonth = day.month == month.month
|
||||
val dayEvents = eventsByDay[day].orEmpty()
|
||||
@@ -443,7 +448,6 @@ fun CalendarMonthGrid(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -456,65 +460,79 @@ private fun CalendarDayCell(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val cellBg = when {
|
||||
isToday -> F7Colors.Green10
|
||||
selected -> F7Colors.PrimaryLight
|
||||
else -> F7Colors.Surface
|
||||
}
|
||||
// Живая тема: ячейка — карточка #FDFDFD r4 с тонкой тенью, высота 120, паддинг 4;
|
||||
// номер дня 14/500 СПРАВА-сверху (чужой месяц — серый); «сегодня» — зелёная плашка
|
||||
// 40×24 r4 с белым числом; выбранный день подсвечиваем #ECF9DE (наша адаптация тапа).
|
||||
Column(
|
||||
modifier = modifier
|
||||
.heightIn(min = 66.dp)
|
||||
.border(0.5.dp, F7Colors.Grey3)
|
||||
.background(cellBg)
|
||||
.height(120.dp)
|
||||
.shadow(
|
||||
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)
|
||||
.padding(horizontal = 4.dp, vertical = 4.dp),
|
||||
.padding(4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
// Номер дня сверху-слева; сегодня — в зелёном круге
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
|
||||
if (isToday) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(22.dp)
|
||||
.then(if (isToday) Modifier.clip(CircleShape).background(F7Colors.Primary) else Modifier),
|
||||
.width(40.dp)
|
||||
.height(24.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(F7Colors.Primary),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = day.toString(),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontWeight = if (isToday) FontWeight.SemiBold else FontWeight.Normal,
|
||||
color = when {
|
||||
isToday -> F7Colors.TextOnPrimary
|
||||
!inMonth -> F7Colors.TextMuted
|
||||
else -> F7Colors.TextPrimary
|
||||
},
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color.White,
|
||||
)
|
||||
}
|
||||
if (inMonth) {
|
||||
events.take(2).forEach { event ->
|
||||
CalendarDayEventPill(event.summary.ifBlank { "Событие" })
|
||||
}
|
||||
if (events.size > 2) {
|
||||
} else {
|
||||
Text(
|
||||
"+${events.size - 2}",
|
||||
text = day.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
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
|
||||
private fun CalendarDayEventPill(text: String) {
|
||||
private fun CalendarDayEventPill(text: String, muted: Boolean = false) {
|
||||
// Живая тема: пилюля события — фон #ECF9DE, рамка #70B62B, r4, текст 10/12;
|
||||
// вне текущего месяца — серые с белым текстом.
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(F7Colors.PrimaryLight)
|
||||
.padding(horizontal = 4.dp, vertical = 1.dp),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = F7Colors.PrimaryDark,
|
||||
.background(if (muted) F7Colors.TextSecondary else F7Colors.PrimaryLight)
|
||||
.border(1.dp, if (muted) F7Colors.TextSecondary else F7Colors.Primary, RoundedCornerShape(4.dp))
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp),
|
||||
fontSize = 10.sp,
|
||||
lineHeight = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = if (muted) Color.White else F7Colors.TextPrimary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user