Skip to content

fix: param settings FAB obstructs StoryContent #68

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

Merged
merged 4 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
agp = "8.7.1"
agp = "8.9.2"
android-compileSdk = "35"
android-minSdk = "26"
android-targetSdk = "35"
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#Tue Oct 29 23:06:22 CST 2024
#Fri May 02 19:34:04 ICT 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.jetbrains.compose.storytale.gallery.compose

import androidx.compose.runtime.staticCompositionLocalOf

inline fun <reified T> noCompositionLocalProvided(): T {
error("CompositionLocal ${T::class.simpleName} not present")
}

val LocalIsEmbeddedView = staticCompositionLocalOf { false }
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
Expand All @@ -32,9 +33,11 @@ import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
Expand All @@ -46,6 +49,7 @@ import androidx.navigation.toRoute
import androidx.window.core.layout.WindowWidthSizeClass
import kotlinx.coroutines.launch
import org.jetbrains.compose.storytale.Story
import org.jetbrains.compose.storytale.gallery.compose.LocalIsEmbeddedView
import org.jetbrains.compose.storytale.gallery.ui.theme.UseCustomDensity
import org.jetbrains.compose.storytale.storiesStorage

Expand Down Expand Up @@ -91,14 +95,20 @@ fun FullStorytaleGallery(
popExitTransition = { ExitTransition.None },
) {
composable<StoryScreen> {
val showOverlayParameters = rememberSaveable { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxSize()) {
GalleryTopAppBar(
appState = appState,
drawerState = drawerState,
activeStoryName = activeStoryItem.value?.story?.name,
activeStory = activeStoryItem.value?.story,
showOverlayParameters = showOverlayParameters,
)
HorizontalDivider()
StoryContent(activeStoryItem.value?.story, modifier = Modifier.fillMaxSize())
StoryContent(
activeStory = activeStoryItem.value?.story,
showOverlayParameters = showOverlayParameters,
modifier = Modifier.fillMaxSize(),
)
}
}
}
Expand Down Expand Up @@ -177,20 +187,23 @@ private fun DrawerContent(
)
}

@Suppress("ktlint:compose:mutable-state-param-check")
@Composable
private fun GalleryTopAppBar(
drawerState: DrawerState,
appState: StorytaleGalleryAppState,
activeStoryName: String?,
activeStory: Story?,
showOverlayParameters: MutableState<Boolean>,
) {
val activeStoryName = activeStory?.name.orEmpty()
val coroutineScope = rememberCoroutineScope()
val currentWindowWidthClass = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass
val isExpanded = currentWindowWidthClass == WindowWidthSizeClass.EXPANDED

CenterAlignedTopAppBar(
title = {
AnimatedContent(activeStoryName) { title ->
Text(title ?: "")
Text(title)
}
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest),
Expand Down Expand Up @@ -226,16 +239,36 @@ private fun GalleryTopAppBar(
}
},
actions = {
val widthClass = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass

val isSmallWidth = widthClass == WindowWidthSizeClass.COMPACT
val useEmbeddedView = LocalIsEmbeddedView.current

ThemeSwitcherIconButton(appState)
AnimatedVisibility(
(isSmallWidth || useEmbeddedView) && activeStory?.parameters?.isNotEmpty() == true,
enter = fadeIn(),
exit = fadeOut(),
) {
IconButton(
onClick = {
showOverlayParameters.value = true
},
) {
Icon(imageVector = Icons.Default.Settings, null)
}
}
},
)
}

@Composable
internal fun ThemeSwitcherIconButton(appState: StorytaleGalleryAppState) {
IconButton(onClick = {
appState.switchTheme(!appState.isDarkTheme())
}) {
IconButton(
onClick = {
appState.switchTheme(!appState.isDarkTheme())
},
) {
AnimatedContent(targetState = appState.isDarkTheme()) { isDarkTheme ->
if (isDarkTheme) {
Icon(imageVector = Light_mode, contentDescription = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
Expand All @@ -46,6 +45,7 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
Expand All @@ -58,27 +58,26 @@ import dev.snipme.highlights.model.SyntaxThemes
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.jetbrains.compose.storytale.Story
import org.jetbrains.compose.storytale.gallery.compose.LocalIsEmbeddedView
import org.jetbrains.compose.storytale.gallery.story.code.CodeBlock

@Suppress("ktlint:compose:mutable-state-param-check")
@Composable
internal fun StoryContent(
activeStory: Story?,
modifier: Modifier = Modifier,
useEmbeddedView: Boolean = false,
useEmbeddedView: Boolean = LocalIsEmbeddedView.current,
showOverlayParameters: MutableState<Boolean> = rememberSaveable { mutableStateOf(false) },
) {
val widthClass = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass
val heightClass = currentWindowAdaptiveInfo().windowSizeClass.windowHeightSizeClass

val isSmallHeight = heightClass == WindowHeightSizeClass.COMPACT
val isSmallWidth = widthClass == WindowWidthSizeClass.COMPACT

val useTabs = isSmallHeight || useEmbeddedView

Box(modifier = modifier) {
val showOverlayParameters = remember { mutableStateOf(false) }

val previewContent = @Composable {
StoryPreview(showOverlayParameters, activeStory, useEmbeddedView, isSmallWidth)
StoryPreview(activeStory)
}

val codeContent: @Composable (BoxScope) -> Unit = { boxScope ->
Expand Down Expand Up @@ -109,13 +108,9 @@ internal fun StoryContent(
}
}

@Suppress("ktlint:compose:mutable-state-param-check")
@Composable
private fun StoryPreview(
showOverlayParameters: MutableState<Boolean>,
activeStory: Story? = null,
useEmbeddedView: Boolean = false,
isSmallWidth: Boolean = false,
) {
Box(modifier = Modifier.fillMaxSize()) {
Box(
Expand All @@ -124,22 +119,6 @@ private fun StoryPreview(
) {
activeStory?.content?.invoke(activeStory)
}

AnimatedVisibility(
(isSmallWidth || useEmbeddedView) && activeStory?.parameters?.isNotEmpty() == true,
enter = fadeIn(),
exit = fadeOut(),
modifier = Modifier.align(Alignment.BottomEnd),
) {
SmallFloatingActionButton(
Copy link
Collaborator

@eymar eymar May 14, 2025

Choose a reason for hiding this comment

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

This Floating Action Button was added here according to the re-design by our team's designer. It should've not been removed from here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry to hear that. I'll make a revert PR later

Copy link
Collaborator

@eymar eymar May 14, 2025

Choose a reason for hiding this comment

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

I understand that you didn't know about it.
I'd be glad to share the design files, but unfortunately they are in private Figma, and we can't share it.

I appreciate your contributions and improvements in the project 🙂


I'm currently working on a branch which I'm going to push soon (today). I already added a revert of this PR there. So you don't need to revert yourself.


In the future please consider:

  • updating the versions of the dependencies in seprate PRs (gradle, agp, etc), unless they are absolutely related to the feature/fix you implement.

In this case the new gradle version affect the Compose project where we use Storytale. We can't update the gradle there yet. So for now we'll need to stick to an older gradle version here.

onClick = {
showOverlayParameters.value = true
},
modifier = Modifier.padding(16.dp),
) {
Icon(imageVector = Icons.Default.Settings, null)
}
}
}
}

Expand Down Expand Up @@ -210,7 +189,10 @@ private fun StoryPreviewAndCodeStacked(
.background(MaterialTheme.colorScheme.surfaceContainerLowest),
) {
Column(modifier = Modifier.fillMaxHeight().weight(0.75f)) {
Box(modifier = Modifier.fillMaxSize().weight(0.5f), contentAlignment = Alignment.Center) {
Box(
modifier = Modifier.fillMaxSize().weight(0.5f),
contentAlignment = Alignment.Center,
) {
previewContent()
}
HorizontalDivider()
Expand All @@ -227,7 +209,8 @@ private fun StoryPreviewAndCodeStacked(
Column(modifier = Modifier.fillMaxHeight().widthIn(max = 250.dp)) {
StoryParametersList(
storyParameters!!,
modifier = Modifier.fillMaxSize().padding(8.dp).verticalScroll(rememberScrollState(0)),
modifier = Modifier.fillMaxSize().padding(8.dp)
.verticalScroll(rememberScrollState(0)),
)
}
}
Expand Down Expand Up @@ -276,7 +259,8 @@ private fun BoxScope.OverlayParametersList(
}
StoryParametersList(
activeStory?.parameters ?: emptyList(),
modifier = Modifier.fillMaxSize().padding(8.dp).verticalScroll(rememberScrollState(0)),
modifier = Modifier.fillMaxSize().padding(8.dp)
.verticalScroll(rememberScrollState(0)),
)
}
}
Expand All @@ -302,7 +286,8 @@ private fun StoryTabs(
},
modifier = Modifier.height(48.dp),
) {
val textColor = if (selectedTabIndex.value == 0) selectedTextColor else unselectedTextColor
val textColor =
if (selectedTabIndex.value == 0) selectedTextColor else unselectedTextColor
Text(text = "Preview", style = MaterialTheme.typography.titleSmall, color = textColor)
}
Tab(
Expand All @@ -312,7 +297,8 @@ private fun StoryTabs(
onCodeTabClick()
},
) {
val textColor = if (selectedTabIndex.value == 1) selectedTextColor else unselectedTextColor
val textColor =
if (selectedTabIndex.value == 1) selectedTextColor else unselectedTextColor
Text(text = "Code", style = MaterialTheme.typography.titleSmall, color = textColor)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import org.jetbrains.compose.storytale.gallery.compose.LocalIsEmbeddedView
import org.jetbrains.compose.storytale.gallery.ui.theme.LocalCustomDensity

@Composable
Expand All @@ -29,6 +30,7 @@ fun StorytaleGalleryApp(

CompositionLocalProvider(
LocalCustomDensity provides Density(LocalDensity.current.density * 0.8f),
LocalIsEmbeddedView provides isEmbedded,
) {
MaterialTheme(colorScheme = if (appState.isDarkTheme()) darkThemeColors else lightThemeColors) {
if (isEmbedded) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetbrains.compose.storytale

import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateMapOf
import kotlin.enums.enumEntries

data class Story(
Expand All @@ -11,7 +12,7 @@ data class Story(
val content: @Composable Story.() -> Unit,
) {
@PublishedApi
internal val nameToParameterMapping = linkedMapOf<String, StoryParameter<*>>() // using linkedMap to keep the order
internal val nameToParameterMapping = mutableStateMapOf<String, StoryParameter<*>>()
val parameters inline get() = nameToParameterMapping.values.toList()

inline fun <reified T> parameter(defaultValue: T) = StoryParameterDelegate(this, T::class, defaultValue)
Expand Down