Skip to content

Commit 80410f5

Browse files
authored
feat(sdds-playground/sandbox): Dynamic components . (#308)
* feat(sdds-playground/sandbox): Add dynamic components list support. * fix(sdds-playground/sandbox): Fix segment and counter screen routes.
1 parent 88c62b3 commit 80410f5

147 files changed

Lines changed: 3669 additions & 2938 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

gradle/libs.versions.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ androidX-compose-compiler = "1.4.8"
1818
androidX-activity-compose = "1.3.1"
1919
androidX-compose-bom = "2024.06.00"
2020
androidX-lifecycle-compose = "2.4.1"
21-
androidX-navigation="2.5.3"
21+
androidX-navigation="2.8.1"
2222
androidX-constraintLayout = "2.1.4"
2323
androidX-lifecycle = "2.4.1"
2424
androidX-activityKtx = "1.3.1"
@@ -57,6 +57,7 @@ base-androidX-constraintLayout = { module = "androidx.constraintlayout:constrain
5757
base-androidX-core = { module = "androidx.core:core-ktx", version.ref = "androidX-core" }
5858
base-androidX-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidX-navigation" }
5959
base-androidX-navigation-ui = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidX-navigation" }
60+
base-androidX-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidX-navigation" }
6061
base-androidX-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "androidX-activityKtx" }
6162
base-androidX-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidX-lifecycle" }
6263

playground/sandbox-compose/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ plugins {
1010
id("kotlin-parcelize")
1111
alias(libs.plugins.roborazzi)
1212
alias(libs.plugins.kotlin.android)
13+
alias(libs.plugins.kotlin.serialization)
1314
id("star-dimens-generator")
1415
}
1516

@@ -133,8 +134,10 @@ dependencies {
133134
implementation(libs.base.androidX.lifecycle.viewmodel)
134135
implementation(libs.base.androidX.navigation.fragment)
135136
implementation(libs.base.androidX.navigation.ui)
137+
implementation(libs.base.androidX.navigation.compose)
136138
implementation(libs.base.androidX.activity.ktx)
137139
implementation(libs.base.glide)
140+
implementation(libs.base.kotlin.serialization.json)
138141

139142
// Preview support
140143
implementation(libs.base.androidX.compose.uiTooling.preview)

playground/sandbox-compose/src/app/kotlin/com/sdds/playground/sandbox/Theme.kt

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,66 +2,65 @@ package com.sdds.playground.sandbox
22

33
import androidx.annotation.StyleRes
44
import androidx.compose.runtime.Composable
5-
import com.sdds.playground.sandbox.core.integration.StylesProviderCompose
6-
import com.sdds.playground.sandbox.core.integration.StylesProviderView
7-
import com.sdds.playground.sandbox.plasma.sd.service.integration.PlasmaSdServiceComposeStylesProvider
5+
import com.sdds.playground.sandbox.core.integration.component.ComponentsProviderCompose
6+
import com.sdds.playground.sandbox.core.integration.component.ComponentsProviderView
7+
import com.sdds.playground.sandbox.plasma.sd.service.integration.PlasmaSdServiceComposeComponents
88
import com.sdds.playground.sandbox.plasma.sd.service.integration.PlasmaSdServiceThemeWrapper
9-
import com.sdds.playground.sandbox.plasma.sd.service.integration.PlasmaSdServiceViewStylesProvider
10-
import com.sdds.playground.sandbox.sdds.serv.integration.SddsServComposeStylesProvider
9+
import com.sdds.playground.sandbox.plasma.sd.service.integration.PlasmaSdServiceViewComponents
10+
import com.sdds.playground.sandbox.sdds.serv.integration.SddsServComposeComponents
1111
import com.sdds.playground.sandbox.sdds.serv.integration.SddsServThemeWrapper
12-
import com.sdds.playground.sandbox.sdds.serv.integration.SddsServViewStylesProvider
13-
import com.sdds.playground.sandbox.stylessalute.integration.StylesSaluteComposeStylesProvider
12+
import com.sdds.playground.sandbox.sdds.serv.integration.SddsServViewComponents
13+
import com.sdds.playground.sandbox.stylessalute.integration.StylesSaluteComposeComponents
1414
import com.sdds.playground.sandbox.stylessalute.integration.StylesSaluteThemeWrapper
15-
import com.sdds.playground.sandbox.stylessalute.integration.StylesSaluteViewStylesProvider
15+
import com.sdds.playground.sandbox.stylessalute.integration.StylesSaluteViewComponents
1616

1717
internal enum class Theme(
1818
val compose: ThemeInfoCompose,
1919
val view: ThemeInfoView,
2020
) {
2121
SddsServ(
2222
compose = ThemeInfoCompose(
23-
stylesProvider = SddsServComposeStylesProvider,
23+
components = SddsServComposeComponents,
2424
themeWrapper = { SddsServThemeWrapper(it) },
2525
),
2626
view = ThemeInfoView(
27-
stylesProvider = SddsServViewStylesProvider,
27+
components = SddsServViewComponents,
2828
themeRes = com.sdds.serv.R.style.Serv_Sdds_MaterialComponents_DayNight,
2929
),
3030
),
3131
PlasmaSdService(
3232
compose = ThemeInfoCompose(
33-
stylesProvider = PlasmaSdServiceComposeStylesProvider,
33+
components = PlasmaSdServiceComposeComponents,
3434
themeWrapper = { PlasmaSdServiceThemeWrapper(it) },
3535
),
3636
view = ThemeInfoView(
37-
stylesProvider = PlasmaSdServiceViewStylesProvider,
37+
components = PlasmaSdServiceViewComponents,
3838
themeRes = com.sdds.plasma.sd.service.R.style.Plasma_SdService_MaterialComponents_DayNight,
3939
),
4040
),
4141
StylesSalute(
4242
compose = ThemeInfoCompose(
43-
stylesProvider = StylesSaluteComposeStylesProvider,
43+
components = StylesSaluteComposeComponents,
4444
themeWrapper = { StylesSaluteThemeWrapper(it) },
4545
),
4646
view = ThemeInfoView(
47-
stylesProvider = StylesSaluteViewStylesProvider,
47+
components = StylesSaluteViewComponents,
4848
themeRes = com.sdds.stylessalute.R.style.Salute_StylesSalute_MaterialComponents_DayNight,
4949
),
5050
),
5151
;
5252

5353
internal class ThemeInfoCompose(
54-
val stylesProvider: StylesProviderCompose,
54+
val components: ComponentsProviderCompose,
5555
val themeWrapper: @Composable (@Composable () -> Unit) -> Unit,
5656
)
5757

5858
internal class ThemeInfoView(
59-
val stylesProvider: StylesProviderView,
59+
val components: ComponentsProviderView,
6060
@StyleRes val themeRes: Int,
6161
)
6262

6363
internal companion object {
64-
val viewDefault = SddsServ
65-
val composeDefault = SddsServ.compose
64+
val Default = SddsServ
6665
}
6766
}

playground/sandbox-compose/src/app/kotlin/com/sdds/playground/sandbox/core/compose/ComponentScaffold.kt

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import androidx.compose.ui.Modifier
3030
import androidx.compose.ui.draw.clip
3131
import androidx.compose.ui.res.dimensionResource
3232
import androidx.compose.ui.unit.dp
33+
import com.sdds.compose.uikit.style.Style
3334
import com.sdds.playground.sandbox.R
35+
import com.sdds.playground.sandbox.core.ThemeManager
36+
import com.sdds.playground.sandbox.core.integration.component.ComponentKey
3437
import com.sdds.serv.theme.SddsServTheme
3538
import kotlinx.coroutines.launch
3639

@@ -41,14 +44,16 @@ import kotlinx.coroutines.launch
4144
*/
4245
@OptIn(ExperimentalMaterialApi::class)
4346
@Composable
44-
internal fun ComponentScaffold(
45-
component: @Composable BoxScope.() -> Unit,
46-
propertiesOwner: PropertiesOwner,
47+
internal fun <State : UiState, S : Style> ComponentScaffold(
48+
key: ComponentKey,
49+
viewModel: ComponentViewModel<State, S>,
50+
themeManager: ThemeManager = ThemeManager,
51+
component: @Composable BoxScope.(State, S) -> Unit,
4752
) {
4853
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
4954
val uiScope = rememberCoroutineScope()
5055
var currentProperty: Property<*>? by remember { mutableStateOf(null) }
51-
val properties by propertiesOwner.properties.collectAsState()
56+
val properties by viewModel.properties.collectAsState()
5257

5358
ModalBottomSheetLayout(
5459
sheetState = sheetState,
@@ -87,7 +92,14 @@ internal fun ComponentScaffold(
8792

8893
contentAlignment = Alignment.Center,
8994
) {
90-
component()
95+
val currentTheme by themeManager.currentTheme.collectAsState()
96+
currentTheme.compose.themeWrapper {
97+
val uiState by viewModel.uiState.collectAsState()
98+
component(
99+
uiState,
100+
currentTheme.compose.components.get<String, S>(key).styleProvider.style(uiState.variant),
101+
)
102+
}
91103
}
92104

93105
PropertiesList(
@@ -96,7 +108,7 @@ internal fun ComponentScaffold(
96108
currentProperty = it
97109
uiScope.launch { sheetState.show() }
98110
},
99-
onReset = { propertiesOwner.resetToDefault() },
111+
onReset = { viewModel.resetToDefault() },
100112
)
101113
}
102114
}
Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package com.sdds.playground.sandbox.activities.compose
22

33
import android.os.Bundle
4-
import androidx.activity.ComponentActivity
54
import androidx.activity.compose.setContent
6-
import androidx.compose.runtime.Composable
7-
import androidx.compose.ui.tooling.preview.Preview
85
import androidx.core.view.WindowCompat
6+
import androidx.fragment.app.FragmentActivity
97
import com.sdds.playground.sandbox.SandboxTheme
108
import com.sdds.playground.sandbox.core.compose.MainContent
119

1210
/**
1311
* Активити демо-приложения на compose
1412
*/
15-
class SandboxActivityCompose : ComponentActivity() {
13+
class SandboxActivityCompose : FragmentActivity() {
1614
override fun onCreate(savedInstanceState: Bundle?) {
1715
WindowCompat.setDecorFitsSystemWindows(window, false)
1816
super.onCreate(savedInstanceState)
@@ -23,14 +21,3 @@ class SandboxActivityCompose : ComponentActivity() {
2321
}
2422
}
2523
}
26-
27-
/**
28-
* Превью [SandboxContainer]
29-
*/
30-
@Preview(showBackground = true)
31-
@Composable
32-
fun SandboxContainerPreview() {
33-
SandboxTheme {
34-
MainContent()
35-
}
36-
}

playground/sandbox-compose/src/main/kotlin/com/sdds/playground/sandbox/activities/vs/SandboxActivity.kt

Lines changed: 81 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,41 @@ package com.sdds.playground.sandbox.activities.vs
22

33
import android.content.Intent
44
import android.os.Bundle
5+
import android.view.Menu
56
import android.view.View
67
import androidx.appcompat.app.AppCompatActivity
78
import androidx.core.view.ViewCompat
89
import androidx.core.view.WindowCompat
910
import androidx.core.view.WindowInsetsCompat
1011
import androidx.core.view.isVisible
1112
import androidx.core.view.updatePadding
13+
import androidx.customview.widget.Openable
1214
import androidx.drawerlayout.widget.DrawerLayout
15+
import androidx.lifecycle.Lifecycle
16+
import androidx.lifecycle.flowWithLifecycle
17+
import androidx.lifecycle.lifecycleScope
18+
import androidx.navigation.NavController
19+
import androidx.navigation.NavDestination.Companion.hierarchy
20+
import androidx.navigation.NavOptions
21+
import androidx.navigation.createGraph
1322
import androidx.navigation.findNavController
1423
import androidx.navigation.ui.AppBarConfiguration
1524
import androidx.navigation.ui.navigateUp
1625
import androidx.navigation.ui.setupActionBarWithNavController
17-
import androidx.navigation.ui.setupWithNavController
1826
import com.google.android.material.navigation.NavigationView
1927
import com.sdds.playground.sandbox.MainSandboxActivity
2028
import com.sdds.playground.sandbox.R
2129
import com.sdds.playground.sandbox.Theme
30+
import com.sdds.playground.sandbox.core.ThemeManager
31+
import com.sdds.playground.sandbox.core.integration.component.ComponentKey
2232
import com.sdds.playground.sandbox.core.vs.ComponentFragment
2333
import com.sdds.playground.sandbox.core.vs.EditorFragment
24-
import com.sdds.playground.sandbox.core.vs.ViewSystemThemeState
34+
import com.sdds.playground.sandbox.core.vs.MenuItem
2535
import com.sdds.playground.sandbox.core.vs.choiceEditor
36+
import com.sdds.playground.sandbox.core.vs.getMenuItems
2637
import com.sdds.playground.sandbox.databinding.MainActivityBinding
38+
import kotlinx.coroutines.flow.launchIn
39+
import kotlinx.coroutines.flow.onEach
2740

2841
/**
2942
* Активити демо-приложения на View-System
@@ -32,6 +45,8 @@ class SandboxActivity : AppCompatActivity() {
3245

3346
private lateinit var appBarConfiguration: AppBarConfiguration
3447
private lateinit var binding: MainActivityBinding
48+
private val themeManager: ThemeManager = ThemeManager
49+
private var lastSelectedKey: ComponentKey? = null
3550

3651
override fun onCreate(savedInstanceState: Bundle?) {
3752
super.onCreate(savedInstanceState)
@@ -41,12 +56,11 @@ class SandboxActivity : AppCompatActivity() {
4156

4257
setSupportActionBar(binding.appBarMain.toolbar)
4358
setUpFullscreen(binding.appBarMain.root)
44-
val drawerLayout: DrawerLayout = binding.drawerLayout
45-
val navView: NavigationView = binding.navView
4659
val navController = findNavController(R.id.nav_host_fragment_content_main)
47-
appBarConfiguration = AppBarConfiguration(navigationSet, drawerLayout)
48-
setupActionBarWithNavController(navController, appBarConfiguration)
49-
navView.setupWithNavController(navController)
60+
themeManager.currentTheme
61+
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
62+
.onEach { updateNavigation(it, navController) }
63+
.launchIn(lifecycleScope)
5064

5165
intent.extras?.let { extra ->
5266
val destinationId = extra.getInt(DESTINATION_ID_ARG, R.id.nav_basic_button)
@@ -59,6 +73,64 @@ class SandboxActivity : AppCompatActivity() {
5973
setupThemePicker()
6074
}
6175

76+
private fun updateNavigation(theme: Theme, navController: NavController) {
77+
val drawerLayout: DrawerLayout = binding.drawerLayout
78+
val navView: NavigationView = binding.navView
79+
val menuItems = theme.view.components.getMenuItems()
80+
val startDestination = menuItems.first()
81+
val graph = navController.createGraph(startDestination = startDestination.id) {
82+
menuItems.forEach {
83+
val builder = it.destination.builder
84+
this.builder(it)
85+
}
86+
}
87+
navController.setGraph(graph, startDestination.componentKeyBundle)
88+
navView.populateMenu(menuItems, navController)
89+
appBarConfiguration = AppBarConfiguration(navView.menu, drawerLayout)
90+
setupActionBarWithNavController(navController, appBarConfiguration)
91+
}
92+
93+
private fun NavigationView.populateMenu(items: List<MenuItem>, navController: NavController) {
94+
menu.clear()
95+
val itemsMap = items.associateBy { it.id }
96+
setNavigationItemSelectedListener {
97+
val item = itemsMap[it.itemId] ?: return@setNavigationItemSelectedListener false
98+
val handled = onNavDestinationSelected(item, navController)
99+
100+
if (handled) {
101+
lastSelectedKey = item.componentKey
102+
val parent = parent
103+
if (parent is Openable) {
104+
parent.close()
105+
}
106+
}
107+
handled
108+
}
109+
items.forEach { menuItem ->
110+
menu.add(Menu.NONE, menuItem.id, Menu.NONE, menuItem.title)
111+
}
112+
}
113+
114+
@Suppress("RestrictedApi")
115+
private fun onNavDestinationSelected(menuItem: MenuItem, navController: NavController): Boolean {
116+
val builder = NavOptions.Builder()
117+
.setLaunchSingleTop(true)
118+
.setRestoreState(true)
119+
.let { optionsBuilder ->
120+
navController.currentBackStackEntry?.destination?.route?.let { prev ->
121+
optionsBuilder.setPopUpTo(prev, true)
122+
} ?: optionsBuilder
123+
}
124+
val options = builder.build()
125+
val destinationId = navController.graph.findNode(menuItem.route)?.id ?: return false
126+
return try {
127+
navController.navigate(destinationId, menuItem.componentKeyBundle, options)
128+
navController.currentDestination?.hierarchy?.any { it.id == destinationId } == true
129+
} catch (e: IllegalArgumentException) {
130+
false
131+
}
132+
}
133+
62134
private fun setupThemePicker() {
63135
val themes = Theme.values()
64136
binding.appBarMain.bSettings.apply {
@@ -67,7 +139,7 @@ class SandboxActivity : AppCompatActivity() {
67139
setOnClickListener {
68140
EditorFragment.choiceEditor(
69141
propertyName = "Theme",
70-
currentValue = ViewSystemThemeState.theme.value.name,
142+
currentValue = themeManager.currentTheme.value.name,
71143
choices = Theme.values().map(Theme::name),
72144
confirmKey = THEME_PICKER_RESULT_KEY,
73145
).show(supportFragmentManager, "ThemePicker")
@@ -78,7 +150,7 @@ class SandboxActivity : AppCompatActivity() {
78150
) { requestKey, bundle ->
79151
if (requestKey != THEME_PICKER_RESULT_KEY) return@setFragmentResultListener
80152
val newValue = bundle.getString(EditorFragment.CONFIRM_VALUE).orEmpty()
81-
ViewSystemThemeState.setTheme(Theme.valueOf(newValue))
153+
themeManager.updateTheme(Theme.valueOf(newValue))
82154
}
83155
}
84156
}
@@ -108,29 +180,5 @@ class SandboxActivity : AppCompatActivity() {
108180
*/
109181
const val DESTINATION_ID_ARG = "DESTINATION_ID_ARG"
110182
private const val THEME_PICKER_RESULT_KEY = "THEME_PICKER_RESULT_KEY"
111-
private val navigationSet = setOf(
112-
R.id.nav_basic_button,
113-
R.id.nav_badge,
114-
R.id.nav_icon_button,
115-
R.id.nav_icon_badge,
116-
R.id.nav_link_button,
117-
R.id.nav_indicator,
118-
R.id.nav_cell,
119-
R.id.nav_checkbox,
120-
R.id.nav_checkbox_group,
121-
R.id.nav_chip,
122-
R.id.nav_chip_group,
123-
R.id.nav_counter,
124-
R.id.nav_radiobox,
125-
R.id.nav_radiobox_group,
126-
R.id.nav_progressbar,
127-
R.id.nav_segment,
128-
R.id.nav_segment_item,
129-
R.id.nav_switch,
130-
R.id.nav_avatar,
131-
R.id.nav_avatargroup,
132-
R.id.nav_textfield,
133-
R.id.nav_textarea,
134-
)
135183
}
136184
}

0 commit comments

Comments
 (0)