diff --git a/app/src/main/java/agzam/vyatsu/ui/schedule/ScheduleScreen.kt b/app/src/main/java/agzam/vyatsu/ui/schedule/ScheduleScreen.kt index 7d9145b..cab661e 100644 --- a/app/src/main/java/agzam/vyatsu/ui/schedule/ScheduleScreen.kt +++ b/app/src/main/java/agzam/vyatsu/ui/schedule/ScheduleScreen.kt @@ -1,13 +1,11 @@ package agzam.vyatsu.ui.schedule -import agzam.vyatsu.domain.model.Day -import agzam.vyatsu.domain.model.LessonType -import agzam.vyatsu.domain.model.SchedulePeriod -import agzam.vyatsu.domain.model.TimeSlot -import android.widget.Toast -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.combinedClickable +import agzam.vyatsu.ui.schedule.components.DayChips +import agzam.vyatsu.ui.schedule.components.FULL_DATE +import agzam.vyatsu.ui.schedule.components.LessonSlot +import agzam.vyatsu.ui.schedule.components.WeekSelector +import agzam.vyatsu.ui.schedule.components.dayName +import agzam.vyatsu.ui.schedule.components.hPadding import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,39 +14,19 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBars -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListItemInfo -import androidx.compose.foundation.lazy.LazyListLayoutInfo -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -57,27 +35,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp -import kotlinx.coroutines.flow.collectLatest -import java.time.DayOfWeek -import java.time.LocalDate -import java.time.format.DateTimeFormatter -import kotlin.math.absoluteValue -import kotlin.math.max -import kotlin.math.min - - -val hPadding = 16.dp @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -226,290 +187,3 @@ fun ScheduleScreen(viewModel: ScheduleViewModel, onSwitchGroup: () -> Unit) { } } } - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun WeekSelector( - periods: List, - selectedWeekStart: LocalDate?, - todayWeekStart: LocalDate?, - onSelect: (LocalDate) -> Unit -) { - val selected = periods.firstOrNull { it.weekStart == selectedWeekStart } - var expanded by remember { mutableStateOf(false) } - - Box { - Row( - modifier = Modifier - .clip(MaterialTheme.shapes.small) - .clickable(enabled = true, onClick = { expanded = true }) - .padding(horizontal = 10.dp, vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - selected?.let(::weekLabel) ?: "Период", - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.primary - ) - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary - ) - } - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.widthIn(min = 220.dp) - ) { - periods.forEach { period -> - val isTodayWeek = todayWeekStart == period.weekStart - DropdownMenuItem( - text = { - Text( - weekLabel(period), - style = MaterialTheme.typography.bodyLarge - ) - }, - trailingIcon = if (isTodayWeek) { - { - Text( - "сегодня", - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.primary - ) - } - } else { - null - }, - onClick = { - expanded = false - onSelect(period.weekStart) - } - ) - } - } - } -} - -private fun getVisiblePercentage(item: LazyListItemInfo, layoutInfo: LazyListLayoutInfo): Float { - val viewportStart = layoutInfo.viewportStartOffset - val viewportEnd = layoutInfo.viewportEndOffset - - val itemStart = item.offset - val itemEnd = item.offset + item.size - - val visibleStart = max(itemStart, viewportStart) - val visibleEnd = min(itemEnd, viewportEnd) - val visibleSize = visibleEnd - visibleStart - - return if (visibleSize <= 0) 0f else visibleSize.toFloat() / item.size -} - -@Composable -private fun DayChips( - days: List, - selectedDate: LocalDate?, - todayDate: LocalDate?, - pagerState: PagerState, - onSelect: (LocalDate) -> Unit -) { - val listState = rememberLazyListState() - - val density = LocalDensity.current - - val paddingPx = with(density) { hPadding.toPx() } - - - LaunchedEffect(pagerState, days) { - snapshotFlow { - pagerState.currentPage - }.collectLatest { targetPage -> - - if (targetPage in days.indices) { - val layoutInfo = listState.layoutInfo - val visibleItems = layoutInfo.visibleItemsInfo - val targetItem = visibleItems.firstOrNull { it.index == targetPage } - - if (targetItem != null) { - val visiblePercentage = getVisiblePercentage(targetItem, layoutInfo) - val minVisibleThreshold = 0.9f - - if (visiblePercentage < minVisibleThreshold) { - val viewPortWidth = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset - val halfViewport = viewPortWidth / 2f - val centerOffset = (halfViewport - (targetItem.size / 2f) - paddingPx).toInt() - - listState.animateScrollToItem(index = targetPage, scrollOffset = -centerOffset) - } - } else { - val viewPortWidth = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset - val halfViewport = viewPortWidth / 2f - - val averageSize = visibleItems.map { it.size }.average().toInt().coerceAtLeast(150) - val centerOffset = (halfViewport - (averageSize / 2f) - paddingPx).toInt() - - listState.animateScrollToItem(index = targetPage, scrollOffset = -centerOffset) - } - } - } - } - - LazyRow(state = listState, horizontalArrangement = Arrangement.spacedBy(6.dp), contentPadding = PaddingValues(horizontal = hPadding, vertical = 2.dp),) { - items(days, key = { it.date.toEpochDay() }) { day -> - val selected = selectedDate == day.date - val isTodayChip = todayDate == day.date - FilterChip( - selected = selected, - onClick = { onSelect(day.date) }, - label = { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text( - dayName(day.date.dayOfWeek), - style = MaterialTheme.typography.labelMedium - ) - Text( - day.date.format(CHIP_DATE), - style = MaterialTheme.typography.labelSmall, - color = if (selected) { - MaterialTheme.colorScheme.onSecondaryContainer - } else { - MaterialTheme.colorScheme.onSurfaceVariant - } - ) - } - }, - modifier = Modifier.padding(vertical = 2.dp), - border = if (isTodayChip && !selected) { - FilterChipDefaults.filterChipBorder( - borderColor = MaterialTheme.colorScheme.primary, - borderWidth = 1.dp, - enabled = true, - selected = false - ) - } else { - FilterChipDefaults.filterChipBorder(enabled = true, selected = selected) - }, - shape = RoundedCornerShape(8.dp) - ) - } - } -} - -private fun weekLabel(period: SchedulePeriod): String { - val first = period.weekStart - return "с ${first.format(CHIP_DATE)}" -} - -@Composable -private fun LessonSlot(slotIndex: Int, slot: TimeSlot) { - val context = LocalContext.current - var indexColor = 0 - Column(Modifier.fillMaxWidth()) { - Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { - HorizontalDivider( - modifier = Modifier.width(10.dp + hPadding).padding(start = hPadding), - color = MaterialTheme.colorScheme.primary - ) - Text( - "${slotIndex + 1} пара · ${slot.start.format(TIME)}–${slot.end.format(TIME)}", - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.primary, - modifier = Modifier.padding(horizontal = 4.dp) - ) - HorizontalDivider(color = MaterialTheme.colorScheme.primary, modifier = Modifier.weight(1f).padding(end = hPadding)) - } - if (slot.lessons.isEmpty()) { - Text("—", style = MaterialTheme.typography.bodyMedium) - } else { - slot.lessons.forEachIndexed { index, lesson -> - var baseSurface = Modifier.fillMaxWidth().padding(vertical = 2.dp) - if ((indexColor) % 2 != 0) { - baseSurface = baseSurface.background( - MaterialTheme.colorScheme.surfaceContainer - ); - } - indexColor++ - // XXX: - // Убрать SelectionContainer? - // Режим редактирования расписания? - // Меню на onClick? - // Меню на onLongClick? - SelectionContainer() { - Column( - baseSurface - // XXX: combinedClickable? - //.combinedClickable( - // onClick = { - // Toast.makeText( - // context, - // lesson.raw, - // Toast.LENGTH_SHORT - // ).show() - // }, - // onLongClick = {}, - // // TODO: onLongClickLabel = "(Accessibility)" - //), - ) { - var any = false - - val meta = listOfNotNull( - if (lesson.type == LessonType.OTHER) { - null - } else { - lesson.type.label - }, - lesson.subgroup, - lesson.teacher, - lesson.room - ).filter { it.isNotBlank() }.joinToString(" · ") - - if (meta.isNotEmpty()) { - Text( - meta, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(horizontal = hPadding) - ) - any = true - } - - val subject = lesson.subject?.takeIf { it.isNotBlank() } ?: lesson.raw - if (subject.isNotEmpty()) { - Text( - subject, - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(horizontal = hPadding) - ) - any = true - } - - if (!any) { - Text( - "пусто", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(horizontal = hPadding) - ) - } - - } - } - } - } - } -} - -private fun dayName(dayOfWeek: DayOfWeek): String = when (dayOfWeek) { - DayOfWeek.MONDAY -> "Пн" - DayOfWeek.TUESDAY -> "Вт" - DayOfWeek.WEDNESDAY -> "Ср" - DayOfWeek.THURSDAY -> "Чт" - DayOfWeek.FRIDAY -> "Пт" - DayOfWeek.SATURDAY -> "Сб" - DayOfWeek.SUNDAY -> "Вс" -} - -private val FULL_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") -private val CHIP_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM") -private val TIME: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") diff --git a/app/src/main/java/agzam/vyatsu/ui/schedule/components/DayChips.kt b/app/src/main/java/agzam/vyatsu/ui/schedule/components/DayChips.kt new file mode 100644 index 0000000..5513908 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/ui/schedule/components/DayChips.kt @@ -0,0 +1,133 @@ +package agzam.vyatsu.ui.schedule.components + +import agzam.vyatsu.domain.model.Day +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListLayoutInfo +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.PagerState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.FilterChip +import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.flow.collectLatest +import java.time.LocalDate +import kotlin.math.max +import kotlin.math.min + +internal fun getVisiblePercentage(item: LazyListItemInfo, layoutInfo: LazyListLayoutInfo): Float { + val viewportStart = layoutInfo.viewportStartOffset + val viewportEnd = layoutInfo.viewportEndOffset + + val itemStart = item.offset + val itemEnd = item.offset + item.size + + val visibleStart = max(itemStart, viewportStart) + val visibleEnd = min(itemEnd, viewportEnd) + val visibleSize = visibleEnd - visibleStart + + return if (visibleSize <= 0) 0f else visibleSize.toFloat() / item.size +} + +@Composable +internal fun DayChips( + days: List, + selectedDate: LocalDate?, + todayDate: LocalDate?, + pagerState: PagerState, + onSelect: (LocalDate) -> Unit +) { + val listState = rememberLazyListState() + + val density = LocalDensity.current + + val paddingPx = with(density) { hPadding.toPx() } + + + LaunchedEffect(pagerState, days) { + snapshotFlow { + pagerState.currentPage + }.collectLatest { targetPage -> + + if (targetPage in days.indices) { + val layoutInfo = listState.layoutInfo + val visibleItems = layoutInfo.visibleItemsInfo + val targetItem = visibleItems.firstOrNull { it.index == targetPage } + + if (targetItem != null) { + val visiblePercentage = getVisiblePercentage(targetItem, layoutInfo) + val minVisibleThreshold = 0.9f + + if (visiblePercentage < minVisibleThreshold) { + val viewPortWidth = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset + val halfViewport = viewPortWidth / 2f + val centerOffset = (halfViewport - (targetItem.size / 2f) - paddingPx).toInt() + + listState.animateScrollToItem(index = targetPage, scrollOffset = -centerOffset) + } + } else { + val viewPortWidth = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset + val halfViewport = viewPortWidth / 2f + + val averageSize = visibleItems.map { it.size }.average().toInt().coerceAtLeast(150) + val centerOffset = (halfViewport - (averageSize / 2f) - paddingPx).toInt() + + listState.animateScrollToItem(index = targetPage, scrollOffset = -centerOffset) + } + } + } + } + + LazyRow(state = listState, horizontalArrangement = Arrangement.spacedBy(6.dp), contentPadding = PaddingValues(horizontal = hPadding, vertical = 2.dp),) { + items(days, key = { it.date.toEpochDay() }) { day -> + val selected = selectedDate == day.date + val isTodayChip = todayDate == day.date + FilterChip( + selected = selected, + onClick = { onSelect(day.date) }, + label = { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text( + dayName(day.date.dayOfWeek), + style = MaterialTheme.typography.labelMedium + ) + Text( + day.date.format(CHIP_DATE), + style = MaterialTheme.typography.labelSmall, + color = if (selected) { + MaterialTheme.colorScheme.onSecondaryContainer + } else { + MaterialTheme.colorScheme.onSurfaceVariant + } + ) + } + }, + modifier = Modifier.padding(vertical = 2.dp), + border = if (isTodayChip && !selected) { + FilterChipDefaults.filterChipBorder( + borderColor = MaterialTheme.colorScheme.primary, + borderWidth = 1.dp, + enabled = true, + selected = false + ) + } else { + FilterChipDefaults.filterChipBorder(enabled = true, selected = selected) + }, + shape = RoundedCornerShape(8.dp) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt b/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt new file mode 100644 index 0000000..76f874f --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/ui/schedule/components/LessonSlot.kt @@ -0,0 +1,118 @@ +package agzam.vyatsu.ui.schedule.components + +import agzam.vyatsu.domain.model.LessonType +import agzam.vyatsu.domain.model.TimeSlot +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp + +@Composable +internal fun LessonSlot(slotIndex: Int, slot: TimeSlot) { + val context = LocalContext.current + var indexColor = 0 + Column(Modifier.fillMaxWidth()) { + Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + HorizontalDivider( + modifier = Modifier.width(10.dp + hPadding).padding(start = hPadding), + color = MaterialTheme.colorScheme.primary + ) + Text( + "${slotIndex + 1} пара · ${slot.start.format(TIME)}–${slot.end.format(TIME)}", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 4.dp) + ) + HorizontalDivider(color = MaterialTheme.colorScheme.primary, modifier = Modifier.weight(1f).padding(end = hPadding)) + } + if (slot.lessons.isEmpty()) { + Text("—", style = MaterialTheme.typography.bodyMedium) + } else { + slot.lessons.forEachIndexed { index, lesson -> + var baseSurface = Modifier.fillMaxWidth().padding(vertical = 2.dp) + if ((indexColor) % 2 != 0) { + baseSurface = baseSurface.background( + MaterialTheme.colorScheme.surfaceContainer + ); + } + indexColor++ + // XXX: + // Убрать SelectionContainer? + // Режим редактирования расписания? + // Меню на onClick? + // Меню на onLongClick? + SelectionContainer() { + Column( + baseSurface + // XXX: combinedClickable? + //.combinedClickable( + // onClick = { + // Toast.makeText( + // context, + // lesson.raw, + // Toast.LENGTH_SHORT + // ).show() + // }, + // onLongClick = {}, + // // TODO: onLongClickLabel = "(Accessibility)" + //), + ) { + var any = false + + val meta = listOfNotNull( + if (lesson.type == LessonType.OTHER) { + null + } else { + lesson.type.label + }, + lesson.subgroup, + lesson.teacher, + lesson.room + ).filter { it.isNotBlank() }.joinToString(" · ") + + if (meta.isNotEmpty()) { + Text( + meta, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(horizontal = hPadding) + ) + any = true + } + + val subject = lesson.subject?.takeIf { it.isNotBlank() } ?: lesson.raw + if (subject.isNotEmpty()) { + Text( + subject, + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(horizontal = hPadding) + ) + any = true + } + + if (!any) { + Text( + "пусто", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(horizontal = hPadding) + ) + } + + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/agzam/vyatsu/ui/schedule/components/ScheduleUi.kt b/app/src/main/java/agzam/vyatsu/ui/schedule/components/ScheduleUi.kt new file mode 100644 index 0000000..32f04b8 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/ui/schedule/components/ScheduleUi.kt @@ -0,0 +1,21 @@ +package agzam.vyatsu.ui.schedule.components + +import androidx.compose.ui.unit.dp +import java.time.DayOfWeek +import java.time.format.DateTimeFormatter + +internal val hPadding = 16.dp + +internal fun dayName(dayOfWeek: DayOfWeek): String = when (dayOfWeek) { + DayOfWeek.MONDAY -> "Пн" + DayOfWeek.TUESDAY -> "Вт" + DayOfWeek.WEDNESDAY -> "Ср" + DayOfWeek.THURSDAY -> "Чт" + DayOfWeek.FRIDAY -> "Пт" + DayOfWeek.SATURDAY -> "Сб" + DayOfWeek.SUNDAY -> "Вс" +} + +internal val FULL_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") +internal val CHIP_DATE: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM") +internal val TIME: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") \ No newline at end of file diff --git a/app/src/main/java/agzam/vyatsu/ui/schedule/components/WeekSelector.kt b/app/src/main/java/agzam/vyatsu/ui/schedule/components/WeekSelector.kt new file mode 100644 index 0000000..8cb8f92 --- /dev/null +++ b/app/src/main/java/agzam/vyatsu/ui/schedule/components/WeekSelector.kt @@ -0,0 +1,96 @@ +package agzam.vyatsu.ui.schedule.components + +import agzam.vyatsu.domain.model.SchedulePeriod +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import java.time.LocalDate + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun WeekSelector( + periods: List, + selectedWeekStart: LocalDate?, + todayWeekStart: LocalDate?, + onSelect: (LocalDate) -> Unit +) { + val selected = periods.firstOrNull { it.weekStart == selectedWeekStart } + var expanded by remember { mutableStateOf(false) } + + Box { + Row( + modifier = Modifier + .clip(MaterialTheme.shapes.small) + .clickable(enabled = true, onClick = { expanded = true }) + .padding(horizontal = 10.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + selected?.let(::weekLabel) ?: "Период", + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary + ) + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.widthIn(min = 220.dp) + ) { + periods.forEach { period -> + val isTodayWeek = todayWeekStart == period.weekStart + DropdownMenuItem( + text = { + Text( + weekLabel(period), + style = MaterialTheme.typography.bodyLarge + ) + }, + trailingIcon = if (isTodayWeek) { + { + Text( + "сегодня", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.primary + ) + } + } else { + null + }, + onClick = { + expanded = false + onSelect(period.weekStart) + } + ) + } + } + } +} + +internal fun weekLabel(period: SchedulePeriod): String { + val first = period.weekStart + return "с ${first.format(CHIP_DATE)}" +} \ No newline at end of file