Skip to content

Commit b32cefd

Browse files
committed
refactor: reuse code between svg and standard implementations and add zoom layout to svg import screen
1 parent ed31e08 commit b32cefd

17 files changed

Lines changed: 188 additions & 211 deletions

File tree

sdk/compose/icons/api/icons.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public final class io/github/composegears/valkyrie/sdk/compose/icons/colored/Goo
3737
public static final fun getGoogleMaterialLogo (Lio/github/composegears/valkyrie/sdk/compose/icons/ValkyrieIcons$Colored;)Landroidx/compose/ui/graphics/vector/ImageVector;
3838
}
3939

40+
public final class io/github/composegears/valkyrie/sdk/compose/icons/colored/HeroiconsLogoKt {
41+
public static final fun getHeroiconsLogo (Lio/github/composegears/valkyrie/sdk/compose/icons/ValkyrieIcons$Colored;)Landroidx/compose/ui/graphics/vector/ImageVector;
42+
}
43+
4044
public final class io/github/composegears/valkyrie/sdk/compose/icons/colored/IoniconsLogoKt {
4145
public static final fun getIoniconsLogo (Lio/github/composegears/valkyrie/sdk/compose/icons/ValkyrieIcons$Colored;)Landroidx/compose/ui/graphics/vector/ImageVector;
4246
}

sdk/compose/icons/api/icons.klib.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ final val io.github.composegears.valkyrie.sdk.compose.icons.colored/FontAwesomeL
2424
final fun (io.github.composegears.valkyrie.sdk.compose.icons/ValkyrieIcons.Colored).<get-FontAwesomeLogo>(): androidx.compose.ui.graphics.vector/ImageVector // io.github.composegears.valkyrie.sdk.compose.icons.colored/FontAwesomeLogo.<get-FontAwesomeLogo>|<get-FontAwesomeLogo>@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored(){}[0]
2525
final val io.github.composegears.valkyrie.sdk.compose.icons.colored/GoogleMaterialLogo // io.github.composegears.valkyrie.sdk.compose.icons.colored/GoogleMaterialLogo|@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored{}GoogleMaterialLogo[0]
2626
final fun (io.github.composegears.valkyrie.sdk.compose.icons/ValkyrieIcons.Colored).<get-GoogleMaterialLogo>(): androidx.compose.ui.graphics.vector/ImageVector // io.github.composegears.valkyrie.sdk.compose.icons.colored/GoogleMaterialLogo.<get-GoogleMaterialLogo>|<get-GoogleMaterialLogo>@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored(){}[0]
27+
final val io.github.composegears.valkyrie.sdk.compose.icons.colored/HeroiconsLogo // io.github.composegears.valkyrie.sdk.compose.icons.colored/HeroiconsLogo|@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored{}HeroiconsLogo[0]
28+
final fun (io.github.composegears.valkyrie.sdk.compose.icons/ValkyrieIcons.Colored).<get-HeroiconsLogo>(): androidx.compose.ui.graphics.vector/ImageVector // io.github.composegears.valkyrie.sdk.compose.icons.colored/HeroiconsLogo.<get-HeroiconsLogo>|<get-HeroiconsLogo>@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored(){}[0]
2729
final val io.github.composegears.valkyrie.sdk.compose.icons.colored/IoniconsLogo // io.github.composegears.valkyrie.sdk.compose.icons.colored/IoniconsLogo|@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored{}IoniconsLogo[0]
2830
final fun (io.github.composegears.valkyrie.sdk.compose.icons/ValkyrieIcons.Colored).<get-IoniconsLogo>(): androidx.compose.ui.graphics.vector/ImageVector // io.github.composegears.valkyrie.sdk.compose.icons.colored/IoniconsLogo.<get-IoniconsLogo>|<get-IoniconsLogo>@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored(){}[0]
2931
final val io.github.composegears.valkyrie.sdk.compose.icons.colored/LucideLogo // io.github.composegears.valkyrie.sdk.compose.icons.colored/LucideLogo|@io.github.composegears.valkyrie.sdk.compose.icons.ValkyrieIcons.Colored{}LucideLogo[0]

tools/idea-plugin/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Added
66

77
- [Web Import] Add `Simple` icons provider
8+
- [Web Import] Add `Hero` icons provider
89
- [Web Import] Add floating zoom control bar to the icon grid — allows changing the visual display size of icons (
910
25%–200%) without affecting import settings
1011
- [Web Import] Add `Feather` icons provider

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/StandardImportScreenUI.kt

Lines changed: 4 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,15 @@ package io.github.composegears.valkyrie.ui.screen.webimport.common
22

33
import androidx.compose.animation.AnimatedContent
44
import androidx.compose.foundation.gestures.detectTapGestures
5-
import androidx.compose.foundation.layout.Arrangement
65
import androidx.compose.foundation.layout.Box
76
import androidx.compose.foundation.layout.Column
8-
import androidx.compose.foundation.layout.PaddingValues
9-
import androidx.compose.foundation.layout.Spacer
10-
import androidx.compose.foundation.layout.fillMaxHeight
117
import androidx.compose.foundation.layout.fillMaxSize
128
import androidx.compose.foundation.layout.padding
139
import androidx.compose.foundation.layout.size
14-
import androidx.compose.foundation.lazy.grid.GridCells
1510
import androidx.compose.foundation.lazy.grid.GridItemSpan
16-
import androidx.compose.foundation.lazy.grid.LazyGridScope
1711
import androidx.compose.foundation.lazy.grid.LazyGridState
18-
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
1912
import androidx.compose.foundation.lazy.grid.items
2013
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
21-
import androidx.compose.foundation.shape.RoundedCornerShape
2214
import androidx.compose.runtime.Composable
2315
import androidx.compose.runtime.collectAsState
2416
import androidx.compose.runtime.getValue
@@ -27,7 +19,6 @@ import androidx.compose.runtime.rememberUpdatedState
2719
import androidx.compose.runtime.setValue
2820
import androidx.compose.ui.Alignment
2921
import androidx.compose.ui.Modifier
30-
import androidx.compose.ui.draw.clip
3122
import androidx.compose.ui.input.pointer.pointerInput
3223
import androidx.compose.ui.platform.LocalFocusManager
3324
import androidx.compose.ui.text.font.FontVariation
@@ -49,7 +40,6 @@ import io.github.composegears.valkyrie.jewel.ui.placeholder.LoadingPlaceholder
4940
import io.github.composegears.valkyrie.sdk.compose.foundation.ObserveEvent
5041
import io.github.composegears.valkyrie.sdk.compose.foundation.animation.Shimmer
5142
import io.github.composegears.valkyrie.sdk.compose.foundation.animation.rememberShimmer
52-
import io.github.composegears.valkyrie.sdk.compose.foundation.animation.shimmer
5343
import io.github.composegears.valkyrie.sdk.compose.foundation.rememberMutableState
5444
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.StandardIconProvider
5545
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.category.InferredCategory
@@ -63,15 +53,16 @@ import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.St
6353
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.settings.SizeSettings
6454
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.CategoryHeaderItem
6555
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.IconCard
56+
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.IconGrid
57+
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.IconLoadingPlaceholder
6658
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.IconSizeCustomization
6759
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.SidePanel
68-
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.StandardTopActions
60+
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.WebIconTopActions
6961
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.ZOOM_DEFAULT_SCALE
7062
import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.ZoomFloatingBar
7163
import io.github.composegears.valkyrie.util.stringResource
7264
import kotlinx.coroutines.launch
7365
import org.jetbrains.jewel.foundation.theme.LocalContentColor
74-
import org.jetbrains.jewel.ui.component.VerticalScrollbar
7566

7667
@Composable
7768
internal fun StandardImportScreen(
@@ -219,7 +210,7 @@ private fun IconsContent(
219210
)
220211
},
221212
) {
222-
StandardTopActions(
213+
WebIconTopActions(
223214
categories = state.config.categories,
224215
styles = state.config.styles,
225216
selectedCategory = state.selectedCategory,
@@ -397,55 +388,3 @@ private fun rememberStandardFont(
397388
fontVariationSettings = fontVariationSettings,
398389
)
399390
}
400-
401-
/**
402-
* Shared icon grid for web import screens.
403-
* Provides a consistent grid layout with scrollbar.
404-
*
405-
* @param state The LazyGridState for the grid
406-
* @param modifier Modifier to be applied to the container
407-
* @param content The content of the grid
408-
*/
409-
@Composable
410-
private fun IconGrid(
411-
state: LazyGridState,
412-
modifier: Modifier = Modifier,
413-
content: LazyGridScope.() -> Unit,
414-
) {
415-
Box(modifier = modifier) {
416-
LazyVerticalGrid(
417-
state = state,
418-
modifier = Modifier.fillMaxSize(),
419-
columns = GridCells.Adaptive(100.dp),
420-
contentPadding = PaddingValues(16.dp),
421-
horizontalArrangement = Arrangement.spacedBy(8.dp),
422-
verticalArrangement = Arrangement.spacedBy(8.dp),
423-
content = content,
424-
)
425-
VerticalScrollbar(
426-
scrollState = state,
427-
modifier = Modifier.fillMaxHeight()
428-
.align(Alignment.CenterEnd)
429-
.padding(end = 4.dp, top = 8.dp, bottom = 4.dp),
430-
)
431-
}
432-
}
433-
434-
/**
435-
* Shimmer placeholder for loading icon state.
436-
*
437-
* @param shimmer The shimmer animation to use
438-
* @param modifier Modifier to be applied to the shimmer placeholder
439-
*/
440-
@Composable
441-
private fun IconLoadingPlaceholder(
442-
shimmer: Shimmer,
443-
modifier: Modifier = Modifier,
444-
) {
445-
Spacer(
446-
modifier = modifier
447-
.fillMaxSize()
448-
.clip(RoundedCornerShape(12.dp))
449-
.shimmer(shimmer = shimmer, cornerRadius = 12.dp),
450-
)
451-
}

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

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.composegears.valkyrie.ui.screen.webimport.svg.common
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common
22

33
import androidx.compose.runtime.Stable
44
import androidx.lifecycle.ViewModel
@@ -8,39 +8,39 @@ import com.composegears.tiamat.navigation.asStateFlow
88
import com.composegears.tiamat.navigation.recordOf
99
import io.github.composegears.valkyrie.parser.unified.util.IconNameFormatter
1010
import io.github.composegears.valkyrie.sdk.core.extensions.safeAs
11+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.WebIconProvider
1112
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.category.InferredCategory
1213
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.GridItem
1314
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.IconStyle
15+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.StyledWebIcon
16+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.WebIconConfig
1417
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.settings.SizeSettings
1518
import io.github.composegears.valkyrie.ui.screen.webimport.common.util.filterByCategory
16-
import io.github.composegears.valkyrie.ui.screen.webimport.svg.common.domain.SvgIconProvider
17-
import io.github.composegears.valkyrie.ui.screen.webimport.svg.common.model.SvgIcon
18-
import io.github.composegears.valkyrie.ui.screen.webimport.svg.common.model.SvgIconConfig
1919
import kotlinx.coroutines.Dispatchers
2020
import kotlinx.coroutines.Job
2121
import kotlinx.coroutines.channels.Channel
2222
import kotlinx.coroutines.flow.receiveAsFlow
2323
import kotlinx.coroutines.launch
2424

25-
class SvgIconViewModel(
25+
class WebIconViewModel<Icon : StyledWebIcon, Config : WebIconConfig<Icon>>(
2626
savedState: MutableSavedState,
27-
private val provider: SvgIconProvider,
27+
private val provider: WebIconProvider<Icon, Config>,
2828
) : ViewModel() {
2929

30-
private val stateRecord = savedState.recordOf<SvgState>(
30+
private val stateRecord = savedState.recordOf<WebIconState<Icon>>(
3131
key = provider.stateKey,
32-
initialValue = SvgState.Loading,
32+
initialValue = WebIconState.Loading,
3333
)
3434
val state = stateRecord.asStateFlow()
3535

36-
private val _events = Channel<SvgIconEvent>()
36+
private val _events = Channel<WebIconEvent>()
3737
val events = _events.receiveAsFlow()
3838

3939
private var downloadJob: Job? = null
4040

4141
init {
4242
when (val initialState = stateRecord.value) {
43-
is SvgState.Success -> {
43+
is WebIconState.Success -> {
4444
val selectedStyle = initialState.selectedStyle
4545
?.takeIf { selected -> initialState.config.styles.any { it.id == selected.id } }
4646
?: initialState.config.styles.firstOrNull()
@@ -60,15 +60,17 @@ class SvgIconViewModel(
6060

6161
private fun loadConfig() {
6262
viewModelScope.launch {
63-
stateRecord.value = SvgState.Loading
63+
stateRecord.value = WebIconState.Loading
6464
runCatching {
6565
val config = provider.loadConfig()
6666
val selectedStyle = config.styles.firstOrNull()
6767
if (config.gridItems.isEmpty()) {
68-
stateRecord.value = SvgState.Error("No ${provider.providerName} icons found. Check network connection.")
68+
stateRecord.value = WebIconState.Error(
69+
"No ${provider.providerName} icons found. Check network connection.",
70+
)
6971
return@launch
7072
}
71-
stateRecord.value = SvgState.Success(
73+
stateRecord.value = WebIconState.Success(
7274
config = config,
7375
gridItems = config.filterByCategory(
7476
category = InferredCategory.All,
@@ -79,19 +81,21 @@ class SvgIconViewModel(
7981
selectedStyle = selectedStyle,
8082
)
8183
}.onFailure { error ->
82-
stateRecord.value = SvgState.Error("Error loading ${provider.providerName} icons: ${error.message}")
84+
stateRecord.value = WebIconState.Error(
85+
"Error loading ${provider.providerName} icons: ${error.message}",
86+
)
8387
}
8488
}
8589
}
8690

87-
fun downloadIcon(icon: SvgIcon) {
91+
fun downloadIcon(icon: Icon) {
8892
downloadJob?.cancel()
8993
downloadJob = viewModelScope.launch {
90-
val currentState = stateRecord.value.safeAs<SvgState.Success>() ?: return@launch
94+
val currentState = stateRecord.value.safeAs<WebIconState.Success<Icon>>() ?: return@launch
9195
runCatching {
92-
val svgContent = provider.downloadSvg(icon, currentState.settings)
96+
val svgContent = provider.downloadSvg(icon, currentState.settings, currentState.selectedStyle)
9397
_events.send(
94-
SvgIconEvent.IconDownloaded(
98+
WebIconEvent.IconDownloaded(
9599
svgContent = svgContent,
96100
name = IconNameFormatter.format(icon.exportName),
97101
),
@@ -140,35 +144,34 @@ class SvgIconViewModel(
140144
}
141145
}
142146

143-
suspend fun loadPreviewSvg(icon: SvgIcon): String = provider.loadPreviewSvg(icon)
144-
145-
private inline fun updateSuccess(crossinline transform: (SvgState.Success) -> SvgState.Success) {
147+
private inline fun updateSuccess(crossinline transform: (WebIconState.Success<Icon>) -> WebIconState.Success<Icon>) {
146148
val current = stateRecord.value
147-
if (current is SvgState.Success) {
149+
if (current is WebIconState.Success) {
148150
stateRecord.value = transform(current)
149151
}
150152
}
151153
}
152154

153-
sealed interface SvgIconEvent {
155+
sealed interface WebIconEvent {
154156
data class IconDownloaded(
155157
val svgContent: String,
156158
val name: String,
157-
) : SvgIconEvent
159+
) : WebIconEvent
158160
}
159161

160162
@Stable
161-
sealed interface SvgState {
162-
data object Loading : SvgState
163+
sealed interface WebIconState<out Icon : StyledWebIcon> {
164+
data object Loading : WebIconState<Nothing>
163165

164-
data class Error(val message: String) : SvgState
166+
data class Error(val message: String) : WebIconState<Nothing>
165167

166-
data class Success(
167-
val config: SvgIconConfig,
168-
val gridItems: List<GridItem>,
169-
val settings: SizeSettings,
168+
@Stable
169+
data class Success<Icon : StyledWebIcon>(
170+
val config: WebIconConfig<Icon>,
171+
val gridItems: List<GridItem> = emptyList(),
172+
val settings: SizeSettings = SizeSettings(),
170173
val searchQuery: String = "",
171174
val selectedCategory: InferredCategory = InferredCategory.All,
172175
val selectedStyle: IconStyle? = null,
173-
) : SvgState
176+
) : WebIconState<Icon>
174177
}

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/domain/StandardIconProvider.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
1212
import kotlinx.coroutines.flow.StateFlow
1313

1414
@Stable
15-
interface StandardIconProvider {
16-
val providerName: String
17-
val stateKey: String
15+
interface StandardIconProvider : WebIconProvider<StandardIcon, StandardIconConfig> {
1816
val fontAlias: String
19-
val persistentSize: Int
2017

2118
/**
2219
* Optional variable font configuration for providers that support font variation axes
@@ -26,7 +23,7 @@ interface StandardIconProvider {
2623
val variableFontConfig: StateFlow<VariableFontConfig?>
2724
get() = NoVariableFontConfig
2825

29-
fun updatePersistentSize(value: Int) {}
26+
override fun updatePersistentSize(value: Int) {}
3027
fun resolveFontWeight(style: IconStyle?): FontWeight = FontWeight.W400
3128

3229
/**
@@ -36,9 +33,8 @@ interface StandardIconProvider {
3633
*/
3734
fun onStyleChanged(style: IconStyle?) {}
3835

39-
suspend fun loadConfig(): StandardIconConfig
4036
suspend fun loadFontBytes(style: IconStyle? = null): FontByteArray
41-
suspend fun downloadSvg(icon: StandardIcon, settings: SizeSettings, style: IconStyle? = null): String
37+
override suspend fun downloadSvg(icon: StandardIcon, settings: SizeSettings, style: IconStyle?): String
4238

4339
companion object {
4440
/** Shared no-op StateFlow returned by the default [variableFontConfig] getter. */
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.github.composegears.valkyrie.ui.screen.webimport.common.domain
2+
3+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.IconStyle
4+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.StyledWebIcon
5+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon.WebIconConfig
6+
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.settings.SizeSettings
7+
8+
interface WebIconProvider<Icon : StyledWebIcon, Config : WebIconConfig<Icon>> {
9+
val providerName: String
10+
val stateKey: String
11+
val persistentSize: Int
12+
13+
fun updatePersistentSize(value: Int) {}
14+
15+
suspend fun loadConfig(): Config
16+
suspend fun downloadSvg(icon: Icon, settings: SizeSettings, style: IconStyle? = null): String
17+
}

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/domain/icon/StandardIcon.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.font.Co
1919
data class StandardIcon(
2020
override val name: String,
2121
val displayName: String,
22-
val exportName: String = name,
22+
override val exportName: String = name,
2323
val codepoint: Codepoint,
2424
val tags: List<String>,
2525
override val category: InferredCategory,

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/domain/icon/StyledWebIcon.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.composegears.valkyrie.ui.screen.webimport.common.domain.icon
33
import io.github.composegears.valkyrie.ui.screen.webimport.common.domain.category.InferredCategory
44

55
interface StyledWebIcon : WebIcon {
6+
val exportName: String
67
val category: InferredCategory
78
val style: IconStyle?
89
}

0 commit comments

Comments
 (0)