calendar: рескин месячной сетки под макет
По «Календарь · Месяц»: дни недели — серые пилюли; сетка безрамочная с гридлайнами (0.5dp) вместо скруглённых боксов; номер дня сверху-слева; сегодня — в зелёном круге (Primary, белый текст); событие — зелёная пилюля PrimaryLight (до 2 + «+N»), было одно превью/точка. Компактная модель (месяц + список дня снизу) сохранена. Компилируется.
This commit is contained in:
+71
-53
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -388,46 +387,55 @@ private fun CalendarIconButton(
|
|||||||
fun CalendarMonthGrid(
|
fun CalendarMonthGrid(
|
||||||
month: YearMonth,
|
month: YearMonth,
|
||||||
selectedDay: LocalDate,
|
selectedDay: LocalDate,
|
||||||
daysWithEvents: Set<LocalDate>,
|
|
||||||
eventsByDay: Map<LocalDate, List<CalendarEventItem>>,
|
eventsByDay: Map<LocalDate, List<CalendarEventItem>>,
|
||||||
onSelectDay: (LocalDate) -> Unit,
|
onSelectDay: (LocalDate) -> Unit,
|
||||||
) {
|
) {
|
||||||
val days = CalendarRepository.monthGridDays(month)
|
val days = CalendarRepository.monthGridDays(month)
|
||||||
val today = LocalDate.now()
|
val today = LocalDate.now()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.fillMaxWidth()
|
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
.clip(RoundedCornerShape(12.dp))
|
) {
|
||||||
.background(F7Colors.Surface)
|
// Заголовок дней недели — серые пилюли (по макету «Календарь · Месяц»)
|
||||||
.border(1.dp, F7Colors.Border, RoundedCornerShape(12.dp))
|
Row(
|
||||||
.padding(8.dp),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
) {
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
|
||||||
weekDayLabels.forEach { label ->
|
weekDayLabels.forEach { label ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.clip(RoundedCornerShape(100.dp))
|
||||||
|
.background(F7Colors.Grey2)
|
||||||
|
.padding(vertical = 6.dp),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = label,
|
||||||
modifier = Modifier.weight(1f),
|
style = MaterialTheme.typography.labelMedium,
|
||||||
textAlign = TextAlign.Center,
|
fontWeight = FontWeight.Medium,
|
||||||
style = MaterialTheme.typography.labelSmall,
|
color = F7Colors.TextSecondary,
|
||||||
color = F7Colors.TextMuted,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Гридлайн-сетка без скруглений (по макету)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.border(0.5.dp, F7Colors.Grey3),
|
||||||
|
) {
|
||||||
days.chunked(7).forEach { week ->
|
days.chunked(7).forEach { week ->
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
week.forEach { day ->
|
week.forEach { day ->
|
||||||
val inMonth = day.month == month.month
|
val inMonth = day.month == month.month
|
||||||
val selected = day == selectedDay
|
|
||||||
val hasEvents = daysWithEvents.contains(day)
|
|
||||||
val dayEvents = eventsByDay[day].orEmpty()
|
val dayEvents = eventsByDay[day].orEmpty()
|
||||||
CalendarDayCell(
|
CalendarDayCell(
|
||||||
day = day.dayOfMonth,
|
day = day.dayOfMonth,
|
||||||
inMonth = inMonth,
|
inMonth = inMonth,
|
||||||
isToday = day == today,
|
isToday = day == today,
|
||||||
selected = selected,
|
selected = day == selectedDay,
|
||||||
hasEvents = hasEvents,
|
events = dayEvents,
|
||||||
preview = dayEvents.firstOrNull()?.summary.orEmpty(),
|
|
||||||
onClick = { onSelectDay(day) },
|
onClick = { onSelectDay(day) },
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
@@ -435,6 +443,7 @@ fun CalendarMonthGrid(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -443,63 +452,72 @@ private fun CalendarDayCell(
|
|||||||
inMonth: Boolean,
|
inMonth: Boolean,
|
||||||
isToday: Boolean,
|
isToday: Boolean,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
hasEvents: Boolean,
|
events: List<CalendarEventItem>,
|
||||||
preview: String,
|
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val bg = when {
|
val cellBg = when {
|
||||||
|
isToday -> F7Colors.Green10
|
||||||
selected -> F7Colors.PrimaryLight
|
selected -> F7Colors.PrimaryLight
|
||||||
isToday -> F7Colors.PrimaryLight.copy(alpha = 0.55f)
|
|
||||||
else -> F7Colors.Surface
|
else -> F7Colors.Surface
|
||||||
}
|
}
|
||||||
val borderColor = when {
|
Column(
|
||||||
selected -> F7Colors.Primary
|
|
||||||
isToday -> F7Colors.PrimaryDark
|
|
||||||
else -> F7Colors.BorderLight
|
|
||||||
}
|
|
||||||
Box(
|
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.aspectRatio(1f)
|
.heightIn(min = 66.dp)
|
||||||
.padding(2.dp)
|
.border(0.5.dp, F7Colors.Grey3)
|
||||||
.clip(RoundedCornerShape(8.dp))
|
.background(cellBg)
|
||||||
.background(bg)
|
|
||||||
.border(1.dp, borderColor, RoundedCornerShape(8.dp))
|
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.padding(2.dp),
|
.padding(horizontal = 4.dp, vertical = 4.dp),
|
||||||
contentAlignment = Alignment.TopCenter,
|
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
) {
|
||||||
|
// Номер дня сверху-слева; сегодня — в зелёном круге
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(22.dp)
|
||||||
|
.then(if (isToday) Modifier.clip(CircleShape).background(F7Colors.Primary) else Modifier),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
|
||||||
Text(
|
Text(
|
||||||
text = day.toString(),
|
text = day.toString(),
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
fontWeight = if (isToday) FontWeight.Bold else FontWeight.Normal,
|
fontWeight = if (isToday) FontWeight.SemiBold else FontWeight.Normal,
|
||||||
color = when {
|
color = when {
|
||||||
|
isToday -> F7Colors.TextOnPrimary
|
||||||
!inMonth -> F7Colors.TextMuted
|
!inMonth -> F7Colors.TextMuted
|
||||||
selected -> F7Colors.PrimaryDark
|
|
||||||
else -> F7Colors.TextPrimary
|
else -> F7Colors.TextPrimary
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (hasEvents && inMonth && preview.isNotBlank()) {
|
}
|
||||||
|
if (inMonth) {
|
||||||
|
events.take(2).forEach { event ->
|
||||||
|
CalendarDayEventPill(event.summary.ifBlank { "Событие" })
|
||||||
|
}
|
||||||
|
if (events.size > 2) {
|
||||||
Text(
|
Text(
|
||||||
preview,
|
"+${events.size - 2}",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = F7Colors.Primary,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CalendarDayEventPill(text: String) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(4.dp))
|
||||||
|
.background(F7Colors.PrimaryLight)
|
||||||
|
.padding(horizontal = 4.dp, vertical = 1.dp),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = F7Colors.PrimaryDark,
|
color = F7Colors.PrimaryDark,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier.padding(top = 1.dp),
|
|
||||||
)
|
)
|
||||||
} else if (hasEvents && inMonth) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(top = 2.dp)
|
|
||||||
.size(5.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(F7Colors.Primary),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -343,7 +343,6 @@ fun CalendarScreen(
|
|||||||
CalendarMonthGrid(
|
CalendarMonthGrid(
|
||||||
month = state.visibleMonth,
|
month = state.visibleMonth,
|
||||||
selectedDay = state.selectedDay,
|
selectedDay = state.selectedDay,
|
||||||
daysWithEvents = vm.daysWithEvents(),
|
|
||||||
eventsByDay = eventsByDay,
|
eventsByDay = eventsByDay,
|
||||||
onSelectDay = vm::selectDay,
|
onSelectDay = vm::selectDay,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user