Skip to content

Commit

Permalink
demo updates; some StickyHeaders changes
Browse files Browse the repository at this point in the history
  • Loading branch information
gregkorossy committed Aug 6, 2024
1 parent 33c09b8 commit d68aea4
Show file tree
Hide file tree
Showing 23 changed files with 1,355 additions and 718 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@

# Lazy Sticky Headers

Kotlin Multiplatform library for adding sticky items to lazy lists.
Compose Multiplatform library for adding sticky headers to lazy lists.

## Preview

<p align="center">
<img src="asset/preview_contacts.gif" width="270">
<img src="asset/preview_calendar.gif" width="270">
</p>

## Getting started

Expand Down
Binary file added asset/preview_calendar.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added asset/preview_contacts.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions demo/composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ android {
getByName("release") {
isMinifyEnabled = false
}
create("composeRelease") {
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2024 Gergely Kőrössy
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#ffffff"
android:pathData="M480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 112.5,324Q145,251 200.5,197Q256,143 330,111.5Q404,80 488,80Q568,80 639,107.5Q710,135 763.5,183.5Q817,232 848.5,298.5Q880,365 880,442Q880,557 810,618.5Q740,680 640,680L566,680Q557,680 553.5,685Q550,690 550,696Q550,708 565,730.5Q580,753 580,782Q580,832 552.5,856Q525,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM260,520Q286,520 303,503Q320,486 320,460Q320,434 303,417Q286,400 260,400Q234,400 217,417Q200,434 200,460Q200,486 217,503Q234,520 260,520ZM380,360Q406,360 423,343Q440,326 440,300Q440,274 423,257Q406,240 380,240Q354,240 337,257Q320,274 320,300Q320,326 337,343Q354,360 380,360ZM580,360Q606,360 623,343Q640,326 640,300Q640,274 623,257Q606,240 580,240Q554,240 537,257Q520,274 520,300Q520,326 537,343Q554,360 580,360ZM700,520Q726,520 743,503Q760,486 760,460Q760,434 743,417Q726,400 700,400Q674,400 657,417Q640,434 640,460Q640,486 657,503Q674,520 700,520ZM480,800Q489,800 494.5,795Q500,790 500,782Q500,768 485,749Q470,730 470,692Q470,650 499,625Q528,600 570,600L640,600Q706,600 753,561.5Q800,523 800,442Q800,321 707.5,240.5Q615,160 488,160Q352,160 256,253Q160,346 160,480Q160,613 253.5,706.5Q347,800 480,800Z" />
</vector>
186 changes: 33 additions & 153 deletions demo/composeApp/src/commonMain/kotlin/me/gingerninja/lazy/sample/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,24 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import me.gingerninja.lazy.sample.list.FINITE_ITEM_COUNT
import me.gingerninja.lazy.sample.list.calendarList
import me.gingerninja.lazy.sample.list.sampleList
import me.gingerninja.lazy.sample.grid.gridScreens
import me.gingerninja.lazy.sample.home.homeScreen
import me.gingerninja.lazy.sample.list.listScreens
import me.gingerninja.lazy.sample.ui.theme.LazySampleTheme
import org.jetbrains.compose.ui.tooling.preview.Preview

Expand All @@ -76,56 +63,54 @@ fun App(onSettingsUpdate: (DemoSettings) -> Unit = {}) {
Theme.DARK -> true
}

LazySampleTheme(
darkTheme = darkTheme,
) {
if (showSettings) {
SettingsDialog(
settings = settings,
onUpdate = {
settings = it
},
onDismiss = {
showSettings = false
val layoutDirection = settings.layoutDirection ?: LocalLayoutDirection.current

CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
LazySampleTheme(
darkTheme = darkTheme,
) {
if (showSettings) {
SettingsDialog(
settings = settings,
onUpdate = {
settings = it
},
onDismiss = {
showSettings = false
},
)
}

AppContent(
showSettings = {
showSettings = true
},
)
}

AppContent(
settings = settings,
showSettings = {
showSettings = true
},
)
}
}

@Composable
private fun AppContent(settings: DemoSettings, showSettings: () -> Unit) {
private fun AppContent(showSettings: () -> Unit) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
AppNavHost(
modifier = Modifier.fillMaxSize(),
settings = settings,
showSettings = showSettings,
)
}
}

@Composable
private fun AppNavHost(
settings: DemoSettings,
showSettings: () -> Unit,
modifier: Modifier = Modifier,
) {
private fun AppNavHost(showSettings: () -> Unit, modifier: Modifier = Modifier) {
val navController = rememberNavController()

NavHost(
modifier = modifier,
navController = navController,
startDestination = Destination.Home.route,
startDestination = Home.route,
enterTransition = {
fadeIn() + slideInHorizontally { it / 2 }
},
Expand All @@ -145,125 +130,20 @@ private fun AppNavHost(
modifier = Modifier.fillMaxSize(),
)

sampleList(
listScreens(
modifier = Modifier.fillMaxSize(),
onScreenClick = { navController.navigate(it.route) },
onBack = {
navController.popBackStack()
},
)

calendarList(
gridScreens(
modifier = Modifier.fillMaxSize(),
onScreenClick = { navController.navigate(it.route) },
onBack = {
navController.popBackStack()
},
)
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SettingsDialog(
settings: DemoSettings,
onUpdate: (DemoSettings) -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
ModalBottomSheet(
modifier = modifier,
onDismissRequest = onDismiss,
) {
var themeSelectorOpen by remember { mutableStateOf(false) }
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
ListItem(
modifier = Modifier.clickable {
themeSelectorOpen = true
},
headlineContent = {
Text("Theme")
},
supportingContent = {
Box(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.TopStart),
) {
Text(settings.theme.text)

DropdownMenu(
expanded = themeSelectorOpen,
onDismissRequest = {
themeSelectorOpen = false
},
) {
Theme.entries.forEach {
DropdownMenuItem(
text = {
Text(text = it.text)
},
onClick = {
onUpdate(settings.copy(theme = it))
themeSelectorOpen = false
},
)
}
}
}
},
)
ListItem(
modifier = Modifier.clickable {
onUpdate(settings.copy(isVertical = !settings.isVertical))
},
headlineContent = {
Text("Vertical layout")
},
supportingContent = {
val countText = if (settings.isVertical) {
"column"
} else {
"row"
}

Text("Showing items in a $countText")
},
trailingContent = {
Switch(
checked = settings.isVertical,
onCheckedChange = {
onUpdate(settings.copy(isVertical = !settings.isVertical))
},
)
},
)

ListItem(
modifier = Modifier.clickable {
onUpdate(settings.copy(isInfinite = !settings.isInfinite))
},
headlineContent = {
Text("Infinite items")
},
supportingContent = {
val countText = if (settings.isInfinite) {
"infinite"
} else {
FINITE_ITEM_COUNT.toString()
}

Text("Displaying $countText items")
},
trailingContent = {
Switch(
checked = settings.isInfinite,
onCheckedChange = {
onUpdate(settings.copy(isInfinite = !settings.isInfinite))
},
)
},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ package me.gingerninja.lazy.sample
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.ui.unit.LayoutDirection

@Immutable
data class DemoSettings(
val theme: Theme = Theme.AUTO,
val layoutDirection: LayoutDirection? = null,
val isVertical: Boolean = true,
val isInfinite: Boolean = true,
) {
Expand All @@ -31,20 +33,27 @@ data class DemoSettings(
*/
val Saver = run {
val themeKey = "theme"
val layoutDirKey = "layoutDir"
val isVerticalKey = "isVertical"
val isInfiniteKey = "isInfinite"

mapSaver(
save = {
mapOf(
themeKey to it.theme.text,
themeKey to it.theme.ordinal,
layoutDirKey to it.layoutDirection?.ordinal,
isVerticalKey to it.isVertical,
isInfiniteKey to it.isInfinite,
)
},
restore = {
DemoSettings(
theme = it[themeKey] as Theme,
theme = (it[themeKey] as Int).let { ordinal ->
Theme.entries[ordinal]
},
layoutDirection = (it[layoutDirKey] as? Int)?.let { ordinal ->
LayoutDirection.entries[ordinal]
},
isVertical = it[isVerticalKey] as Boolean,
isInfinite = it[isInfiniteKey] as Boolean,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,22 @@
*/
package me.gingerninja.lazy.sample

sealed class Destination(
import me.gingerninja.lazy.sample.grid.GridDestinations
import me.gingerninja.lazy.sample.list.ListDestinations

data class Destination(
val route: String,
val title: String,
val description: String? = null,
) {
data object Home : Destination(
route = "home",
title = "Lazy Sticky Headers",
)

data object ListVertical : Destination(
route = "list/vertical",
title = "LazyColumn",
description = "Items are placed in a LazyColumn",
)

data object ListHorizontal : Destination(
route = "list/horizontal",
title = "LazyRow",
description = "Items are placed in a LazyRow",
)
val enabled: Boolean = true,
)

data object ListCalendar : Destination(
route = "list/calendar",
title = "Calendar",
description = "Sample calendar schedule view",
)
}
val Home = Destination(
route = "home",
title = "Lazy Sticky Headers",
)

val topDestinations = listOf(
Destination.ListVertical,
Destination.ListHorizontal,
Destination.ListCalendar,
ListDestinations.root,
GridDestinations.root,
)
Loading

0 comments on commit d68aea4

Please sign in to comment.