From 97551f03327697a26b26131f34e8431157ca2435 Mon Sep 17 00:00:00 2001 From: michael Date: Mon, 3 Apr 2023 22:36:14 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20=D0=B8=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=B0?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B4=D0=B0=D1=80=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetDropDown.xml | 2 +- .../java/ru/megboyzz/dnevnik/MainActivity.kt | 2 +- .../main/java/ru/megboyzz/dnevnik/Utils.kt | 58 +- .../megboyzz/dnevnik/navigation/AppNavHost.kt | 7 - .../megboyzz/dnevnik/screens/ui/BottomBar.kt | 115 +++ .../megboyzz/dnevnik/screens/ui/Calendar.kt | 205 ------ .../megboyzz/dnevnik/screens/ui/Components.kt | 691 +----------------- .../dnevnik/screens/ui/LoginScreen.kt | 158 ++++ .../dnevnik/screens/ui/MainScaffold.kt | 78 ++ .../megboyzz/dnevnik/screens/ui/Previews.kt | 34 +- .../dnevnik/screens/ui/SubScreenNavigation.kt | 89 +++ .../dnevnik/screens/ui/calendar/Calendar.kt | 259 +++++++ .../screens/ui/calendar/CalendarConstants.kt | 8 + .../screens/ui/{ => piechart}/DataPieChart.kt | 4 +- .../screens/ui/{ => piechart}/PieChart.kt | 4 +- .../java/ru/megboyzz/dnevnik/UtilsTest.kt | 8 - 16 files changed, 760 insertions(+), 962 deletions(-) create mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/BottomBar.kt delete mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Calendar.kt create mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/LoginScreen.kt create mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/MainScaffold.kt create mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/SubScreenNavigation.kt create mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/Calendar.kt create mode 100644 app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/CalendarConstants.kt rename app/src/main/java/ru/megboyzz/dnevnik/screens/ui/{ => piechart}/DataPieChart.kt (93%) rename app/src/main/java/ru/megboyzz/dnevnik/screens/ui/{ => piechart}/PieChart.kt (94%) diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 3892a83..cdcfba2 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -12,6 +12,6 @@ - + \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/dnevnik/MainActivity.kt b/app/src/main/java/ru/megboyzz/dnevnik/MainActivity.kt index b2dbe24..e33e501 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/MainActivity.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/MainActivity.kt @@ -1,5 +1,6 @@ package ru.megboyzz.dnevnik +import android.content.Context import android.os.Bundle import android.window.SplashScreen import androidx.activity.ComponentActivity @@ -13,7 +14,6 @@ import ru.megboyzz.dnevnik.ui.theme.* class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) setContent { Surface( diff --git a/app/src/main/java/ru/megboyzz/dnevnik/Utils.kt b/app/src/main/java/ru/megboyzz/dnevnik/Utils.kt index b84f2d8..6395b4e 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/Utils.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/Utils.kt @@ -7,9 +7,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.material.ripple.rememberRipple -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color @@ -24,14 +22,12 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.navigation.NavController +import com.kizitonwose.calendar.compose.CalendarLayoutInfo +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.core.CalendarMonth +import kotlinx.coroutines.flow.filterNotNull import ru.megboyzz.dnevnik.navigation.BaseNavRote -import ru.megboyzz.dnevnik.screens.ui.Month import ru.megboyzz.dnevnik.ui.theme.dark -import java.time.DayOfWeek -import java.time.LocalDate -import java.time.Year -import java.time.temporal.TemporalAdjusters -import java.util.* @Composable fun Int.AsPainter() = painterResource(this) @@ -110,21 +106,6 @@ fun Modifier.drawColoredShadow( } } else this -fun getMonthDaysBy(year: Int, month: Month, dayOfWeek: DayOfWeek): List{ - var date: LocalDate = - Year.of(year).atMonth(month.ordinal + 1).atDay(1).with(TemporalAdjusters.firstInMonth(dayOfWeek)) - var mi = date.month - - val list = mutableListOf() - - while (mi.ordinal == month.ordinal) { - list.add(date.dayOfMonth) - date = date.with(TemporalAdjusters.next(dayOfWeek)) - mi = date.month - } - return list -} - @Composable fun Modifier.mainClickable( radius: Dp = Dp.Unspecified, @@ -138,4 +119,33 @@ fun Modifier.mainClickable( onClick = onClick ) +@Composable +fun rememberFirstMostVisibleMonth( + state: CalendarState, + viewportPercent: Float = 50f, +): CalendarMonth { + val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } + LaunchedEffect(state) { + snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } + .filterNotNull() + .collect { month -> visibleMonth.value = month } + } + return visibleMonth.value +} + + +private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { + return if (visibleMonthsInfo.isEmpty()) { + null + } else { + val viewportSize = (viewportEndOffset + viewportStartOffset) * viewportPercent / 100f + visibleMonthsInfo.firstOrNull { itemInfo -> + if (itemInfo.offset < 0) { + itemInfo.offset + itemInfo.size >= viewportSize + } else { + itemInfo.size - itemInfo.offset >= viewportSize + } + }?.month + } +} diff --git a/app/src/main/java/ru/megboyzz/dnevnik/navigation/AppNavHost.kt b/app/src/main/java/ru/megboyzz/dnevnik/navigation/AppNavHost.kt index 597edab..b62b542 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/navigation/AppNavHost.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/navigation/AppNavHost.kt @@ -38,13 +38,6 @@ sealed class AppNavRoute(override val route: String): BaseNavRote(route){ @Composable fun AppNavHost(){ - /*val activity = LocalContext.current as MainActivity - - val application = activity.application as App - - val viewModel: AppViewModel = viewModel(factory = AppViewModelFactory(application))*/ - - val scaffoldState = rememberScaffoldState() val navController = rememberAnimatedNavController() diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/BottomBar.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/BottomBar.kt new file mode 100644 index 0000000..14bfeec --- /dev/null +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/BottomBar.kt @@ -0,0 +1,115 @@ +package ru.megboyzz.dnevnik.screens.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import ru.megboyzz.dnevnik.* +import ru.megboyzz.dnevnik.navigation.AppNavRoute +import ru.megboyzz.dnevnik.ui.theme.MainText +import ru.megboyzz.dnevnik.ui.theme.mainBlue +import ru.megboyzz.dnevnik.ui.theme.white +import ru.megboyzz.dnevnik.R + +@Composable +fun BottomBar(navController: NavController) { + //Box(Modifier.fillMaxWidth().height(90.dp).shadow(8.dp)) + + // УЖАСНЫЙ КОСТЫЛЬ ДЛЯ ПЕРЕХОДОВ С ПОМОЩЬЮ СТЕЙТОВ TODO - УБРАТЬ + val goToMarks = remember { mutableStateOf(false) } + val goToHw = remember { mutableStateOf(false) } + val goToSchedule = remember { mutableStateOf(false) } + + if(goToMarks.value){ + navController.navigate(AppNavRoute.Marks) + //goToMarks.value = false + } + if(goToHw.value){ + navController.navigate(AppNavRoute.HomeWorks) + //goToHw.value = false + } + if(goToSchedule.value){ + navController.navigate(AppNavRoute.Schedule) + //goToSchedule.value = false + } + + Box( + modifier = Modifier + .fillMaxWidth() + .drawColoredShadow( + offsetY = 1.dp + ) + .height(90.dp) + .background(white) + .padding(10.dp, 10.dp, 10.dp, 15.dp), + ){ + Box( + Modifier + .fillMaxSize() + .clip(RoundedCornerShape(20.dp)) + .background(mainBlue) + ){ + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxSize() + ) { + BottomNavButton( + icon = R.drawable.ic_marks.AsPainter(), + text = R.string.title_marks.AsString() + ) { goToMarks.value = true } + BottomNavButton( + icon = R.drawable.ic_homework.AsPainter(), + text = R.string.title_hw.AsString() + ) { goToHw.value = true } + BottomNavButton( + icon = R.drawable.ic_schedule.AsPainter(), + text = R.string.title_schedule.AsString() + ) { goToSchedule.value = true } + } + } + } +} + +@Composable +fun BottomNavButton( + icon: Painter, + text: String, + color: Color = white, + onClick: () -> Unit, +) { + //Возможно стоит убрать анимацию клика + Box( + Modifier + .mainClickable(onClick = onClick, radius = 20.dp) + .padding(10.dp)) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Image( + painter = icon, + contentDescription = "", + modifier = Modifier.padding(5.dp) + ) + Text( + text = text, + style = MainText, + color = color, + overflow = TextOverflow.Ellipsis + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Calendar.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Calendar.kt deleted file mode 100644 index b5aa8e6..0000000 --- a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Calendar.kt +++ /dev/null @@ -1,205 +0,0 @@ -package ru.megboyzz.dnevnik.screens.ui - -import android.util.Log -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Card -import androidx.compose.material.Divider -import androidx.compose.material.IconButton -import androidx.compose.material.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.kizitonwose.calendar.compose.CalendarState -import com.kizitonwose.calendar.compose.HorizontalCalendar -import com.kizitonwose.calendar.compose.WeekCalendar -import com.kizitonwose.calendar.compose.rememberCalendarState -import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState -import com.kizitonwose.calendar.core.CalendarDay -import com.kizitonwose.calendar.core.CalendarMonth -import com.kizitonwose.calendar.core.DayPosition -import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale -import ru.megboyzz.dnevnik.AsPainter -import ru.megboyzz.dnevnik.AsString -import ru.megboyzz.dnevnik.R -import ru.megboyzz.dnevnik.ui.theme.* -import java.time.DayOfWeek -import java.time.LocalDate -import java.time.YearMonth -import java.time.format.TextStyle -import java.util.* - -@Composable -fun MonthToggler(calendarState: CalendarState) { - - //TODO убрать это куда нибудь - var listOfMonth = listOf( - R.string.title_january.AsString(), - R.string.title_february.AsString(), - R.string.title_march.AsString(), - R.string.title_april.AsString(), - R.string.title_may.AsString(), - R.string.title_june.AsString(), - R.string.title_july.AsString(), - R.string.title_august.AsString(), - R.string.title_september.AsString(), - R.string.title_october.AsString(), - R.string.title_november.AsString(), - R.string.title_december.AsString() - ) - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(0.dp, 20.dp), - horizontalArrangement = Arrangement.Center - ) { - Row( - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically - ) { - IconButton(onClick = { calendarState.startMonth = calendarState.startMonth.minusMonths(1) }) { - Image( - painter = R.drawable.ic_month_swipe_left.AsPainter(), - contentDescription = "left", - alignment = Alignment.Center - ) - } - for(style in TextStyle.values()) - Log.i("check", calendarState.startMonth.month.getDisplayName(style, Locale.getDefault())) - Text( - //TODO Возможно стоит сделать эту строчку меньше(( - text = "${listOfMonth[calendarState.startMonth.month.ordinal]} ${calendarState.startMonth.year}", - style = H1, - color = dark - ) - IconButton( - onClick = { calendarState.startMonth = calendarState.startMonth.plusMonths(1) } - ) { - Image( - painter = R.drawable.ic_month_swipe_right.AsPainter(), - contentDescription = "right", - alignment = Alignment.Center - ) - } - } - } -} - -//TODO доделать свертку календаря, он почти идеально работает -@Composable -fun NiceCalendar( - startMonth: YearMonth, - onClick: (day: CalendarDay, month: YearMonth, year: Int) -> Unit -) { - val endMonth = startMonth.plusMonths(100) // Adjust as needed - val firstDayOfWeek = remember { firstDayOfWeekFromLocale() } // Available from the library - - val state = rememberCalendarState( - startMonth = startMonth, - endMonth = endMonth, - firstVisibleMonth = startMonth, - firstDayOfWeek = firstDayOfWeek - ) - - val weekState = rememberWeekCalendarState( - startDate = startMonth.atDay(1), - endDate = startMonth.atDay(7), - firstVisibleWeekDate = startMonth.atDay(1), - firstDayOfWeek = firstDayOfWeek - ) - - var selectedDate by remember { mutableStateOf(null) } - - Column { - - MonthToggler(calendarState = state) - - Card( - shape = RoundedCornerShape(10.dp), - modifier = Modifier.defaultMinSize(300.dp) - ) { - if(selectedDate == null) - HorizontalCalendar( - state = state, - dayContent = { day -> - Day(day, isSelected = selectedDate == day.date) { day -> - selectedDate = if (selectedDate == day.date) null else day.date - onClick(day, startMonth, startMonth.year) - } - }, - monthHeader = { DaysOfWeekTitle(daysOfWeek = DayOfWeek.values().asList()) }, - modifier = Modifier.padding(10.dp) - ) - else - //TODO по возможности устранить повторы - WeekCalendar( - state = weekState, - weekHeader = { _ -> - DaysOfWeekTitle(daysOfWeek = DayOfWeek.values().asList()) - }, - dayContent = { day -> - Day(CalendarDay(day.date, DayPosition.InDate), isSelected = selectedDate == day.date) { day -> - selectedDate = if (selectedDate == day.date) null else day.date - onClick(day, startMonth, startMonth.year) - } - }, - modifier = Modifier.padding(10.dp) - ) - } - } -} - -@Composable -fun DaysOfWeekTitle(daysOfWeek: List) { - Column { - Row(modifier = Modifier.fillMaxWidth()) { - for (dayOfWeek in daysOfWeek) { - Text( - modifier = Modifier.weight(1f), - textAlign = TextAlign.Center, - text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()).uppercase(), - style = MainText - ) - } - } - Divider( - thickness = 1.dp, - color = dark, - modifier = Modifier.padding(vertical = 10.dp) - ) - } -} -@Composable -fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { - Box( - modifier = Modifier - .aspectRatio(ratio = 1f) // This is important for square sizing! - .clip(CircleShape) - .background(color = if (isSelected) lightGray else Color.Transparent) - .border( - border = BorderStroke( - width = 1.dp, - color = if (isSelected) mainBlue else Color.Transparent - ), - shape = CircleShape - ) - .clickable( - enabled = day.position == DayPosition.MonthDate, - onClick = { onClick(day) } - ), - contentAlignment = Alignment.Center - ) { - Text( - text = day.date.dayOfMonth.toString(), - style = MainText, - color = if (day.position == DayPosition.MonthDate) dark else Color.Gray - ) - } -} diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Components.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Components.kt index cc27c38..2aef2f5 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Components.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Components.kt @@ -50,152 +50,10 @@ import java.time.DayOfWeek import java.util.* -@Composable -fun MainScaffold( - icon: Painter, - title: String, - /* TODO viewModel */ - navController: NavController, - scaffoldState: ScaffoldState, - content: (@Composable (it: PaddingValues) -> Unit), -) { - val scope = rememberCoroutineScope() - Scaffold( - scaffoldState = scaffoldState, - topBar = { - TopAppBar( - backgroundColor = mainBlue, - contentColor = Color.White - ) { - IconButton( - onClick = { - val drawerState = scaffoldState.drawerState - scope.launch { - if (drawerState.isClosed) - drawerState.open() - else - drawerState.close() - } - } - ) { +//Рефакторинг компонентов: разбиение файла с компонентами на отдельные файлы +//Этот файл должен хранить только базовые реализации компонентов и общие компонеты компонеты - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "menu", - tint = white - ) - } - SpacerWidth(width = 5.dp) - Text( - text = title, - fontSize = 16.sp, - color = white - ) - Spacer(Modifier.weight(1f)) - Box(Modifier.padding(15.dp, 15.dp)) { - Image(painter = icon, contentDescription = "icon") - } - } - }, - content = content, - drawerBackgroundColor = mainBlue, - drawerContentColor = white, - drawerShape = drawerShape, - drawerContent = { DrawerContent(navController, scaffoldState) }, - bottomBar = { BottomBar(navController) } - ) -} -@Composable -fun BottomBar(navController: NavController) { - //Box(Modifier.fillMaxWidth().height(90.dp).shadow(8.dp)) - - // УЖАСНЫЙ КОСТЫЛЬ ДЛЯ ПЕРЕХОДОВ С ПОМОЩЬЮ СТЕЙТОВ TODO - УБРАТЬ - val goToMarks = remember { mutableStateOf(false) } - val goToHw = remember { mutableStateOf(false) } - val goToSchedule = remember { mutableStateOf(false) } - - if(goToMarks.value){ - navController.navigate(AppNavRoute.Marks) - //goToMarks.value = false - } - if(goToHw.value){ - navController.navigate(AppNavRoute.HomeWorks) - //goToHw.value = false - } - if(goToSchedule.value){ - navController.navigate(AppNavRoute.Schedule) - //goToSchedule.value = false - } - - Box( - modifier = Modifier - .fillMaxWidth() - .drawColoredShadow( - offsetY = 1.dp - ) - .height(90.dp) - .background(white) - .padding(10.dp, 10.dp, 10.dp, 15.dp), - ){ - Box( - Modifier - .fillMaxSize() - .clip(RoundedCornerShape(20.dp)) - .background(mainBlue) - ){ - Row( - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxSize() - ) { - BottomNavButton( - icon = R.drawable.ic_marks.AsPainter(), - text = R.string.title_marks.AsString() - ) { goToMarks.value = true } - BottomNavButton( - icon = R.drawable.ic_homework.AsPainter(), - text = R.string.title_hw.AsString() - ) { goToHw.value = true } - BottomNavButton( - icon = R.drawable.ic_schedule.AsPainter(), - text = R.string.title_schedule.AsString() - ) { goToSchedule.value = true } - } - } - } -} - -@Composable -fun BottomNavButton( - icon: Painter, - text: String, - color: Color = white, - onClick: () -> Unit, -) { - //Возможно стоит убрать анимацию клика - Box( - Modifier - .mainClickable(onClick = onClick, radius = 20.dp) - .padding(10.dp)) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center - ) { - Image( - painter = icon, - contentDescription = "", - modifier = Modifier.padding(5.dp) - ) - Text( - text = text, - style = MainText, - color = color, - overflow = TextOverflow.Ellipsis - ) - } - } -} @Composable fun AlmostOutlinedText( @@ -251,34 +109,6 @@ fun ProfileCard( } } -@Composable -fun LoginBackground() { - val icon = R.drawable.ic_header.AsPainter() - val size = icon.intrinsicSize - Column { - Image( - painter = icon, - contentDescription = "", - modifier = Modifier.fillMaxWidth(), - contentScale = ContentScale.FillWidth, - alignment = Alignment.TopStart - ) - } -} - -@Composable -fun BigLoginBackground() { - val icon = R.drawable.header_big.AsPainter() - Column(Modifier.fillMaxWidth()) { - Image( - painter = icon, - contentDescription = "", - modifier = Modifier.fillMaxWidth(), - alignment = Alignment.TopStart - ) - } -} - @Composable fun MainTextField( value: MutableState, @@ -425,180 +255,6 @@ fun MainButton( } } -@Composable -fun LoginScreenContent( - login: MutableState, - password: MutableState, - isError: MutableState, - isRememberMe: MutableState, - onSignIn: () -> Unit, - onSignUp: () -> Unit, - onForgetPassword: () -> Unit -) { - val configuration = LocalConfiguration.current - val screenWidth = configuration.screenWidthDp - - var defaultPadding = 65.dp - - val isNormal = screenWidth < 500 - - Box(Modifier.fillMaxSize()) { - - if(!isNormal) BigLoginBackground() - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - if (isNormal) LoginBackground() - else { - defaultPadding *= 4 - } - Row(horizontalArrangement = Arrangement.Center) { - Text( - text = R.string.title_welcome.AsString(), - style = Big, - textAlign = TextAlign.Center, - modifier = Modifier.padding(defaultPadding, 20.dp, defaultPadding, 40.dp), - color = dark - ) - } - Column( - modifier = Modifier.padding(defaultPadding, 0.dp) - ) { - Column(Modifier.fillMaxWidth()) { - MainTextField( - value = login, - label = R.string.title_login.AsString(), - isError = isError - ) - SpacerHeight(15.dp) - MainTextField( - value = password, - keyboardType = KeyboardType.Password, - label = R.string.title_password.AsString(), - isError = isError - ) - } - SpacerHeight(5.dp) - Row( - modifier = Modifier - .fillMaxWidth() - .height(30.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row( - modifier = Modifier.fillMaxHeight(), - verticalAlignment = Alignment.CenterVertically - ) { - MainCheckbox(isRememberMe) { } - SpacerWidth(5.dp) - Text( - text = R.string.title_remember_me.AsString(), - style = MainText, - color = dark - ) - } - Text( - text = R.string.title_forget_password.AsString(), - style = MainText, - color = dark, - modifier = Modifier.clickable(onClick = onForgetPassword) - ) - } - Column( - modifier = Modifier - .fillMaxSize() - .padding(0.dp, 10.dp), - verticalArrangement = Arrangement.Center - ) { - MainButton( - text = R.string.title_sign_in.AsString(), - onClick = onSignIn - ) - SpacerHeight(5.dp) - Text( - text = R.string.title_sign_up.AsString(), - modifier = Modifier - .clickable(onClick = onSignUp) - .padding(5.dp) - .fillMaxWidth(), - textAlign = TextAlign.Center, - style = Mini, - color = dark - ) - } - } - } - } -} - -@Composable -fun SubScreenNavButton( - text: String, - isClicked: MutableState, - onCLick: () -> Unit -) { - val colors = ButtonDefaults.buttonColors( - backgroundColor = if(isClicked.value) mainBlue else white, - contentColor = if(isClicked.value) white else dark - ) - Button( - onClick = { - isClicked.value = !isClicked.value - onCLick.invoke() - }, - shape = RoundedCornerShape(20.dp), - border = BorderStroke( - width = 1.dp, - color = mainBlue, - ), - colors = colors, - ) { - Text( - text = text, - style= MainText - ) - } -} - -data class SubScreenData( - val title: String, - val to: BaseNavRote, - val isClicked: MutableState -) - - -@Composable -fun SubScreenNavBar( - list: List, - subScreenNavController: NavController -) { - val horizontalScroll = rememberScrollState() - Row( - modifier = Modifier - .fillMaxWidth() - .horizontalScroll(horizontalScroll) - .padding(15.dp), - horizontalArrangement = Arrangement.spacedBy(15.dp) - ){ - for(data in list){ - if(data.isClicked.value) - subScreenNavController.navigate(data.to) - SubScreenNavButton( - text = data.title, - isClicked = data.isClicked - ) { - for(d in list){ - if(data != d) - d.isClicked.value = false - } - } - } - } -} - //Базовая карточка для оценок, карточек с диаграммами @Composable fun BaseCard( @@ -746,349 +402,6 @@ fun LastMarkCard( } } -enum class Month{ - January, - February, - March, - April, - May, - June, - July, - August, - September, - October, - November, - December, -} - - -@OptIn(ExperimentalFoundationApi::class) -@Composable -fun NewCardCalendar( - month: Month, - year: Int, - onClick: (day: Int) -> Unit, -) { - val listOfDays = listOf( - R.string.title_mon.AsString(), - R.string.title_tue.AsString(), - R.string.title_wed.AsString(), - R.string.title_thu.AsString(), - R.string.title_fri.AsString(), - R.string.title_sat.AsString(), - ) - - //TODO Сделать сворачивание календаря - val expanded = remember { - mutableStateOf(true) - } - - Card( - backgroundColor = white, - modifier = Modifier.defaultMinSize(minWidth = 300.dp), - shape = RoundedCornerShape(10.dp), - elevation = 5.dp - ){ - Column( - modifier = Modifier.padding(0.dp) - ) { - /*LazyRow( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .padding(10.dp, 10.dp, 10.dp, 5.dp) - .defaultMinSize(minWidth = 300.dp) - ){ - items(listOfDays){ - Text( - text = it, - style = MainText, - color = dark - ) - } - } - Box( - Modifier - .defaultMinSize(minWidth = 320.dp) - .height(1.dp) - .background(Color.Black))*/ - LazyVerticalGrid( - columns = GridCells.Fixed(count = 6), - horizontalArrangement = Arrangement.spacedBy(0.dp), - ){ - items(listOfDays){ - Text( - text = it, - style = MainText, - color = dark - ) - } - items((1..30).toList()){ - NumberOnCalendar( - isClicked = remember { mutableStateOf(false) }, - number = it.toString() - ) { - - } - } - } - - val offset = 8 - getMonthDaysBy(year, month, DayOfWeek.MONDAY)[0] - - val clicked = mutableListOf>() - } - } -} - -@Composable -fun CardCalendar( - month: Month, - year: Int, - onClick: (day: Int) -> Unit, -) { - val listOfDays = listOf( - R.string.title_mon.AsString(), - R.string.title_tue.AsString(), - R.string.title_wed.AsString(), - R.string.title_thu.AsString(), - R.string.title_fri.AsString(), - R.string.title_sat.AsString(), - ) - - //TODO Сделать сворачивание календаря - val expanded = remember { - mutableStateOf(true) - } - - Card( - backgroundColor = white, - modifier = Modifier.defaultMinSize(minWidth = 200.dp), - shape = RoundedCornerShape(10.dp), - elevation = 5.dp - ){ - Box( - modifier = Modifier - .fillMaxWidth() - .padding(15.dp) - ){ - Column( - modifier = Modifier.fillMaxWidth() - ) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(10.dp, 0.dp, 10.dp, 5.dp) - ){ - for(day in listOfDays){ - Text( - text = day, - style = MainText, - color = dark - ) - } - } - Divider( - thickness = 1.dp, - color = dark, - modifier = Modifier.padding(0.dp, 0.dp, 0.dp, 10.dp) - ) - - val offset = 8 - getMonthDaysBy(year, month, DayOfWeek.MONDAY)[0] - - val clicked = mutableListOf>() - var lastClicked = 0 - var lastInd = 0 - - Row( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier - .fillMaxWidth() - .padding(0.dp, 10.dp, 0.dp, 5.dp) - ) { - if(expanded.value) - for(day in DayOfWeek.values()){ - if(day != DayOfWeek.SUNDAY) Column { - val list = getMonthDaysBy(year, month, day) - var len = 0 - if(day.ordinal + 1 <= offset && offset !in 6..7){ - EmptyNumberOnCalendar() - len++ - } - for(i in list.indices){ - val data = list[i] - val isClicked = remember { mutableStateOf(false) } - clicked.add(isClicked) - NumberOnCalendar( - isClicked = isClicked, - number = data.toString() - ) { - expanded.value != expanded.value - lastClicked = data - lastInd = i - for(click in clicked) - if(click != isClicked) - click.value = false - onClick.invoke(data) - } - len++ - } - if(day.ordinal + 1 > offset && len < 5){ - EmptyNumberOnCalendar() - } - } - } - else{ - - } - } - } - } - } -} - -@Composable -fun EmptyNumberOnCalendar(){ - NumberOnCalendar(isClicked = remember { - mutableStateOf(false) - }, number = "") {} -} - -@Composable -fun NumberOnCalendar( - isClicked: MutableState, - number: String, - onClick: () -> Unit -) { - val isClickedModifier = if(isClicked.value) - Modifier - .border( - width = 1.dp, - color = mainBlue, - shape = RoundedCornerShape(20.dp) - ) - .background( - color = lightGray, - shape = RoundedCornerShape(20.dp) - ) - .padding(2.dp) - else Modifier - Box( - modifier = Modifier - .size(30.dp) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(radius = 15.dp, color = dark), - onClick = { - if (number != "") { - isClicked.value = !isClicked.value - onClick.invoke() - } - } - ) - .then(isClickedModifier), - contentAlignment = Alignment.Center, - ){ - Text( - text = number, - style = H2, - color = dark, - ) - } -} - - -@Composable -fun MonthToggle( - startMonth: Month, - startYear: Int, - onClick: (day: Int) -> Unit -) { - - var listOfMonth = listOf( - R.string.title_january.AsString(), - R.string.title_february.AsString(), - R.string.title_march.AsString(), - R.string.title_april.AsString(), - R.string.title_may.AsString(), - R.string.title_june.AsString(), - R.string.title_july.AsString(), - R.string.title_august.AsString(), - R.string.title_september.AsString(), - R.string.title_october.AsString(), - R.string.title_november.AsString(), - R.string.title_december.AsString() - ) - - val month = remember { - mutableStateOf(startMonth) - } - val year = remember { - mutableStateOf(startYear) - } - - val configuration = LocalConfiguration.current - val screenWidth = configuration.screenWidthDp - - var defaultPadding = 35.dp - - val isNormal = screenWidth < 500 - if(!isNormal) defaultPadding *= 4 - Column() { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(0.dp, 20.dp), - horizontalArrangement = Arrangement.Center - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(10.dp), - verticalAlignment = Alignment.CenterVertically - ) { - IconButton(onClick = { - var ind = month.value.ordinal - 1 - if(ind == -1){ - ind = 11 - year.value = year.value - 1 - } - month.value = Month.values()[ind] - }) { - Image( - painter = R.drawable.ic_month_swipe_left.AsPainter(), - contentDescription = "left", - alignment = Alignment.Center - ) - } - Text( - text = "${listOfMonth[month.value.ordinal]} ${year.value}", - style = H1, - color = dark - ) - IconButton( - onClick = { - var ind = month.value.ordinal + 1 - if(ind == 12){ - ind = 0 - year.value = year.value + 1 - } - month.value = Month.values()[ind] - } - ) { - Image( - painter = R.drawable.ic_month_swipe_right.AsPainter(), - contentDescription = "right", - alignment = Alignment.Center - ) - } - } - } - CardCalendar( - month = month.value, - year = year.value, - onClick = onClick - ) - } -} - @Composable fun UnderlinedText( text: String diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/LoginScreen.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/LoginScreen.kt new file mode 100644 index 0000000..f349e40 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/LoginScreen.kt @@ -0,0 +1,158 @@ +package ru.megboyzz.dnevnik.screens.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import ru.megboyzz.dnevnik.* +import ru.megboyzz.dnevnik.ui.theme.Big +import ru.megboyzz.dnevnik.ui.theme.MainText +import ru.megboyzz.dnevnik.ui.theme.Mini +import ru.megboyzz.dnevnik.ui.theme.dark +import ru.megboyzz.dnevnik.R + +@Composable +fun LoginScreenContent( + login: MutableState, + password: MutableState, + isError: MutableState, + isRememberMe: MutableState, + onSignIn: () -> Unit, + onSignUp: () -> Unit, + onForgetPassword: () -> Unit +) { + val configuration = LocalConfiguration.current + val screenWidth = configuration.screenWidthDp + + var defaultPadding = 65.dp + + val isNormal = screenWidth < 500 + + Box(Modifier.fillMaxSize()) { + + if(!isNormal) BigLoginBackground() + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + if (isNormal) LoginBackground() + else { + defaultPadding *= 4 + } + Row(horizontalArrangement = Arrangement.Center) { + Text( + text = R.string.title_welcome.AsString(), + style = Big, + textAlign = TextAlign.Center, + modifier = Modifier.padding(defaultPadding, 20.dp, defaultPadding, 40.dp), + color = dark + ) + } + Column( + modifier = Modifier.padding(defaultPadding, 0.dp) + ) { + Column(Modifier.fillMaxWidth()) { + MainTextField( + value = login, + label = R.string.title_login.AsString(), + isError = isError + ) + SpacerHeight(15.dp) + MainTextField( + value = password, + keyboardType = KeyboardType.Password, + label = R.string.title_password.AsString(), + isError = isError + ) + } + SpacerHeight(5.dp) + Row( + modifier = Modifier + .fillMaxWidth() + .height(30.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + modifier = Modifier.fillMaxHeight(), + verticalAlignment = Alignment.CenterVertically + ) { + MainCheckbox(isRememberMe) { } + SpacerWidth(5.dp) + Text( + text = R.string.title_remember_me.AsString(), + style = MainText, + color = dark + ) + } + Text( + text = R.string.title_forget_password.AsString(), + style = MainText, + color = dark, + modifier = Modifier.clickable(onClick = onForgetPassword) + ) + } + Column( + modifier = Modifier + .fillMaxSize() + .padding(0.dp, 10.dp), + verticalArrangement = Arrangement.Center + ) { + MainButton( + text = R.string.title_sign_in.AsString(), + onClick = onSignIn + ) + SpacerHeight(5.dp) + Text( + text = R.string.title_sign_up.AsString(), + modifier = Modifier + .clickable(onClick = onSignUp) + .padding(5.dp) + .fillMaxWidth(), + textAlign = TextAlign.Center, + style = Mini, + color = dark + ) + } + } + } + } +} + +@Composable +fun LoginBackground() { + val icon = R.drawable.ic_header.AsPainter() + val size = icon.intrinsicSize + Column { + Image( + painter = icon, + contentDescription = "", + modifier = Modifier.fillMaxWidth(), + contentScale = ContentScale.FillWidth, + alignment = Alignment.TopStart + ) + } +} + +@Composable +fun BigLoginBackground() { + val icon = R.drawable.header_big.AsPainter() + Column(Modifier.fillMaxWidth()) { + Image( + painter = icon, + contentDescription = "", + modifier = Modifier.fillMaxWidth(), + alignment = Alignment.TopStart + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/MainScaffold.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/MainScaffold.kt new file mode 100644 index 0000000..748ff17 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/MainScaffold.kt @@ -0,0 +1,78 @@ +package ru.megboyzz.dnevnik.screens.ui + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import kotlinx.coroutines.launch +import ru.megboyzz.dnevnik.SpacerWidth +import ru.megboyzz.dnevnik.ui.theme.mainBlue +import ru.megboyzz.dnevnik.ui.theme.white + +@Composable +fun MainScaffold( + icon: Painter, + title: String, + /* TODO viewModel */ + navController: NavController, + scaffoldState: ScaffoldState, + content: (@Composable (it: PaddingValues) -> Unit), +) { + val scope = rememberCoroutineScope() + Scaffold( + scaffoldState = scaffoldState, + topBar = { + TopAppBar( + backgroundColor = mainBlue, + contentColor = Color.White + ) { + IconButton( + onClick = { + val drawerState = scaffoldState.drawerState + scope.launch { + if (drawerState.isClosed) + drawerState.open() + else + drawerState.close() + } + } + ) { + + Icon( + imageVector = Icons.Filled.Menu, + contentDescription = "menu", + tint = white + ) + } + SpacerWidth(width = 5.dp) + Text( + text = title, + fontSize = 16.sp, + color = white + ) + Spacer(Modifier.weight(1f)) + Box(Modifier.padding(15.dp, 15.dp)) { + Image(painter = icon, contentDescription = "icon") + } + } + }, + content = content, + drawerBackgroundColor = mainBlue, + drawerContentColor = white, + drawerShape = drawerShape, + drawerContent = { DrawerContent(navController, scaffoldState) }, + bottomBar = { BottomBar(navController) } + ) +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Previews.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Previews.kt index 6ec873e..f247523 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Previews.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/Previews.kt @@ -8,7 +8,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.* -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -16,6 +16,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.google.accompanist.navigation.animation.rememberAnimatedNavController import ru.megboyzz.dnevnik.screens.ui.* +import ru.megboyzz.dnevnik.screens.ui.calendar.NiceCalendar +import ru.megboyzz.dnevnik.screens.ui.piechart.PieChart +import ru.megboyzz.dnevnik.screens.ui.piechart.PieChartCard +import ru.megboyzz.dnevnik.screens.ui.piechart.PieChartData import ru.megboyzz.dnevnik.ui.theme.mainBlue import ru.megboyzz.dnevnik.ui.theme.white import java.time.YearMonth @@ -103,16 +107,6 @@ fun BaseCardPrev() { } } -@Preview -@Composable -fun MonthTglPrev() { - MonthToggle( - startMonth = Month.January, startYear = 2023 - ){ - - } -} - @Preview @Composable fun UnderlinedTextPrev() { @@ -217,22 +211,16 @@ fun AllMarksCardPrev1() { } } -@Preview -@Composable -fun NewCalendarPrev() { - NewCardCalendar( - month = Month.January, - year = 2023 - ){ - - } -} - @Preview @Composable fun NiceCalendarPrev() { + + var startMonth by remember{ + mutableStateOf(YearMonth.now()) + } + NiceCalendar( - startMonth = YearMonth.now() + currentMonth = startMonth, ){ day, month, year -> } diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/SubScreenNavigation.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/SubScreenNavigation.kt new file mode 100644 index 0000000..1d76714 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/SubScreenNavigation.kt @@ -0,0 +1,89 @@ +package ru.megboyzz.dnevnik.screens.ui + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import ru.megboyzz.dnevnik.navigate +import ru.megboyzz.dnevnik.navigation.BaseNavRote +import ru.megboyzz.dnevnik.ui.theme.MainText +import ru.megboyzz.dnevnik.ui.theme.dark +import ru.megboyzz.dnevnik.ui.theme.mainBlue +import ru.megboyzz.dnevnik.ui.theme.white + +@Composable +fun SubScreenNavButton( + text: String, + isClicked: MutableState, + onCLick: () -> Unit +) { + val colors = ButtonDefaults.buttonColors( + backgroundColor = if(isClicked.value) mainBlue else white, + contentColor = if(isClicked.value) white else dark + ) + Button( + onClick = { + isClicked.value = !isClicked.value + onCLick.invoke() + }, + shape = RoundedCornerShape(20.dp), + border = BorderStroke( + width = 1.dp, + color = mainBlue, + ), + colors = colors, + ) { + Text( + text = text, + style= MainText + ) + } +} + +data class SubScreenData( + val title: String, + val to: BaseNavRote, + val isClicked: MutableState +) + + +@Composable +fun SubScreenNavBar( + list: List, + subScreenNavController: NavController +) { + val horizontalScroll = rememberScrollState() + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(horizontalScroll) + .padding(15.dp), + horizontalArrangement = Arrangement.spacedBy(15.dp) + ){ + for(data in list){ + if(data.isClicked.value) + subScreenNavController.navigate(data.to) + SubScreenNavButton( + text = data.title, + isClicked = data.isClicked + ) { + for(d in list){ + if(data != d) + d.isClicked.value = false + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/Calendar.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/Calendar.kt new file mode 100644 index 0000000..19b1c6e --- /dev/null +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/Calendar.kt @@ -0,0 +1,259 @@ +package ru.megboyzz.dnevnik.screens.ui.calendar + +import android.util.Log +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.WeekCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState +import com.kizitonwose.calendar.core.* +import kotlinx.coroutines.launch +import ru.megboyzz.dnevnik.* +import ru.megboyzz.dnevnik.R +import ru.megboyzz.dnevnik.ui.theme.* +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.Month +import java.time.YearMonth +import java.time.format.TextStyle +import java.util.* + +//TODO доделать свертку календаря, он почти идеально работает +@Composable +fun NiceCalendar( + currentMonth: YearMonth, + onClick: (day: CalendarDay, month: YearMonth, year: Int) -> Unit +) { + + val selections = remember { mutableStateListOf() } + val daysOfWeek = remember { daysOfWeek() } + + val state = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstVisibleMonth = currentMonth, + firstDayOfWeek = daysOfWeek.first() + ) + + val weekState = rememberWeekCalendarState( + startDate = currentMonth.atDay(1), + endDate = currentMonth.atDay(7), + firstVisibleWeekDate = currentMonth.atDay(1), + firstDayOfWeek = daysOfWeek.first() + ) + + var selectedDate by remember { mutableStateOf(null) } + val coroutineScope = rememberCoroutineScope() + val visibleMonth = rememberFirstMostVisibleMonth(state, viewportPercent = 90f) + + Column { + + SimpleCalendarTitle( + currentMonth = visibleMonth.yearMonth, + goToPrevious = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.previousMonth) + } + }, + goToNext = { + coroutineScope.launch { + state.animateScrollToMonth(state.firstVisibleMonth.yearMonth.nextMonth) + } + } + ) + + Card( + shape = RoundedCornerShape(10.dp), + modifier = Modifier.defaultMinSize(300.dp) + ) { + if(selectedDate == null) { + HorizontalCalendar( + state = state, + dayContent = { day -> + Day(day, isSelected = selectedDate == day.date) { day -> + selectedDate = if (selectedDate == day.date) null else day.date + onClick(day, currentMonth, currentMonth.year) + } + }, + monthHeader = { DaysOfWeekTitle(daysOfWeek = DayOfWeek.values().asList()) }, + modifier = Modifier.padding(10.dp) + ) + } + else { + //TODO по возможности устранить повторы + WeekCalendar( + state = weekState, + weekHeader = { _ -> + DaysOfWeekTitle(daysOfWeek = DayOfWeek.values().asList()) + }, + dayContent = { day -> + Day( + CalendarDay(day.date, DayPosition.InDate), + isSelected = selectedDate == day.date + ) { calendarDay -> + selectedDate = if (selectedDate == calendarDay.date) null else calendarDay.date + onClick(calendarDay, currentMonth, currentMonth.year) + } + }, + modifier = Modifier.padding(10.dp) + ) + } + } + } +} + + +@Composable +fun CalendarNavigationIcon( + icon: Painter, + contentDescription: String, + onClick: () -> Unit, +) = Box( + modifier = Modifier + .size(35.dp) + .aspectRatio(1f) + .clip(shape = CircleShape) + .clickable(role = Role.Button, onClick = onClick), +) { + Icon( + modifier = Modifier + .fillMaxSize() + .padding(4.dp) + .align(Alignment.Center), + painter = icon, + contentDescription = contentDescription, + ) +} + + +@Composable +fun SimpleCalendarTitle( + modifier: Modifier = Modifier, + currentMonth: YearMonth, + goToPrevious: () -> Unit, + goToNext: () -> Unit, +) { + + val listOfMonth = listOf( + R.string.title_january.AsString(), + R.string.title_february.AsString(), + R.string.title_march.AsString(), + R.string.title_april.AsString(), + R.string.title_may.AsString(), + R.string.title_june.AsString(), + R.string.title_july.AsString(), + R.string.title_august.AsString(), + R.string.title_september.AsString(), + R.string.title_october.AsString(), + R.string.title_november.AsString(), + R.string.title_december.AsString() + ) + + Box( + modifier = Modifier.fillMaxWidth().padding(10.dp), + contentAlignment = Alignment.Center, + ) { + Row( + modifier = modifier + .height(40.dp) + .padding(vertical = 0.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + CalendarNavigationIcon( + icon = R.drawable.ic_month_swipe_left.AsPainter(), + contentDescription = "Previous", + onClick = goToPrevious, + ) + Text( + text = currentMonth.displayText(listOfMonth), + style = H1, + color = dark + ) + CalendarNavigationIcon( + icon = R.drawable.ic_month_swipe_right.AsPainter(), + contentDescription = "Next", + onClick = goToNext, + ) + } + } +} + + + +fun YearMonth.displayText(list: List): String { + return "${list[this.month.ordinal]} ${this.year}" +} + +fun Month.displayText(short: Boolean = true): String { + val style = if (short) TextStyle.SHORT else TextStyle.FULL_STANDALONE + return getDisplayName(style, Locale.getDefault()) +} + +fun DayOfWeek.displayText(uppercase: Boolean = false): String { + return getDisplayName(TextStyle.SHORT, Locale.getDefault()).let { value -> + if (uppercase) value.uppercase(Locale.getDefault()) else value + } +} + +@Composable +fun DaysOfWeekTitle(daysOfWeek: List) { + Column { + Row(modifier = Modifier.fillMaxWidth()) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + text = dayOfWeek.getDisplayName(TextStyle.SHORT, Locale.getDefault()).uppercase(), + style = MainText + ) + } + } + Divider( + thickness = 1.dp, + color = dark, + modifier = Modifier.padding(vertical = 10.dp) + ) + } +} +@Composable +fun Day(day: CalendarDay, isSelected: Boolean, onClick: (CalendarDay) -> Unit) { + Box( + modifier = Modifier + .aspectRatio(ratio = 1f) // This is important for square sizing! + .clip(CircleShape) + .background(color = if (isSelected) lightGray else Color.Transparent) + .border( + border = BorderStroke( + width = 1.dp, + color = if (isSelected) mainBlue else Color.Transparent + ), + shape = CircleShape + ) + .clickable( + enabled = day.position == DayPosition.MonthDate, + onClick = { onClick(day) } + ), + contentAlignment = Alignment.Center + ) { + Text( + text = day.date.dayOfMonth.toString(), + style = MainText, + color = if (day.position == DayPosition.MonthDate) dark else Color.Gray + ) + } +} diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/CalendarConstants.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/CalendarConstants.kt new file mode 100644 index 0000000..2673879 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/calendar/CalendarConstants.kt @@ -0,0 +1,8 @@ +package ru.megboyzz.dnevnik.screens.ui.calendar + +import java.time.YearMonth + +//Константы для календаря + +val startMonth = YearMonth.of(2009, 9) +val endMonth = YearMonth.of(2100, 9) \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/DataPieChart.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/piechart/DataPieChart.kt similarity index 93% rename from app/src/main/java/ru/megboyzz/dnevnik/screens/ui/DataPieChart.kt rename to app/src/main/java/ru/megboyzz/dnevnik/screens/ui/piechart/DataPieChart.kt index 0ce33a6..ef51ef6 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/DataPieChart.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/piechart/DataPieChart.kt @@ -1,8 +1,7 @@ -package ru.megboyzz.dnevnik.screens.ui +package ru.megboyzz.dnevnik.screens.ui.piechart import android.content.Context import android.graphics.Typeface -import android.view.ViewGroup import android.widget.LinearLayout import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb @@ -12,7 +11,6 @@ import com.github.mikephil.charting.data.PieData import com.github.mikephil.charting.data.PieDataSet import com.github.mikephil.charting.data.PieEntry import com.github.mikephil.charting.formatter.PercentFormatter -import ru.megboyzz.dnevnik.ui.theme.MainText class DataPieChart(context: Context) : PieChart(context) { diff --git a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/PieChart.kt b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/piechart/PieChart.kt similarity index 94% rename from app/src/main/java/ru/megboyzz/dnevnik/screens/ui/PieChart.kt rename to app/src/main/java/ru/megboyzz/dnevnik/screens/ui/piechart/PieChart.kt index e96367b..929a582 100644 --- a/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/PieChart.kt +++ b/app/src/main/java/ru/megboyzz/dnevnik/screens/ui/piechart/PieChart.kt @@ -1,4 +1,4 @@ -package ru.megboyzz.dnevnik.screens.ui +package ru.megboyzz.dnevnik.screens.ui.piechart import androidx.compose.foundation.layout.* import androidx.compose.material.Text @@ -12,6 +12,8 @@ import ru.megboyzz.dnevnik.AsString import ru.megboyzz.dnevnik.ui.theme.H2 import ru.megboyzz.dnevnik.ui.theme.dark import ru.megboyzz.dnevnik.R +import ru.megboyzz.dnevnik.screens.ui.BaseCard +import ru.megboyzz.dnevnik.screens.ui.EmptyMessage /** * Класс для предоставления данных для круговых диаграм diff --git a/app/src/test/java/ru/megboyzz/dnevnik/UtilsTest.kt b/app/src/test/java/ru/megboyzz/dnevnik/UtilsTest.kt index 1e13ec9..0ebc180 100644 --- a/app/src/test/java/ru/megboyzz/dnevnik/UtilsTest.kt +++ b/app/src/test/java/ru/megboyzz/dnevnik/UtilsTest.kt @@ -8,12 +8,4 @@ import java.time.DayOfWeek class UtilsTest { - @Test - fun testForDaysFunc(){ - val days = getMonthDaysBy(2023, Month.January, DayOfWeek.MONDAY) - assertEquals( - listOf(2, 9, 16, 23, 30), - days - ) - } } \ No newline at end of file