Skip to content

Commit 4fed3e2

Browse files
authored
New gallery UI implementation (#56)
* new gallery UI and demo * rebase and update ListParameter UI implementation according to new re-design * spotless fixes
1 parent 9f0d46a commit 4fed3e2

File tree

41 files changed

+2347
-254
lines changed

Some content is hidden

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

41 files changed

+2347
-254
lines changed

.github/workflows/smokebuild.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ jobs:
2727
- name: Publish to Maven Local
2828
run: ./gradlew publishToMavenLocal
2929

30+
- name: Build gallery-demo
31+
run: |
32+
./gradlew :gallery-demo:wasmJsBrowserDevelopmentExecutableDistribution :gallery-demo:packageReleaseUberJarForCurrentOS
33+
3034
- name: Build Stories for Wasm target
3135
run: |
3236
cd examples

gallery-demo/build.gradle.kts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import org.jetbrains.compose.reload.ComposeHotRun
2+
import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
3+
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
4+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
5+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin
6+
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
7+
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption
8+
9+
plugins {
10+
alias(libs.plugins.kotlinMultiplatform)
11+
alias(libs.plugins.jetbrainsCompose)
12+
alias(libs.plugins.compose.compiler)
13+
alias(libs.plugins.serialization)
14+
id("org.jetbrains.compose.hot-reload") version "1.0.0-alpha03"
15+
}
16+
17+
class StorytaleCompilerPlugin : KotlinCompilerPluginSupportPlugin {
18+
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
19+
return kotlinCompilation.project.provider { emptyList() }
20+
}
21+
22+
override fun getCompilerPluginId(): String {
23+
return "org.jetbrains.compose.compiler.plugins.storytale"
24+
}
25+
26+
override fun getPluginArtifact(): SubpluginArtifact {
27+
return SubpluginArtifact("org.jetbrains.compose.storytale", "local-compiler-plugin")
28+
}
29+
30+
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean {
31+
return kotlinCompilation.target.platformType in setOf(
32+
org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm,
33+
org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.wasm,
34+
)
35+
}
36+
}
37+
38+
apply<StorytaleCompilerPlugin>()
39+
40+
configurations.all {
41+
resolutionStrategy.dependencySubstitution {
42+
substitute(module("org.jetbrains.compose.storytale:local-compiler-plugin"))
43+
.using(project(":modules:compiler-plugin"))
44+
}
45+
}
46+
47+
kotlin {
48+
js {
49+
browser()
50+
binaries.executable()
51+
}
52+
wasmJs {
53+
moduleName = "gallery-demo"
54+
browser {
55+
commonWebpackConfig {
56+
outputFileName = "gallery-demo.js"
57+
}
58+
}
59+
binaries.executable()
60+
}
61+
62+
jvm("desktop")
63+
64+
applyDefaultHierarchyTemplate()
65+
66+
sourceSets {
67+
val commonMain by getting {
68+
dependencies {
69+
implementation(compose.runtime)
70+
implementation(compose.foundation)
71+
implementation(compose.material3)
72+
implementation(compose.ui)
73+
implementation(compose.components.resources)
74+
implementation(compose.components.uiToolingPreview)
75+
implementation(libs.navigation.compose)
76+
implementation(libs.compose.highlights)
77+
implementation(libs.kotlinx.serialization.json)
78+
implementation(projects.modules.runtimeApi)
79+
implementation(projects.modules.gallery)
80+
implementation("org.jetbrains.compose.material3.adaptive:adaptive:1.1.0-beta01")
81+
}
82+
}
83+
84+
val desktopMain by getting {
85+
dependsOn(commonMain)
86+
dependencies {
87+
implementation(compose.desktop.currentOs)
88+
}
89+
}
90+
}
91+
92+
@OptIn(ExperimentalKotlinGradlePluginApi::class)
93+
compilerOptions {
94+
freeCompilerArgs = listOf(
95+
"-opt-in=androidx.compose.animation.ExperimentalSharedTransitionApi",
96+
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
97+
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
98+
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
99+
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
100+
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
101+
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
102+
"-opt-in=kotlinx.coroutines.FlowPreview",
103+
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
104+
"-opt-in=com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi",
105+
"-Xexpect-actual-classes",
106+
)
107+
}
108+
}
109+
110+
compose.desktop {
111+
application {
112+
mainClass = "storytale.gallery.demo.MainKt"
113+
}
114+
}
115+
116+
composeCompiler {
117+
featureFlags.add(ComposeFeatureFlag.OptimizeNonSkippingGroups)
118+
}
119+
120+
tasks.register<ComposeHotRun>("runHot") {
121+
mainClass.set("storytale.gallery.demo.MainKt")
122+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
@file:Suppress("ktlint:standard:property-naming")
2+
3+
package storytale.gallery.demo
4+
5+
import androidx.compose.foundation.layout.Arrangement
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.Spacer
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.material.icons.Icons
10+
import androidx.compose.material.icons.filled.Add
11+
import androidx.compose.material.icons.filled.AddCircle
12+
import androidx.compose.material3.Button
13+
import androidx.compose.material3.ElevatedButton
14+
import androidx.compose.material3.ExtendedFloatingActionButton
15+
import androidx.compose.material3.FilledTonalButton
16+
import androidx.compose.material3.FloatingActionButton
17+
import androidx.compose.material3.Icon
18+
import androidx.compose.material3.LargeFloatingActionButton
19+
import androidx.compose.material3.MaterialTheme
20+
import androidx.compose.material3.OutlinedButton
21+
import androidx.compose.material3.SegmentedButton
22+
import androidx.compose.material3.SegmentedButtonDefaults
23+
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
24+
import androidx.compose.material3.SmallFloatingActionButton
25+
import androidx.compose.material3.Text
26+
import androidx.compose.material3.TextButton
27+
import androidx.compose.runtime.CompositionLocalProvider
28+
import androidx.compose.runtime.mutableIntStateOf
29+
import androidx.compose.runtime.remember
30+
import androidx.compose.ui.Alignment
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.platform.LocalDensity
33+
import androidx.compose.ui.unit.dp
34+
import org.jetbrains.compose.storytale.story
35+
36+
val `Floating Action Buttons` by story {
37+
val Density by parameter(LocalDensity.current)
38+
val `Container color` by parameter(MaterialTheme.colorScheme.primary)
39+
val bgColor = `Container color`
40+
41+
CompositionLocalProvider(LocalDensity provides Density) {
42+
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
43+
SmallFloatingActionButton(onClick = {}, containerColor = bgColor) {
44+
Icon(imageVector = Icons.Default.Add, contentDescription = null)
45+
}
46+
FloatingActionButton(onClick = {}, containerColor = bgColor) {
47+
Icon(imageVector = Icons.Default.Add, contentDescription = null)
48+
}
49+
ExtendedFloatingActionButton(onClick = {}, containerColor = bgColor) {
50+
Icon(imageVector = Icons.Default.AddCircle, contentDescription = null)
51+
Spacer(Modifier.padding(4.dp))
52+
Text("Extended")
53+
}
54+
LargeFloatingActionButton(onClick = {}, containerColor = bgColor) {
55+
Text("Large")
56+
}
57+
}
58+
}
59+
}
60+
61+
val `Segmented buttons` by story {
62+
val selectedIndex = remember { mutableIntStateOf(0) }
63+
64+
SingleChoiceSegmentedButtonRow {
65+
repeat(3) { index ->
66+
SegmentedButton(
67+
selected = index == selectedIndex.value,
68+
onClick = { selectedIndex.value = index },
69+
shape = SegmentedButtonDefaults.itemShape(index, 3),
70+
) {
71+
Text("Button $index", modifier = Modifier.padding(4.dp))
72+
}
73+
}
74+
}
75+
}
76+
77+
val `Common buttons` by story {
78+
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
79+
ElevatedButton(onClick = {}) {
80+
Text("Elevated Button")
81+
}
82+
83+
Button(onClick = {}) {
84+
Text("Filled", softWrap = false)
85+
}
86+
87+
FilledTonalButton(onClick = {}) {
88+
Text("Tonal", softWrap = false)
89+
}
90+
91+
OutlinedButton(onClick = {}) {
92+
Text("Outlined", softWrap = false)
93+
}
94+
95+
TextButton(onClick = {}) {
96+
Text("Text", softWrap = false)
97+
}
98+
}
99+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package storytale.gallery.demo
2+
3+
import androidx.compose.foundation.layout.size
4+
import androidx.compose.material3.Button
5+
import androidx.compose.material3.Text
6+
import androidx.compose.ui.Modifier
7+
import androidx.compose.ui.unit.dp
8+
import org.jetbrains.compose.storytale.story
9+
10+
val foodEmojiList = listOf(
11+
"Apple 🍎",
12+
"Banana 🍌",
13+
"Cherry 🍒",
14+
"Grapes 🍇",
15+
"Strawberry 🍓",
16+
"Watermelon 🍉",
17+
"Pineapple 🍍",
18+
"Pizza 🍕",
19+
"Burger 🍔",
20+
"Fries 🍟",
21+
"Ice Cream 🍦",
22+
"Cake 🍰",
23+
"Coffee ☕",
24+
"Beer 🍺",
25+
)
26+
27+
val `List Parameters` by story {
28+
val food by parameter(foodEmojiList)
29+
30+
Button(onClick = {}) {
31+
Text(food)
32+
}
33+
}
34+
35+
enum class PrimaryButtonSize {
36+
Small,
37+
Medium,
38+
Large,
39+
}
40+
41+
val `Enum Parameters` by story {
42+
val size by parameter(PrimaryButtonSize.Medium, label = null)
43+
44+
Button(
45+
onClick = {},
46+
modifier = Modifier.size(
47+
when (size) {
48+
PrimaryButtonSize.Small -> 90.dp
49+
PrimaryButtonSize.Medium -> 120.dp
50+
PrimaryButtonSize.Large -> 150.dp
51+
},
52+
),
53+
) {
54+
Text(size.toString())
55+
}
56+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@file:Suppress("ktlint:standard:property-naming")
2+
3+
package storytale.gallery.demo
4+
5+
import androidx.compose.material3.Button
6+
import androidx.compose.material3.ButtonDefaults
7+
import androidx.compose.material3.Checkbox
8+
import androidx.compose.material3.MaterialTheme
9+
import androidx.compose.material3.Switch
10+
import androidx.compose.material3.Text
11+
import org.jetbrains.compose.storytale.story
12+
13+
val Button by story {
14+
val Label by parameter("Click Me")
15+
val Enabled by parameter(true)
16+
val bgColorAlpha by parameter(1f)
17+
18+
Button(
19+
enabled = Enabled,
20+
onClick = {},
21+
colors = ButtonDefaults.buttonColors().copy(
22+
containerColor = MaterialTheme.colorScheme.primary.copy(alpha = bgColorAlpha),
23+
),
24+
) {
25+
Text(Label)
26+
}
27+
}
28+
29+
val Checkbox by story {
30+
var checked by parameter(false)
31+
Checkbox(checked, onCheckedChange = { checked = it })
32+
}
33+
34+
val Switch by story {
35+
var checked by parameter(false)
36+
Switch(checked, onCheckedChange = { checked = it })
37+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.jetbrains.compose.storytale.generated
2+
3+
import androidx.compose.ui.unit.dp
4+
import androidx.compose.ui.window.WindowState
5+
import androidx.compose.ui.window.singleWindowApplication
6+
import org.jetbrains.compose.reload.DevelopmentEntryPoint
7+
import org.jetbrains.compose.storytale.gallery.material3.StorytaleGalleryApp
8+
9+
// To let the Storytale compiler plugin add the initializations for stories
10+
@Suppress("ktlint:standard:function-naming")
11+
fun MainViewController() {
12+
singleWindowApplication(
13+
state = WindowState(width = 800.dp, height = 800.dp),
14+
) {
15+
DevelopmentEntryPoint {
16+
StorytaleGalleryApp()
17+
}
18+
}
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package storytale.gallery.demo
2+
3+
import org.jetbrains.compose.storytale.generated.MainViewController
4+
5+
fun main() {
6+
MainViewController()
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.jetbrains.compose.storytale.generated
2+
3+
@Suppress("ktlint:standard:function-naming")
4+
fun MainViewController() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package storytale.gallery.demo
2+
3+
import androidx.compose.runtime.LaunchedEffect
4+
import androidx.compose.ui.window.ComposeViewport
5+
import androidx.navigation.ExperimentalBrowserHistoryApi
6+
import androidx.navigation.bindToNavigation
7+
import androidx.navigation.compose.rememberNavController
8+
import kotlinx.browser.window
9+
import org.jetbrains.compose.resources.ExperimentalResourceApi
10+
import org.jetbrains.compose.resources.preloadFont
11+
import org.jetbrains.compose.storytale.gallery.material3.StorytaleGalleryApp
12+
import org.jetbrains.compose.storytale.gallery.story.code.JetBrainsMonoRegularRes
13+
import org.jetbrains.compose.storytale.generated.MainViewController
14+
15+
@OptIn(ExperimentalResourceApi::class, ExperimentalBrowserHistoryApi::class)
16+
fun main() {
17+
MainViewController() // Storytale compiler will initialize the stories
18+
19+
val useEmbedded = window.location.search.contains("embedded=true")
20+
21+
ComposeViewport(viewportContainerId = "composeApplication") {
22+
val hasResourcePreloadCompleted = preloadFont(JetBrainsMonoRegularRes).value != null
23+
val navHostController = rememberNavController()
24+
25+
if (hasResourcePreloadCompleted) {
26+
StorytaleGalleryApp(isEmbedded = useEmbedded, navHostController)
27+
28+
LaunchedEffect(Unit) {
29+
window.bindToNavigation(navHostController)
30+
}
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)