diff --git a/README.md b/README.md index 9914c00..69f1c1d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - [x] Hide API key from code by using expect/actual for OpenAPI key - [x] Switch AI integration from OpenAI to Gemini - [x] Refactor network layer for cleaner architecture -- [ ] Create a separate module for reusable UI components +- [x] Create a separate module for reusable UI components --- diff --git a/build.gradle.kts b/build.gradle.kts index 5b452ac..200c5e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,4 +9,5 @@ plugins { alias(libs.plugins.serialization) apply false alias(libs.plugins.googleSecrets) apply false alias(libs.plugins.jetbrainsKotlinJvm) apply false + alias(libs.plugins.androidKotlinMultiplatformLibrary) apply false } \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 0840d4d..4cd8b46 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -71,6 +71,15 @@ kotlin { sourceSets { val desktopMain by getting + val commonMain by getting + val commonTest by getting + + val desktopTest by getting { + dependencies { + implementation(kotlin("test")) + implementation(libs.konsist) + } + } androidMain.dependencies { implementation(libs.androidx.activity.compose) @@ -105,6 +114,7 @@ kotlin { implementation(libs.kermit) implementation(libs.arrow.core) implementation(libs.arrow.fx.coroutines) + implementation(project(":design-system")) } desktopMain.dependencies { implementation(compose.desktop.currentOs) diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/ShowAlertDialog.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/ShowAlertDialog.kt deleted file mode 100644 index 678b3af..0000000 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/ShowAlertDialog.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.developersbreach.kotlindictionarymultiplatform.ui.components - -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import kotlindictionarymultiplatform.composeapp.generated.resources.Res -import kotlindictionarymultiplatform.composeapp.generated.resources.ok -import org.jetbrains.compose.resources.stringResource - -@Composable -fun ShowAlertDialog( - onButtonClick: () -> Unit, - modifier: Modifier = Modifier, - title: String, - description: String, -) { - AlertDialog( - onDismissRequest = onButtonClick, - title = { Text(text = title) }, - text = { Text(text = description) }, - confirmButton = { - TextButton(onClick = onButtonClick) { - Text(text = stringResource(Res.string.ok)) - } - }, - modifier = modifier, - ) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/UiStateHandler.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/UiStateHandler.kt index 9ef0afc..2ce8840 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/UiStateHandler.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/components/UiStateHandler.kt @@ -2,17 +2,18 @@ package com.developersbreach.kotlindictionarymultiplatform.ui.components import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import com.developersbreach.designsystem.components.KdAlertDialog +import com.developersbreach.designsystem.components.KdCircularProgressIndicator import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.error_info_unavailable import kotlindictionarymultiplatform.composeapp.generated.resources.error_occurred +import kotlindictionarymultiplatform.composeapp.generated.resources.ok import org.jetbrains.compose.resources.stringResource @Composable @@ -30,20 +31,23 @@ fun UiStateHandler( Box(modifier = Modifier.fillMaxSize()) { when (uiState) { is UiState.Loading -> { - CircularProgressIndicator( + KdCircularProgressIndicator( modifier = Modifier.align(Alignment.Center), - color = MaterialTheme.colorScheme.onBackground, ) } is UiState.Error -> { if (!shouldDismissErrorDialog.value) { val errorDetails = uiState.throwable - ShowAlertDialog( - onButtonClick = { shouldDismissErrorDialog.value = true }, + KdAlertDialog( + onDismissRequest = { shouldDismissErrorDialog.value = true }, modifier = Modifier, title = stringResource(Res.string.error_occurred), description = errorDetails.message ?: stringResource(Res.string.error_info_unavailable), + buttonTitle = stringResource(Res.string.ok), + onButtonClick = { + shouldDismissErrorDialog.value = true + }, ) } } @@ -52,9 +56,8 @@ fun UiStateHandler( } if (isLoading) { - CircularProgressIndicator( + KdCircularProgressIndicator( modifier = Modifier.align(Alignment.Center), - color = MaterialTheme.colorScheme.onBackground, ) } } diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/CodeExampleBox.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/CodeExampleBox.kt index 1c6643c..f058e01 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/CodeExampleBox.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/CodeExampleBox.kt @@ -16,9 +16,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.ContentCopy -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -32,6 +30,8 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.developersbreach.designsystem.components.KdIcon +import com.developersbreach.designsystem.components.KdText import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.copied import kotlindictionarymultiplatform.composeapp.generated.resources.copy @@ -78,7 +78,7 @@ fun CodeExampleBox( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { - Text( + KdText( text = stringResource(Res.string.kotlin), color = MaterialTheme.colorScheme.onPrimary, fontWeight = FontWeight.Bold, @@ -99,13 +99,15 @@ fun CodeExampleBox( } .padding(4.dp), ) { - Icon( + KdIcon( + modifier = Modifier, imageVector = if (copied) Icons.Default.Check else Icons.Default.ContentCopy, contentDescription = stringResource(Res.string.copy), tint = MaterialTheme.colorScheme.onPrimary, ) Spacer(modifier = Modifier.width(4.dp)) - Text( + KdText( + modifier = Modifier, text = if (copied) stringResource(Res.string.copied) else stringResource(Res.string.copy), fontSize = 12.sp, color = MaterialTheme.colorScheme.onPrimary, @@ -116,7 +118,7 @@ fun CodeExampleBox( Spacer(modifier = Modifier.height(8.dp)) - Text( + KdText( text = code, modifier = Modifier.padding( start = 12.dp, diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenComponents.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenComponents.kt index 7f38b24..e85b89d 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenComponents.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenComponents.kt @@ -4,11 +4,11 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import com.developersbreach.designsystem.components.KdText import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.Section import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.Syntax import kotlindictionarymultiplatform.composeapp.generated.resources.Res @@ -25,7 +25,7 @@ fun TableOfContents( item: String, onClick: () -> Unit, ) { - Text( + KdText( text = item, modifier = Modifier.clickable { onClick() }, color = MaterialTheme.colorScheme.onSurface, @@ -37,13 +37,15 @@ fun TableOfContents( fun IntroductionSection( intro: String, ) { - Text( + KdText( + modifier = Modifier, text = stringResource(resource = Res.string.introduction), style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onPrimary, ) Spacer(Modifier.height(4.dp)) - Text( + KdText( + modifier = Modifier, text = intro, style = MaterialTheme.typography.bodyMedium, ) @@ -54,19 +56,22 @@ fun IntroductionSection( fun SyntaxSection( syntax: Syntax, ) { - Text( + KdText( + modifier = Modifier, text = stringResource(resource = Res.string.syntax), style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onPrimary, ) Spacer(Modifier.height(4.dp)) - Text( + KdText( + modifier = Modifier, text = syntax.signature, style = MaterialTheme.typography.bodyMedium, ) syntax.notes?.let { Spacer(Modifier.height(4.dp)) - Text( + KdText( + modifier = Modifier, text = stringResource( Res.string.notes_with_value, it, @@ -82,7 +87,8 @@ fun SectionBlock( section: Section, ) { section.heading?.let { - Text( + KdText( + modifier = Modifier, text = it, style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onPrimary, @@ -91,7 +97,8 @@ fun SectionBlock( } section.content?.let { - Text( + KdText( + modifier = Modifier, text = it, style = MaterialTheme.typography.bodyMedium, ) @@ -100,7 +107,8 @@ fun SectionBlock( section.codeExamples.forEach { example -> example.description?.let { - Text( + KdText( + modifier = Modifier, text = it, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium, @@ -116,14 +124,16 @@ fun SectionBlock( fun PitfallsSection( pitfalls: List, ) { - Text( + KdText( + modifier = Modifier, text = stringResource(resource = Res.string.pitfalls), style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onPrimary, ) Spacer(Modifier.height(4.dp)) pitfalls.forEach { - Text( + KdText( + modifier = Modifier, text = stringResource( Res.string.bullet_item, it, @@ -138,14 +148,16 @@ fun PitfallsSection( fun RelatedTopicsSection( relatedTopics: List, ) { - Text( + KdText( + modifier = Modifier, text = stringResource(resource = Res.string.related_topics), style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.onPrimary, ) Spacer(Modifier.height(4.dp)) relatedTopics.forEach { - Text( + KdText( + modifier = Modifier, text = stringResource( Res.string.bullet_item, it, diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenUI.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenUI.kt index 4a32901..a20d9e8 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenUI.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailScreenUI.kt @@ -8,12 +8,12 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.developersbreach.designsystem.components.KdScaffold +import com.developersbreach.designsystem.components.KdText import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.table_of_contents import kotlinx.coroutines.launch @@ -24,20 +24,21 @@ fun DetailScreenUI( detailUiState: DetailUiState, navigateUp: () -> Unit, ) { - Scaffold( + KdScaffold( + modifier = Modifier, topBar = { DetailTopBar( title = detailUiState.topicName, navigateUp = navigateUp, ) }, - containerColor = MaterialTheme.colorScheme.background, - ) { innerPadding -> - DetailContent( - detailUiState = detailUiState, - modifier = Modifier.padding(innerPadding), - ) - } + content = { innerPadding -> + DetailContent( + detailUiState = detailUiState, + modifier = Modifier.padding(innerPadding), + ) + }, + ) } @Composable @@ -55,7 +56,8 @@ private fun DetailContent( .padding(horizontal = 16.dp), ) { item { - Text( + KdText( + modifier = Modifier, text = stringResource(Res.string.table_of_contents), style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onPrimary, diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailTopAppBar.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailTopAppBar.kt index ad1912f..8df468b 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailTopAppBar.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/detail/DetailTopAppBar.kt @@ -4,14 +4,13 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.developersbreach.designsystem.components.KdIconButton +import com.developersbreach.designsystem.components.KdText +import com.developersbreach.designsystem.components.KdTopAppBar import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.back import org.jetbrains.compose.resources.stringResource @@ -22,9 +21,9 @@ fun DetailTopBar( title: String, navigateUp: () -> Unit, ) { - TopAppBar( + KdTopAppBar( title = { - Text( + KdText( text = title, style = MaterialTheme.typography.displayMedium, color = MaterialTheme.colorScheme.onPrimary, @@ -32,12 +31,13 @@ fun DetailTopBar( ) }, navigationIcon = { - IconButton(onClick = navigateUp) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(Res.string.back), - ) - } + KdIconButton( + modifier = Modifier, + iconModifier = Modifier, + onClick = navigateUp, + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(Res.string.back), + ) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.background, diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/SearchField.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/SearchField.kt index c53f9e2..50438c9 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/SearchField.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/SearchField.kt @@ -7,10 +7,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -20,6 +17,9 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.developersbreach.designsystem.components.KdIcon +import com.developersbreach.designsystem.components.KdText +import com.developersbreach.designsystem.components.KdTextField import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.search import kotlindictionarymultiplatform.composeapp.generated.resources.search_kotlin_terms @@ -30,7 +30,7 @@ fun SearchField( searchQuery: String, onQueryChange: (String) -> Unit, ) { - TextField( + KdTextField( value = searchQuery, onValueChange = onQueryChange, modifier = Modifier @@ -38,15 +38,17 @@ fun SearchField( .clip(RoundedCornerShape(25.dp)) .padding(4.dp), placeholder = { - Text( + KdText( stringResource(Res.string.search_kotlin_terms), maxLines = 1, overflow = TextOverflow.Ellipsis, color = MaterialTheme.colorScheme.onBackground, + modifier = Modifier, ) }, leadingIcon = { - Icon( + KdIcon( + modifier = Modifier, imageVector = Icons.Filled.Search, contentDescription = stringResource(Res.string.search), tint = MaterialTheme.colorScheme.onBackground, diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt index 1a66905..28de7c0 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt @@ -16,17 +16,16 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.outlined.BookmarkBorder -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import com.developersbreach.designsystem.components.KdIconButton +import com.developersbreach.designsystem.components.KdSurface +import com.developersbreach.designsystem.components.KdText import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.add_bookmark import kotlindictionarymultiplatform.composeapp.generated.resources.remove_bookmark @@ -41,7 +40,7 @@ fun TopicCard( onBookmarkClick: () -> Unit, onCardClick: () -> Unit, ) { - Surface( + KdSurface( modifier = Modifier .fillMaxWidth() .padding(8.dp) @@ -51,8 +50,8 @@ fun TopicCard( clip = true, ) .clickable { onCardClick() }, - shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surface, + shape = RoundedCornerShape(16.dp), ) { Row( modifier = Modifier @@ -69,7 +68,10 @@ fun TopicCard( ), contentAlignment = Alignment.Center, ) { - Text(text = itemTopic.initial) + KdText( + modifier = Modifier, + text = itemTopic.initial, + ) } Spacer(modifier = Modifier.width(12.dp)) @@ -77,7 +79,8 @@ fun TopicCard( Column( modifier = Modifier.weight(1f), ) { - Text( + KdText( + modifier = Modifier, text = topic, style = MaterialTheme.typography.headlineMedium.copy( color = MaterialTheme.colorScheme.onPrimary, @@ -86,7 +89,8 @@ fun TopicCard( overflow = TextOverflow.Ellipsis, ) Spacer(modifier = Modifier.height(6.dp)) - Text( + KdText( + modifier = Modifier, text = subtitle, style = MaterialTheme.typography.labelMedium.copy( color = MaterialTheme.colorScheme.onBackground, @@ -96,13 +100,22 @@ fun TopicCard( ) } - IconButton(onClick = onBookmarkClick) { - Icon( - imageVector = if (isBookmarked) Icons.Outlined.BookmarkBorder else Icons.Filled.Bookmark, - contentDescription = if (isBookmarked) stringResource(Res.string.remove_bookmark) else stringResource(Res.string.add_bookmark), - tint = MaterialTheme.colorScheme.primary, - ) - } + KdIconButton( + modifier = Modifier, + iconModifier = Modifier, + onClick = onBookmarkClick, + imageVector = if (isBookmarked) { + Icons.Outlined.BookmarkBorder + } else { + Icons.Filled.Bookmark + }, + contentDescription = if (isBookmarked) { + stringResource(Res.string.remove_bookmark) + } else { + stringResource(Res.string.add_bookmark) + }, + tint = MaterialTheme.colorScheme.primary, + ) } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt index 8cbcd3e..105aa93 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt @@ -4,11 +4,10 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import com.developersbreach.designsystem.components.KdScaffold @Composable fun TopicScreenUI( @@ -19,8 +18,8 @@ fun TopicScreenUI( onBookmarkClick: (Int) -> Unit, onTopicClick: (String) -> Unit, ) { - Scaffold( - containerColor = MaterialTheme.colorScheme.background, + KdScaffold( + modifier = Modifier, topBar = { TopicTopBar() }, ) { paddingValues -> Column( diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicTopAppBar.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicTopAppBar.kt index b2c044a..b1bb812 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicTopAppBar.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicTopAppBar.kt @@ -4,15 +4,14 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign +import com.developersbreach.designsystem.components.KdIconButton +import com.developersbreach.designsystem.components.KdText +import com.developersbreach.designsystem.components.KdTopAppBar import kotlindictionarymultiplatform.composeapp.generated.resources.Res import kotlindictionarymultiplatform.composeapp.generated.resources.back import kotlindictionarymultiplatform.composeapp.generated.resources.topics @@ -21,9 +20,9 @@ import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable fun TopicTopBar() { - TopAppBar( + KdTopAppBar( title = { - Text( + KdText( text = stringResource(Res.string.topics), style = MaterialTheme.typography.displayMedium, modifier = Modifier.fillMaxWidth(), @@ -31,12 +30,13 @@ fun TopicTopBar() { ) }, navigationIcon = { - IconButton(onClick = { /* Optional Back Logic */ }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(Res.string.back), - ) - } + KdIconButton( + modifier = Modifier, + iconModifier = Modifier, + onClick = {}, + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(Res.string.back), + ) }, colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.background, diff --git a/composeApp/src/desktopTest/kotlin/com/developersbreach/kotlindictionarymultiplatform/DesignSystemTest.kt b/composeApp/src/desktopTest/kotlin/com/developersbreach/kotlindictionarymultiplatform/DesignSystemTest.kt new file mode 100644 index 0000000..8ee6686 --- /dev/null +++ b/composeApp/src/desktopTest/kotlin/com/developersbreach/kotlindictionarymultiplatform/DesignSystemTest.kt @@ -0,0 +1,101 @@ +package com.developersbreach.kotlindictionarymultiplatform + +import kotlin.test.Test + +class DesignSystemTest { + + @Test + fun `all material3 components have design-system wrappers`() { + val ignoreComponents = setOf( + "androidx.compose.material3.Typography", + "androidx.compose.material3.darkColorScheme", + "androidx.compose.material3.lightColorScheme", + "androidx.compose.material3.MaterialTheme", + "androidx.compose.material3.ExperimentalMaterial3Api", + "androidx.compose.material3.TopAppBarDefaults", + "androidx.compose.material3.TextFieldDefaults", + ) + + checkAllMaterial3ComponentsMoved( + allowedComponents = ignoreComponents, + excludePaths = arrayOf(DESIGN_SYSTEM_PATH), + ) + } + + @Test + fun `no direct usage of androidx compose Surface should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.Surface", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/Surface.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose scaffold should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.Scaffold", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/Scaffold.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose icon should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.Icon", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/Icon.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose iconButton should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.IconButton", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/IconButton.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose textField should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.TextField", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/TextField.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose alertDialog should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.AlertDialog", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/AlertDialog.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose textButton should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.TextButton", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/TextButton.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose circularProgressIndicator should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.CircularProgressIndicator", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/CircularProgressIndicator.kt"), + ) + } + + @Test + fun `no direct usage of androidx compose topAppBar should be allowed except designSystem`() { + checkNoDirectUsageExceptAllowed( + componentName = "androidx.compose.material3.TopAppBar", + excludePaths = arrayOf("$DESIGN_SYSTEM_PATH/TopAppBar.kt"), + ) + } + + companion object { + private const val DESIGN_SYSTEM_PATH = + "design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components" + } +} \ No newline at end of file diff --git a/composeApp/src/desktopTest/kotlin/com/developersbreach/kotlindictionarymultiplatform/KonsistUtils.kt b/composeApp/src/desktopTest/kotlin/com/developersbreach/kotlindictionarymultiplatform/KonsistUtils.kt new file mode 100644 index 0000000..6d8d356 --- /dev/null +++ b/composeApp/src/desktopTest/kotlin/com/developersbreach/kotlindictionarymultiplatform/KonsistUtils.kt @@ -0,0 +1,51 @@ +package com.developersbreach.kotlindictionarymultiplatform + +import com.lemonappdev.konsist.api.Konsist +import kotlin.test.assertTrue + +internal fun checkNoDirectUsageExceptAllowed( + componentName: String, + excludePaths: Array, +) { + val offendingFiles = Konsist.scopeFromProject() + .files + .filter { file -> + val normalizedFilePath = file.path.replace("\\", "/").lowercase() + excludePaths.none { normalizedFilePath.contains(it.replace("\\", "/").lowercase()) } + } + .filter { file -> + file.imports.any { it.name == componentName } + } + + assertTrue( + actual = offendingFiles.isEmpty(), + message = "Found forbidden imports of $componentName in files:\n" + offendingFiles.joinToString("\n") { it.path }, + ) +} + +internal fun checkAllMaterial3ComponentsMoved( + allowedComponents: Set, + excludePaths: Array = emptyArray(), +) { + val files = Konsist + .scopeFromProject() + .files + .filter { file -> + val path = file.path.replace("\\", "/").lowercase() + excludePaths.none { path.contains(it.replace("\\", "/").lowercase()) } + } + + val allMaterial3Imports = files + .flatMap { it.imports } + .map { it.name } + .filter { it.startsWith("androidx.compose.material3.") } + .distinct() + + val pending = allMaterial3Imports - allowedComponents + + assertTrue( + actual = pending.isEmpty(), + message = "Found pending Material3 components not in design-system:\n" + + pending.joinToString("\n") { " • $it" }, + ) +} \ No newline at end of file diff --git a/design-system/build.gradle.kts b/design-system/build.gradle.kts new file mode 100644 index 0000000..c197325 --- /dev/null +++ b/design-system/build.gradle.kts @@ -0,0 +1,107 @@ +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidKotlinMultiplatformLibrary) + alias(libs.plugins.composeMultiplatform) + alias(libs.plugins.composeCompiler) + alias(libs.plugins.ktlint) +} + +kotlin { + + // Target declarations - add or remove as needed below. These define + // which platforms this KMP module supports. + // See: https://kotlinlang.org/docs/multiplatform-discover-project.html#targets + androidLibrary { + namespace = "com.example.design_system" + compileSdk = 35 + minSdk = 24 + + withDeviceTestBuilder { + sourceSetTreeName = "test" + }.configure { + instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + } + + // For iOS targets, this is also where you should + // configure native binary output. For more information, see: + // https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks + + // A step-by-step guide on how to include this library in an XCode + // project can be found here: + // https://developer.android.com/kotlin/multiplatform/migrate + val xcfName = "design-systemKit" + + iosX64 { + binaries.framework { + baseName = xcfName + } + } + + iosArm64 { + binaries.framework { + baseName = xcfName + } + } + + iosSimulatorArm64 { + binaries.framework { + baseName = xcfName + } + } + + jvm("desktop") + + // Source set declarations. + // Declaring a target automatically creates a source set with the same name. By default, the + // Kotlin Gradle Plugin creates additional source sets that depend on each other, since it is + // common to share sources between related targets. + // See: https://kotlinlang.org/docs/multiplatform-hierarchy.html + sourceSets { + commonMain { + dependencies { + implementation(libs.kotlin.stdlib) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + } + } + + commonTest { + dependencies { + implementation(libs.kotlin.test) + implementation(compose.ui) + } + } + + androidMain { + dependencies { + // Add Android-specific dependencies here. Note that this source set depends on + // commonMain by default and will correctly pull the Android artifacts of any KMP + // dependencies declared in commonMain. + } + } + + getByName("androidDeviceTest") { + dependencies { + implementation(libs.androidx.runner) + implementation(libs.androidx.core) + implementation(libs.androidx.test.junit) + } + } + + iosMain { + dependencies { + // Add iOS-specific dependencies here. This a source set created by Kotlin Gradle + // Plugin (KGP) that each specific iOS target (e.g., iosX64) depends on as + // part of KMP’s default source set hierarchy. Note that this source set depends + // on common by default and will correctly pull the iOS artifacts of any + // KMP dependencies declared in commonMain. + } + } + } +} + +dependencies { + ktlint(project(":custom-ktlint-rules")) +} \ No newline at end of file diff --git a/design-system/src/androidDeviceTest/kotlin/com/example/design_system/ExampleInstrumentedTest.kt b/design-system/src/androidDeviceTest/kotlin/com/example/design_system/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..7b8a813 --- /dev/null +++ b/design-system/src/androidDeviceTest/kotlin/com/example/design_system/ExampleInstrumentedTest.kt @@ -0,0 +1,25 @@ +@file:Suppress("ktlint") +package com.example.design_system + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.design_system.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/design-system/src/androidHostTest/kotlin/com/example/design_system/ExampleUnitTest.kt b/design-system/src/androidHostTest/kotlin/com/example/design_system/ExampleUnitTest.kt new file mode 100644 index 0000000..ac78ab1 --- /dev/null +++ b/design-system/src/androidHostTest/kotlin/com/example/design_system/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.example.design_system + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/design-system/src/androidMain/AndroidManifest.xml b/design-system/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/design-system/src/androidMain/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/design-system/src/appleMain/kotlin/com/example/design_system/platform.kt b/design-system/src/appleMain/kotlin/com/example/design_system/platform.kt new file mode 100644 index 0000000..6103572 --- /dev/null +++ b/design-system/src/appleMain/kotlin/com/example/design_system/platform.kt @@ -0,0 +1,4 @@ +@file:Suppress("ktlint") +package com.example.design_system + +expect fun platform(): String \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/AlertDialog.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/AlertDialog.kt new file mode 100644 index 0000000..29aadc2 --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/AlertDialog.kt @@ -0,0 +1,64 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.AlertDialog +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun KdAlertDialog( + modifier: Modifier, + onDismissRequest: () -> Unit, + title: @Composable (() -> Unit)?, + text: @Composable (() -> Unit)?, + confirmButton: @Composable () -> Unit, +) { + AlertDialog( + modifier = modifier, + onDismissRequest = onDismissRequest, + title = title, + text = text, + confirmButton = confirmButton, + ) +} + +@Composable +fun KdAlertDialog( + onDismissRequest: () -> Unit, + modifier: Modifier, + title: String, + description: String, + buttonTitle: String, + onButtonClick: () -> Unit, +) { + KdAlertDialog( + onDismissRequest = onDismissRequest, + title = { + KdText( + modifier = Modifier, + text = title, + ) + }, + text = { + KdText( + modifier = Modifier, + text = description, + ) + }, + confirmButton = { + KdTextButton( + modifier = Modifier, + onClick = { + onDismissRequest() + onButtonClick() + }, + content = { + KdText( + modifier = Modifier, + text = buttonTitle, + ) + }, + ) + }, + modifier = modifier, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/CircularProgressIndicator.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/CircularProgressIndicator.kt new file mode 100644 index 0000000..560c2c7 --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/CircularProgressIndicator.kt @@ -0,0 +1,18 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +fun KdCircularProgressIndicator( + modifier: Modifier, + color: Color = MaterialTheme.colorScheme.onBackground, +) { + CircularProgressIndicator( + modifier = modifier, + color = color, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Icon.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Icon.kt new file mode 100644 index 0000000..3197853 --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Icon.kt @@ -0,0 +1,23 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector + +@Composable +fun KdIcon( + modifier: Modifier, + imageVector: ImageVector, + contentDescription: String?, + tint: Color = LocalContentColor.current, +) { + Icon( + modifier = modifier, + imageVector = imageVector, + contentDescription = contentDescription, + tint = tint, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/IconButton.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/IconButton.kt new file mode 100644 index 0000000..477d1bb --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/IconButton.kt @@ -0,0 +1,31 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector + +@Composable +fun KdIconButton( + modifier: Modifier, + iconModifier: Modifier, + onClick: () -> Unit, + imageVector: ImageVector, + contentDescription: String?, + tint: Color = LocalContentColor.current, +) { + IconButton( + modifier = modifier, + onClick = onClick, + content = { + KdIcon( + imageVector = imageVector, + contentDescription = contentDescription, + tint = tint, + modifier = iconModifier, + ) + }, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Scaffold.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Scaffold.kt new file mode 100644 index 0000000..8bfef3c --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Scaffold.kt @@ -0,0 +1,23 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +fun KdScaffold( + modifier: Modifier, + topBar: @Composable () -> Unit, + containerColor: Color = MaterialTheme.colorScheme.background, + content: @Composable (PaddingValues) -> Unit, +) { + Scaffold( + modifier = modifier, + topBar = topBar, + containerColor = containerColor, + content = { content(it) }, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Surface.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Surface.kt new file mode 100644 index 0000000..75782db --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Surface.kt @@ -0,0 +1,22 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape + +@Composable +fun KdSurface( + modifier: Modifier, + color: Color, + shape: Shape, + content: @Composable () -> Unit, +) { + Surface( + modifier = modifier, + content = content, + shape = shape, + color = color, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Text.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Text.kt new file mode 100644 index 0000000..07de47b --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/Text.kt @@ -0,0 +1,42 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit + +@Composable +fun KdText( + text: String, + modifier: Modifier, + style: TextStyle = LocalTextStyle.current, + textAlign: TextAlign? = null, + maxLines: Int = Int.MAX_VALUE, + overflow: TextOverflow = TextOverflow.Clip, + color: Color = Color.Unspecified, + fontWeight: FontWeight? = null, + fontSize: TextUnit = TextUnit.Unspecified, + fontFamily: FontFamily? = null, + lineHeight: TextUnit = TextUnit.Unspecified, +) { + Text( + text = text, + modifier = modifier, + style = style, + textAlign = textAlign, + maxLines = maxLines, + overflow = overflow, + color = color, + fontWeight = fontWeight, + fontSize = fontSize, + fontFamily = fontFamily, + lineHeight = lineHeight, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TextButton.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TextButton.kt new file mode 100644 index 0000000..82065a8 --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TextButton.kt @@ -0,0 +1,19 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.foundation.layout.RowScope +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun KdTextButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable RowScope.() -> Unit, +) { + TextButton( + onClick = onClick, + modifier = modifier, + content = content, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TextField.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TextField.kt new file mode 100644 index 0000000..dc4eb5a --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TextField.kt @@ -0,0 +1,39 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle + +@Composable +fun KdTextField( + modifier: Modifier, + value: String, + onValueChange: (String) -> Unit, + placeholder: @Composable (() -> Unit)?, + leadingIcon: @Composable (() -> Unit)?, + colors: TextFieldColors, + shape: Shape, + singleLine: Boolean, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + textStyle: TextStyle, +) { + TextField( + modifier = modifier, + value = value, + onValueChange = onValueChange, + leadingIcon = leadingIcon, + placeholder = placeholder, + shape = shape, + keyboardActions = keyboardActions, + textStyle = textStyle, + singleLine = singleLine, + keyboardOptions = keyboardOptions, + colors = colors, + ) +} \ No newline at end of file diff --git a/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TopAppBar.kt b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TopAppBar.kt new file mode 100644 index 0000000..f6872bf --- /dev/null +++ b/design-system/src/commonMain/kotlin/com/developersbreach/designsystem/components/TopAppBar.kt @@ -0,0 +1,23 @@ +package com.developersbreach.designsystem.components + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun KdTopAppBar( + title: @Composable () -> Unit, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit, + colors: TopAppBarColors, +) { + TopAppBar( + title = title, + modifier = modifier, + navigationIcon = navigationIcon, + colors = colors, + ) +} \ No newline at end of file diff --git a/design-system/src/iosMain/kotlin/com/example/design_system/Platform.ios.kt b/design-system/src/iosMain/kotlin/com/example/design_system/Platform.ios.kt new file mode 100644 index 0000000..1d900fb --- /dev/null +++ b/design-system/src/iosMain/kotlin/com/example/design_system/Platform.ios.kt @@ -0,0 +1,4 @@ +@file:Suppress("ktlint") +package com.example.design_system + +actual fun platform() = "iOS" \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 80c5c24..f76afe7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,6 +16,7 @@ compose-multiplatform = "1.7.3" generativeai = "0.9.0" junit = "4.13.2" kermit = "2.0.4" +konsist = "0.17.3" kotlin = "2.1.10" kotlinx-coroutines = "1.10.2" kotlinx-serialization-json = "1.7.3" @@ -25,6 +26,9 @@ navigation-compose = "2.9.0" ktlint = "12.2.0" secrets = "2.0.1" jetbrainsKotlinJvm = "2.1.10" +kotlinStdlib = "2.1.10" +runner = "1.6.2" +core = "1.6.1" [libraries] androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" } @@ -32,6 +36,7 @@ arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrowFxCoroutin arrow-fx-coroutines = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrowFxCoroutines" } generativeai = { module = "com.google.ai.client.generativeai:generativeai", version.ref = "generativeai" } kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } +konsist = { module = "com.lemonappdev:konsist", version.ref = "konsist" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -59,6 +64,9 @@ koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin" } koin-compose = { module = "io.insert-koin:koin-compose" } koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" } koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlinStdlib" } +androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" } +androidx-core = { group = "androidx.test", name = "core", version.ref = "core" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } @@ -70,3 +78,4 @@ serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } googleSecrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" } +androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 622b012..45223d3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,4 +29,5 @@ dependencyResolutionManagement { } include(":composeApp") -include(":custom-ktlint-rules") \ No newline at end of file +include(":custom-ktlint-rules") +include(":design-system")