Add SubCategory Search screens.
This commit is contained in:
parent
3b5f1dae97
commit
42b4e414a0
11 changed files with 428 additions and 106 deletions
|
|
@ -20,7 +20,6 @@ import androidx.compose.ui.focus.FocusRequester
|
|||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pixelized.biblib.R
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ package com.pixelized.biblib.ui.scaffold
|
|||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ModalBottomSheetValue.Hidden
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
|
@ -37,8 +40,7 @@ fun BottomDetailScaffold(
|
|||
scrimColor = Color.Black.copy(alpha = 0.37f),
|
||||
sheetState = bottomDetailState.bottomSheetState,
|
||||
sheetContent = {
|
||||
val detail by remember { bottomDetailState.bookDetail }
|
||||
DetailScreen(detail = detail)
|
||||
DetailScreen(detail = bottomDetailState.bookDetail)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
|
|
@ -60,11 +62,15 @@ fun rememberBottomDetailState(
|
|||
),
|
||||
): BottomDetailState {
|
||||
val context: Context = LocalContext.current
|
||||
val detail = rememberSaveable(scope, viewModel, bottomSheetState) {
|
||||
mutableStateOf<BookUio?>(null)
|
||||
}
|
||||
val controller = BottomDetailState(
|
||||
context = context,
|
||||
viewModel = viewModel,
|
||||
scope = scope,
|
||||
bottomSheetState = bottomSheetState
|
||||
bottomSheetState = bottomSheetState,
|
||||
bookDetail = detail,
|
||||
)
|
||||
return remember(scope, viewModel, bottomSheetState) { controller }
|
||||
}
|
||||
|
|
@ -76,8 +82,9 @@ class BottomDetailState constructor(
|
|||
private val viewModel: BookDetailViewModel,
|
||||
private val scope: CoroutineScope,
|
||||
val bottomSheetState: ModalBottomSheetState,
|
||||
bookDetail: MutableState<BookUio?>,
|
||||
) {
|
||||
var bookDetail = mutableStateOf<BookUio?>(null)
|
||||
var bookDetail: BookUio? by bookDetail
|
||||
private set
|
||||
|
||||
fun expandBookDetail(id: Int) {
|
||||
|
|
@ -88,7 +95,7 @@ class BottomDetailState constructor(
|
|||
context.showToast(message = mes)
|
||||
}
|
||||
is StateUio.Success -> {
|
||||
bookDetail.value = book.value
|
||||
bookDetail = book.value
|
||||
bottomSheetState.show()
|
||||
}
|
||||
else -> Unit
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
package com.pixelized.biblib.ui.scaffold
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ModalBottomSheetValue.Hidden
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.screen.home.page.search.CategorySearchPage
|
||||
import com.pixelized.biblib.ui.screen.home.page.search.SearchViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.Serializable
|
||||
|
||||
val LocalSearchViewModel = staticCompositionLocalOf<SearchViewModel> {
|
||||
error("SearchViewModel is not ready yet")
|
||||
}
|
||||
val LocalBottomSearchState = staticCompositionLocalOf<BottomSearchState> {
|
||||
error("BottomSearchState is not ready yet")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun BottomSearchScaffold(
|
||||
state: BottomSearchState = rememberBottomSearchState(),
|
||||
searchViewModel: SearchViewModel = hiltViewModel(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalSearchViewModel provides searchViewModel,
|
||||
LocalBottomSearchState provides state,
|
||||
) {
|
||||
ModalBottomSheetLayout(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
scrimColor = Color.Black.copy(alpha = 0.37f),
|
||||
sheetState = state.bottomSheetState,
|
||||
sheetContent = {
|
||||
CategorySearchPage(
|
||||
searchViewModel = searchViewModel,
|
||||
focusRequester = state.focusRequester,
|
||||
filter = state.filter,
|
||||
onClose = {
|
||||
state.collapse()
|
||||
}
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
|
||||
BackHandler(state.bottomSheetState.isVisible) {
|
||||
state.collapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun rememberBottomSearchState(
|
||||
scope: CoroutineScope = rememberCoroutineScope(),
|
||||
bottomSheetState: ModalBottomSheetState = rememberModalBottomSheetState(
|
||||
initialValue = Hidden,
|
||||
skipHalfExpanded = true,
|
||||
),
|
||||
): BottomSearchState {
|
||||
val filter = rememberSaveable(scope, bottomSheetState) {
|
||||
mutableStateOf<SearchFilter?>(null)
|
||||
}
|
||||
val focusRequester = remember {
|
||||
FocusRequester()
|
||||
}
|
||||
val controller = BottomSearchState(
|
||||
scope = scope,
|
||||
bottomSheetState = bottomSheetState,
|
||||
focusRequester = focusRequester,
|
||||
filter = filter,
|
||||
)
|
||||
return remember(scope, bottomSheetState) { controller }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Stable
|
||||
class BottomSearchState constructor(
|
||||
private val scope: CoroutineScope,
|
||||
val bottomSheetState: ModalBottomSheetState,
|
||||
val focusRequester: FocusRequester,
|
||||
filter: MutableState<SearchFilter?>,
|
||||
) {
|
||||
var filter: SearchFilter? by filter
|
||||
private set
|
||||
|
||||
fun expandSearch(filter: SearchFilter?) {
|
||||
this.filter = filter
|
||||
scope.launch {
|
||||
bottomSheetState.show()
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
scope.launch {
|
||||
bottomSheetState.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SearchFilter(
|
||||
@StringRes val label: Int,
|
||||
val value: String?,
|
||||
) : Serializable {
|
||||
val isSelected: Boolean get() = value != null
|
||||
|
||||
class Author(
|
||||
value: String? = null,
|
||||
) : SearchFilter(
|
||||
label = R.string.search_filter_author,
|
||||
value = value,
|
||||
)
|
||||
|
||||
class Genre(
|
||||
value: String? = null,
|
||||
) : SearchFilter(
|
||||
label = R.string.search_filter_genre,
|
||||
value = value,
|
||||
)
|
||||
|
||||
class Language(
|
||||
value: String? = null,
|
||||
) : SearchFilter(
|
||||
label = R.string.search_filter_language,
|
||||
value = value,
|
||||
)
|
||||
|
||||
companion object {
|
||||
val all = listOf(Author(), Genre(), Language())
|
||||
}
|
||||
}
|
||||
|
|
@ -98,26 +98,26 @@ class SearchScaffoldState(
|
|||
expended: Boolean,
|
||||
state: ContentState = ContentState.INITIAL,
|
||||
) {
|
||||
private var expended: Boolean by mutableStateOf(expended)
|
||||
private var isExpended: Boolean by mutableStateOf(expended)
|
||||
|
||||
var content: ContentState by mutableStateOf(state)
|
||||
private set
|
||||
|
||||
fun isCollapsed(): Boolean = expended.not()
|
||||
fun isCollapsed(): Boolean = isExpended.not()
|
||||
|
||||
fun expand(state: ContentState) {
|
||||
content = state
|
||||
expended = true
|
||||
isExpended = true
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
expended = false
|
||||
isExpended = false
|
||||
content = ContentState.INITIAL
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Saver: Saver<SearchScaffoldState, Pair<Boolean, Int>> = Saver(
|
||||
save = { it.expended to it.content.ordinal },
|
||||
save = { it.isExpended to it.content.ordinal },
|
||||
restore = { SearchScaffoldState(it.first, ContentState.values()[it.second]) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ScrollableTabRow
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -26,11 +23,8 @@ import com.google.accompanist.pager.HorizontalPager
|
|||
import com.google.accompanist.pager.PagerState
|
||||
import com.google.accompanist.pager.rememberPagerState
|
||||
import com.pixelized.biblib.ui.composable.Search
|
||||
import com.pixelized.biblib.ui.scaffold.BottomDetailScaffold
|
||||
import com.pixelized.biblib.ui.scaffold.SearchScaffold
|
||||
import com.pixelized.biblib.ui.scaffold.SearchScaffoldState
|
||||
import com.pixelized.biblib.ui.scaffold.*
|
||||
import com.pixelized.biblib.ui.scaffold.SearchScaffoldState.ContentState
|
||||
import com.pixelized.biblib.ui.scaffold.rememberSearchScaffoldState
|
||||
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
||||
import com.pixelized.biblib.ui.screen.home.common.composable.ConnectivityHeader
|
||||
import com.pixelized.biblib.ui.screen.home.page.Page
|
||||
|
|
@ -44,7 +38,9 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalAnimationApi::class)
|
||||
@OptIn(ExperimentalComposeUiApi::class, ExperimentalAnimationApi::class,
|
||||
ExperimentalMaterialApi::class
|
||||
)
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
accountViewModel: HomeViewModel = hiltViewModel(),
|
||||
|
|
@ -57,78 +53,82 @@ fun HomeScreen(
|
|||
val focusRequester: FocusRequester = remember { FocusRequester() }
|
||||
|
||||
BottomDetailScaffold {
|
||||
SearchScaffold(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
state = state,
|
||||
topBar = {
|
||||
Search(
|
||||
state = state,
|
||||
avatar = accountViewModel.avatar,
|
||||
focusRequester = focusRequester,
|
||||
onSearch = {
|
||||
if (state.content != ContentState.SEARCH || state.isCollapsed()) {
|
||||
state.expand(ContentState.SEARCH)
|
||||
scope.launch {
|
||||
delay(100)
|
||||
focusRequester.requestFocus()
|
||||
BottomSearchScaffold {
|
||||
SearchScaffold(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
state = state,
|
||||
topBar = {
|
||||
Search(
|
||||
state = state,
|
||||
avatar = accountViewModel.avatar,
|
||||
focusRequester = focusRequester,
|
||||
onSearch = {
|
||||
if (state.content != ContentState.SEARCH || state.isCollapsed()) {
|
||||
state.expand(ContentState.SEARCH)
|
||||
scope.launch {
|
||||
delay(100)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
} else {
|
||||
focusManager.clearFocus(force = true)
|
||||
keyboard?.hide()
|
||||
state.collapse()
|
||||
}
|
||||
} else {
|
||||
focusManager.clearFocus(force = true)
|
||||
keyboard?.hide()
|
||||
state.collapse()
|
||||
}
|
||||
},
|
||||
onAvatar = {
|
||||
if (state.content != ContentState.PROFILE || state.isCollapsed()) {
|
||||
focusManager.clearFocus(force = true)
|
||||
keyboard?.hide()
|
||||
state.expand(ContentState.PROFILE)
|
||||
} else {
|
||||
state.collapse()
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
search = {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
AnimatedContent(
|
||||
targetState = state.content,
|
||||
transitionSpec = {
|
||||
when {
|
||||
targetState == ContentState.INITIAL -> {
|
||||
EnterTransition.None with fadeOut()
|
||||
}
|
||||
targetState.ordinal < initialState.ordinal -> {
|
||||
val enter = slideInHorizontally { -it } + fadeIn()
|
||||
val exit = slideOutHorizontally { +it } + fadeOut()
|
||||
enter with exit
|
||||
}
|
||||
else -> {
|
||||
val enter = slideInHorizontally { +it } + fadeIn()
|
||||
val exit = slideOutHorizontally { -it } + fadeOut()
|
||||
enter with exit
|
||||
}
|
||||
},
|
||||
onAvatar = {
|
||||
if (state.content != ContentState.PROFILE || state.isCollapsed()) {
|
||||
focusManager.clearFocus(force = true)
|
||||
keyboard?.hide()
|
||||
state.expand(ContentState.PROFILE)
|
||||
} else {
|
||||
state.collapse()
|
||||
}
|
||||
}
|
||||
) {
|
||||
when (it) {
|
||||
ContentState.SEARCH -> SearchPage()
|
||||
ContentState.PROFILE -> ProfilePage()
|
||||
else -> Unit
|
||||
)
|
||||
},
|
||||
search = {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
AnimatedContent(
|
||||
targetState = state.content,
|
||||
transitionSpec = {
|
||||
when {
|
||||
targetState == ContentState.INITIAL -> {
|
||||
EnterTransition.None with fadeOut()
|
||||
}
|
||||
targetState.ordinal < initialState.ordinal -> {
|
||||
val enter = slideInHorizontally { -it } + fadeIn()
|
||||
val exit = slideOutHorizontally { +it } + fadeOut()
|
||||
enter with exit
|
||||
}
|
||||
else -> {
|
||||
val enter = slideInHorizontally { +it } + fadeIn()
|
||||
val exit = slideOutHorizontally { -it } + fadeOut()
|
||||
enter with exit
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
when (it) {
|
||||
ContentState.SEARCH -> SearchPage()
|
||||
ContentState.PROFILE -> ProfilePage()
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
HomeScreenContent(
|
||||
isNetworkAvailable = { connectivityViewModel.isNetworkAvailable },
|
||||
pages = Page.all
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = {
|
||||
HomeScreenContent(
|
||||
isNetworkAvailable = { connectivityViewModel.isNetworkAvailable },
|
||||
pages = Page.all
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
BackHandler(state.isCollapsed().not()) {
|
||||
state.collapse()
|
||||
|
||||
val bottomSearchState = LocalBottomSearchState.current
|
||||
BackHandler(state.isCollapsed().not() && bottomSearchState.bottomSheetState.isVisible.not()) {
|
||||
state.collapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.pixelized.biblib.ui.screen.home.common.uio
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
data class BookUio(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
|
|
@ -10,4 +12,4 @@ data class BookUio(
|
|||
val series: String?,
|
||||
val description: String,
|
||||
val cover: String,
|
||||
)
|
||||
) : Serializable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
package com.pixelized.biblib.ui.screen.home.page.search
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
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.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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.ui.scaffold.LocalBottomSearchState
|
||||
import com.pixelized.biblib.ui.scaffold.SearchFilter
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
|
||||
@Composable
|
||||
fun CategorySearchPage(
|
||||
searchViewModel: SearchViewModel = hiltViewModel(),
|
||||
focusRequester: FocusRequester = FocusRequester(),
|
||||
filter: SearchFilter?,
|
||||
onClose: () -> Unit = default(),
|
||||
) {
|
||||
val bottomSearchState = LocalBottomSearchState.current
|
||||
CategorySearchPageContent(
|
||||
focusRequester = focusRequester,
|
||||
filter = filter,
|
||||
onClose = onClose,
|
||||
searchValue = {
|
||||
when (bottomSearchState.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.author
|
||||
is SearchFilter.Genre -> searchViewModel.genre
|
||||
is SearchFilter.Language -> searchViewModel.language
|
||||
null -> ""
|
||||
}
|
||||
},
|
||||
onSearchChange = {
|
||||
when (bottomSearchState.filter) {
|
||||
is SearchFilter.Author -> searchViewModel.filterAuthor(it)
|
||||
is SearchFilter.Genre -> searchViewModel.filterGenre(it)
|
||||
is SearchFilter.Language -> searchViewModel.filterLanguage(it)
|
||||
null -> Unit
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CategorySearchPageContent(
|
||||
focusRequester: FocusRequester = FocusRequester(),
|
||||
filter: SearchFilter?,
|
||||
searchValue: () -> String,
|
||||
onSearchChange: (String) -> Unit = default<String>(),
|
||||
onClose: () -> Unit = default(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
.fillMaxSize()
|
||||
) {
|
||||
TopAppBar(
|
||||
backgroundColor = MaterialTheme.colors.surface,
|
||||
elevation = 0.dp,
|
||||
title = {
|
||||
filter?.let {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = stringResource(id = it.label),
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onClose) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
TextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester = focusRequester)
|
||||
.fillMaxWidth(),
|
||||
label = {
|
||||
Text(
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = "Rechercher"
|
||||
)
|
||||
},
|
||||
value = searchValue(),
|
||||
singleLine = true,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
),
|
||||
onValueChange = onSearchChange
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun CategorySearchPageContentPreview() {
|
||||
BibLibTheme {
|
||||
CategorySearchPageContent(
|
||||
filter = SearchFilter.Author(),
|
||||
searchValue = { "Asimov" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,22 +13,49 @@ import androidx.compose.material.*
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.pixelized.biblib.ui.scaffold.LocalBottomSearchState
|
||||
import com.pixelized.biblib.ui.scaffold.LocalSearchViewModel
|
||||
import com.pixelized.biblib.ui.scaffold.SearchFilter
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
|
||||
|
||||
@Composable
|
||||
fun SearchPage() {
|
||||
fun SearchPage(
|
||||
searchViewModel: SearchViewModel = LocalSearchViewModel.current
|
||||
) {
|
||||
val bottomSearchState = LocalBottomSearchState.current
|
||||
val filters by remember {
|
||||
derivedStateOf {
|
||||
listOf(
|
||||
SearchFilter.Author(value = searchViewModel.author),
|
||||
SearchFilter.Genre(value = searchViewModel.genre),
|
||||
SearchFilter.Language(value = searchViewModel.language),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SearchPageContent(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
filters = filters,
|
||||
onFilter = {
|
||||
bottomSearchState.expandSearch(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchPageContent(
|
||||
modifier: Modifier = Modifier,
|
||||
filters: List<SearchFilter> = SearchFilter.all,
|
||||
onFilter: (filter: SearchFilter) -> Unit = default<SearchFilter>(),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
|
|
@ -36,7 +63,9 @@ private fun SearchPageContent(
|
|||
) {
|
||||
item(key = "Search Filter") {
|
||||
SearchFilter(
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16)
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
|
||||
filters = filters,
|
||||
onFilter = onFilter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -45,24 +74,28 @@ private fun SearchPageContent(
|
|||
@Composable
|
||||
private fun SearchFilter(
|
||||
modifier: Modifier = Modifier,
|
||||
filters: List<SearchFilter> = SearchFilter.all,
|
||||
onFilter: (filter: SearchFilter) -> Unit = default<SearchFilter>(),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.horizontalScroll(rememberScrollState())
|
||||
.then(modifier)
|
||||
) {
|
||||
SearchChipFilter(
|
||||
label = "Autheur",
|
||||
selected = true,
|
||||
value = "Isaac Asimov"
|
||||
)
|
||||
SearchChipFilter(
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp8),
|
||||
label = "Genre",
|
||||
)
|
||||
SearchChipFilter(
|
||||
label = "Langue",
|
||||
)
|
||||
filters.forEachIndexed { index, filter ->
|
||||
val modifier = if (index != filters.lastIndex) {
|
||||
Modifier.padding(end = MaterialTheme.bibLib.dimen.dp8)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
SearchChipFilter(
|
||||
modifier = modifier,
|
||||
selected = filter.isSelected,
|
||||
label = stringResource(id = filter.label),
|
||||
value = filter.value,
|
||||
onClick = { onFilter(filter) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,8 +130,8 @@ private fun SearchChipFilter(
|
|||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun SearchPageContentPreview() {
|
||||
BibLibTheme {
|
||||
SearchPageContent()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,14 @@ package com.pixelized.biblib.utils.extention
|
|||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
@Composable
|
||||
fun navigationBarsHeight(): Dp =
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
|
||||
|
||||
@Composable
|
||||
fun statusBarsHeight(): Dp =
|
||||
WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
|
||||
|
|
@ -41,4 +41,8 @@
|
|||
<string name="detail_series">Séries</string>
|
||||
<string name="detail_emails_title">Envoyer cet eBook à :</string>
|
||||
|
||||
<string name="search_filter_author">Auteur</string>
|
||||
<string name="search_filter_genre">Genre</string>
|
||||
<string name="search_filter_language">Langue</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">BibLibrary</string>
|
||||
<string name="app_version" translatable="false">%1$s: %2$s - %3$d</string>
|
||||
<string name="not_implemented_yet" translatable="false">Not implemented yet.</string>
|
||||
|
||||
<!-- Actions -->
|
||||
|
||||
|
|
@ -46,5 +47,8 @@
|
|||
<string name="detail_series">Series</string>
|
||||
<string name="detail_emails_title">Send this eBook to:</string>
|
||||
|
||||
<string name="not_implemented_yet" translatable="false">Not implemented yet.</string>
|
||||
<string name="search_filter_author">Author</string>
|
||||
<string name="search_filter_genre">Genre</string>
|
||||
<string name="search_filter_language">Language</string>
|
||||
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue