diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..4486e6b 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -5,6 +5,9 @@ <SelectionState runConfigName="app"> <option name="selectionMode" value="DROPDOWN" /> </SelectionState> + <SelectionState runConfigName="presentation"> + <option name="selectionMode" value="DROPDOWN" /> + </SelectionState> </selectionStates> </component> </project> \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index ae733f1..b4c0dd5 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> + <component name="GradleMigrationSettings" migrationVersion="1" /> <component name="GradleSettings"> <option name="linkedExternalProjectsSettings"> <GradleProjectSettings> @@ -9,7 +10,9 @@ <option name="modules"> <set> <option value="$PROJECT_DIR$" /> - <option value="$PROJECT_DIR$/app" /> + <option value="$PROJECT_DIR$/data" /> + <option value="$PROJECT_DIR$/domain" /> + <option value="$PROJECT_DIR$/presentation" /> </set> </option> <option name="resolveExternalAnnotations" value="false" /> diff --git a/presentation/src/main/java/com/nto/presentation/composable/DecoratedButton.kt b/presentation/src/main/java/com/nto/presentation/composable/DecoratedButton.kt new file mode 100644 index 0000000..cfcf089 --- /dev/null +++ b/presentation/src/main/java/com/nto/presentation/composable/DecoratedButton.kt @@ -0,0 +1,148 @@ +package com.nto.presentation.composable + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.nto.presentation.composable.DecoratedButtonType.Default +import com.nto.presentation.composable.DecoratedButtonType.Disabled +import com.nto.presentation.theme.NTOTheme +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +enum class DecoratedButtonType { + Default, Disabled +} + +object DecoratedButtonValues { + private val DefaultColors: ButtonColors + @Composable get() = ButtonColors( + contentColor = NTOTheme.colors.primaryBackground, + containerColor = NTOTheme.colors.button, + disabledContentColor = Color.Unspecified, + disabledContainerColor = Color.Unspecified + ) + + private val DisabledColors: ButtonColors + @Composable get() = ButtonColors( + contentColor = NTOTheme.colors.primaryBackground, + containerColor = NTOTheme.colors.buttonDisabled, + disabledContentColor = Color.Unspecified, + disabledContainerColor = Color.Unspecified + ) + + @Composable + fun getDefaultColor(type: DecoratedButtonType): ButtonColors { + return when (type) { + Default -> DefaultColors + Disabled -> DisabledColors + } + } +} + +/** + * High level element that utilizes [DecoratedButtonType] to obtain [DecoratedButtonValues] and use it's colors accordingly. + * + * Consists of a button with a text inside it. By fact that's just a wrapper to simplify reusing process. + * + * @param text text to display inside a button. + * @param isDisabled mutable variable that represents button state. Should be in viewmodel or screen state. After being converted to the instance of [DecoratedButtonType] that is used to obtain [ButtonColors] inside [DecoratedButtonValues]. + * @param modifier modifier that should contain [Modifier.height] and [Modifier.width] or other size definition to work correctly. + * @param shape element will be clipped to that shape. + * @param onClick function to be invoked on button click. + * + * @sample [DecoratedButtonSample] + * + */ + +@Composable +fun DecoratedButton( + text: String, + isDisabled: Boolean, + modifier: Modifier = Modifier, + shape: Shape = RoundedCornerShape(10.dp), + onClick: () -> Unit, +) { + NTOTheme { + Button( + modifier = modifier, shape = shape, colors = DecoratedButtonValues.getDefaultColor( + when (isDisabled) { + true -> Disabled + false -> Default + } + ), onClick = onClick + ) { + Text( + text, + style = NTOTheme.typography.displaySmall, + color = NTOTheme.colors.secondaryText + ) + } + } +} + +@Stable +internal class DecoratedButtonSample { + class SampleViewModel { + private val _state = MutableStateFlow(SampleState()) + + val state: StateFlow<SampleState> + get() = _state.asStateFlow() + + fun setDisabledState(data: Boolean) { + _state.value = _state.value.copy(data = data) + } + } + + data class SampleState( + var data: Boolean = false + ) + + @Preview + @Composable + private fun InputFieldPreview() { + NTOTheme { + val sampleViewModel = SampleViewModel() + val state = sampleViewModel.state.collectAsState() + Column(modifier = Modifier.fillMaxSize()) { + //Filled button preset + DecoratedButton( + text = "Get Started", + isDisabled = state.value.data, + modifier = Modifier + .height(60.dp) + .width(300.dp), + shape = RoundedCornerShape(10.dp) + ) { + //... + } + //Disabled button preset + Spacer(modifier = Modifier.height(10.dp)) + DecoratedButton( + text = "Get Started", + isDisabled = !state.value.data, + modifier = Modifier + .height(60.dp) + .width(300.dp), + shape = RoundedCornerShape(10.dp) + ) { + //... + } + } + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/nto/presentation/screens/loginScreen/LoginScreen.kt b/presentation/src/main/java/com/nto/presentation/screens/loginScreen/LoginScreen.kt index 8d08cc8..d208adb 100644 --- a/presentation/src/main/java/com/nto/presentation/screens/loginScreen/LoginScreen.kt +++ b/presentation/src/main/java/com/nto/presentation/screens/loginScreen/LoginScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -29,6 +30,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.nto.presentation.R +import com.nto.presentation.composable.DecoratedButton import com.nto.presentation.composable.InputField import com.nto.presentation.composable.InputFieldOptions import com.nto.presentation.theme.NTOTheme @@ -114,7 +116,15 @@ fun LoginScreen( ) } Spacer(Modifier.height(50.dp)) - //TODO: LoginButton + DecoratedButton( + stringResource(R.string.action_login), + state.disabled, + modifier = Modifier + .width(364.dp) + .height(60.dp) + ) { + viewModel.login(navController) + } } } } diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 340bfa2..7ea03d5 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ <resources> <string name="app_name">presentation</string> - <string name="login_button">Войти</string> + <string name="action_login">Войти</string> <string name="text_email">Почта</string> <string name="text_password">Пароль</string> <string name="greeting_login_description">Войдите в свой аккаунт чтобы продолжить</string>