Skip to content

New gallery UI implementation #56

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
242f888
New gallery demo wip
eymar Mar 24, 2025
28c5fe8
Stories list update
eymar Mar 25, 2025
bd2906e
Use compiler plugin in the gallery demo
eymar Mar 25, 2025
746de16
Apply story search
eymar Mar 25, 2025
552c0dd
Improve demo stories
eymar Mar 25, 2025
70cc3d2
upd versions + add Web target
eymar Mar 27, 2025
4ab7239
upd story parameters UI
eymar Mar 27, 2025
6b08117
Add UseCustomDensity
eymar Mar 27, 2025
08c1e5a
Add JetBrains Mono font
eymar Mar 27, 2025
15001df
Upd menu icon animation
eymar Mar 27, 2025
072f0ec
wip
eymar Mar 27, 2025
6a3f366
Parse hex color string to Compose Color
eymar Mar 27, 2025
6007b7f
wip
eymar Mar 27, 2025
26539db
Add a slider control for Density parameter
eymar Mar 28, 2025
9078f13
Use compose navigation
eymar Mar 28, 2025
fee5f26
Added EmbeddedStoryView
eymar Mar 28, 2025
c8dc47a
Refactoring
eymar Mar 28, 2025
44e02c8
Refactoring 2
eymar Mar 28, 2025
47b10e6
Refactoring 3
eymar Mar 28, 2025
62005f4
Navigation refactoring
eymar Mar 31, 2025
cd56f0d
Add a toast-like message for "Copy code" action
eymar Mar 31, 2025
ff16930
Add expect/actual fun Clipboard.copyCodeToClipboard
eymar Mar 31, 2025
1cee8f2
Add ParameterUiControllerCustomizer for ad-hoc parameters visualization
eymar Mar 31, 2025
44ec60d
Support Dark and Light theme switching
eymar Apr 1, 2025
59a43ad
Simplify compiler plugin usage in gallery-demo
eymar Apr 2, 2025
62d118c
fixes
eymar Apr 2, 2025
dbd0f20
Spotless fixes
eymar Apr 2, 2025
256adac
Add Color parameter type UI controller
eymar Apr 2, 2025
6e341c9
CI workarounds
eymar Apr 2, 2025
5ff1d1a
add spotless suppression
eymar Apr 2, 2025
87627c5
fixes by spotless
eymar Apr 2, 2025
157b7a2
bump deploy version to 0.0.4
eymar Apr 2, 2025
21791f1
Use new StorytaleGalleryApp in Gallery Composable
eymar Apr 3, 2025
de12fc2
Spotless fixes
eymar Apr 3, 2025
d98d306
Add operator setValue to StoryParameterDelegate to allow changing its…
eymar Apr 3, 2025
b286cc3
Disable NavHost transitions
eymar Apr 4, 2025
369bd12
re-enable CI check
eymar Apr 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/smokebuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ jobs:
- name: Publish to Maven Local
run: ./gradlew publishToMavenLocal

- name: Build gallery-demo
run: |
./gradlew :gallery-demo:wasmJsBrowserDevelopmentExecutableDistribution :gallery-demo:packageReleaseUberJarForCurrentOS

- name: Build Stories for Wasm target
run: |
cd examples
Expand Down
122 changes: 122 additions & 0 deletions gallery-demo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import org.jetbrains.compose.reload.ComposeHotRun
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a small question related to this module. We have the example one, which contains an example of the demo. Was it intentional to create a new one instead of re-using the example?

import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilerPluginSupportPlugin
import org.jetbrains.kotlin.gradle.plugin.SubpluginArtifact
import org.jetbrains.kotlin.gradle.plugin.SubpluginOption

plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.serialization)
id("org.jetbrains.compose.hot-reload") version "1.0.0-alpha03"
}

class StorytaleCompilerPlugin : KotlinCompilerPluginSupportPlugin {
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
return kotlinCompilation.project.provider { emptyList() }
}

override fun getCompilerPluginId(): String {
return "org.jetbrains.compose.compiler.plugins.storytale"
}

override fun getPluginArtifact(): SubpluginArtifact {
return SubpluginArtifact("org.jetbrains.compose.storytale", "local-compiler-plugin")
}

override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean {
return kotlinCompilation.target.platformType in setOf(
org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.jvm,
org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.wasm,
)
}
}

apply<StorytaleCompilerPlugin>()

configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("org.jetbrains.compose.storytale:local-compiler-plugin"))
.using(project(":modules:compiler-plugin"))
}
}

kotlin {
js {
browser()
binaries.executable()
}
wasmJs {
moduleName = "gallery-demo"
browser {
commonWebpackConfig {
outputFileName = "gallery-demo.js"
}
}
binaries.executable()
}

jvm("desktop")

applyDefaultHierarchyTemplate()

sourceSets {
val commonMain by getting {
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.navigation.compose)
implementation(libs.compose.highlights)
implementation(libs.kotlinx.serialization.json)
implementation(projects.modules.runtimeApi)
implementation(projects.modules.gallery)
implementation("org.jetbrains.compose.material3.adaptive:adaptive:1.1.0-beta01")
}
}

val desktopMain by getting {
dependsOn(commonMain)
dependencies {
implementation(compose.desktop.currentOs)
}
}
}

@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
freeCompilerArgs = listOf(
"-opt-in=androidx.compose.animation.ExperimentalSharedTransitionApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi",
"-Xexpect-actual-classes",
)
}
}

compose.desktop {
application {
mainClass = "storytale.gallery.demo.MainKt"
}
}

composeCompiler {
featureFlags.add(ComposeFeatureFlag.OptimizeNonSkippingGroups)
}

tasks.register<ComposeHotRun>("runHot") {
mainClass.set("storytale.gallery.demo.MainKt")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
@file:Suppress("ktlint:standard:property-naming")

package storytale.gallery.demo

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material3.Button
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.LargeFloatingActionButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.storytale.story

val `Floating Action Buttons` by story {
val Density by parameter(LocalDensity.current)
val `Container color` by parameter(MaterialTheme.colorScheme.primary)
val bgColor = `Container color`

CompositionLocalProvider(LocalDensity provides Density) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
SmallFloatingActionButton(onClick = {}, containerColor = bgColor) {
Icon(imageVector = Icons.Default.Add, contentDescription = null)
}
FloatingActionButton(onClick = {}, containerColor = bgColor) {
Icon(imageVector = Icons.Default.Add, contentDescription = null)
}
ExtendedFloatingActionButton(onClick = {}, containerColor = bgColor) {
Icon(imageVector = Icons.Default.AddCircle, contentDescription = null)
Spacer(Modifier.padding(4.dp))
Text("Extended")
}
LargeFloatingActionButton(onClick = {}, containerColor = bgColor) {
Text("Large")
}
}
}
}

val `Segmented buttons` by story {
val selectedIndex = remember { mutableIntStateOf(0) }

SingleChoiceSegmentedButtonRow {
repeat(3) { index ->
SegmentedButton(
selected = index == selectedIndex.value,
onClick = { selectedIndex.value = index },
shape = SegmentedButtonDefaults.itemShape(index, 3),
) {
Text("Button $index", modifier = Modifier.padding(4.dp))
}
}
}
}

val `Common buttons` by story {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) {
ElevatedButton(onClick = {}) {
Text("Elevated Button")
}

Button(onClick = {}) {
Text("Filled", softWrap = false)
}

FilledTonalButton(onClick = {}) {
Text("Tonal", softWrap = false)
}

OutlinedButton(onClick = {}) {
Text("Outlined", softWrap = false)
}

TextButton(onClick = {}) {
Text("Text", softWrap = false)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@file:Suppress("ktlint:standard:property-naming")

package storytale.gallery.demo

import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import org.jetbrains.compose.storytale.story

val Button by story {
val Label by parameter("Click Me")
val Enabled by parameter(true)
val bgColorAlpha by parameter(1f)

Button(
enabled = Enabled,
onClick = {},
colors = ButtonDefaults.buttonColors().copy(
containerColor = MaterialTheme.colorScheme.primary.copy(alpha = bgColorAlpha),
),
) {
Text(Label)
}
}

val Checkbox by story {
var checked by parameter(false)
Checkbox(checked, onCheckedChange = { checked = it })
}

val Switch by story {
var checked by parameter(false)
Switch(checked, onCheckedChange = { checked = it })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.jetbrains.compose.storytale.generated

import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.singleWindowApplication
import org.jetbrains.compose.reload.DevelopmentEntryPoint
import org.jetbrains.compose.storytale.gallery.material3.StorytaleGalleryApp

// To let the Storytale compiler plugin add the initializations for stories
@Suppress("ktlint:standard:function-naming")
fun MainViewController() {
singleWindowApplication(
state = WindowState(width = 800.dp, height = 800.dp),
) {
DevelopmentEntryPoint {
StorytaleGalleryApp()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package storytale.gallery.demo

import org.jetbrains.compose.storytale.generated.MainViewController

fun main() {
MainViewController()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.jetbrains.compose.storytale.generated

@Suppress("ktlint:standard:function-naming")
fun MainViewController() {}
33 changes: 33 additions & 0 deletions gallery-demo/src/wasmJsMain/kotlin/storytale/gallery/demo/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package storytale.gallery.demo

import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.window.ComposeViewport
import androidx.navigation.ExperimentalBrowserHistoryApi
import androidx.navigation.bindToNavigation
import androidx.navigation.compose.rememberNavController
import kotlinx.browser.window
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.preloadFont
import org.jetbrains.compose.storytale.gallery.material3.StorytaleGalleryApp
import org.jetbrains.compose.storytale.gallery.story.code.JetBrainsMonoRegularRes
import org.jetbrains.compose.storytale.generated.MainViewController

@OptIn(ExperimentalResourceApi::class, ExperimentalBrowserHistoryApi::class)
fun main() {
MainViewController() // Storytale compiler will initialize the stories

val useEmbedded = window.location.search.contains("embedded=true")

ComposeViewport(viewportContainerId = "composeApplication") {
val hasResourcePreloadCompleted = preloadFont(JetBrainsMonoRegularRes).value != null
val navHostController = rememberNavController()

if (hasResourcePreloadCompleted) {
StorytaleGalleryApp(isEmbedded = useEmbedded, navHostController)

LaunchedEffect(Unit) {
window.bindToNavigation(navHostController)
}
}
}
}
13 changes: 13 additions & 0 deletions gallery-demo/src/wasmJsMain/resources/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Storytale Gallery Demo</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<script src="gallery-demo.js"> </script>
</head>
<body>
<div id="composeApplication"/>
</body>
</html>
42 changes: 42 additions & 0 deletions gallery-demo/src/wasmJsMain/resources/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}

h1 {
padding: 0 16px;
font-size: 2em;
}

#composeApplication {
width: 100%;
height: 100%;
}

body:has(textarea) {
background-color: aliceblue;
}

body:has(textarea:focus) {
background-color: #eaffe3;
}
Loading