mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 08:45:27 +02:00
Use Pager in PagedIsland
This commit is contained in:
parent
93404c1fac
commit
a58cb01f58
@ -18,76 +18,97 @@
|
||||
|
||||
package com.sadellie.unitto.core.ui.common
|
||||
|
||||
import androidx.annotation.IntRange
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
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.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.contentColorFor
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* [HorizontalPager] with a background and a page indicator.
|
||||
*
|
||||
* @param modifier [Modifier] that will be applied to the surround [Column].
|
||||
* @param pagerState [PagerState] that will passed [HorizontalPager].
|
||||
* @param backgroundColor [Color] for background.
|
||||
* @param pageIndicatorAlignment [Alignment.Horizontal] for page indicator.
|
||||
* @param onClick Called on all clicks, even if the page didn't change.
|
||||
* @param pageContent Page content. Use passed `currentPage` value.
|
||||
*/
|
||||
@Composable
|
||||
fun PagedIsland(
|
||||
modifier: Modifier = Modifier,
|
||||
@IntRange(from = 1) pagesCount: Int,
|
||||
onPageChange: (currentPage: Int) -> Unit = {},
|
||||
pagerState: PagerState,
|
||||
backgroundColor: Color = MaterialTheme.colorScheme.secondaryContainer,
|
||||
pageContent: @Composable ColumnScope.(currentPage: Int) -> Unit,
|
||||
pageIndicatorAlignment: Alignment.Horizontal = Alignment.End,
|
||||
onClick: () -> Unit = {},
|
||||
pageContent: @Composable (currentPage: Int) -> Unit,
|
||||
) {
|
||||
var currentPage: Int by remember { mutableIntStateOf(0) }
|
||||
val contentColor = MaterialTheme.colorScheme.contentColorFor(backgroundColor)
|
||||
val disabledContentColor = contentColor.copy(alpha = 0.5f)
|
||||
val corScope = rememberCoroutineScope()
|
||||
|
||||
AnimatedContent(
|
||||
modifier = modifier
|
||||
.squashable(
|
||||
onClick = {
|
||||
if (currentPage == pagesCount - 1) currentPage = 0 else currentPage++
|
||||
onPageChange(currentPage)
|
||||
},
|
||||
cornerRadiusRange = 8.dp..32.dp,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
.background(backgroundColor),
|
||||
targetState = currentPage
|
||||
) { state ->
|
||||
ProvideColor(color = contentColor) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
repeat(pagesCount) {
|
||||
ADot(color = if (it == state) contentColor else disabledContentColor)
|
||||
ProvideColor(color = contentColor) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(32.dp))
|
||||
.clickable {
|
||||
onClick()
|
||||
if (pagerState.currentPage == (pagerState.pageCount - 1)) return@clickable
|
||||
|
||||
corScope.launch {
|
||||
pagerState.animateScrollToPage(pagerState.currentPage + 1)
|
||||
}
|
||||
}
|
||||
pageContent(state)
|
||||
.background(backgroundColor)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, pageIndicatorAlignment),
|
||||
) {
|
||||
repeat(pagerState.pageCount) {
|
||||
PageDot(if (it == pagerState.currentPage) contentColor else disabledContentColor)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalPager(
|
||||
modifier = Modifier
|
||||
.animateContentSize()
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.Top,
|
||||
state = pagerState
|
||||
) { page ->
|
||||
pageContent(page)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ADot(
|
||||
private fun PageDot(
|
||||
color: Color,
|
||||
) {
|
||||
Canvas(modifier = Modifier.size(4.dp)) {
|
||||
@ -98,11 +119,16 @@ private fun ADot(
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewPagedIsland() {
|
||||
PagedIsland(pagesCount = 5) { currentPage ->
|
||||
Text("Current page: $currentPage")
|
||||
PagedIsland(
|
||||
modifier = Modifier.size(400.dp, 250.dp),
|
||||
pagerState = rememberPagerState { 5 }
|
||||
) { currentPage ->
|
||||
Column {
|
||||
Text("Current page: $currentPage")
|
||||
|
||||
if (currentPage == 3) {
|
||||
Text("Middle in: $currentPage")
|
||||
if (currentPage == 3) {
|
||||
Text("Middle in: $currentPage")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,14 @@ package com.sadellie.unitto.feature.datecalculator.components
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.sadellie.unitto.core.base.R
|
||||
@ -38,94 +41,112 @@ internal fun DateTimeResultBlock(
|
||||
diff: ZonedDateTimeDifference.Default,
|
||||
format: (BigDecimal) -> String
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
PagedIsland(
|
||||
modifier = modifier,
|
||||
pagesCount = 6,
|
||||
pagerState = rememberPagerState { 6 },
|
||||
onClick = { focusManager.clearFocus() },
|
||||
backgroundColor = MaterialTheme.colorScheme.tertiaryContainer
|
||||
) { currentPage ->
|
||||
when(currentPage) {
|
||||
0 -> {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_difference),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
Column {
|
||||
// Years
|
||||
if (diff.years > 0) {
|
||||
DateText(R.string.date_calculator_years, diff.years.toBigDecimal(), format)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_difference),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
Column {
|
||||
// Years
|
||||
if (diff.years > 0) {
|
||||
DateText(R.string.date_calculator_years, diff.years.toBigDecimal(), format)
|
||||
}
|
||||
|
||||
// Months
|
||||
if (diff.months > 0) {
|
||||
DateText(R.string.date_calculator_months, diff.months.toBigDecimal(), format)
|
||||
}
|
||||
// Months
|
||||
if (diff.months > 0) {
|
||||
DateText(R.string.date_calculator_months, diff.months.toBigDecimal(), format)
|
||||
}
|
||||
|
||||
// Days
|
||||
if (diff.days > 0) {
|
||||
DateText(R.string.date_calculator_days, diff.days.toBigDecimal(), format)
|
||||
}
|
||||
// Days
|
||||
if (diff.days > 0) {
|
||||
DateText(R.string.date_calculator_days, diff.days.toBigDecimal(), format)
|
||||
}
|
||||
|
||||
// Hours
|
||||
if (diff.hours > 0) {
|
||||
DateText(R.string.date_calculator_hours, diff.hours.toBigDecimal(), format)
|
||||
}
|
||||
// Hours
|
||||
if (diff.hours > 0) {
|
||||
DateText(R.string.date_calculator_hours, diff.hours.toBigDecimal(), format)
|
||||
}
|
||||
|
||||
// Minutes
|
||||
if (diff.minutes > 0) {
|
||||
DateText(R.string.date_calculator_minutes, diff.minutes.toBigDecimal(), format)
|
||||
// Minutes
|
||||
if (diff.minutes > 0) {
|
||||
DateText(R.string.date_calculator_minutes, diff.minutes.toBigDecimal(), format)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_years),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumYears, format)
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_years),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumYears, format)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2 -> {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_months),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumMonths, format)
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_months),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumMonths, format)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3 -> {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_days),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumDays, format)
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_days),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumDays, format)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4 -> {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_hours),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumHours, format)
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_hours),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumHours, format)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5 -> {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_minutes),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumMinutes, format)
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.date_calculator_minutes),
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
SelectionContainer {
|
||||
DateText(diff.sumMinutes, format)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,13 @@ package com.sadellie.unitto.feature.datecalculator.difference
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -47,6 +50,7 @@ import com.sadellie.unitto.feature.datecalculator.components.DateTimeDialogs
|
||||
import com.sadellie.unitto.feature.datecalculator.components.DateTimeResultBlock
|
||||
import com.sadellie.unitto.feature.datecalculator.components.DateTimeSelectorBlock
|
||||
import com.sadellie.unitto.feature.datecalculator.components.DialogState
|
||||
import java.math.BigDecimal
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
@ -72,50 +76,59 @@ private fun DateDifferenceView(
|
||||
) {
|
||||
var dialogState by remember { mutableStateOf(DialogState.NONE) }
|
||||
|
||||
FlowRow(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
maxItemsInEachRow = 2,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
DateTimeSelectorBlock(
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||
.weight(1f)
|
||||
.padding(top = 16.dp, start = 16.dp, end = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_calculator_start),
|
||||
dateTime = uiState.start,
|
||||
onClick = { dialogState = DialogState.FROM },
|
||||
onLongClick = { setStartDate(ZonedDateTimeUtils.nowWithMinutes()) },
|
||||
onTimeClick = { dialogState = DialogState.FROM_TIME },
|
||||
onDateClick = { dialogState = DialogState.FROM_DATE },
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
maxItemsInEachRow = 2,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_calculator_start),
|
||||
dateTime = uiState.start,
|
||||
onClick = { dialogState = DialogState.FROM },
|
||||
onLongClick = { setStartDate(ZonedDateTimeUtils.nowWithMinutes()) },
|
||||
onTimeClick = { dialogState = DialogState.FROM_TIME },
|
||||
onDateClick = { dialogState = DialogState.FROM_DATE },
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_calculator_end),
|
||||
dateTime = uiState.end,
|
||||
onClick = { dialogState = DialogState.TO },
|
||||
onLongClick = { setEndDate(ZonedDateTimeUtils.nowWithMinutes()) },
|
||||
onTimeClick = { dialogState = DialogState.TO_TIME },
|
||||
onDateClick = { dialogState = DialogState.TO_DATE },
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
DateTimeSelectorBlock(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
title = stringResource(R.string.date_calculator_end),
|
||||
dateTime = uiState.end,
|
||||
onClick = { dialogState = DialogState.TO },
|
||||
onLongClick = { setEndDate(ZonedDateTimeUtils.nowWithMinutes()) },
|
||||
onTimeClick = { dialogState = DialogState.TO_TIME },
|
||||
onDateClick = { dialogState = DialogState.TO_DATE },
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedContent(
|
||||
targetState = uiState.result,
|
||||
modifier = Modifier.weight(2f)
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { result ->
|
||||
when (result) {
|
||||
is ZonedDateTimeDifference.Default -> {
|
||||
DateTimeResultBlock(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
diff = result,
|
||||
format = {
|
||||
it.format(uiState.precision, uiState.outputFormat)
|
||||
@ -157,7 +170,18 @@ fun DateDifferenceViewPreview() {
|
||||
uiState = DifferenceUIState.Ready(
|
||||
start = ZonedDateTimeUtils.nowWithMinutes(),
|
||||
end = ZonedDateTimeUtils.nowWithMinutes().truncatedTo(ChronoUnit.MINUTES),
|
||||
result = ZonedDateTimeDifference.Zero,
|
||||
result = ZonedDateTimeDifference.Default(
|
||||
years = 1,
|
||||
months = 2,
|
||||
days = 3,
|
||||
hours = 4,
|
||||
minutes = 5,
|
||||
sumYears = BigDecimal("0.083"),
|
||||
sumMonths = BigDecimal("1.000"),
|
||||
sumDays = BigDecimal("30.000"),
|
||||
sumHours = BigDecimal("720.000"),
|
||||
sumMinutes = BigDecimal("43200.000"),
|
||||
),
|
||||
precision = 3,
|
||||
outputFormat = OutputFormat.PLAIN,
|
||||
formatterSymbols = FormatterSymbols.Spaces
|
||||
|
@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Architecture
|
||||
@ -122,7 +123,7 @@ fun FormattingScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
pagesCount = 2,
|
||||
pagerState = rememberPagerState { 2 },
|
||||
) { currentPage ->
|
||||
val preview = when (currentPage) {
|
||||
0 -> "123456.${"789123456".repeat(ceil(uiState.precision.toDouble() / 9.0).toInt())}"
|
||||
|
Loading…
x
Reference in New Issue
Block a user