refactor(Search): 封装搜索框、结果列表等组件

This commit is contained in:
HuanCheng65 2024-01-28 18:09:08 +08:00
parent d99f723cbc
commit 0207f830f7
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
3 changed files with 296 additions and 232 deletions

View File

@ -1,6 +1,5 @@
package com.huanchengfly.tieba.post.ui.page.search package com.huanchengfly.tieba.post.ui.page.search
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -14,7 +13,6 @@ import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@ -27,20 +25,16 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ButtonDefaults import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ContentAlpha import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.ProvideTextStyle import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.Surface
import androidx.compose.material.Tab import androidx.compose.material.Tab
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Clear
import androidx.compose.material.icons.rounded.ExpandLess import androidx.compose.material.icons.rounded.ExpandLess
import androidx.compose.material.icons.rounded.ExpandMore import androidx.compose.material.icons.rounded.ExpandMore
import androidx.compose.material.icons.rounded.Search import androidx.compose.material.icons.rounded.Search
@ -59,13 +53,11 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -87,13 +79,13 @@ import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadPage
import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadSortType import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadSortType
import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadUiEvent import com.huanchengfly.tieba.post.ui.page.search.thread.SearchThreadUiEvent
import com.huanchengfly.tieba.post.ui.page.search.user.SearchUserPage import com.huanchengfly.tieba.post.ui.page.search.user.SearchUserPage
import com.huanchengfly.tieba.post.ui.widgets.compose.BaseTextField
import com.huanchengfly.tieba.post.ui.widgets.compose.Button import com.huanchengfly.tieba.post.ui.widgets.compose.Button
import com.huanchengfly.tieba.post.ui.widgets.compose.Container import com.huanchengfly.tieba.post.ui.widgets.compose.Container
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoadHorizontalPager import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoadHorizontalPager
import com.huanchengfly.tieba.post.ui.widgets.compose.MyBackHandler import com.huanchengfly.tieba.post.ui.widgets.compose.MyBackHandler
import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold import com.huanchengfly.tieba.post.ui.widgets.compose.MyScaffold
import com.huanchengfly.tieba.post.ui.widgets.compose.PagerTabIndicator import com.huanchengfly.tieba.post.ui.widgets.compose.PagerTabIndicator
import com.huanchengfly.tieba.post.ui.widgets.compose.SearchBox
import com.huanchengfly.tieba.post.ui.widgets.compose.TabClickMenu import com.huanchengfly.tieba.post.ui.widgets.compose.TabClickMenu
import com.huanchengfly.tieba.post.ui.widgets.compose.TabRow import com.huanchengfly.tieba.post.ui.widgets.compose.TabRow
import com.huanchengfly.tieba.post.ui.widgets.compose.TopAppBarContainer import com.huanchengfly.tieba.post.ui.widgets.compose.TopAppBarContainer
@ -269,8 +261,10 @@ fun SearchPage(
} }
} }
} }
) { ) { paddingValues ->
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier
.fillMaxSize()
.padding(paddingValues)) {
if (!isKeywordEmpty) { if (!isKeywordEmpty) {
ProvideNavigator(navigator = navigator) { ProvideNavigator(navigator = navigator) {
LazyLoadHorizontalPager( LazyLoadHorizontalPager(
@ -562,20 +556,18 @@ private fun SearchTopBar(
onKeywordSubmit: (String) -> Unit = {}, onKeywordSubmit: (String) -> Unit = {},
onBack: () -> Unit = {}, onBack: () -> Unit = {},
) { ) {
val isKeywordNotEmpty = remember(keyword) { keyword.isNotEmpty() } SearchBox(
var isFocused by remember { mutableStateOf(false) } keyword = keyword,
Surface( onKeywordChange = onKeywordChange,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
shape = RoundedCornerShape(6.dp), onKeywordSubmit = onKeywordSubmit,
color = ExtendedTheme.colors.topBarSurface, placeholder = {
contentColor = ExtendedTheme.colors.onTopBarSurface, Text(
elevation = 0.dp text = stringResource(id = R.string.hint_search),
) { color = ExtendedTheme.colors.onTopBarSurface.copy(alpha = ContentAlpha.medium)
Row( )
modifier = Modifier.padding(horizontal = 16.dp), },
verticalAlignment = Alignment.CenterVertically, prependIcon = {
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Box( Box(
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(100)) .clip(RoundedCornerShape(100))
@ -588,76 +580,13 @@ private fun SearchTopBar(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Icon( Icon(
imageVector = Icons.Rounded.ArrowBack, imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = stringResource(id = R.string.button_back) contentDescription = stringResource(id = R.string.button_back)
) )
} }
BaseTextField(
value = keyword,
onValueChange = {
onKeywordChange(it)
}, },
singleLine = true, shape = RoundedCornerShape(6.dp)
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search,
),
keyboardActions = KeyboardActions(
onSearch = {
onKeywordSubmit(keyword)
}
),
placeholder = {
Text(
text = stringResource(id = R.string.hint_search),
color = ExtendedTheme.colors.onTopBarSurface.copy(alpha = ContentAlpha.medium)
) )
},
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.onFocusEvent { isFocused = it.isFocused }
)
AnimatedVisibility(visible = isKeywordNotEmpty && isFocused) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(100))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, 24.dp),
role = Role.Button
) { onKeywordChange("") },
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Rounded.Clear,
contentDescription = stringResource(id = R.string.button_clear)
)
}
}
}
AnimatedVisibility(visible = isKeywordNotEmpty) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(100))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, 24.dp),
role = Role.Button
) { onKeywordSubmit(keyword) },
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Rounded.Search,
contentDescription = stringResource(id = R.string.button_search)
)
}
}
}
}
} }
@Preview("SearchBox") @Preview("SearchBox")

View File

@ -1,15 +1,9 @@
package com.huanchengfly.tieba.post.ui.page.search.thread package com.huanchengfly.tieba.post.ui.page.search.thread
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.LocalContentColor
import androidx.compose.material.Text
import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
@ -20,8 +14,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.huanchengfly.tieba.post.api.models.SearchThreadBean
import com.huanchengfly.tieba.post.arch.collectPartialAsState import com.huanchengfly.tieba.post.arch.collectPartialAsState
import com.huanchengfly.tieba.post.arch.onGlobalEvent import com.huanchengfly.tieba.post.arch.onGlobalEvent
import com.huanchengfly.tieba.post.arch.pageViewModel import com.huanchengfly.tieba.post.arch.pageViewModel
@ -32,24 +24,12 @@ import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination import com.huanchengfly.tieba.post.ui.page.destinations.ThreadPageDestination
import com.huanchengfly.tieba.post.ui.page.destinations.UserProfilePageDestination import com.huanchengfly.tieba.post.ui.page.destinations.UserProfilePageDestination
import com.huanchengfly.tieba.post.ui.page.search.SearchUiEvent import com.huanchengfly.tieba.post.ui.page.search.SearchUiEvent
import com.huanchengfly.tieba.post.ui.widgets.compose.Avatar
import com.huanchengfly.tieba.post.ui.widgets.compose.Card
import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen import com.huanchengfly.tieba.post.ui.widgets.compose.ErrorScreen
import com.huanchengfly.tieba.post.ui.widgets.compose.ForumInfoChip
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
import com.huanchengfly.tieba.post.ui.widgets.compose.LoadMoreLayout import com.huanchengfly.tieba.post.ui.widgets.compose.LoadMoreLayout
import com.huanchengfly.tieba.post.ui.widgets.compose.LocalShouldLoad import com.huanchengfly.tieba.post.ui.widgets.compose.LocalShouldLoad
import com.huanchengfly.tieba.post.ui.widgets.compose.MyLazyColumn import com.huanchengfly.tieba.post.ui.widgets.compose.SearchThreadList
import com.huanchengfly.tieba.post.ui.widgets.compose.Sizes
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadAgreeBtn
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadContent
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadReplyBtn
import com.huanchengfly.tieba.post.ui.widgets.compose.ThreadShareBtn
import com.huanchengfly.tieba.post.ui.widgets.compose.UserHeader
import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen import com.huanchengfly.tieba.post.ui.widgets.compose.states.StateScreen
import com.huanchengfly.tieba.post.utils.DateTimeUtils
import com.huanchengfly.tieba.post.utils.StringUtil
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@ -59,7 +39,6 @@ fun SearchThreadPage(
initialSortType: Int = SearchThreadSortType.SORT_TYPE_NEWEST, initialSortType: Int = SearchThreadSortType.SORT_TYPE_NEWEST,
viewModel: SearchThreadViewModel = pageViewModel(), viewModel: SearchThreadViewModel = pageViewModel(),
) { ) {
val context = LocalContext.current
val navigator = LocalNavigator.current val navigator = LocalNavigator.current
LazyLoad(loaded = viewModel.initialized) { LazyLoad(loaded = viewModel.initialized) {
viewModel.send(SearchThreadUiIntent.Refresh(keyword, initialSortType)) viewModel.send(SearchThreadUiIntent.Refresh(keyword, initialSortType))
@ -187,121 +166,3 @@ fun SearchThreadPage(
} }
} }
} }
@Composable
private fun SearchThreadList(
data: ImmutableList<SearchThreadBean.ThreadInfoBean>,
lazyListState: LazyListState,
onItemClick: (SearchThreadBean.ThreadInfoBean) -> Unit,
onItemUserClick: (SearchThreadBean.UserInfoBean) -> Unit,
onItemForumClick: (SearchThreadBean.ForumInfo) -> Unit,
modifier: Modifier = Modifier,
) {
MyLazyColumn(
state = lazyListState,
modifier = modifier
) {
items(data) {
SearchThreadItem(
item = it,
onClick = onItemClick,
onUserClick = onItemUserClick,
onForumClick = onItemForumClick,
)
}
}
}
@Composable
private fun SearchThreadUserHeader(
user: SearchThreadBean.UserInfoBean,
time: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
UserHeader(
avatar = {
Avatar(
data = StringUtil.getAvatarUrl(user.portrait),
size = Sizes.Small,
contentDescription = null
)
},
name = {
Text(
text = StringUtil.getUsernameAnnotatedString(
LocalContext.current,
user.userName.orEmpty(),
user.showNickname,
color = LocalContentColor.current
)
)
},
desc = {
Text(
text = DateTimeUtils.getRelativeTimeString(LocalContext.current, time)
)
},
onClick = onClick,
modifier = modifier
)
}
@Composable
private fun SearchThreadItem(
item: SearchThreadBean.ThreadInfoBean,
onClick: (SearchThreadBean.ThreadInfoBean) -> Unit,
onUserClick: (SearchThreadBean.UserInfoBean) -> Unit,
onForumClick: (SearchThreadBean.ForumInfo) -> Unit,
modifier: Modifier = Modifier,
) {
Card(
modifier = modifier,
header = {
SearchThreadUserHeader(
user = item.user,
time = item.time,
onClick = { onUserClick(item.user) }
)
},
content = {
ThreadContent(
title = item.title,
abstractText = item.content,
showTitle = item.title.isNotBlank(),
showAbstract = item.content.isNotBlank(),
)
if (item.forumName.isNotEmpty()) {
ForumInfoChip(
imageUriProvider = { item.forumInfo.avatar },
nameProvider = { item.forumName }
) {
onForumClick(item.forumInfo)
}
}
},
action = {
Row(modifier = Modifier.fillMaxWidth()) {
ThreadReplyBtn(
replyNum = item.postNum.toInt(),
onClick = {},
modifier = Modifier.weight(1f)
)
ThreadAgreeBtn(
hasAgree = false,
agreeNum = item.likeNum.toInt(),
onClick = {},
modifier = Modifier.weight(1f)
)
ThreadShareBtn(
shareNum = item.shareNum.toLong(),
onClick = {},
modifier = Modifier.weight(1f)
)
}
},
onClick = { onClick(item) },
)
}

View File

@ -0,0 +1,274 @@
package com.huanchengfly.tieba.post.ui.widgets.compose
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Icon
import androidx.compose.material.LocalContentColor
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Clear
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.api.models.SearchThreadBean
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
import com.huanchengfly.tieba.post.utils.DateTimeUtils
import com.huanchengfly.tieba.post.utils.StringUtil
import kotlinx.collections.immutable.ImmutableList
@Composable
fun SearchThreadList(
data: ImmutableList<SearchThreadBean.ThreadInfoBean>,
lazyListState: LazyListState,
onItemClick: (SearchThreadBean.ThreadInfoBean) -> Unit,
onItemUserClick: (SearchThreadBean.UserInfoBean) -> Unit,
onItemForumClick: (SearchThreadBean.ForumInfo) -> Unit,
modifier: Modifier = Modifier,
hideForum: Boolean = false,
header: LazyListScope.() -> Unit = {},
) {
MyLazyColumn(
state = lazyListState,
modifier = modifier
) {
header()
itemsIndexed(data) { index, item ->
if (index > 0) {
VerticalDivider(modifier = Modifier.padding(horizontal = 16.dp))
}
SearchThreadItem(
item = item,
onClick = onItemClick,
onUserClick = onItemUserClick,
onForumClick = onItemForumClick,
hideForum = hideForum,
)
}
}
}
@Composable
fun SearchThreadUserHeader(
user: SearchThreadBean.UserInfoBean,
time: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
UserHeader(
avatar = {
Avatar(
data = StringUtil.getAvatarUrl(user.portrait),
size = Sizes.Small,
contentDescription = null
)
},
name = {
Text(
text = StringUtil.getUsernameAnnotatedString(
LocalContext.current,
user.userName.orEmpty(),
user.showNickname,
color = LocalContentColor.current
)
)
},
desc = {
Text(
text = DateTimeUtils.getRelativeTimeString(LocalContext.current, time)
)
},
onClick = onClick,
modifier = modifier
)
}
@Composable
fun SearchThreadItem(
item: SearchThreadBean.ThreadInfoBean,
onClick: (SearchThreadBean.ThreadInfoBean) -> Unit,
onUserClick: (SearchThreadBean.UserInfoBean) -> Unit,
onForumClick: (SearchThreadBean.ForumInfo) -> Unit,
modifier: Modifier = Modifier,
hideForum: Boolean = false,
) {
Card(
modifier = modifier,
header = {
SearchThreadUserHeader(
user = item.user,
time = item.time,
onClick = { onUserClick(item.user) }
)
},
content = {
ThreadContent(
title = item.title,
abstractText = item.content,
showTitle = item.title.isNotBlank(),
showAbstract = item.content.isNotBlank(),
)
if (!hideForum && item.forumName.isNotEmpty()) {
ForumInfoChip(
imageUriProvider = { item.forumInfo.avatar },
nameProvider = { item.forumName }
) {
onForumClick(item.forumInfo)
}
}
},
action = {
Row(modifier = Modifier.fillMaxWidth()) {
ThreadReplyBtn(
replyNum = item.postNum.toInt(),
onClick = {},
modifier = Modifier.weight(1f)
)
ThreadAgreeBtn(
hasAgree = false,
agreeNum = item.likeNum.toInt(),
onClick = {},
modifier = Modifier.weight(1f)
)
ThreadShareBtn(
shareNum = item.shareNum.toLong(),
onClick = {},
modifier = Modifier.weight(1f)
)
}
},
onClick = { onClick(item) },
)
}
@Composable
fun SearchBox(
keyword: String,
onKeywordChange: (String) -> Unit,
modifier: Modifier = Modifier,
onKeywordSubmit: (String) -> Unit = {},
placeholder: @Composable () -> Unit = {},
prependIcon: @Composable () -> Unit = {},
appendIcon: @Composable () -> Unit = {},
focusRequester: FocusRequester = remember { FocusRequester() },
shape: Shape = RectangleShape,
color: Color = ExtendedTheme.colors.topBarSurface,
contentColor: Color = ExtendedTheme.colors.onTopBarSurface,
elevation: Dp = 0.dp,
) {
val isKeywordNotEmpty = remember(keyword) { keyword.isNotEmpty() }
var isFocused by remember { mutableStateOf(false) }
Surface(
modifier = modifier,
shape = shape,
color = color,
contentColor = contentColor,
elevation = 0.dp
) {
Row(
modifier = Modifier.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
prependIcon()
BaseTextField(
value = keyword,
onValueChange = {
onKeywordChange(it)
},
singleLine = true,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search,
),
keyboardActions = KeyboardActions(
onSearch = {
onKeywordSubmit(keyword)
}
),
placeholder = placeholder,
modifier = Modifier
.fillMaxHeight()
.weight(1f)
.focusRequester(focusRequester)
.onFocusEvent {
isFocused = it.isFocused
}
)
appendIcon()
AnimatedVisibility(visible = isKeywordNotEmpty && isFocused) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(100))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, 24.dp),
role = Role.Button
) { onKeywordChange("") },
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Rounded.Clear,
contentDescription = stringResource(id = R.string.button_clear)
)
}
}
}
AnimatedVisibility(visible = isKeywordNotEmpty) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(100))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false, 24.dp),
role = Role.Button
) { onKeywordSubmit(keyword) },
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Rounded.Search,
contentDescription = stringResource(id = R.string.button_search)
)
}
}
}
}
}