From 1f85df8a1aa8a38d019c4ce3a53abd0246c6897a Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Mon, 6 Mar 2023 16:25:26 +0100 Subject: [PATCH] Update SearchBar by using the DockedSearchBar from material3 --- .../createroom/root/CreateRoomRootScreen.kt | 122 ++++++++++-------- .../theme/components/DockedSearchBar.kt | 91 +++++++++++++ .../src/main/res/drawable/ic_arrow_back.xml | 9 ++ .../src/main/res/values/strings_eax.xml | 3 + .../kotlin/extension/DependencyHandleScope.kt | 4 +- 5 files changed, 175 insertions(+), 54 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DockedSearchBar.kt create mode 100644 libraries/designsystem/src/main/res/drawable/ic_arrow_back.xml diff --git a/features/createroom/src/main/kotlin/io/element/android/features/createroom/root/CreateRoomRootScreen.kt b/features/createroom/src/main/kotlin/io/element/android/features/createroom/root/CreateRoomRootScreen.kt index c1d9e8e05d..3701a0329c 100644 --- a/features/createroom/src/main/kotlin/io/element/android/features/createroom/root/CreateRoomRootScreen.kt +++ b/features/createroom/src/main/kotlin/io/element/android/features/createroom/root/CreateRoomRootScreen.kt @@ -16,22 +16,22 @@ package io.element.android.features.createroom.root -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SearchBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -40,6 +40,7 @@ import androidx.compose.ui.unit.sp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar +import io.element.android.libraries.designsystem.theme.components.DockedSearchBar import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -54,22 +55,22 @@ fun CreateRoomRootScreen( modifier: Modifier = Modifier, onClosePressed: () -> Unit = {} ) { + val isSearchActive = rememberSaveable { mutableStateOf(false) } Scaffold( modifier = modifier.fillMaxWidth(), topBar = { - CreateRoomRootViewTopBar(onClosePressed = onClosePressed) + if (!isSearchActive.value) { + CreateRoomRootViewTopBar(onClosePressed = onClosePressed) + } } ) { Column( modifier = Modifier.padding(it) ) { - SearchBar( - modifier = Modifier.padding(horizontal = 16.dp), - // TODO use resource string - placeHolderTitle = "Search for someone", - // TODO implement click behavior - onClickDescription = "", - onClick = {} + CreateRoomSearchBar( + modifier = Modifier.fillMaxWidth(), + placeHolderTitle = stringResource(StringR.string.search_for_someone), + active = isSearchActive, ) } } @@ -98,47 +99,62 @@ fun CreateRoomRootViewTopBar( ) } -// TODO export into design system package -// TODO comment that SearchBar is not yet implemented in Material3 compose -// and that TextField cannot be used since contentPadding cannot be changed +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun SearchBar( - modifier: Modifier = Modifier, +fun CreateRoomSearchBar( placeHolderTitle: String, - onClickDescription: String = "", - onClick: () -> Unit = {}, + active: MutableState, + modifier: Modifier = Modifier, ) { - Row( - modifier = modifier - .fillMaxWidth() - .height(48.dp) - .background( - color = MaterialTheme.colorScheme.surfaceVariant, - shape = RoundedCornerShape(28.dp), - ) - .clickable( - role = Role.Button, - onClickLabel = onClickDescription, - onClick = onClick, - ), - ) { - Text( - modifier = Modifier - .padding(horizontal = 16.dp) - .align(Alignment.CenterVertically) - .weight(1f) - .alpha(0.4f), - text = placeHolderTitle, - ) - Icon( - modifier = Modifier - .padding(horizontal = 16.dp) - .align(Alignment.CenterVertically) - .alpha(0.4f), - resourceId = DrawableR.drawable.ic_search, - contentDescription = stringResource(id = StringR.string.search) - ) + var text by rememberSaveable { mutableStateOf("") } + val focusManager = LocalFocusManager.current + + fun closeSearchBar() { + focusManager.clearFocus() + active.value = false } + + DockedSearchBar( + query = text, + onQueryChange = { text = it }, + onSearch = { closeSearchBar() }, + active = active.value, + onActiveChange = { + active.value = it + if (!active.value) focusManager.clearFocus() + }, + modifier = modifier + .padding(horizontal = if (!active.value) 16.dp else 0.dp), + placeholder = { + Text( + text = placeHolderTitle, + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + }, + leadingIcon = if (active.value) { + { + IconButton(onClick = { closeSearchBar() }) { + Icon(DrawableR.drawable.ic_arrow_back, stringResource(StringR.string.a11y_back)) + } + } + } else null, + trailingIcon = { + if (active.value) { + IconButton(onClick = { text = "" }) { + Icon(DrawableR.drawable.ic_close, stringResource(StringR.string.a11y_clear)) + } + } else { + Icon( + resourceId = DrawableR.drawable.ic_search, + contentDescription = stringResource(StringR.string.search), + modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine) + ) + } + }, + shape = if (!active.value) SearchBarDefaults.dockedShape else SearchBarDefaults.fullScreenShape, + colors = if (!active.value) SearchBarDefaults.colors() else SearchBarDefaults.colors(containerColor = Color.Transparent), + content = {}, + ) } @Preview diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DockedSearchBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DockedSearchBar.kt new file mode 100644 index 0000000000..100ae259f8 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DockedSearchBar.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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. + */ + +@file:OptIn(ExperimentalMaterial3Api::class) + +package io.element.android.libraries.designsystem.theme.components + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SearchBarColors +import androidx.compose.material3.SearchBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DockedSearchBar( + query: String, + onQueryChange: (String) -> Unit, + onSearch: (String) -> Unit, + active: Boolean, + onActiveChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + shape: Shape = SearchBarDefaults.dockedShape, + colors: SearchBarColors = SearchBarDefaults.colors(), + tonalElevation: Dp = SearchBarDefaults.Elevation, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + content: @Composable ColumnScope.() -> Unit, +) { + androidx.compose.material3.DockedSearchBar( + query = query, + onQueryChange = onQueryChange, + onSearch = onSearch, + active = active, + onActiveChange = onActiveChange, + modifier = modifier, + enabled = enabled, + placeholder = placeholder, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + shape = shape, + colors = colors, + tonalElevation = tonalElevation, + interactionSource = interactionSource, + content = content, + ) +} + +@Preview +@Composable +internal fun DockedSearchBarLightPreview() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun DockedSearchBarDarkPreview() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + DockedSearchBar( + query = "Some text", + onQueryChange = {}, + onSearch = {}, + active = false, + onActiveChange = {}, + content = {}, + ) +} diff --git a/libraries/designsystem/src/main/res/drawable/ic_arrow_back.xml b/libraries/designsystem/src/main/res/drawable/ic_arrow_back.xml new file mode 100644 index 0000000000..4adfb72ad7 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_arrow_back.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/ui-strings/src/main/res/values/strings_eax.xml b/libraries/ui-strings/src/main/res/values/strings_eax.xml index 0ed87ec9f0..b2d63ec96c 100644 --- a/libraries/ui-strings/src/main/res/values/strings_eax.xml +++ b/libraries/ui-strings/src/main/res/values/strings_eax.xml @@ -14,5 +14,8 @@ You can only connect to an existing server that supports sliding sync. Your homeserver admin will need to configure it. Server not supported This server currently doesn\'t support sliding sync. + Back + Clear + Search for someone diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 4e58d0bf0d..9cdc2bd032 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -39,7 +39,9 @@ fun DependencyHandlerScope.composeDependencies(libs: LibrariesForLibs) { androidTestImplementation(composeBom) implementation("androidx.compose.ui:ui") implementation("androidx.compose.material:material") - implementation("androidx.compose.material3:material3") + // Override BOM version, SearchBar is not available in the actual version + // do not use latest version because of clashes on androidx lifecycle dependency + implementation("androidx.compose.material3:material3:1.1.0-alpha04") implementation("androidx.compose.material:material-icons-extended") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.activity:activity-compose:1.6.1")