Skip to content

Commit 2c27974

Browse files
authored
Merge pull request #11 from DevelopersBreach/yuga/topic-screen-ui-modularization
Refactor `TopicScreen` into smaller, reusable Composables
2 parents 54ad00a + 076f8c5 commit 2c27974

File tree

13 files changed

+349
-243
lines changed

13 files changed

+349
-243
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
package com.developersbreach.kotlindictionarymultiplatform.previews
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.detail
22

33
import androidx.compose.foundation.layout.padding
44
import androidx.compose.runtime.Composable
55
import androidx.compose.ui.Modifier
66
import androidx.compose.ui.tooling.preview.PreviewLightDark
77
import androidx.compose.ui.unit.dp
8+
import com.developersbreach.kotlindictionarymultiplatform.previews.sampleCodeSnippet
89
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.detail.CodeExampleBox
910
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme
1011

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
package com.developersbreach.kotlindictionarymultiplatform.previews
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.detail
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.ui.tooling.preview.PreviewLightDark
5+
import com.developersbreach.kotlindictionarymultiplatform.previews.fakeTopicDetails
56
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.detail.DetailScreenContent
67
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme
78

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.developersbreach.kotlindictionarymultiplatform.previews
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.topic
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.getValue
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
package com.developersbreach.kotlindictionarymultiplatform.previews
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.topic
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.ui.tooling.preview.PreviewLightDark
5+
import com.developersbreach.kotlindictionarymultiplatform.previews.subtitle
6+
import com.developersbreach.kotlindictionarymultiplatform.previews.topic
57
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.TopicCard
68
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme
79

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.topic
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.ui.tooling.preview.PreviewLightDark
5+
import com.developersbreach.kotlindictionarymultiplatform.previews.sampleTopicList
6+
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.TopicList
7+
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme
8+
9+
@PreviewLightDark
10+
@Composable
11+
fun TopicListPreview() {
12+
KotlinDictionaryTheme {
13+
TopicList(
14+
topics = sampleTopicList(),
15+
bookmarkedStates = List(sampleTopicList().size) { true },
16+
onBookmarkClick = {},
17+
onTopicClick = {},
18+
)
19+
}
20+
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
package com.developersbreach.kotlindictionarymultiplatform.previews
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.topic
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.collectAsState
55
import androidx.compose.runtime.getValue
66
import androidx.compose.ui.tooling.preview.PreviewLightDark
77
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic
8+
import com.developersbreach.kotlindictionarymultiplatform.previews.sampleTopicList
89
import com.developersbreach.kotlindictionarymultiplatform.ui.components.UiState
910
import com.developersbreach.kotlindictionarymultiplatform.ui.components.UiStateHandler
10-
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.TopicScreenLayout
11+
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.TopicScreenUI
1112
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme
1213
import kotlinx.coroutines.flow.MutableStateFlow
1314
import kotlinx.coroutines.flow.StateFlow
@@ -47,7 +48,7 @@ fun TopicScreenContent(
4748
val bookmarkedStates by viewModel.bookmarkedStates.collectAsState()
4849

4950
UiStateHandler(uiState = topicState) {
50-
TopicScreenLayout(
51+
TopicScreenUI(
5152
topics = filteredTopics,
5253
bookmarkedStates = bookmarkedStates,
5354
searchQuery = searchQuery,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.previews.topic
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.ui.tooling.preview.PreviewLightDark
5+
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.TopicTopBar
6+
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme
7+
8+
@PreviewLightDark
9+
@Composable
10+
fun TopicTopBarPreview() {
11+
KotlinDictionaryTheme {
12+
TopicTopBar()
13+
}
14+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic
2+
3+
import androidx.compose.foundation.layout.fillMaxWidth
4+
import androidx.compose.foundation.layout.padding
5+
import androidx.compose.foundation.shape.RoundedCornerShape
6+
import androidx.compose.foundation.text.KeyboardActions
7+
import androidx.compose.foundation.text.KeyboardOptions
8+
import androidx.compose.material.icons.Icons
9+
import androidx.compose.material.icons.filled.Search
10+
import androidx.compose.material3.Icon
11+
import androidx.compose.material3.MaterialTheme
12+
import androidx.compose.material3.Text
13+
import androidx.compose.material3.TextField
14+
import androidx.compose.material3.TextFieldDefaults
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.draw.clip
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.text.input.ImeAction
20+
import androidx.compose.ui.text.style.TextOverflow
21+
import androidx.compose.ui.unit.dp
22+
import androidx.compose.ui.unit.sp
23+
import kotlindictionarymultiplatform.composeapp.generated.resources.Res
24+
import kotlindictionarymultiplatform.composeapp.generated.resources.search
25+
import kotlindictionarymultiplatform.composeapp.generated.resources.search_kotlin_terms
26+
import org.jetbrains.compose.resources.stringResource
27+
28+
@Composable
29+
fun SearchField(
30+
searchQuery: String,
31+
onQueryChange: (String) -> Unit,
32+
) {
33+
TextField(
34+
value = searchQuery,
35+
onValueChange = onQueryChange,
36+
modifier = Modifier
37+
.fillMaxWidth()
38+
.clip(RoundedCornerShape(25.dp))
39+
.padding(4.dp),
40+
placeholder = {
41+
Text(
42+
stringResource(Res.string.search_kotlin_terms),
43+
maxLines = 1,
44+
overflow = TextOverflow.Ellipsis,
45+
color = MaterialTheme.colorScheme.onBackground,
46+
)
47+
},
48+
leadingIcon = {
49+
Icon(Icons.Filled.Search, contentDescription = stringResource(Res.string.search), tint = MaterialTheme.colorScheme.onBackground)
50+
},
51+
colors = TextFieldDefaults.colors(
52+
focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
53+
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
54+
cursorColor = MaterialTheme.colorScheme.onBackground,
55+
focusedTextColor = MaterialTheme.colorScheme.onBackground,
56+
unfocusedTextColor = MaterialTheme.colorScheme.onBackground,
57+
focusedIndicatorColor = Color.Transparent,
58+
unfocusedIndicatorColor = Color.Transparent,
59+
),
60+
shape = RoundedCornerShape(25.dp),
61+
singleLine = true,
62+
keyboardOptions = KeyboardOptions.Default.copy(
63+
imeAction = ImeAction.Done,
64+
),
65+
keyboardActions = KeyboardActions(
66+
onDone = {
67+
// Optional: handle enter key logic
68+
},
69+
),
70+
textStyle = MaterialTheme.typography.titleMedium.copy(
71+
color = MaterialTheme.colorScheme.onBackground,
72+
lineHeight = 24.sp,
73+
letterSpacing = .5.sp,
74+
),
75+
)
76+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.clickable
5+
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.layout.Spacer
9+
import androidx.compose.foundation.layout.fillMaxWidth
10+
import androidx.compose.foundation.layout.height
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.size
13+
import androidx.compose.foundation.layout.width
14+
import androidx.compose.foundation.shape.CircleShape
15+
import androidx.compose.foundation.shape.RoundedCornerShape
16+
import androidx.compose.material.icons.Icons
17+
import androidx.compose.material.icons.filled.Bookmark
18+
import androidx.compose.material.icons.filled.Search
19+
import androidx.compose.material.icons.outlined.BookmarkBorder
20+
import androidx.compose.material3.Icon
21+
import androidx.compose.material3.IconButton
22+
import androidx.compose.material3.MaterialTheme
23+
import androidx.compose.material3.Surface
24+
import androidx.compose.material3.Text
25+
import androidx.compose.runtime.Composable
26+
import androidx.compose.ui.Alignment
27+
import androidx.compose.ui.Modifier
28+
import androidx.compose.ui.draw.shadow
29+
import androidx.compose.ui.text.style.TextOverflow
30+
import androidx.compose.ui.unit.dp
31+
import kotlindictionarymultiplatform.composeapp.generated.resources.Res
32+
import kotlindictionarymultiplatform.composeapp.generated.resources.add_bookmark
33+
import kotlindictionarymultiplatform.composeapp.generated.resources.icon
34+
import kotlindictionarymultiplatform.composeapp.generated.resources.remove_bookmark
35+
import org.jetbrains.compose.resources.stringResource
36+
37+
@Composable
38+
fun TopicCard(
39+
topic: String,
40+
subtitle: String,
41+
isBookmarked: Boolean,
42+
onBookmarkClick: () -> Unit,
43+
onCardClick: () -> Unit,
44+
) {
45+
Surface(
46+
modifier = Modifier
47+
.fillMaxWidth()
48+
.padding(8.dp)
49+
.shadow(6.dp, RoundedCornerShape(16.dp), clip = true)
50+
.clickable { onCardClick() },
51+
shape = RoundedCornerShape(16.dp),
52+
color = MaterialTheme.colorScheme.surface,
53+
) {
54+
Row(
55+
modifier = Modifier
56+
.fillMaxWidth()
57+
.padding(12.dp),
58+
verticalAlignment = Alignment.CenterVertically,
59+
) {
60+
Box(
61+
modifier = Modifier
62+
.size(36.dp)
63+
.background(MaterialTheme.colorScheme.primary, CircleShape),
64+
contentAlignment = Alignment.Center,
65+
) {
66+
Icon(Icons.Filled.Search, contentDescription = stringResource(Res.string.icon))
67+
}
68+
69+
Spacer(modifier = Modifier.width(12.dp))
70+
71+
Column(
72+
modifier = Modifier.weight(1f),
73+
) {
74+
Text(
75+
text = topic,
76+
style = MaterialTheme.typography.headlineMedium.copy(
77+
color = MaterialTheme.colorScheme.onPrimary,
78+
),
79+
maxLines = 1,
80+
overflow = TextOverflow.Ellipsis,
81+
)
82+
Spacer(modifier = Modifier.height(6.dp))
83+
Text(
84+
text = subtitle,
85+
style = MaterialTheme.typography.labelMedium.copy(
86+
color = MaterialTheme.colorScheme.onBackground,
87+
),
88+
maxLines = 1,
89+
overflow = TextOverflow.Ellipsis,
90+
)
91+
}
92+
93+
IconButton(onClick = onBookmarkClick) {
94+
Icon(
95+
imageVector = if (isBookmarked) Icons.Outlined.BookmarkBorder else Icons.Filled.Bookmark,
96+
contentDescription = if (isBookmarked) stringResource(Res.string.remove_bookmark) else stringResource(Res.string.add_bookmark),
97+
tint = MaterialTheme.colorScheme.primary,
98+
)
99+
}
100+
}
101+
}
102+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic
2+
3+
import androidx.compose.foundation.layout.PaddingValues
4+
import androidx.compose.foundation.layout.fillMaxSize
5+
import androidx.compose.foundation.lazy.LazyColumn
6+
import androidx.compose.foundation.lazy.itemsIndexed
7+
import androidx.compose.runtime.Composable
8+
import androidx.compose.ui.Modifier
9+
import androidx.compose.ui.unit.dp
10+
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic
11+
import kotlindictionarymultiplatform.composeapp.generated.resources.Res
12+
import kotlindictionarymultiplatform.composeapp.generated.resources.description_subtitle
13+
import org.jetbrains.compose.resources.stringResource
14+
15+
@Composable
16+
fun TopicList(
17+
topics: List<Topic>,
18+
bookmarkedStates: List<Boolean>,
19+
onBookmarkClick: (Int) -> Unit,
20+
onTopicClick: (String) -> Unit,
21+
) {
22+
LazyColumn(
23+
modifier = Modifier.fillMaxSize(),
24+
contentPadding = PaddingValues(bottom = 40.dp),
25+
) {
26+
itemsIndexed(topics) { index, topic ->
27+
val isBookmarked = bookmarkedStates.getOrNull(index) ?: true
28+
TopicCard(
29+
topic = topic.name,
30+
subtitle = stringResource(Res.string.description_subtitle),
31+
isBookmarked = isBookmarked,
32+
onBookmarkClick = { onBookmarkClick(index) },
33+
onCardClick = { onTopicClick(topic.name) },
34+
)
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)