diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 81ab612..4b613d3 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,9 +10,11 @@ jobs: name: Run tests runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - uses: actions/cache@v4 + - name: Read cache + uses: actions/cache@v4 with: path: | ~/.gradle/caches @@ -21,10 +23,11 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - - uses: actions/setup-java@v4 + - name: Setup Java + uses: actions/setup-java@v4 with: distribution: 'corretto' - java-version: '17' + java-version: '21' - name: Run tests run: | diff --git a/README.md b/README.md index b4aa7cb..66a2ad4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Add the following dependency to your project: ```gradle dependencies { - implementation("dev.voir.formica:1.0.0-alpha01") + implementation("dev.voir.formica:1.0.0-alpha01") // Not available on public now, you can publish to mavenLocal. } ``` diff --git a/formica/build.gradle.kts b/formica/build.gradle.kts index 42022ed..c2c7fe8 100644 --- a/formica/build.gradle.kts +++ b/formica/build.gradle.kts @@ -64,11 +64,11 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } kotlin { - jvmToolchain(11) + jvmToolchain(21) } } diff --git a/formica/src/commonMain/kotlin/dev/voir/formica/ui/FormicaField.kt b/formica/src/commonMain/kotlin/dev/voir/formica/ui/FormicaField.kt index 9d6bd3e..e5b21f4 100644 --- a/formica/src/commonMain/kotlin/dev/voir/formica/ui/FormicaField.kt +++ b/formica/src/commonMain/kotlin/dev/voir/formica/ui/FormicaField.kt @@ -1,7 +1,8 @@ package dev.voir.formica.ui import androidx.compose.runtime.Composable -import dev.voir.formica.FormicaField +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.remember import dev.voir.formica.FormicaFieldResult import dev.voir.formica.rules.ValidationRule import dev.voir.formica.scopes.FormicaFieldScope @@ -17,18 +18,21 @@ fun FormicaScope.FormicaField( customValidation: ((Value?) -> FormicaFieldResult)? = null, validateOnChange: Boolean = true, content: @Composable FormicaFieldScope.() -> Unit -): FormicaField { - val field = - registerField( - name = name, - required = required, - requiredError = requiredError, - validators = validators, - customValidation = customValidation, - validateOnChange = validateOnChange - ) - val scope = FormicaFieldScope(formField = field) - scope.content() +) { + val scope = remember { + derivedStateOf { + FormicaFieldScope( + formField = registerField( + name = name, + required = required, + requiredError = requiredError, + validators = validators, + customValidation = customValidation, + validateOnChange = validateOnChange + ) + ) + } + } - return field + scope.value.content() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ffcd8aa..8c4d4a5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] -agp = "8.7.2" -android-compileSdk = "34" +agp = "8.7.3" +android-compileSdk = "35" android-minSdk = "24" -android-targetSdk = "34" -compose-plugin = "1.7.1" # https://github.com/JetBrains/compose-multiplatform +android-targetSdk = "35" +compose-plugin = "1.8.1" # https://github.com/JetBrains/compose-multiplatform junit = "4.13.2" -kotlin = "2.0.21" # https://kotlinlang.org/docs/releases.html -kotlin-coroutines = "1.9.0" # https://github.com/Kotlin/kotlinx.coroutines -androidx-activityCompose = "1.9.3" +kotlin = "2.1.21" # https://kotlinlang.org/docs/releases.html +kotlin-coroutines = "1.10.2" # https://github.com/Kotlin/kotlinx.coroutines +androidx-activityCompose = "1.10.1" # https://mvnrepository.com/artifact/androidx.activity/activity-compose [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 1092169..4e4e5eb 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -12,7 +12,7 @@ kotlin { androidTarget { @OptIn(ExperimentalKotlinGradlePluginApi::class) compilerOptions { - jvmTarget.set(JvmTarget.JVM_11) + jvmTarget.set(JvmTarget.JVM_21) } } @@ -70,8 +70,8 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } buildFeatures { compose = true diff --git a/sample/src/commonMain/kotlin/dev/voir/formica/sample/App.kt b/sample/src/commonMain/kotlin/dev/voir/formica/sample/App.kt index 31fa40b..eeb48f8 100644 --- a/sample/src/commonMain/kotlin/dev/voir/formica/sample/App.kt +++ b/sample/src/commonMain/kotlin/dev/voir/formica/sample/App.kt @@ -3,6 +3,10 @@ package dev.voir.formica.sample import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.Checkbox import androidx.compose.material.Text @@ -11,15 +15,18 @@ 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.graphics.Color import androidx.compose.ui.unit.dp import dev.voir.formica.FormicaFieldResult import dev.voir.formica.FormicaResult +import dev.voir.formica.collectDataAsState +import dev.voir.formica.rememberFormica import dev.voir.formica.rules.NotEmptyRule import dev.voir.formica.sample.ui.FormFieldWrapper import dev.voir.formica.ui.Formica import dev.voir.formica.ui.FormicaField -import dev.voir.formica.ui.rememberFormica data class FormSchema( var text: String, @@ -38,11 +45,17 @@ fun App() { additionalText = null, ) ) + val formData by formica.collectDataAsState() + var formError by remember { mutableStateOf(null) } + var formResult by remember { mutableStateOf(null) } - Column { + Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) { Formica(formica = formica) { - Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() + ) { FormicaField( name = FormSchema::text, required = true, @@ -50,6 +63,7 @@ fun App() { ) { FormFieldWrapper { androidx.compose.material.TextField( + modifier = Modifier.fillMaxWidth(), value = field.value ?: "", label = { Text("Required text") @@ -70,6 +84,7 @@ fun App() { FormicaField(name = FormSchema::optionalText, required = false) { FormFieldWrapper { androidx.compose.material.TextField( + modifier = Modifier.fillMaxWidth(), value = field.value ?: "", label = { Text("Optional text") @@ -91,13 +106,17 @@ fun App() { name = FormSchema::activateAdditionalText, required = true, ) { - Row { - Text("Activate additional text?") + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { Checkbox( checked = field.value ?: false, onCheckedChange = { onChange(FormSchema::activateAdditionalText, it) }) + Text("Activate additional text?") } } @@ -106,16 +125,16 @@ fun App() { name = FormSchema::additionalText, required = false, customValidation = { value -> - if (data.activateAdditionalText && value.isNullOrBlank()) { + if (formData.activateAdditionalText && value.isNullOrBlank()) { FormicaFieldResult.Error(message = "Field is required") } else { - FormicaFieldResult.Success } } ) { FormFieldWrapper { androidx.compose.material.TextField( + modifier = Modifier.fillMaxWidth(), value = field.value ?: "", label = { Text("Required text if checkbox activated") @@ -136,22 +155,40 @@ fun App() { } } - formError?.let { - Text(text = it, color = Color.Red) - } + Spacer(modifier = Modifier.height(12.dp)) Button( + modifier = Modifier.fillMaxWidth(), onClick = { + // Reset result and error + formResult = null formError = null val state = formica.validate() if (state is FormicaResult.Valid) { formError = null + formResult = formica.data.value } else if (state is FormicaResult.Error) { formError = state.message + formResult = null } }) { Text("Submit") } + + formError?.let { + Column(modifier = Modifier.fillMaxWidth()) { + Text("Form submit error") + Text(text = it, color = Color.Red) + } + } + + formResult?.let { + Column(modifier = Modifier.fillMaxWidth()) { + Text("Form submit result") + + Text(text = it.toString()) + } + } } }