Change the system theme + filter bottomsheet
This commit is contained in:
parent
785e181482
commit
7cc08cf300
19 changed files with 387 additions and 147 deletions
|
|
@ -9,7 +9,6 @@ import androidx.compose.material.Surface
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.pixelized.biblib.ui.composable.SystemThemeColor
|
||||
import com.pixelized.biblib.ui.screen.launch.LauncherViewModel
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -48,12 +47,10 @@ class MainActivity : ComponentActivity() {
|
|||
content: @Composable () -> Unit,
|
||||
) {
|
||||
BibLibTheme {
|
||||
SystemThemeColor {
|
||||
Surface(
|
||||
color = MaterialTheme.colors.surface,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
color = MaterialTheme.colors.surface,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.pixelized.biblib.ui.composable
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
|
||||
@Composable
|
||||
fun Handle(
|
||||
modifier: Modifier = Modifier,
|
||||
width: Dp = MaterialTheme.bibLib.dimen.handle.width,
|
||||
thickness: Dp = MaterialTheme.bibLib.dimen.handle.thickness,
|
||||
color: Color = MaterialTheme.bibLib.colors.handle,
|
||||
shape: Shape = CircleShape
|
||||
) = Box(
|
||||
modifier = modifier
|
||||
.size(width = width, height = thickness)
|
||||
.background(
|
||||
color = color,
|
||||
shape = shape,
|
||||
)
|
||||
)
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package com.pixelized.biblib.ui.composable
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.google.accompanist.systemuicontroller.SystemUiController
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.pixelized.biblib.utils.extention.navigationBarsHeight
|
||||
import com.pixelized.biblib.utils.extention.statusBarsHeight
|
||||
|
||||
val LocalStatusTheme = staticCompositionLocalOf<StatusThemeController> {
|
||||
error("LocalStatusTheme is not ready yet.")
|
||||
}
|
||||
|
||||
class StatusThemeController(
|
||||
private val systemController: SystemUiController,
|
||||
private val statusBackground: MutableState<Brush>,
|
||||
private var statusDarkIcons: Boolean,
|
||||
private val navigationBackground: MutableState<Brush>,
|
||||
private var navigationDarkIcons: Boolean,
|
||||
) {
|
||||
init {
|
||||
systemController.setStatusBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = statusDarkIcons,
|
||||
)
|
||||
systemController.setNavigationBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = navigationDarkIcons,
|
||||
navigationBarContrastEnforced = false,
|
||||
)
|
||||
}
|
||||
|
||||
fun updateStatusTheme(
|
||||
statusBackground: Brush = this.statusBackground.value,
|
||||
statusDarkIcons: Boolean = this.statusDarkIcons,
|
||||
navigationBackground: Brush = this.navigationBackground.value,
|
||||
navigationDarkIcons: Boolean = this.navigationDarkIcons,
|
||||
) {
|
||||
if (this.statusDarkIcons != statusDarkIcons) {
|
||||
this.statusDarkIcons = statusDarkIcons
|
||||
systemController.setStatusBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = statusDarkIcons,
|
||||
)
|
||||
}
|
||||
if (this.statusBackground.value != statusBackground) {
|
||||
this.statusBackground.value = statusBackground
|
||||
}
|
||||
if (navigationDarkIcons != this.navigationDarkIcons) {
|
||||
this.navigationDarkIcons = navigationDarkIcons
|
||||
systemController.setNavigationBarColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = navigationDarkIcons,
|
||||
navigationBarContrastEnforced = false,
|
||||
)
|
||||
}
|
||||
if (this.navigationBackground.value != navigationBackground) {
|
||||
this.statusBackground.value = navigationBackground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SystemTheme(
|
||||
systemController: SystemUiController = rememberSystemUiController(),
|
||||
statusBackground: Color,
|
||||
statusDarkIcons: Boolean,
|
||||
navigationBackground: Color,
|
||||
navigationDarkIcons: Boolean,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val statusBackgroundState = remember {
|
||||
mutableStateOf(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(statusBackground, Color.Transparent)
|
||||
)
|
||||
)
|
||||
}
|
||||
val navigationBackgroundState = remember {
|
||||
mutableStateOf(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(Color.Transparent, navigationBackground)
|
||||
)
|
||||
)
|
||||
}
|
||||
val statusController = remember(systemController) {
|
||||
StatusThemeController(
|
||||
systemController = systemController,
|
||||
statusBackground = statusBackgroundState,
|
||||
statusDarkIcons = statusDarkIcons,
|
||||
navigationBackground = navigationBackgroundState,
|
||||
navigationDarkIcons = navigationDarkIcons,
|
||||
)
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalStatusTheme provides statusController,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
content()
|
||||
|
||||
Overlay(
|
||||
height = statusBarsHeight(),
|
||||
brush = statusBackgroundState,
|
||||
)
|
||||
|
||||
Overlay(
|
||||
modifier = Modifier.align(Alignment.BottomStart),
|
||||
height = navigationBarsHeight(),
|
||||
brush = navigationBackgroundState,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Overlay(
|
||||
modifier: Modifier = Modifier,
|
||||
height: Dp,
|
||||
brush: State<Brush>,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(height = height)
|
||||
.fillMaxWidth()
|
||||
.background(brush = brush.value)
|
||||
)
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.pixelized.biblib.ui.composable
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.google.accompanist.systemuicontroller.SystemUiController
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
||||
|
||||
val LocalSystemUiController = compositionLocalOf<SystemUiController> { error("") }
|
||||
|
||||
@Composable
|
||||
fun SystemThemeColor(
|
||||
systemUiController: SystemUiController = rememberSystemUiController(),
|
||||
color: Color = ShadowPalette.system,
|
||||
statusDarkIcons: Boolean = false,
|
||||
navigationDarkIcons: Boolean = false,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
CompositionLocalProvider(LocalSystemUiController provides systemUiController) {
|
||||
SideEffect {
|
||||
systemUiController.setStatusBarColor(
|
||||
color = color,
|
||||
darkIcons = statusDarkIcons,
|
||||
)
|
||||
systemUiController.setNavigationBarColor(
|
||||
color = color,
|
||||
darkIcons = navigationDarkIcons,
|
||||
navigationBarContrastEnforced = false,
|
||||
)
|
||||
}
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.pixelized.biblib.ui.composable
|
||||
|
||||
import androidx.compose.material.LocalAbsoluteElevation
|
||||
import androidx.compose.material.LocalElevationOverlay
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
@Composable
|
||||
fun colorElevation(
|
||||
base: Color = MaterialTheme.colors.surface,
|
||||
elevation: Dp,
|
||||
): Color {
|
||||
val localElevation = LocalElevationOverlay.current
|
||||
val absoluteElevation = LocalAbsoluteElevation.current
|
||||
return localElevation?.apply(color = base, elevation = absoluteElevation + elevation) ?: base
|
||||
}
|
||||
|
|
@ -2,18 +2,18 @@ package com.pixelized.biblib.ui.composable.scaffold
|
|||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.ModalBottomSheetValue.Hidden
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -26,6 +26,8 @@ import com.pixelized.biblib.ui.screen.home.filter.viewModel.AuthorFilterViewMode
|
|||
import com.pixelized.biblib.ui.screen.home.filter.viewModel.GenreFilterViewModel
|
||||
import com.pixelized.biblib.ui.screen.home.filter.viewModel.LanguageFilterViewModel
|
||||
import com.pixelized.biblib.ui.screen.home.filter.viewModel.SeriesFilterViewModel
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.statusBarsHeight
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
|
@ -102,6 +104,7 @@ fun FilterBottomSheet(
|
|||
skipHalfExpanded = true,
|
||||
),
|
||||
sheetState: FilterBottomSheetState = rememberFilterBottomSheetState(),
|
||||
sheetShape: Shape = MaterialTheme.bibLib.shapes.filterSheet,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
|
|
@ -134,47 +137,53 @@ fun FilterBottomSheet(
|
|||
onBack = { currentBottomSheetData?.dismiss() },
|
||||
)
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
sheetState = bottomSheetState,
|
||||
content = content,
|
||||
scrimColor = Color.Transparent,
|
||||
sheetContent = {
|
||||
val viewModel: IFilterViewModel? = when (sheetState.type) {
|
||||
FilterType.Author -> hiltViewModel<AuthorFilterViewModel>()
|
||||
FilterType.Series -> hiltViewModel<SeriesFilterViewModel>()
|
||||
FilterType.Genre -> hiltViewModel<GenreFilterViewModel>()
|
||||
FilterType.Language -> hiltViewModel<LanguageFilterViewModel>()
|
||||
else -> null
|
||||
}
|
||||
BoxWithConstraints {
|
||||
val statusBarHeight = statusBarsHeight()
|
||||
val bottomSheetHeight = remember(statusBarHeight) {
|
||||
maxHeight - statusBarHeight - 16.dp
|
||||
}
|
||||
|
||||
if (viewModel != null) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
FilterPage(
|
||||
viewModel = viewModel,
|
||||
focusRequester = focusRequester,
|
||||
onFilter = {
|
||||
currentBottomSheetData?.performAction(filter = it)
|
||||
},
|
||||
onClose = {
|
||||
currentBottomSheetData?.dismiss()
|
||||
},
|
||||
onIMEDone = {
|
||||
currentBottomSheetData?.dismiss()
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(key1 = "FilterPageFocusRequest-${sheetState.type}") {
|
||||
if (currentBottomSheetData != null) {
|
||||
focusRequester.requestFocus()
|
||||
keyboard?.show()
|
||||
}
|
||||
ModalBottomSheetLayout(
|
||||
sheetState = bottomSheetState,
|
||||
sheetShape = sheetShape,
|
||||
content = content,
|
||||
scrimColor = Color.Transparent,
|
||||
sheetContent = {
|
||||
val viewModel: IFilterViewModel? = when (sheetState.type) {
|
||||
FilterType.Author -> hiltViewModel<AuthorFilterViewModel>()
|
||||
FilterType.Series -> hiltViewModel<SeriesFilterViewModel>()
|
||||
FilterType.Genre -> hiltViewModel<GenreFilterViewModel>()
|
||||
FilterType.Language -> hiltViewModel<LanguageFilterViewModel>()
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
Box(modifier = Modifier.height(1.dp))
|
||||
}
|
||||
},
|
||||
)
|
||||
if (viewModel != null) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
FilterPage(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(bottomSheetHeight),
|
||||
viewModel = viewModel,
|
||||
focusRequester = focusRequester,
|
||||
onFilter = {
|
||||
currentBottomSheetData?.performAction(filter = it)
|
||||
},
|
||||
onIMEDone = {
|
||||
currentBottomSheetData?.dismiss()
|
||||
}
|
||||
)
|
||||
LaunchedEffect(key1 = "FilterPageFocusRequest-${sheetState.type}") {
|
||||
if (currentBottomSheetData != null) {
|
||||
focusRequester.requestFocus()
|
||||
keyboard?.show()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Box(modifier = Modifier.height(1.dp))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import com.pixelized.biblib.model.search.FilterType
|
|||
import com.pixelized.biblib.model.search.SortType
|
||||
import com.pixelized.biblib.model.search.SortValue
|
||||
import com.pixelized.biblib.ui.LocalSnackHostState
|
||||
import com.pixelized.biblib.ui.composable.colorElevation
|
||||
import com.pixelized.biblib.ui.composable.scaffold.*
|
||||
import com.pixelized.biblib.ui.navigation.LocalScreenNavHostController
|
||||
import com.pixelized.biblib.ui.navigation.navigateToProfile
|
||||
|
|
@ -444,14 +445,10 @@ private fun HomeToolbar(
|
|||
onAvatarTap: () -> Unit,
|
||||
) {
|
||||
val direction = LocalLayoutDirection.current
|
||||
val elevation = LocalElevationOverlay.current
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
shape = CircleShape,
|
||||
color = elevation?.apply(
|
||||
MaterialTheme.colors.surface,
|
||||
LocalAbsoluteElevation.current + 1.dp
|
||||
) ?: MaterialTheme.colors.surface,
|
||||
color = colorElevation(elevation = 1.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import com.bumptech.glide.load.engine.GlideException
|
|||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.composable.Handle
|
||||
import com.pixelized.biblib.ui.composable.SpannedText
|
||||
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
|
||||
import com.pixelized.biblib.ui.composable.animation.AnimatedOffset
|
||||
|
|
@ -117,6 +118,7 @@ fun DetailScreenContent(
|
|||
.verticalScroll(rememberScrollState())
|
||||
.padding(all = 16.dp)
|
||||
.systemBarsPadding(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -148,7 +150,6 @@ fun DetailScreenContent(
|
|||
|
||||
AnimatedOffset {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -161,7 +162,7 @@ fun DetailScreenContent(
|
|||
Text(
|
||||
modifier = Modifier.clickable(onClick = { onSeries(series) }),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
color = MaterialTheme.bibLib.colors.typography.emphasis,
|
||||
textAlign = TextAlign.Center,
|
||||
text = series.label,
|
||||
)
|
||||
|
|
@ -171,15 +172,14 @@ fun DetailScreenContent(
|
|||
|
||||
AnimatedOffset {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
book.authors.forEach { author ->
|
||||
Text(
|
||||
modifier = Modifier.clickable(onClick = { onAuthor(author) }),
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontWeight = FontWeight.Normal,
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
color = MaterialTheme.bibLib.colors.typography.emphasis,
|
||||
textAlign = TextAlign.Center,
|
||||
text = author.name,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
package com.pixelized.biblib.ui.screen.home.filter
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -8,24 +13,27 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.max
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.items
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.model.search.FilterType
|
||||
import com.pixelized.biblib.ui.composable.Handle
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.imeHeight
|
||||
import com.pixelized.biblib.utils.extention.navigationBarsHeight
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
||||
@Stable
|
||||
@Immutable
|
||||
|
|
@ -37,26 +45,23 @@ data class FilterItemUio(
|
|||
|
||||
@Composable
|
||||
fun FilterPage(
|
||||
modifier: Modifier,
|
||||
viewModel: IFilterViewModel,
|
||||
focusRequester: FocusRequester,
|
||||
onFilter: (FilterItemUio) -> Unit,
|
||||
onClose: () -> Unit,
|
||||
onIMEDone: KeyboardActionScope.() -> Unit,
|
||||
) {
|
||||
val filters = viewModel.paging.collectAsLazyPagingItems()
|
||||
|
||||
FilterPageContent(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding()
|
||||
.systemBarsPadding(),
|
||||
modifier = modifier,
|
||||
focusRequester = focusRequester,
|
||||
title = viewModel.title,
|
||||
search = viewModel.search,
|
||||
onSearchUpdate = { viewModel.updateSearch(it) },
|
||||
onClear = { viewModel.updateSearch("") },
|
||||
filters = filters,
|
||||
onFilter = onFilter,
|
||||
onClose = onClose,
|
||||
onIMEDone = onIMEDone,
|
||||
)
|
||||
}
|
||||
|
|
@ -68,56 +73,64 @@ private fun FilterPageContent(
|
|||
title: String,
|
||||
search: State<String>,
|
||||
onSearchUpdate: (String) -> Unit,
|
||||
onClear: () -> Unit,
|
||||
filters: LazyPagingItems<FilterItemUio>,
|
||||
onFilter: (FilterItemUio) -> Unit,
|
||||
onClose: () -> Unit,
|
||||
onIMEDone: KeyboardActionScope.() -> Unit,
|
||||
) {
|
||||
val navigationBarsHeight = navigationBarsHeight()
|
||||
val imeHeight = imeHeight()
|
||||
val paddingValues = remember(navigationBarsHeight, imeHeight) {
|
||||
PaddingValues(
|
||||
bottom = max(navigationBarsHeight, imeHeight)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
TopAppBar(
|
||||
backgroundColor = MaterialTheme.colors.surface,
|
||||
elevation = 0.dp,
|
||||
title = {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = title,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onClose) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Handle(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(vertical = 16.dp),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
style = MaterialTheme.bibLib.typography.base.caption,
|
||||
color = MaterialTheme.bibLib.colors.typography.light,
|
||||
text = title,
|
||||
)
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester = focusRequester)
|
||||
.fillMaxWidth(),
|
||||
label = {
|
||||
Text(
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = stringResource(id = R.string.search_filter_title)
|
||||
)
|
||||
trailingIcon = {
|
||||
AnimatedVisibility(
|
||||
visible = search.value.isNotBlank(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClear,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
tint = MaterialTheme.bibLib.colors.typography.emphasis,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
value = search.value,
|
||||
singleLine = true,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
),
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(),
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = onIMEDone),
|
||||
onValueChange = onSearchUpdate
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = paddingValues,
|
||||
) {
|
||||
items(items = filters, key = { it.filterId }) {
|
||||
Text(
|
||||
|
|
@ -132,4 +145,22 @@ private fun FilterPageContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun FilterPageContentPreview() {
|
||||
BibLibTheme {
|
||||
FilterPageContent(
|
||||
focusRequester = remember { FocusRequester() },
|
||||
title = "Search by Author",
|
||||
search = remember { mutableStateOf("") },
|
||||
onSearchUpdate = { },
|
||||
onClear = { },
|
||||
filters = remember { emptyFlow<PagingData<FilterItemUio>>() }.collectAsLazyPagingItems(),
|
||||
onFilter = { },
|
||||
onIMEDone = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,10 @@ class AuthorFilterViewModel @Inject constructor(
|
|||
private val searchRepository: ISearchRepository,
|
||||
) : ViewModel(), IFilterViewModel {
|
||||
|
||||
override val title: String = application.getString(R.string.search_filter_author)
|
||||
override val title: String = application.getString(
|
||||
R.string.search_filter_title,
|
||||
application.getString(R.string.search_filter_author),
|
||||
)
|
||||
|
||||
private var source: AuthorSearchSource? = null
|
||||
override val paging: Flow<PagingData<FilterItemUio>>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ class GenreFilterViewModel @Inject constructor(
|
|||
private val searchRepository: ISearchRepository,
|
||||
) : ViewModel(), IFilterViewModel {
|
||||
|
||||
override val title: String = application.getString(R.string.search_filter_genre)
|
||||
override val title: String = application.getString(
|
||||
R.string.search_filter_title,
|
||||
application.getString(R.string.search_filter_genre),
|
||||
)
|
||||
|
||||
private var source: GenreSearchSource? = null
|
||||
override val paging: Flow<PagingData<FilterItemUio>>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ class LanguageFilterViewModel @Inject constructor(
|
|||
private val searchRepository: ISearchRepository,
|
||||
) : ViewModel(), IFilterViewModel {
|
||||
|
||||
override val title: String = application.getString(R.string.search_filter_language)
|
||||
override val title: String = application.getString(
|
||||
R.string.search_filter_title,
|
||||
application.getString(R.string.search_filter_language),
|
||||
)
|
||||
|
||||
private var source: LanguageSearchSource? = null
|
||||
override val paging: Flow<PagingData<FilterItemUio>>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,10 @@ class SeriesFilterViewModel @Inject constructor(
|
|||
private val searchRepository: ISearchRepository,
|
||||
) : ViewModel(), IFilterViewModel {
|
||||
|
||||
override val title: String = application.getString(R.string.search_filter_series)
|
||||
override val title: String = application.getString(
|
||||
R.string.search_filter_title,
|
||||
application.getString(R.string.search_filter_series),
|
||||
)
|
||||
|
||||
private var source: SeriesSearchSource? = null
|
||||
override val paging: Flow<PagingData<FilterItemUio>>
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
|||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import com.pixelized.biblib.ui.composable.SystemTheme
|
||||
import com.pixelized.biblib.ui.theme.color.bibLibDarkColors
|
||||
import com.pixelized.biblib.ui.theme.color.bibLibLightColors
|
||||
import com.pixelized.biblib.ui.theme.dimen.BibLibDimen
|
||||
import com.pixelized.biblib.ui.theme.shape.BibLibShape
|
||||
import com.pixelized.biblib.ui.theme.typography.BibLibTypography
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
|
||||
@Composable
|
||||
fun BibLibTheme(
|
||||
|
|
@ -26,7 +28,15 @@ fun BibLibTheme(
|
|||
colors = theme.colors.base,
|
||||
typography = theme.typography.base,
|
||||
shapes = theme.shapes.base,
|
||||
content = content
|
||||
content = {
|
||||
SystemTheme(
|
||||
statusBackground = MaterialTheme.bibLib.colors.system.status,
|
||||
statusDarkIcons = darkTheme.not(),
|
||||
navigationBackground = MaterialTheme.bibLib.colors.system.navigation,
|
||||
navigationDarkIcons = darkTheme.not(),
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package com.pixelized.biblib.ui.theme.color
|
||||
|
||||
import androidx.compose.material.Colors
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import javax.annotation.concurrent.Immutable
|
||||
|
||||
|
||||
|
|
@ -12,9 +14,18 @@ import javax.annotation.concurrent.Immutable
|
|||
@Immutable
|
||||
data class BibLibColor(
|
||||
val base: Colors,
|
||||
val system: System,
|
||||
val handle: Color,
|
||||
val typography: Typography,
|
||||
val placeHolder: PlaceHolder,
|
||||
) {
|
||||
@Stable
|
||||
@Immutable
|
||||
data class System(
|
||||
val status: Color,
|
||||
val navigation: Color,
|
||||
)
|
||||
|
||||
@Stable
|
||||
@Immutable
|
||||
data class Typography(
|
||||
|
|
@ -40,6 +51,11 @@ fun bibLibDarkColors(
|
|||
error = BibLibColorPalette.Red,
|
||||
onError = Color.White,
|
||||
),
|
||||
system: BibLibColor.System = BibLibColor.System(
|
||||
status = base.surface.copy(alpha = 0.5f),
|
||||
navigation = base.surface.copy(alpha = 0.5f),
|
||||
),
|
||||
handle: Color = base.onSurface.copy(alpha = 0.2f),
|
||||
typography: BibLibColor.Typography = BibLibColor.Typography(
|
||||
light = base.onSurface.copy(alpha = 0.67f),
|
||||
medium = base.onSurface,
|
||||
|
|
@ -51,6 +67,8 @@ fun bibLibDarkColors(
|
|||
),
|
||||
) = BibLibColor(
|
||||
base = base,
|
||||
system = system,
|
||||
handle = handle,
|
||||
typography = typography,
|
||||
placeHolder = placeHolder,
|
||||
)
|
||||
|
|
@ -68,6 +86,11 @@ fun bibLibLightColors(
|
|||
surface = Color.White,
|
||||
onSurface = BibLibColorPalette.VeryDarkGrey,
|
||||
),
|
||||
system: BibLibColor.System = BibLibColor.System(
|
||||
status = base.surface.copy(alpha = 0.5f),
|
||||
navigation = base.surface.copy(alpha = 0.5f),
|
||||
),
|
||||
handle: Color = base.onSurface.copy(alpha = 0.1f),
|
||||
typography: BibLibColor.Typography = BibLibColor.Typography(
|
||||
light = base.onSurface.copy(alpha = 0.67f),
|
||||
medium = base.onSurface,
|
||||
|
|
@ -79,6 +102,8 @@ fun bibLibLightColors(
|
|||
),
|
||||
) = BibLibColor(
|
||||
base = base,
|
||||
system = system,
|
||||
handle = handle,
|
||||
typography = typography,
|
||||
placeHolder = placeHolder,
|
||||
)
|
||||
|
|
@ -11,6 +11,7 @@ import androidx.compose.ui.unit.dp
|
|||
data class BibLibDimen(
|
||||
val avatar: Avatar = Avatar(),
|
||||
val progress: Progress = Progress(),
|
||||
val handle: Handle = Handle(),
|
||||
val detail: BookDetail = BookDetail(),
|
||||
val thumbnail: BookThumbnail = BookThumbnail(),
|
||||
) {
|
||||
|
|
@ -26,6 +27,13 @@ data class BibLibDimen(
|
|||
val iconSize: Dp = 52.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
@Immutable
|
||||
data class Handle(
|
||||
val width: Dp = 32.dp,
|
||||
val thickness: Dp = 4.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
@Immutable
|
||||
data class BookDetail(
|
||||
|
|
|
|||
|
|
@ -17,4 +17,5 @@ data class BibLibShape(
|
|||
),
|
||||
val bookThumbnailCoverSmall: Shape = RoundedCornerShape(4.dp),
|
||||
val bookThumbnailCoverLarge: Shape = RoundedCornerShape(4.dp),
|
||||
val filterSheet: Shape = RoundedCornerShape(topEnd = 16.dp, topStart = 16.dp),
|
||||
)
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
<string name="detail_send_success">eBook envoyé avec succès</string>
|
||||
|
||||
<string name="search_title">Rechercher sur Biblib</string>
|
||||
<string name="search_filter_title">Rechercher</string>
|
||||
<string name="search_filter_title">Rechercher par %1$s</string>
|
||||
<string name="search_filter_param">%1$s : %2$s</string>
|
||||
<string name="search_filter_new">Nouveauté</string>
|
||||
<string name="search_filter_author">Auteur</string>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
<string name="detail_send_success">eBook sent with success</string>
|
||||
|
||||
<string name="search_title">Search on Biblib</string>
|
||||
<string name="search_filter_title">Search</string>
|
||||
<string name="search_filter_title">Search by %1$s</string>
|
||||
<string name="search_filter_param">%1$s: %2$s</string>
|
||||
<string name="search_filter_new">New</string>
|
||||
<string name="search_filter_author">Authors</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue