-
-
Notifications
You must be signed in to change notification settings - Fork 584
feat(Android): Expose configuration for showAsAction with collapse #3009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout | |
import androidx.core.view.ViewCompat | ||
import androidx.core.view.WindowInsetsAnimationCompat | ||
import androidx.core.view.WindowInsetsCompat | ||
import androidx.core.view.children | ||
import com.facebook.react.uimanager.PixelUtil | ||
import com.facebook.react.uimanager.UIManagerHelper | ||
import com.google.android.material.appbar.AppBarLayout | ||
|
@@ -412,32 +413,43 @@ class ScreenStackFragment : | |
return super.onCreateOptionsMenu(menu, inflater) | ||
} | ||
|
||
private fun shouldShowSearchBar(): Boolean { | ||
private fun locateSearchBarSubviewInConfig(): ScreenStackHeaderSubview? { | ||
val config = screen.headerConfig | ||
val numberOfSubViews = config?.configSubviewsCount ?: 0 | ||
if (config != null && numberOfSubViews > 0) { | ||
for (i in 0 until numberOfSubViews) { | ||
val subView = config.getConfigSubview(i) | ||
if (subView.type == ScreenStackHeaderSubview.Type.SEARCH_BAR) { | ||
return true | ||
return subView | ||
} | ||
} | ||
} | ||
return false | ||
return null | ||
} | ||
|
||
private fun extractShowAsActionFromSearchBar(searchSubview: ScreenStackHeaderSubview): Int { | ||
for (child in searchSubview.children) { | ||
if (child is SearchBarView) { | ||
return child.showAsAction | ||
} | ||
} | ||
|
||
return MenuItem.SHOW_AS_ACTION_ALWAYS | ||
} | ||
|
||
private fun updateToolbarMenu(menu: Menu) { | ||
menu.clear() | ||
if (shouldShowSearchBar()) { | ||
locateSearchBarSubviewInConfig()?.let { | ||
val currentContext = context | ||
if (searchView == null && currentContext != null) { | ||
val newSearchView = CustomSearchView(currentContext, this) | ||
searchView = newSearchView | ||
onSearchViewCreate?.invoke(newSearchView) | ||
} | ||
menu.add("").apply { | ||
setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) | ||
menu.add("Search").apply { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to change that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this title used? |
||
actionView = searchView | ||
setIcon(R.drawable.ic_action_search) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to set this icon manually? |
||
setShowAsAction(extractShowAsActionFromSearchBar(it)) | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this additional resource? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:width="24dp" | ||
android:height="24dp" | ||
android:viewportWidth="24" | ||
android:viewportHeight="24" | ||
android:tint="#333333" | ||
android:alpha="0.6"> | ||
<path | ||
android:fillColor="@android:color/white" | ||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> | ||
</vector> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import React from 'react'; | ||
import { NavigationContainer, ParamListBase } from '@react-navigation/native'; | ||
import { NativeStackNavigationProp, createNativeStackNavigator } from '@react-navigation/native-stack'; | ||
import { Button, View, ScrollView } from 'react-native'; | ||
|
||
type StackRouteParamList = { | ||
Home: undefined; | ||
SearchAlways: undefined; | ||
SearchCollapse: undefined; | ||
}; | ||
|
||
type NavigationProp<ParamList extends ParamListBase> = { | ||
navigation: NativeStackNavigationProp<ParamList>; | ||
}; | ||
|
||
type StackNavigationProp = NavigationProp<StackRouteParamList>; | ||
|
||
const SearchAlways = ({navigation}: StackNavigationProp) => { | ||
return <ScrollView contentInsetAdjustmentBehavior='automatic'> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's use braces Another thing - in general I prefer (and try to enforce) usage of regular functions over arrow functions, due to symbol hoisting. Therefore let's rewrite this a bit. |
||
<Button onPress={() => navigation.pop()} title='Go back'/> | ||
</ScrollView> | ||
} | ||
|
||
const SearchCollapse = ({navigation}: StackNavigationProp) => { | ||
return <ScrollView contentInsetAdjustmentBehavior='automatic'> | ||
<Button onPress={() => navigation.pop()} title='Go back'/> | ||
</ScrollView> | ||
} | ||
|
||
const Home = ({navigation}: StackNavigationProp) => | ||
<View> | ||
<Button onPress={() => navigation.push("SearchAlways")} title='showAsAction=always'/> | ||
<Button onPress={() => navigation.push("SearchCollapse")} title='showAsAction=collapse'/> | ||
</View> | ||
|
||
const Stack = createNativeStackNavigator<StackRouteParamList>(); | ||
|
||
export default function App() { | ||
return ( | ||
<NavigationContainer> | ||
<Stack.Navigator> | ||
<Stack.Screen | ||
name="Home" | ||
component={Home} | ||
/> | ||
<Stack.Screen | ||
name="SearchAlways" | ||
component={SearchAlways} | ||
options={{ | ||
title: "showAsAction=default", | ||
headerSearchBarOptions: {} | ||
}} | ||
/> | ||
<Stack.Screen | ||
name="SearchCollapse" | ||
component={SearchCollapse} | ||
options={{ | ||
title: "showAsAction=collapse", | ||
headerSearchBarOptions: { | ||
showAsAction: "collapse" | ||
} | ||
}} | ||
/> | ||
</Stack.Navigator> | ||
</NavigationContainer> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,8 @@ type SearchBarPlacement = 'automatic' | 'inline' | 'stacked'; | |
|
||
type AutoCapitalizeType = 'none' | 'words' | 'sentences' | 'characters'; | ||
|
||
type SearchShowAsAction = 'default' | 'always' | 'collapse'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need |
||
|
||
export interface NativeProps extends ViewProps { | ||
onSearchFocus?: DirectEventHandler<SearchBarEvent> | null; | ||
onSearchBlur?: DirectEventHandler<SearchBarEvent> | null; | ||
|
@@ -43,6 +45,7 @@ export interface NativeProps extends ViewProps { | |
|
||
// Android only | ||
disableBackButtonOverride?: boolean; | ||
showAsAction?: WithDefault<SearchShowAsAction, 'default'> | ||
// TODO: consider creating enum here | ||
inputType?: string; | ||
onClose?: DirectEventHandler<SearchBarEvent> | null; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,6 +80,7 @@ export type HeaderSubviewTypes = | |
| 'left' | ||
| 'center' | ||
| 'searchBar'; | ||
export type SearchShowAsAction = 'default' | 'always' | 'collapse'; | ||
|
||
export type HeaderHeightChangeEventType = { | ||
headerHeight: number; | ||
|
@@ -733,6 +734,16 @@ export interface SearchBarProps { | |
* @platform ios | ||
*/ | ||
tintColor?: ColorValue; | ||
/** | ||
* @platform android | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This annotation should be placed in the end of the comment block. No technical reason, just to keep the convention. |
||
* | ||
* Controls the behavior of SearchView on the Toolbar/ActionBar. When set to `collapse`, | ||
* clicking 'Up' for the first time takes the focus from the SearchView, and current screen | ||
* is popped on the second click. When set to `default` or `always`, current screen is dismissed immediately. | ||
* This is equivalent to setting android's `showAsAction` to `SHOW_AS_ACTION_ALWAYS` | ||
* or `SHOW_AS_ACTION_ALWAYS | SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW`. | ||
*/ | ||
showAsAction?: SearchShowAsAction; | ||
/** | ||
* The text to be used instead of default `Cancel` button text | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldShowSearchBar()
was effectively true when config fortype==SEARCH_BAR
existed, so this refactor should not change the behaviourThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ok, thanks for the note.