Skip to content

Commit 0b9b24f

Browse files
committed
Add sections for percentage, price, and weight formatting in the decimal formatter
1 parent 5bf1bdc commit 0b9b24f

File tree

11 files changed

+446
-102
lines changed

11 files changed

+446
-102
lines changed

shared/src/commonMain/kotlin/dev/robercoding/decimal/formatter/App.kt

Lines changed: 17 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,20 @@ package dev.robercoding.decimal.formatter
33
import androidx.compose.foundation.layout.Arrangement
44
import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.padding
67
import androidx.compose.foundation.layout.systemBarsPadding
7-
import androidx.compose.foundation.text.KeyboardOptions
8-
import androidx.compose.material3.MaterialTheme
9-
import androidx.compose.material3.OutlinedTextFieldDefaults
8+
import androidx.compose.foundation.rememberScrollState
9+
import androidx.compose.foundation.verticalScroll
1010
import androidx.compose.material3.Surface
11-
import androidx.compose.material3.Text
1211
import androidx.compose.runtime.Composable
12+
import androidx.compose.runtime.collectAsState
1313
import androidx.compose.runtime.getValue
14-
import androidx.compose.runtime.mutableStateOf
1514
import androidx.compose.runtime.remember
16-
import androidx.compose.runtime.setValue
1715
import androidx.compose.ui.Alignment
1816
import androidx.compose.ui.Modifier
19-
import androidx.compose.ui.graphics.Color
20-
import androidx.compose.ui.text.input.KeyboardType
2117
import androidx.compose.ui.unit.dp
22-
import dev.robercoding.decimal.formatter.compose.components.DecimalTextField
23-
import dev.robercoding.decimal.formatter.compose.components.OutlinedDecimalTextField
24-
import dev.robercoding.decimal.formatter.compose.formatter.UiDecimalFormatter
25-
import dev.robercoding.decimal.formatter.compose.formatter.rememberUiDecimalFormatter
26-
import dev.robercoding.decimal.formatter.compose.model.DecimalValue
27-
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatterConfiguration
28-
import dev.robercoding.decimal.formatter.core.utils.logMessage
18+
import dev.robercoding.decimal.formatter.percentage.PercentageSection
19+
import dev.robercoding.decimal.formatter.price.PriceSection
2920
import org.jetbrains.compose.resources.ExperimentalResourceApi
3021

3122
@OptIn(ExperimentalResourceApi::class)
@@ -34,98 +25,22 @@ fun App() {
3425
Surface(
3526
modifier = Modifier.fillMaxSize().systemBarsPadding()
3627
) {
37-
val decimalFormatterPrefix = rememberUiDecimalFormatter(DecimalFormatterConfiguration.european(), prefix = "")
38-
val decimalFormatter = rememberUiDecimalFormatter(DecimalFormatterConfiguration.european())
39-
40-
var outlinedAmountValuePrefix by remember { mutableStateOf(decimalFormatterPrefix.format(50524.02.toString())) }
41-
var outlinedAmountValueWithoutPrefix by remember { mutableStateOf(decimalFormatter.format("552423232323,64")) }
42-
43-
var decimalValuePrefix by remember { mutableStateOf(decimalFormatterPrefix.format("552423232323,64")) }
44-
var decimalValueWithoutPrefix by remember { mutableStateOf(decimalFormatter.format("552423232323,64")) }
28+
val viewModel = remember { DecimalFormatterViewModel() }
29+
val state by viewModel.uiState.collectAsState()
4530

4631
Column(
4732
horizontalAlignment = Alignment.CenterHorizontally,
48-
verticalArrangement = Arrangement.spacedBy(16.dp)
33+
verticalArrangement = Arrangement.spacedBy(16.dp),
34+
modifier = Modifier.padding(horizontal = 32.dp).verticalScroll(rememberScrollState())
4935
) {
50-
// Outlined region
51-
OutlinedDecimalTextFieldComposable(
52-
currentAmount = outlinedAmountValuePrefix,
53-
onValueChange = { decimalValue -> outlinedAmountValuePrefix = decimalValue },
54-
decimalFormatter = decimalFormatterPrefix
55-
)
56-
57-
OutlinedDecimalTextFieldComposable(
58-
currentAmount = outlinedAmountValueWithoutPrefix,
59-
onValueChange = { decimalValue -> outlinedAmountValueWithoutPrefix = decimalValue },
60-
decimalFormatter = decimalFormatterPrefix
61-
)
62-
63-
// No decor TextField region
64-
DecimalTextFieldComposable(
65-
currentAmount = decimalValuePrefix,
66-
onFormattedValueChange = { decimalValue -> decimalValuePrefix = decimalValue },
67-
decimalFormatter = decimalFormatterPrefix
68-
)
69-
70-
DecimalTextFieldComposable(
71-
currentAmount = decimalValueWithoutPrefix,
72-
onFormattedValueChange = { decimalValue -> decimalValueWithoutPrefix = decimalValue },
73-
decimalFormatter = decimalFormatterPrefix
36+
PercentageSection()
37+
WeightSection()
38+
PriceSection(
39+
priceEuro = state.priceEuro,
40+
priceEuropean = state.priceEuropean,
41+
onPriceEuroChange = { newPrice -> viewModel.setPriceEuro(newPrice) },
42+
onPriceEuropeanChange = { newPrice -> viewModel.setPriceEuropean(newPrice) },
7443
)
7544
}
76-
7745
}
7846
}
79-
80-
@Composable
81-
fun OutlinedDecimalTextFieldComposable(
82-
currentAmount: DecimalValue,
83-
onValueChange: (DecimalValue) -> Unit,
84-
decimalFormatter: UiDecimalFormatter
85-
) {
86-
OutlinedDecimalTextField(
87-
modifier = Modifier,
88-
decimalValue = currentAmount,
89-
onValueChange = onValueChange,
90-
label = { Text("Price (€)", style = MaterialTheme.typography.labelSmall) },
91-
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
92-
textStyle = MaterialTheme.typography.bodyLarge,
93-
shape = MaterialTheme.shapes.small,
94-
colors = OutlinedTextFieldDefaults.colors().copy(
95-
focusedTextColor = if (currentAmount.display == "0,00") {
96-
logMessage("Using gray color for zero value")
97-
Color.Gray
98-
} else {
99-
logMessage("Using default color for non-zero value: $currentAmount")
100-
MaterialTheme.colorScheme.onSurface
101-
},
102-
unfocusedTextColor = if (currentAmount.display == "0,00") {
103-
logMessage("Using gray color for zero value")
104-
Color.Gray
105-
} else {
106-
logMessage("Using default color for non-zero value: $currentAmount")
107-
MaterialTheme.colorScheme.onSurface
108-
}
109-
),
110-
decimalFormatter = decimalFormatter
111-
)
112-
}
113-
114-
@Composable
115-
fun DecimalTextFieldComposable(
116-
currentAmount: DecimalValue,
117-
onFormattedValueChange: (DecimalValue) -> Unit,
118-
decimalFormatter: UiDecimalFormatter,
119-
) {
120-
DecimalTextField(
121-
modifier = Modifier,
122-
value = currentAmount,
123-
onValueChange = { value ->
124-
onFormattedValueChange(value)
125-
},
126-
prefix = "$",
127-
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
128-
textStyle = MaterialTheme.typography.headlineLarge,
129-
decimalFormatter = decimalFormatter
130-
)
131-
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dev.robercoding.decimal.formatter
2+
3+
import androidx.lifecycle.ViewModel
4+
import com.ionspin.kotlin.bignum.decimal.BigDecimal
5+
import dev.robercoding.decimal.formatter.core.model.FormattedDecimal
6+
import dev.robercoding.decimal.formatter.core.utils.logMessage
7+
import kotlinx.coroutines.flow.MutableStateFlow
8+
import kotlinx.coroutines.flow.StateFlow
9+
import kotlinx.coroutines.flow.asStateFlow
10+
import kotlinx.coroutines.flow.update
11+
12+
class DecimalFormatterViewModel : ViewModel() {
13+
14+
private val _uiState = MutableStateFlow(UiState())
15+
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
16+
17+
fun setPriceEuro(price: FormattedDecimal) {
18+
_uiState.update { it.copy(priceEuro = price) }
19+
}
20+
21+
fun setPriceEuropean(price: FormattedDecimal) {
22+
_uiState.update { it.copy(priceEuropean = price) }
23+
}
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dev.robercoding.decimal.formatter
2+
3+
import androidx.compose.foundation.layout.fillMaxWidth
4+
import androidx.compose.material3.MaterialTheme
5+
import androidx.compose.material3.Text
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
8+
9+
@Composable
10+
internal fun Header(
11+
text: String,
12+
modifier: Modifier = Modifier.fillMaxWidth()
13+
) {
14+
Text(
15+
modifier = modifier,
16+
text = text,
17+
style = MaterialTheme.typography.headlineSmall,
18+
color = MaterialTheme.colorScheme.onSurface
19+
)
20+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package dev.robercoding.decimal.formatter
2+
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.Spacer
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.height
7+
import androidx.compose.runtime.Composable
8+
import androidx.compose.ui.Modifier
9+
import androidx.compose.ui.unit.dp
10+
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatter
11+
import dev.robercoding.decimal.formatter.core.model.FormattedDecimal
12+
import dev.robercoding.decimal.formatter.price.DecimalTextFieldComposable
13+
import dev.robercoding.decimal.formatter.price.OutlinedDecimalTextFieldComposable
14+
15+
@Composable
16+
internal fun HeaderWithTextFields(
17+
header: String,
18+
value: FormattedDecimal?,
19+
onValueChange: (FormattedDecimal) -> Unit,
20+
decimalFormatter: DecimalFormatter,
21+
) {
22+
Column {
23+
Header(
24+
text = header,
25+
modifier = Modifier.fillMaxWidth()
26+
)
27+
OutlinedDecimalTextFieldComposable(
28+
currentAmount = value ?: decimalFormatter.createEmptyValue(),
29+
onValueChange = onValueChange,
30+
decimalFormatter = decimalFormatter
31+
)
32+
Spacer(modifier = Modifier.height(12.dp))
33+
DecimalTextFieldComposable(
34+
currentAmount = value ?: decimalFormatter.createEmptyValue(),
35+
onValueChange = onValueChange,
36+
decimalFormatter = decimalFormatter
37+
)
38+
}
39+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dev.robercoding.decimal.formatter
2+
3+
import dev.robercoding.decimal.formatter.core.model.FormattedDecimal
4+
5+
data class UiState(val priceEuropean: FormattedDecimal? = null, val priceEuro: FormattedDecimal? = null)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package dev.robercoding.decimal.formatter
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.getValue
5+
import androidx.compose.runtime.mutableStateOf
6+
import androidx.compose.runtime.remember
7+
import androidx.compose.runtime.setValue
8+
import dev.robercoding.decimal.formatter.compose.formatter.rememberDecimalFormatter
9+
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatterConfiguration
10+
import dev.robercoding.decimal.formatter.core.model.DecimalSeparator
11+
import dev.robercoding.decimal.formatter.core.model.ThousandSeparator
12+
13+
@Composable
14+
internal fun WeightSection() {
15+
val decimalFormatterEuropeanWeight = rememberDecimalFormatter(DecimalFormatterConfiguration(
16+
decimalSeparator = DecimalSeparator.COMMA,
17+
thousandSeparator = ThousandSeparator.DOT,
18+
decimalPlaces = 3,
19+
maxDigits = 6,
20+
suffix = " kg"
21+
22+
))
23+
var weight by remember { mutableStateOf(decimalFormatterEuropeanWeight.format("")) }
24+
25+
HeaderWithTextFields(
26+
header = "Weight format",
27+
value = weight,
28+
onValueChange = { weight = it },
29+
decimalFormatter = decimalFormatterEuropeanWeight
30+
)
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dev.robercoding.decimal.formatter.percentage
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.getValue
5+
import androidx.compose.runtime.mutableStateOf
6+
import androidx.compose.runtime.remember
7+
import androidx.compose.runtime.setValue
8+
import com.ionspin.kotlin.bignum.decimal.toBigDecimal
9+
import dev.robercoding.decimal.formatter.HeaderWithTextFields
10+
import dev.robercoding.decimal.formatter.compose.formatter.rememberDecimalFormatter
11+
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatterConfiguration
12+
import dev.robercoding.decimal.formatter.core.utils.logMessage
13+
import dev.robercoding.decimal.formatter.price.OutlinedDecimalTextFieldComposable
14+
15+
@Composable
16+
internal fun PercentageSection() {
17+
val decimalFormatterPercentage = rememberDecimalFormatter(DecimalFormatterConfiguration.percentage())
18+
var percentage by remember { mutableStateOf(decimalFormatterPercentage.format("")) }
19+
20+
HeaderWithTextFields(
21+
header = "Percentage format",
22+
value = percentage,
23+
onValueChange = {
24+
percentage = if (it.parseableValue.toBigDecimal() > 100.0) {
25+
decimalFormatterPercentage.format("100.00")
26+
} else {
27+
it
28+
}
29+
},
30+
decimalFormatter = decimalFormatterPercentage
31+
)
32+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package dev.robercoding.decimal.formatter.price
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Spacer
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.height
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Alignment
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.unit.dp
12+
import dev.robercoding.decimal.formatter.Header
13+
import dev.robercoding.decimal.formatter.HeaderWithTextFields
14+
import dev.robercoding.decimal.formatter.compose.formatter.rememberDecimalFormatter
15+
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatter
16+
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatterConfiguration
17+
import dev.robercoding.decimal.formatter.core.model.FormattedDecimal
18+
19+
@Composable
20+
internal fun EuSection(
21+
priceEuropean: FormattedDecimal?, // VM example
22+
priceEuro: FormattedDecimal?, // VM example
23+
onPriceEuroChange: (FormattedDecimal) -> Unit,
24+
onPriceEuropeanChange: (FormattedDecimal) -> Unit,
25+
) {
26+
val decimalFormatterEuro = rememberDecimalFormatter(DecimalFormatterConfiguration.euro())
27+
val decimalFormatterEuropean = rememberDecimalFormatter(DecimalFormatterConfiguration.european())
28+
29+
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
30+
31+
HeaderWithTextFields(
32+
header = "Euro format",
33+
value = priceEuro,
34+
onValueChange = onPriceEuroChange,
35+
decimalFormatter = decimalFormatterEuro
36+
)
37+
38+
// European section header
39+
HeaderWithTextFields(
40+
header = "European format",
41+
value = priceEuropean,
42+
onValueChange = onPriceEuropeanChange,
43+
decimalFormatter = decimalFormatterEuropean
44+
)
45+
}
46+
}

0 commit comments

Comments
 (0)