Skip to content

Commit f8358be

Browse files
committed
refactor: extract common components from material web import
1 parent e05237b commit f8358be

File tree

11 files changed

+525
-375
lines changed

11 files changed

+525
-375
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common.model
2+
3+
/**
4+
* Base interface for grid items that can be displayed in icon grids
5+
*/
6+
sealed interface GridItem {
7+
val id: String
8+
}
9+
10+
/**
11+
* A category header in the grid
12+
*/
13+
data class CategoryHeader(
14+
val categoryName: String,
15+
) : GridItem {
16+
override val id: String = "header-$categoryName"
17+
}
18+
19+
/**
20+
* An icon item in the grid
21+
*/
22+
data class IconItem<T>(
23+
val icon: T,
24+
val iconId: String,
25+
) : GridItem {
26+
override val id: String = "icon-$iconId"
27+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common.ui
2+
3+
import androidx.compose.desktop.ui.tooling.preview.Preview
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.Modifier
6+
import io.github.composegears.valkyrie.ui.foundation.DropdownMenu
7+
import io.github.composegears.valkyrie.ui.foundation.theme.PreviewTheme
8+
9+
/**
10+
* Generic category dropdown that works with any category type
11+
*/
12+
@Composable
13+
fun <T> CategoriesDropdown(
14+
selectedCategory: T,
15+
categories: List<T>,
16+
categoryName: (T) -> String,
17+
onSelectCategory: (T) -> Unit,
18+
modifier: Modifier = Modifier,
19+
) {
20+
DropdownMenu(
21+
modifier = modifier,
22+
current = categoryName(selectedCategory),
23+
values = categories.map(categoryName),
24+
onSelect = { selectedName ->
25+
val selected = categories.first { categoryName(it) == selectedName }
26+
onSelectCategory(selected)
27+
},
28+
)
29+
}
30+
31+
@Preview
32+
@Composable
33+
private fun CategoriesDropdownPreview() = PreviewTheme {
34+
data class TestCategory(val name: String)
35+
36+
CategoriesDropdown(
37+
selectedCategory = TestCategory("All"),
38+
categories = listOf(
39+
TestCategory("All"),
40+
TestCategory("Arrows"),
41+
TestCategory("Text"),
42+
),
43+
categoryName = { it.name },
44+
onSelectCategory = {},
45+
)
46+
}

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/SearchInputField.kt renamed to tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/ui/SearchInputField.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.composegears.valkyrie.ui.screen.webimport.material.ui
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common.ui
22

33
import androidx.compose.animation.AnimatedContent
44
import androidx.compose.animation.core.tween

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/SidePanel.kt renamed to tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/ui/SidePanel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.composegears.valkyrie.ui.screen.webimport.material.ui
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common.ui
22

33
import androidx.compose.animation.AnimatedVisibility
44
import androidx.compose.animation.slideInHorizontally
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common.ui
2+
3+
import androidx.compose.foundation.basicMarquee
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.PaddingValues
8+
import androidx.compose.foundation.layout.Spacer
9+
import androidx.compose.foundation.layout.aspectRatio
10+
import androidx.compose.foundation.layout.fillMaxSize
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.width
13+
import androidx.compose.foundation.lazy.grid.GridCells
14+
import androidx.compose.foundation.lazy.grid.LazyGridScope
15+
import androidx.compose.foundation.lazy.grid.LazyGridState
16+
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
17+
import androidx.compose.foundation.rememberScrollbarAdapter
18+
import androidx.compose.foundation.shape.RoundedCornerShape
19+
import androidx.compose.material3.Card
20+
import androidx.compose.material3.CardDefaults
21+
import androidx.compose.material3.CircularProgressIndicator
22+
import androidx.compose.material3.MaterialTheme
23+
import androidx.compose.material3.Text
24+
import androidx.compose.runtime.Composable
25+
import androidx.compose.ui.Alignment
26+
import androidx.compose.ui.Modifier
27+
import androidx.compose.ui.draw.clip
28+
import androidx.compose.ui.graphics.Color
29+
import androidx.compose.ui.unit.dp
30+
import io.github.composegears.valkyrie.compose.core.animatedBorder
31+
import io.github.composegears.valkyrie.compose.core.animation.Shimmer
32+
import io.github.composegears.valkyrie.compose.core.animation.shimmer
33+
import io.github.composegears.valkyrie.compose.core.applyIf
34+
import io.github.composegears.valkyrie.compose.ui.foundation.VerticalScrollbar
35+
36+
/**
37+
* Shared loading content for web import screens
38+
*/
39+
@Composable
40+
fun LoadingContent(
41+
modifier: Modifier = Modifier,
42+
message: String = "Loading icons...",
43+
) {
44+
Box(
45+
modifier = modifier.fillMaxSize(),
46+
contentAlignment = Alignment.Center,
47+
) {
48+
Column(
49+
horizontalAlignment = Alignment.CenterHorizontally,
50+
verticalArrangement = Arrangement.spacedBy(16.dp),
51+
) {
52+
CircularProgressIndicator()
53+
Text(
54+
text = message,
55+
style = MaterialTheme.typography.bodyMedium,
56+
)
57+
}
58+
}
59+
}
60+
61+
/**
62+
* Shared error content for web import screens
63+
*/
64+
@Composable
65+
fun ErrorContent(
66+
message: String,
67+
modifier: Modifier = Modifier,
68+
) {
69+
Box(
70+
modifier = modifier
71+
.fillMaxSize()
72+
.padding(16.dp),
73+
contentAlignment = Alignment.Center,
74+
) {
75+
Text(
76+
text = message,
77+
style = MaterialTheme.typography.bodySmall,
78+
color = MaterialTheme.colorScheme.error,
79+
)
80+
}
81+
}
82+
83+
/**
84+
* Shared empty state content for web import screens
85+
*/
86+
@Composable
87+
fun EmptyContent(
88+
modifier: Modifier = Modifier,
89+
message: String = "No icons found",
90+
) {
91+
Box(
92+
modifier = modifier.fillMaxSize(),
93+
contentAlignment = Alignment.Center,
94+
) {
95+
Text(
96+
modifier = Modifier.padding(16.dp),
97+
text = message,
98+
style = MaterialTheme.typography.titleSmall,
99+
)
100+
}
101+
}
102+
103+
/**
104+
* Shared category header for web import screens
105+
*/
106+
@Composable
107+
fun CategoryHeader(
108+
title: String,
109+
modifier: Modifier = Modifier,
110+
) {
111+
Text(
112+
modifier = modifier
113+
.padding(vertical = 8.dp)
114+
.padding(start = 4.dp),
115+
text = title,
116+
style = MaterialTheme.typography.titleMedium,
117+
)
118+
}
119+
120+
/**
121+
* Shared icon grid for web import screens.
122+
* Provides a consistent grid layout with scrollbar.
123+
*
124+
* @param state The LazyGridState for the grid
125+
* @param modifier Modifier to be applied to the container
126+
* @param content The content of the grid
127+
*/
128+
@Composable
129+
fun IconGrid(
130+
state: LazyGridState,
131+
modifier: Modifier = Modifier,
132+
content: LazyGridScope.() -> Unit,
133+
) {
134+
Box(modifier = modifier) {
135+
LazyVerticalGrid(
136+
state = state,
137+
modifier = Modifier.fillMaxSize(),
138+
columns = GridCells.Adaptive(100.dp),
139+
contentPadding = PaddingValues(16.dp),
140+
horizontalArrangement = Arrangement.spacedBy(8.dp),
141+
verticalArrangement = Arrangement.spacedBy(8.dp),
142+
content = content,
143+
)
144+
VerticalScrollbar(adapter = rememberScrollbarAdapter(state))
145+
}
146+
}
147+
148+
/**
149+
* Shared icon card wrapper for web import screens.
150+
* Provides consistent card styling, selection state, and text display.
151+
*
152+
* @param name The name of the icon to display
153+
* @param selected Whether this icon is currently selected
154+
* @param onClick Callback when the card is clicked
155+
* @param modifier Modifier to be applied to the card
156+
* @param iconContent The icon content to display in the center
157+
*/
158+
@Composable
159+
fun IconCard(
160+
name: String,
161+
selected: Boolean,
162+
onClick: () -> Unit,
163+
modifier: Modifier = Modifier,
164+
iconContent: @Composable () -> Unit,
165+
) {
166+
Box(
167+
modifier = modifier,
168+
contentAlignment = Alignment.Center,
169+
) {
170+
Card(
171+
modifier = Modifier
172+
.width(100.dp)
173+
.applyIf(selected) {
174+
animatedBorder(
175+
borderColors = listOf(
176+
Color.Transparent,
177+
MaterialTheme.colorScheme.primary,
178+
),
179+
shape = CardDefaults.shape,
180+
)
181+
},
182+
onClick = onClick,
183+
colors = CardDefaults.cardColors(
184+
containerColor = MaterialTheme.colorScheme.surface,
185+
),
186+
) {
187+
Column(
188+
horizontalAlignment = Alignment.CenterHorizontally,
189+
verticalArrangement = Arrangement.spacedBy(8.dp),
190+
modifier = Modifier
191+
.clip(shape = RoundedCornerShape(8.dp))
192+
.padding(8.dp),
193+
) {
194+
Box(
195+
modifier = Modifier
196+
.aspectRatio(1f)
197+
.padding(16.dp),
198+
contentAlignment = Alignment.Center,
199+
) {
200+
iconContent()
201+
}
202+
Text(
203+
modifier = Modifier.basicMarquee(),
204+
text = name,
205+
style = MaterialTheme.typography.bodySmall,
206+
maxLines = 1,
207+
)
208+
}
209+
}
210+
}
211+
}
212+
213+
/**
214+
* Shimmer placeholder for loading icon state.
215+
*
216+
* @param shimmer The shimmer animation to use
217+
* @param modifier Modifier to be applied to the shimmer placeholder
218+
*/
219+
@Composable
220+
fun IconLoadingPlaceholder(
221+
shimmer: Shimmer,
222+
modifier: Modifier = Modifier,
223+
) {
224+
Spacer(
225+
modifier = modifier
226+
.fillMaxSize()
227+
.clip(RoundedCornerShape(12.dp))
228+
.shimmer(shimmer = shimmer, cornerRadius = 12.dp),
229+
)
230+
}
231+
232+

0 commit comments

Comments
 (0)