MM-18455 Replace search bar component (#3998)

* Initiated basic migration to react-native-elements

- Search bar receives all right props without custom animations being necessary
- Need to test on different layouts and cases
- May be possible to unify android and iOS components. Attempt later

* Removing separated android/ios files

- Adding implementation of android searchbar 
- updating test snapshots

* WIP: Integrating leftComponent and adding focus animations

* Adding animations to left component on focus and blur events

* Removing repeated styles and useless props

* Updating snapshots

* Ensuring clearIcon renders when tapping search suggestions

- Also makes sure it prioritizes custom color attributes over default values for the search input

* Refactoring styles, removing clear icon animations

- Also removed useless ternary operations since the library itself already checks whether the search input is empty

* Replacing icon components with props whenever possible

- Removing useless styles

* memoizing styles, adding default props/values

* Memoize searchBarStyle with params

* Fixing backgroundColor

* Removing minWidth for cancel button

* Fixing styling issues on both iOS and android

* Updating snapshots post merge

* Making sure showCancel can be controlled by props

* Updating snapshots

* Fix clipped edges on iOS & cancel icon on Android

* Fixed radius, memoized styles

* Fixing styling issues found on Android

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Andre Vasconcelos 2020-03-27 04:53:25 +09:00 committed by GitHub
parent 55cac2e062
commit a114b19ecc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 803 additions and 1076 deletions

View file

@ -20,17 +20,23 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
Array [
Object {
"backgroundColor": "rgba(61,60,64,0.2)",
"height": 50,
"paddingLeft": 8,
"paddingVertical": 5,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -40,18 +46,20 @@ exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onAnimationComplete={[Function]}
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.8)"
titleCancelColor="#3d3c40"

View file

@ -20,7 +20,6 @@ export default class EmojiPicker extends EmojiPickerBase {
backgroundColor: theme.centerChannelBg,
color: theme.centerChannelColor,
fontSize: 13,
marginBottom: -3,
};
return (

View file

@ -506,6 +506,12 @@ export const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
searchBar: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
paddingVertical: 5,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
height: 50,
},
sectionList: {
...Platform.select({

View file

@ -1,276 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
InteractionManager,
Keyboard,
TextInput,
TouchableWithoutFeedback,
StyleSheet,
View,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import CustomPropTypes from 'app/constants/custom_prop_types';
import {changeOpacity} from 'app/utils/theme';
export default class SearchBarAndroid extends PureComponent {
static propTypes = {
autoFocus: PropTypes.bool,
backArrowSize: PropTypes.number,
deleteIconSize: PropTypes.number,
searchIconSize: PropTypes.number,
onCancelButtonPress: PropTypes.func,
onChangeText: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onSearchButtonPress: PropTypes.func,
onSelectionChange: PropTypes.func,
backgroundColor: PropTypes.string,
selectionColor: PropTypes.string,
placeholderTextColor: PropTypes.string,
titleCancelColor: PropTypes.string,
tintColorSearch: PropTypes.string,
tintColorDelete: PropTypes.string,
inputStyle: CustomPropTypes.Style,
placeholder: PropTypes.string,
returnKeyType: PropTypes.string,
keyboardType: PropTypes.string,
autoCapitalize: PropTypes.string,
inputHeight: PropTypes.number,
inputBorderRadius: PropTypes.number,
blurOnSubmit: PropTypes.bool,
showArrow: PropTypes.bool,
value: PropTypes.string,
containerStyle: CustomPropTypes.Style,
leftComponent: PropTypes.element,
keyboardAppearance: PropTypes.string,
};
static defaultProps = {
backArrowSize: 24,
deleteIconSize: 20,
searchIconSize: 24,
blurOnSubmit: true,
placeholder: 'Search',
showCancelButton: true,
showArrow: true,
placeholderTextColor: changeOpacity('#000', 0.5),
containerStyle: {},
onSearchButtonPress: () => true,
onCancelButtonPress: () => true,
onChangeText: () => true,
onFocus: () => true,
onBlur: () => true,
onSelectionChange: () => true,
value: '',
leftComponent: null,
};
constructor(props) {
super(props);
this.state = {
isFocused: false,
refocusInput: true,
};
}
setInputRef = (ref) => {
this.inputRef = ref;
}
cancel = () => {
this.onCancelButtonPress();
};
onBlur = () => {
this.props.onBlur();
};
onSearchButtonPress = () => {
const {value} = this.props;
this.setState({refocusInput: false}, () => {
if (value) {
this.props.onSearchButtonPress(value);
}
this.setState({refocusInput: true});
});
};
onCancelButtonPress = () => {
Keyboard.dismiss();
InteractionManager.runAfterInteractions(() => {
this.setState({
isFocused: false,
}, () => {
this.props.onCancelButtonPress();
});
});
};
onClearPress = () => {
this.onChangeText('');
};
onChangeText = (value) => {
this.props.onChangeText(value);
};
onSelectionChange = (event) => {
this.props.onSelectionChange(event);
};
onFocus = () => {
this.setState({isFocused: true});
this.props.onFocus();
};
blur = () => {
this.inputRef.blur();
};
focus = () => {
this.inputRef.focus();
};
render() {
const {
autoCapitalize,
backArrowSize,
deleteIconSize,
searchIconSize,
backgroundColor,
blurOnSubmit,
inputHeight,
inputStyle,
keyboardType,
placeholder,
placeholderTextColor,
selectionColor,
returnKeyType,
titleCancelColor,
tintColorDelete,
tintColorSearch,
containerStyle,
value,
showArrow,
keyboardAppearance,
} = this.props;
const {isFocused} = this.state;
const {
backgroundColor: bgColor, //eslint-disable-line no-unused-vars
...otherStyles
} = inputStyle;
const inputNoBackground = otherStyles;
let inputColor = styles.searchBarInput.backgroundColor;
if (inputStyle) {
inputColor = inputStyle.backgroundColor;
} else {
inputNoBackground.backgroundColor = '#fff';
}
return (
<View
style={[
styles.container,
containerStyle,
{height: inputHeight},
backgroundColor && {backgroundColor},
]}
>
{!isFocused && this.props.leftComponent}
<View
style={[
styles.searchBar,
{
backgroundColor: inputColor,
height: inputHeight,
paddingLeft: 7,
},
]}
>
{isFocused && showArrow ?
<TouchableWithoutFeedback
onPress={this.onCancelButtonPress}
style={{paddingRight: 15}}
>
<Icon
name='arrow-back'
size={backArrowSize}
color={titleCancelColor || placeholderTextColor}
/>
</TouchableWithoutFeedback> :
<Icon
name={'search'}
size={searchIconSize}
color={tintColorSearch || placeholderTextColor}
/>
}
<TextInput
ref={this.setInputRef}
blurOnSubmit={blurOnSubmit}
refocusInput={this.state.refocusInput}
value={this.props.value}
autoCapitalize={autoCapitalize}
autoCorrect={false}
returnKeyType={returnKeyType || 'search'}
keyboardType={keyboardType || 'default'}
onFocus={this.onFocus}
onBlur={this.onBlur}
onChangeText={this.onChangeText}
onSubmitEditing={this.onSearchButtonPress}
onSelectionChange={this.onSelectionChange}
placeholder={placeholder}
placeholderTextColor={placeholderTextColor}
selectionColor={selectionColor}
underlineColorAndroid='transparent'
disableFullscreenUI={true}
keyboardAppearance={keyboardAppearance}
style={[
styles.searchBarInput,
inputNoBackground,
]}
/>
{isFocused && value ?
<TouchableWithoutFeedback onPress={this.onClearPress}>
<Icon
style={[{paddingRight: 7}]}
name='close'
size={deleteIconSize}
color={tintColorDelete || placeholderTextColor}
/>
</TouchableWithoutFeedback> : null
}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'grey',
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
},
searchBar: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
searchBarInput: {
flex: 1,
fontWeight: 'normal',
textAlignVertical: 'center',
fontSize: 15,
includeFontPadding: true,
},
searchBarBlurredInput: {
padding: 0,
},
});

View file

@ -1,126 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import PropTypes from 'prop-types';
import React, {PureComponent} from 'react';
import {InteractionManager, Keyboard} from 'react-native';
import CustomPropTypes from 'app/constants/custom_prop_types';
import Search from './search_box';
export default class SearchBarIos extends PureComponent {
static propTypes = {
onCancelButtonPress: PropTypes.func,
onChangeText: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onSearchButtonPress: PropTypes.func,
onSelectionChange: PropTypes.func,
backgroundColor: PropTypes.string,
placeholderTextColor: PropTypes.string,
titleCancelColor: PropTypes.string,
tintColorSearch: PropTypes.string,
tintColorDelete: PropTypes.string,
cancelButtonStyle: CustomPropTypes.Style,
inputStyle: CustomPropTypes.Style,
placeholder: PropTypes.string,
cancelTitle: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
returnKeyType: PropTypes.string,
keyboardType: PropTypes.string,
autoCapitalize: PropTypes.string,
inputHeight: PropTypes.number,
inputBorderRadius: PropTypes.number,
blurOnSubmit: PropTypes.bool,
value: PropTypes.string,
leftComponent: PropTypes.element,
searchIconCollapsedMargin: PropTypes.number,
searchIconExpandedMargin: PropTypes.number,
keyboardAppearance: PropTypes.string,
onAnimationComplete: PropTypes.func,
};
static defaultProps = {
onSearchButtonPress: () => true,
onCancelButtonPress: () => true,
onChangeText: () => true,
onFocus: () => true,
onBlur: () => true,
onSelectionChange: () => true,
blurOnSubmit: true,
leftComponent: null,
searchIconCollapsedMargin: 10,
searchIconExpandedMargin: 10,
};
setSearchRef = (ref) => {
this.searchRef = ref;
}
cancel = () => {
this.searchRef.onCancel();
};
onBlur = () => {
this.props.onBlur();
};
onCancel = () => {
Keyboard.dismiss();
InteractionManager.runAfterInteractions(() => {
this.props.onCancelButtonPress();
});
};
onChangeText = (text) => {
this.props.onChangeText(text);
};
onDelete = () => {
this.props.onChangeText('', true);
};
onFocus = () => {
this.props.onFocus();
};
onSearch = (text) => {
if (text) {
this.props.onSearchButtonPress(text);
}
};
onSelectionChange = (event) => {
this.props.onSelectionChange(event);
};
blur = () => {
this.searchRef.blur();
};
focus = () => {
this.searchRef.focus();
};
render() {
return (
<Search
{...this.props}
ref={this.setSearchRef}
placeholderCollapsedMargin={33}
placeholderExpandedMargin={33}
shadowVisible={false}
onCancel={this.onCancel}
onChangeText={this.onChangeText}
onFocus={this.onFocus}
onBlur={this.onBlur}
onSearch={this.onSearch}
onSelectionChange={this.onSelectionChange}
onDelete={this.onDelete}
/>
);
}
}

View file

@ -0,0 +1,453 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Animated,
InteractionManager,
Keyboard,
TouchableWithoutFeedback,
StyleSheet,
View,
Platform,
} from 'react-native';
import {intlShape} from 'react-intl';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import EvilIcon from 'react-native-vector-icons/EvilIcons';
import {SearchBar} from 'react-native-elements';
import {memoizeResult} from 'mattermost-redux/utils/helpers';
import CustomPropTypes from 'app/constants/custom_prop_types';
export default class Search extends PureComponent {
static propTypes = {
onBlur: PropTypes.func,
onFocus: PropTypes.func,
onSearchButtonPress: PropTypes.func,
onChangeText: PropTypes.func,
onCancelButtonPress: PropTypes.func,
onSelectionChange: PropTypes.func,
backgroundColor: PropTypes.string,
placeholderTextColor: PropTypes.string,
titleCancelColor: PropTypes.string,
tintColorSearch: PropTypes.string,
tintColorDelete: PropTypes.string,
selectionColor: PropTypes.string,
inputStyle: CustomPropTypes.Style,
cancelButtonStyle: CustomPropTypes.Style,
autoFocus: PropTypes.bool,
placeholder: PropTypes.string,
cancelTitle: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
returnKeyType: PropTypes.string,
keyboardType: PropTypes.string,
autoCapitalize: PropTypes.string,
inputHeight: PropTypes.number,
editable: PropTypes.bool,
blurOnSubmit: PropTypes.bool,
keyboardShouldPersist: PropTypes.bool,
value: PropTypes.string,
keyboardAppearance: PropTypes.string,
showArrow: PropTypes.bool,
searchBarRightMargin: PropTypes.number,
leftComponent: PropTypes.element,
searchIconSize: PropTypes.number,
backArrowSize: PropTypes.number,
deleteIconSize: PropTypes.number,
showCancel: PropTypes.bool,
containerHeight: PropTypes.number,
};
static contextTypes = {
intl: intlShape,
};
static defaultProps = {
onSelectionChange: () => true,
onBlur: () => true,
editable: true,
blurOnSubmit: false,
keyboardShouldPersist: false,
placeholderTextColor: 'grey',
value: '',
showArrow: false,
showCancel: true,
searchIconSize: 24,
backArrowSize: 24,
deleteIconSize: 20,
searchBarRightMargin: 0,
returnKeyType: 'search',
keyboardType: 'default',
containerHeight: 40,
};
constructor(props) {
super(props);
this.state = {
leftComponentWidth: 0,
};
this.leftComponentAnimated = new Animated.Value(0);
this.searchContainerAnimated = new Animated.Value(0);
}
setSearchContainerRef = (ref) => {
this.searchContainerRef = ref;
}
setInputKeywordRef = (ref) => {
this.inputKeywordRef = ref;
}
blur = () => {
this.inputKeywordRef.blur();
};
focus = () => {
this.inputKeywordRef.focus();
};
onBlur = async () => {
if (this.props.leftComponent) {
await this.collapseAnimation();
}
this.props.onBlur();
};
onLeftComponentLayout = (event) => {
const leftComponentWidth = event.nativeEvent.layout.width;
this.setState({leftComponentWidth});
};
onSearch = async () => {
if (this.props.keyboardShouldPersist === false) {
await Keyboard.dismiss();
}
this.props.onSearchButtonPress(this.props.value);
};
onChangeText = (text) => {
if (this.props.onChangeText) {
this.props.onChangeText(text);
}
};
onFocus = () => {
InteractionManager.runAfterInteractions(async () => {
if (this.props.leftComponent) {
await this.expandAnimation();
}
if (this.props.onFocus) {
this.props.onFocus();
}
});
};
onClear = () => {
this.focus();
this.props.onChangeText('', true);
};
onCancel = () => {
Keyboard.dismiss();
InteractionManager.runAfterInteractions(() => {
if (this.props.onCancelButtonPress) {
this.props.onCancelButtonPress();
}
});
};
onSelectionChange = (event) => {
this.props.onSelectionChange(event);
};
expandAnimation = () => {
return new Promise((resolve) => {
Animated.parallel([
Animated.timing(
this.leftComponentAnimated,
{
toValue: 100,
duration: 200,
},
),
Animated.timing(
this.searchContainerAnimated,
{
toValue: this.state.leftComponentWidth * -1,
duration: 200,
},
),
]).start(resolve);
});
}
collapseAnimation = () => {
return new Promise((resolve) => {
Animated.parallel([
Animated.timing(
this.leftComponentAnimated,
{
toValue: 0,
duration: 200,
},
),
Animated.timing(
this.searchContainerAnimated,
{
toValue: 0,
duration: 200,
},
),
]).start(resolve);
});
}
render() {
const {backgroundColor, inputHeight, inputStyle, placeholderTextColor, tintColorSearch, cancelButtonStyle, tintColorDelete, titleCancelColor, searchBarRightMargin, containerHeight} = this.props;
const searchBarStyle = getSearchBarStyle(
backgroundColor,
cancelButtonStyle,
inputHeight,
inputStyle,
placeholderTextColor,
tintColorDelete,
tintColorSearch,
titleCancelColor,
searchBarRightMargin,
containerHeight,
);
const {intl} = this.context;
let clearIcon = null;
let searchIcon = null;
let cancelIcon = null;
if (Platform.OS === 'ios') {
clearIcon = {
type: 'ionicon',
name: 'ios-close-circle',
size: 17,
color: searchBarStyle.clearIconColorIos,
};
searchIcon = (
<EvilIcon
name='search'
size={24}
style={[
styles.fullWidth,
searchBarStyle.searchIcon,
]}
/>
);
} else {
searchIcon = this.props.showArrow ?
(
<TouchableWithoutFeedback onPress={this.onCancel}>
<MaterialIcon
name='arrow-back'
size={this.props.backArrowSize}
color={searchBarStyle.clearIconColorAndroid}
/>
</TouchableWithoutFeedback>
) :
{
type: 'material',
size: this.props.searchIconSize,
color: searchBarStyle.searchIconColor,
name: 'search',
};
// Making sure the icon won't change depending on whether the input is in focus on Android devices
cancelIcon = {
type: 'material',
size: 25,
color: searchBarStyle.clearIconColorAndroid,
name: 'arrow-back',
};
clearIcon = {
type: 'material',
size: this.props.deleteIconSize,
color: searchBarStyle.clearIconColorAndroid,
name: 'close',
};
}
return (
<View style={searchBarStyle.container}>
{((this.props.leftComponent) ?
<Animated.View
style={{
right: this.leftComponentAnimated,
}}
onLayout={this.onLeftComponentLayout}
>
{this.props.leftComponent}
</Animated.View> :
null
)}
<Animated.View
style={[
styles.fullWidth,
searchBarStyle.searchBarWrapper,
{
marginLeft: this.searchContainerAnimated,
},
]}
>
<SearchBar
ref={this.setInputKeywordRef}
containerStyle={{
...styles.searchContainer,
...styles.fullWidth,
...searchBarStyle.searchBarContainer,
}}
inputContainerStyle={{
...styles.inputContainer,
...searchBarStyle.inputContainer,
}}
inputStyle={{
...styles.text,
...styles.inputMargin,
...searchBarStyle.inputStyle,
}}
leftIconContainerStyle={styles.leftIcon}
placeholder={this.props.placeholder || intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
placeholderTextColor={this.props.placeholderTextColor}
selectionColor={this.props.selectionColor}
autoCorrect={false}
blurOnSubmit={this.props.blurOnSubmit}
editable={this.props.editable}
cancelButtonTitle={this.props.cancelTitle || intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
cancelButtonProps={{
buttonTextStyle: {
...styles.text,
...searchBarStyle.cancelButtonText,
},
}}
onChangeText={this.onChangeText}
onSubmitEditing={this.onSearch}
returnKeyType={this.props.returnKeyType}
keyboardType={this.props.keyboardType}
autoCapitalize={this.props.autoCapitalize}
onBlur={this.onBlur}
onFocus={this.onFocus}
onCancel={this.onCancel}
onClear={this.onClear}
onSelectionChange={this.onSelectionChange}
underlineColorAndroid='transparent'
enablesReturnKeyAutomatically={true}
keyboardAppearance={this.props.keyboardAppearance}
autoFocus={this.props.autoFocus}
showCancel={this.props.showCancel}
value={this.props.value}
platform={Platform.OS}
clearIcon={clearIcon}
searchIcon={searchIcon}
cancelIcon={cancelIcon}
/>
</Animated.View>
</View>
);
}
}
const getSearchBarStyle = memoizeResult((
backgroundColor,
cancelButtonStyle,
inputHeight,
inputStyle,
placeholderTextColor,
tintColorDelete,
tintColorSearch,
titleCancelColor,
searchBarRightMargin,
containerHeight,
) => ({
cancelButtonText: {
...cancelButtonStyle,
color: titleCancelColor,
},
container: {
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
height: containerHeight,
flex: 1,
},
clearIconColorIos: tintColorDelete || styles.defaultColor.color,
clearIconColorAndroid: titleCancelColor || placeholderTextColor,
inputStyle: {
...inputStyle,
backgroundColor: 'transparent',
height: inputHeight,
},
inputContainer: {
backgroundColor: inputStyle.backgroundColor,
height: inputHeight,
},
searchBarWrapper: {
marginRight: searchBarRightMargin,
height: Platform.select({
ios: inputHeight || containerHeight - 10,
android: inputHeight,
}),
},
searchBarContainer: {
backgroundColor,
},
searchIcon: {
color: tintColorSearch || placeholderTextColor,
top: 10,
},
searchIconColor: tintColorSearch || placeholderTextColor,
}));
const styles = StyleSheet.create({
defaultColor: {
color: 'grey',
},
fullWidth: {
flex: 1,
},
inputContainer: {
marginLeft: 0,
borderRadius: Platform.select({
ios: 2,
android: 0,
}),
},
inputMargin: {
marginLeft: 4,
paddingTop: 0,
marginTop: Platform.select({
ios: 0,
android: 8,
}),
},
leftIcon: {
marginLeft: 4,
},
searchContainer: {
paddingTop: 0,
paddingBottom: 0,
marginLeft: 0,
},
text: {
fontSize: Platform.select({
ios: 14,
android: 15,
}),
color: '#fff',
},
});

View file

@ -1,578 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
Animated,
Dimensions,
InteractionManager,
Keyboard,
Text,
TextInput,
TouchableWithoutFeedback,
StyleSheet,
View,
} from 'react-native';
import EvilIcon from 'react-native-vector-icons/EvilIcons';
import IonIcon from 'react-native-vector-icons/Ionicons';
import CustomPropTypes from 'app/constants/custom_prop_types';
import {emptyFunction} from 'app/utils/general';
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
const AnimatedIonIcon = Animated.createAnimatedComponent(IonIcon);
const AnimatedEvilcon = Animated.createAnimatedComponent(EvilIcon);
const containerHeight = 40;
const middleHeight = 20;
export default class Search extends Component {
static propTypes = {
onBlur: PropTypes.func,
onFocus: PropTypes.func,
onSearch: PropTypes.func,
onChangeText: PropTypes.func,
onCancel: PropTypes.func,
onDelete: PropTypes.func,
onSelectionChange: PropTypes.func,
backgroundColor: PropTypes.string,
placeholderTextColor: PropTypes.string,
titleCancelColor: PropTypes.string,
tintColorSearch: PropTypes.string,
tintColorDelete: PropTypes.string,
selectionColor: PropTypes.string,
inputStyle: CustomPropTypes.Style,
onLayout: PropTypes.func,
cancelButtonStyle: CustomPropTypes.Style,
autoFocus: PropTypes.bool,
placeholder: PropTypes.string,
cancelTitle: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
iconDelete: PropTypes.object,
iconSearch: PropTypes.object,
returnKeyType: PropTypes.string,
keyboardType: PropTypes.string,
autoCapitalize: PropTypes.string,
inputHeight: PropTypes.number,
inputBorderRadius: PropTypes.number,
contentWidth: PropTypes.number,
middleWidth: PropTypes.number,
editable: PropTypes.bool,
blurOnSubmit: PropTypes.bool,
keyboardShouldPersist: PropTypes.bool,
value: PropTypes.string,
positionRightDelete: PropTypes.number,
searchIconCollapsedMargin: PropTypes.number,
searchIconExpandedMargin: PropTypes.number,
placeholderCollapsedMargin: PropTypes.number,
placeholderExpandedMargin: PropTypes.number,
shadowOffsetHeightCollapsed: PropTypes.number,
shadowOffsetHeightExpanded: PropTypes.number,
shadowOffsetWidth: PropTypes.number,
shadowColor: PropTypes.string,
shadowOpacityCollapsed: PropTypes.number,
shadowOpacityExpanded: PropTypes.number,
shadowRadius: PropTypes.number,
shadowVisible: PropTypes.bool,
leftComponent: PropTypes.element,
inputCollapsedMargin: PropTypes.number,
keyboardAppearance: PropTypes.string,
onAnimationComplete: PropTypes.func,
};
static defaultProps = {
onSelectionChange: () => true,
onBlur: () => true,
editable: true,
blurOnSubmit: false,
keyboardShouldPersist: false,
placeholderTextColor: 'grey',
searchIconCollapsedMargin: 25,
searchIconExpandedMargin: 10,
placeholderCollapsedMargin: 15,
placeholderExpandedMargin: 20,
shadowOffsetWidth: 0,
shadowOffsetHeightCollapsed: 2,
shadowOffsetHeightExpanded: 4,
shadowColor: '#000',
shadowOpacityCollapsed: 0.12,
shadowOpacityExpanded: 0.24,
shadowRadius: 4,
shadowVisible: false,
value: '',
leftComponent: null,
inputCollapsedMargin: 10,
onAnimationComplete: emptyFunction,
};
constructor(props) {
super(props);
this.state = {
expanded: false,
leftComponentWidth: 0,
};
const {width} = Dimensions.get('window');
this.contentWidth = width;
this.middleWidth = width / 2;
this.iconSearchAnimated = new Animated.Value(this.props.searchIconCollapsedMargin);
this.iconDeleteAnimated = new Animated.Value(0);
this.leftComponentAnimated = new Animated.Value(0);
this.inputFocusAnimated = new Animated.Value(0);
this.inputFocusWidthAnimated = new Animated.Value(this.contentWidth - 10);
this.inputFocusPlaceholderAnimated = new Animated.Value(this.props.placeholderCollapsedMargin);
this.btnCancelAnimated = new Animated.Value(this.contentWidth);
this.shadowOpacityAnimated = new Animated.Value(this.props.shadowOpacityCollapsed);
this.placeholder = this.props.placeholder || 'Search';
this.cancelTitle = this.props.cancelTitle || 'Cancel';
this.shadowHeight = this.props.shadowOffsetHeightCollapsed;
}
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
if (this.props.value) {
this.iconDeleteAnimated = new Animated.Value(1);
} else {
this.iconDeleteAnimated = new Animated.Value(0);
}
}
}
setSearchContainerRef = (ref) => {
this.searchContainerRef = ref;
}
setInputKeywordRef = (ref) => {
this.inputKeywordRef = ref;
}
blur = () => {
this.inputKeywordRef.getNode().blur();
this.setState({expanded: false});
this.collapseAnimation();
};
focus = () => {
InteractionManager.runAfterInteractions(() => {
const input = this.inputKeywordRef.getNode();
if (!input.isFocused()) {
input.focus();
}
});
};
onBlur = () => {
this.props.onBlur();
};
onLayout = (event) => {
const contentWidth = event.nativeEvent.layout.width;
this.contentWidth = contentWidth;
this.middleWidth = contentWidth / 2;
if (this.state.expanded) {
this.expandAnimation();
} else {
this.collapseAnimation();
}
};
onLeftComponentLayout = (event) => {
const leftComponentWidth = event.nativeEvent.layout.width;
this.setState({leftComponentWidth});
};
onSearch = async () => {
if (this.props.keyboardShouldPersist === false) {
await Keyboard.dismiss();
}
if (this.props.onSearch) {
this.props.onSearch(this.props.value);
}
};
onChangeText = (text) => {
Animated.timing(
this.iconDeleteAnimated,
{
toValue: (text.length > 0) ? 1 : 0,
duration: 200,
useNativeDriver: true,
},
).start();
if (this.props.onChangeText) {
this.props.onChangeText(text);
}
};
onFocus = () => {
InteractionManager.runAfterInteractions(async () => {
this.setState({expanded: true});
await this.expandAnimation();
if (this.props.onFocus) {
this.props.onFocus(this.props.value);
}
});
};
onDelete = () => {
Animated.timing(
this.iconDeleteAnimated,
{
toValue: 0,
duration: 200,
useNativeDriver: true,
},
).start();
this.focus();
if (this.props.onDelete) {
this.props.onDelete();
}
};
onCancel = async () => {
this.setState({expanded: false});
await this.collapseAnimation(true);
if (this.props.onCancel) {
this.props.onCancel();
}
};
onSelectionChange = (event) => {
this.props.onSelectionChange(event);
};
expandAnimation = () => {
return new Promise((resolve) => {
Animated.parallel([
Animated.timing(
this.inputFocusWidthAnimated,
{
toValue: this.contentWidth - 90,
duration: 200,
},
),
Animated.timing(
this.inputFocusAnimated,
{
toValue: this.state.leftComponentWidth,
duration: 200,
},
),
Animated.timing(
this.leftComponentAnimated,
{
toValue: this.contentWidth,
duration: 200,
},
),
Animated.timing(
this.btnCancelAnimated,
{
toValue: this.state.leftComponentWidth ? 15 - this.state.leftComponentWidth : 5,
duration: 200,
},
),
Animated.timing(
this.inputFocusPlaceholderAnimated,
{
toValue: this.props.placeholderExpandedMargin,
duration: 200,
},
),
Animated.timing(
this.iconSearchAnimated,
{
toValue: this.props.searchIconExpandedMargin,
duration: 200,
},
),
Animated.timing(
this.iconDeleteAnimated,
{
toValue: (this.props.value.length > 0) ? 1 : 0,
duration: 200,
useNativeDriver: true,
},
),
Animated.timing(
this.shadowOpacityAnimated,
{
toValue: this.props.shadowOpacityExpanded,
duration: 200,
useNativeDriver: true,
},
),
]).start();
this.shadowHeight = this.props.shadowOffsetHeightExpanded;
resolve();
});
};
collapseAnimation = (isForceAnim = false) => {
return new Promise((resolve) => {
Animated.parallel([
((this.props.keyboardShouldPersist === false) ? Keyboard.dismiss() : null),
Animated.timing(
this.inputFocusWidthAnimated,
{
toValue: this.contentWidth - this.state.leftComponentWidth - this.props.inputCollapsedMargin,
duration: 200,
},
),
Animated.timing(
this.inputFocusAnimated,
{
toValue: 0,
duration: 200,
},
),
Animated.timing(
this.leftComponentAnimated,
{
toValue: 0,
duration: 200,
},
),
Animated.timing(
this.btnCancelAnimated,
{
toValue: this.contentWidth,
duration: 200,
},
),
((this.props.keyboardShouldPersist === false) ?
Animated.timing(
this.inputFocusPlaceholderAnimated,
{
toValue: this.props.placeholderCollapsedMargin,
duration: 200,
},
) : null),
((this.props.keyboardShouldPersist === false || isForceAnim === true) ?
Animated.timing(
this.iconSearchAnimated,
{
toValue: (this.props.searchIconCollapsedMargin + this.state.leftComponentWidth),
duration: 200,
},
) : null),
Animated.timing(
this.iconDeleteAnimated,
{
toValue: 0,
duration: 200,
useNativeDriver: true,
},
),
Animated.timing(
this.shadowOpacityAnimated,
{
toValue: this.props.shadowOpacityCollapsed,
duration: 200,
useNativeDriver: true,
},
),
]).start(({finished}) => this.props.onAnimationComplete(finished));
this.shadowHeight = this.props.shadowOffsetHeightCollapsed;
resolve();
});
};
render() {
const {backgroundColor, ...restOfInputPropStyles} = this.props.inputStyle;
return (
<Animated.View
ref={this.setSearchContainerRef}
style={[
styles.container,
this.props.backgroundColor && {backgroundColor: this.props.backgroundColor},
this.state.leftComponentWidth && {padding: 0},
]}
onLayout={this.onLayout}
>
{((this.props.leftComponent) ?
<Animated.View
style={{right: this.leftComponentAnimated}}
onLayout={this.onLeftComponentLayout}
>
{this.props.leftComponent}
</Animated.View> :
null
)}
<Animated.View style={{backgroundColor, right: this.inputFocusAnimated, borderRadius: 2}}>
<AnimatedTextInput
ref={this.setInputKeywordRef}
style={[
styles.input,
this.props.placeholderTextColor && {color: this.props.placeholderTextColor},
this.props.inputHeight && {height: this.props.inputHeight},
this.props.inputBorderRadius && {borderRadius: this.props.inputBorderRadius},
{
width: this.inputFocusWidthAnimated,
paddingLeft: this.inputFocusPlaceholderAnimated,
},
restOfInputPropStyles,
this.props.shadowVisible && {
shadowOffset: {width: this.props.shadowOffsetWidth, height: this.shadowHeight},
shadowColor: this.props.shadowColor,
shadowOpacity: this.shadowOpacityAnimated,
shadowRadius: this.props.shadowRadius,
},
]}
autoFocus={this.props.autoFocus}
editable={this.props.editable}
value={this.props.value}
onChangeText={this.onChangeText}
placeholder={this.placeholder}
placeholderTextColor={this.props.placeholderTextColor}
selectionColor={this.props.selectionColor}
onSubmitEditing={this.onSearch}
onSelectionChange={this.onSelectionChange}
autoCorrect={false}
blurOnSubmit={this.props.blurOnSubmit}
returnKeyType={this.props.returnKeyType || 'search'}
keyboardType={this.props.keyboardType || 'default'}
autoCapitalize={this.props.autoCapitalize}
onBlur={this.onBlur}
onFocus={this.onFocus}
underlineColorAndroid='transparent'
enablesReturnKeyAutomatically={true}
keyboardAppearance={this.props.keyboardAppearance}
/>
</Animated.View>
<TouchableWithoutFeedback onPress={this.onFocus}>
{((this.props.iconSearch) ?
<Animated.View
style={[
styles.iconSearch,
{left: this.iconSearchAnimated},
]}
>
{this.props.iconSearch}
</Animated.View> :
<AnimatedEvilcon
name='search'
size={24}
style={[
styles.iconSearch,
styles.iconSearchDefault,
this.props.tintColorSearch && {color: this.props.tintColorSearch},
{
left: this.iconSearchAnimated,
top: middleHeight - 10,
},
]}
/>
)}
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.onDelete}>
{((this.props.iconDelete) ?
<Animated.View
style={[
styles.iconDelete,
this.props.positionRightDelete && {right: this.props.positionRightDelete},
{opacity: this.iconDeleteAnimated},
]}
>
{this.props.iconDelete}
</Animated.View> :
<View style={[styles.iconDelete, this.props.inputHeight && {height: this.props.inputHeight}]}>
<AnimatedIonIcon
name='ios-close-circle'
size={17}
style={[
styles.iconDeleteDefault,
this.props.tintColorDelete && {color: this.props.tintColorDelete},
this.props.positionRightDelete && {right: this.props.positionRightDelete},
{
opacity: this.iconDeleteAnimated,
},
]}
/>
</View>
)}
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.onCancel}>
<Animated.View
style={[
styles.cancelButton,
this.props.cancelButtonStyle && this.props.cancelButtonStyle,
{left: this.btnCancelAnimated},
]}
>
<Text
style={[
styles.cancelButtonText,
this.props.titleCancelColor && {color: this.props.titleCancelColor},
this.props.cancelButtonStyle && this.props.cancelButtonStyle,
]}
>
{this.cancelTitle}
</Text>
</Animated.View>
</TouchableWithoutFeedback>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'grey',
height: containerHeight,
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
padding: 5,
},
input: {
height: containerHeight - 10,
paddingTop: 7,
paddingBottom: 5,
paddingRight: 32,
borderColor: '#444',
borderRadius: 5,
fontSize: 15,
},
iconSearch: {
flex: 1,
position: 'absolute',
},
iconSearchDefault: {
color: 'grey',
},
iconDelete: {
alignItems: 'flex-start',
justifyContent: 'center',
position: 'absolute',
paddingLeft: 1,
paddingTop: 3,
right: 80,
width: 25,
},
iconDeleteDefault: {
color: 'grey',
},
cancelButton: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent',
minWidth: 75,
height: 50,
},
cancelButtonText: {
fontSize: 14,
color: '#fff',
},
});

View file

@ -59,7 +59,7 @@ export default class ChannelsList extends PureComponent {
cancelSearch = () => {
if (this.searchBarRef) {
this.searchBarRef.cancel();
this.searchBarRef.onCancel();
}
};
@ -124,11 +124,6 @@ export default class ChannelsList extends PureComponent {
backgroundColor: changeOpacity(theme.sidebarHeaderTextColor, 0.2),
color: theme.sidebarHeaderTextColor,
fontSize: 15,
...Platform.select({
android: {
marginBottom: -5,
},
}),
};
const title = (
@ -137,7 +132,6 @@ export default class ChannelsList extends PureComponent {
ref={this.setSearchBarRef}
placeholder={intl.formatMessage({id: 'mobile.channel_drawer.search', defaultMessage: 'Jump to...'})}
cancelTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
inputCollapsedMargin={0}
backgroundColor='transparent'
inputHeight={33}
inputStyle={searchBarInput}
@ -150,16 +144,15 @@ export default class ChannelsList extends PureComponent {
onCancelButtonPress={this.onSearchCancel}
onChangeText={this.onSearch}
onFocus={this.onSearchFocused}
searchIconCollapsedMargin={5}
searchIconExpandedMargin={5}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
value={term}
showCancel={Platform.OS === 'android'}
leftComponentWidth={67}
leftComponent={(
<SwitchTeamsButton
onShowTeams={onShowTeams}
/>
)}
positionRightDelete={5}
/>
</View>
);
@ -228,10 +221,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
searchContainer: {
flex: 1,
paddingRight: 10,
flexDirection: 'row',
...Platform.select({
android: {
marginBottom: 1,
paddingRight: 17,
},
ios: {
marginBottom: 3,

View file

@ -306,11 +306,6 @@ export default class ChannelAddMembers extends PureComponent {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
color: theme.centerChannelColor,
fontSize: 15,
...Platform.select({
android: {
marginBottom: -5,
},
}),
};
let data;
@ -385,6 +380,12 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
searchBar: {
marginVertical: 5,
height: 38,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
},
loadingContainer: {
alignItems: 'center',

View file

@ -7,17 +7,23 @@ exports[`ChannelMembers should match snapshot 1`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -27,17 +33,20 @@ exports[`ChannelMembers should match snapshot 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -338,11 +338,7 @@ export default class ChannelMembers extends PureComponent {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
color: theme.centerChannelColor,
fontSize: 15,
...Platform.select({
android: {
marginBottom: -5,
},
}),
};
let data;
@ -413,6 +409,12 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
searchBar: {
marginVertical: 5,
height: 38,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
},
loadingContainer: {
alignItems: 'center',

View file

@ -3,6 +3,8 @@
import React from 'react';
import {Platform} from 'react-native';
import {ThemeProvider} from 'react-native-elements';
import {Navigation} from 'react-native-navigation';
import {gestureHandlerRootHOC} from 'react-native-gesture-handler';
@ -16,9 +18,11 @@ export function registerScreens(store, Provider) {
const wrapper = (Comp) => (props) => ( // eslint-disable-line react/display-name
<Provider store={store}>
<Root>
<Comp {...props}/>
</Root>
<ThemeProvider>
<Root>
<Comp {...props}/>
</Root>
</ThemeProvider>
</Provider>
);

View file

@ -8,16 +8,22 @@ exports[`MoreChannels should match snapshot 1`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -27,16 +33,19 @@ exports[`MoreChannels should match snapshot 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -423,11 +423,6 @@ export default class MoreChannels extends PureComponent {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
color: theme.centerChannelColor,
fontSize: 15,
...Platform.select({
android: {
marginBottom: -5,
},
}),
};
let activeChannels = channels;
@ -508,6 +503,12 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
return {
searchBar: {
marginVertical: 5,
height: 38,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
},
loadingContainer: {
alignItems: 'center',

View file

@ -424,11 +424,6 @@ export default class MoreDirectMessages extends PureComponent {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
color: theme.centerChannelColor,
fontSize: 15,
...Platform.select({
android: {
marginBottom: -5,
},
}),
};
let data;
@ -508,6 +503,12 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
searchBar: {
marginVertical: 5,
height: 38,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
},
loadingContainer: {
alignItems: 'center',

View file

@ -729,10 +729,12 @@ export default class Search extends PureComponent {
onCancelButtonPress={this.cancelSearch}
onSelectionChange={this.handleSelectionChange}
autoCapitalize='none'
showArrow={true}
value={value}
containerStyle={style.searchBarContainer}
backArrowSize={28}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
containerHeight={33}
/>
</View>
<SectionList
@ -773,6 +775,8 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
ios: {
height: 44,
paddingLeft: 8,
paddingBottom: 10,
},
}),
},

View file

@ -13,17 +13,23 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -33,17 +39,20 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -109,17 +118,23 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -129,17 +144,20 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -205,17 +223,23 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -225,17 +249,20 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -308,17 +335,23 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -328,17 +361,20 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -404,17 +440,23 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -424,17 +466,20 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -500,17 +545,23 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
style={
Array [
Object {
"height": 38,
"marginVertical": 5,
"paddingLeft": 8,
},
null,
]
}
>
<SearchBarIos
<Search
autoCapitalize="none"
backArrowSize={24}
backgroundColor="transparent"
blurOnSubmit={true}
blurOnSubmit={false}
cancelTitle="Cancel"
containerHeight={40}
deleteIconSize={20}
editable={true}
inputHeight={33}
inputStyle={
Object {
@ -520,17 +571,20 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
}
}
keyboardAppearance="light"
leftComponent={null}
keyboardShouldPersist={false}
keyboardType="default"
onBlur={[Function]}
onCancelButtonPress={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
returnKeyType="search"
searchBarRightMargin={0}
searchIconSize={24}
showArrow={false}
showCancel={true}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -300,11 +300,6 @@ export default class SelectorScreen extends PureComponent {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
color: theme.centerChannelColor,
fontSize: 15,
...Platform.select({
android: {
marginBottom: -5,
},
}),
};
let rowComponent;
@ -366,6 +361,12 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
},
searchBar: {
marginVertical: 5,
height: 38,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
},
loadingContainer: {
alignItems: 'center',

View file

@ -157,6 +157,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
header: {
backgroundColor: theme.sidebarHeaderBg,
height: 38,
width: '100%',
...Platform.select({
android: {
@ -165,6 +166,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
ios: {
height: 44,
paddingLeft: 8,
},
}),
},

102
package-lock.json generated
View file

@ -5302,6 +5302,23 @@
"csstype": "^2.2.0"
}
},
"@types/react-native": {
"version": "0.61.23",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.61.23.tgz",
"integrity": "sha512-upHmySsrVBDBokWWhYIKkKnpvadsHdioSjbBTu4xl7fjN0yb94KR5ngUOBXsyqAYqQzF+hP6qpvobG9M7Jr6hw==",
"requires": {
"@types/react": "*"
}
},
"@types/react-native-vector-icons": {
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.5.tgz",
"integrity": "sha512-JBpcjWQE4n0GlE0p6HpDDclT+uXpFC453T5k4h+B38q0utlGJhvgNr8899BoJGc1xOktA2cgqFKmFMJd0h7YaA==",
"requires": {
"@types/react": "*",
"@types/react-native": "*"
}
},
"@types/stack-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@ -6804,6 +6821,15 @@
"object-visit": "^1.0.0"
}
},
"color": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
"integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -6817,6 +6843,15 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@ -9742,7 +9777,8 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"optional": true
}
}
@ -18295,6 +18331,11 @@
"is-wsl": "^1.1.0"
}
},
"opencollective-postinstall": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
"integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw=="
},
"opn": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
@ -19215,6 +19256,36 @@
"resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-3.3.0.tgz",
"integrity": "sha512-h/O8USFx1TWQT7Sx5Qj1OVpADabHawckVPDrEiGUf6qCJF2h5kKZs3ZuxZe7wHSZX4LgQE0agZqagQUK+HLIVQ=="
},
"react-native-elements": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-1.2.7.tgz",
"integrity": "sha512-0S+0R1cbItl15i64qrkWnyMztwpw60d0SUsZGVDKRAMf0Jvq9Clgyh/MzxJx2sr42mbedQP1sg5Et4fZM7Fp1w==",
"requires": {
"@types/react-native-vector-icons": "^6.4.4",
"color": "^3.1.0",
"deepmerge": "^3.1.0",
"hoist-non-react-statics": "^3.1.0",
"opencollective-postinstall": "^2.0.0",
"prop-types": "^15.7.2",
"react-native-ratings": "^6.3.0",
"react-native-status-bar-height": "^2.2.0"
},
"dependencies": {
"deepmerge": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
"integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA=="
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
}
}
},
"react-native-exception-handler": {
"version": "2.10.8",
"resolved": "https://registry.npmjs.org/react-native-exception-handler/-/react-native-exception-handler-2.10.8.tgz",
@ -19352,6 +19423,15 @@
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-2.0.10.tgz",
"integrity": "sha512-oQmWgm4tqUYyWmMNtYzNO7U/+6+WHyKiRd5cwNeE1FCJTGh8cJ5HapjHw3d6ZVfhSLzNwWqgJy1P4NpTANYa/g=="
},
"react-native-ratings": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-6.5.0.tgz",
"integrity": "sha512-YMcfQ7UQCmXGEc/WPlukHSHs5yvckTwjq5fTRk1FG8gaO7fZCNygEUGPuw4Dbvvp3IlsCUn0bOQd63RYsb7NDQ==",
"requires": {
"lodash": "^4.17.4",
"prop-types": "^15.5.10"
}
},
"react-native-safe-area": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-native-safe-area/-/react-native-safe-area-0.5.1.tgz",
@ -19389,6 +19469,11 @@
"prop-types": "^15.5.6"
}
},
"react-native-status-bar-height": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/react-native-status-bar-height/-/react-native-status-bar-height-2.4.0.tgz",
"integrity": "sha512-pWvZFlyIHiuxLugLioq97vXiaGSovFXEyxt76wQtbq0gxv4dGXMPqYow46UmpwOgeJpBhqL1E0EKxnfJRrFz5w=="
},
"react-native-status-bar-size": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/react-native-status-bar-size/-/react-native-status-bar-size-0.3.3.tgz",
@ -20566,6 +20651,21 @@
"plist": "^3.0.1"
}
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
"requires": {
"is-arrayish": "^0.3.1"
},
"dependencies": {
"is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
}
}
},
"sisteransi": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz",

View file

@ -39,6 +39,7 @@
"react-native-device-info": "github:mattermost/react-native-device-info#f7175f10822d8f66b9806206e3313eaf2f4aabc6",
"react-native-doc-viewer": "github:mattermost/react-native-doc-viewer#c913e54ec8e4a60753bc7dd39256fa4be8229d19",
"react-native-document-picker": "3.3.0",
"react-native-elements": "1.2.7",
"react-native-exception-handler": "2.10.8",
"react-native-fast-image": "8.1.5",
"react-native-gesture-handler": "1.6.0",

View file

@ -0,0 +1,51 @@
diff --git a/node_modules/react-native-elements/src/searchbar/SearchBar-android.js b/node_modules/react-native-elements/src/searchbar/SearchBar-android.js
index eeaeb81..93f58de 100644
--- a/node_modules/react-native-elements/src/searchbar/SearchBar-android.js
+++ b/node_modules/react-native-elements/src/searchbar/SearchBar-android.js
@@ -78,6 +78,12 @@ class SearchBar extends Component {
};
}
+ componentDidUpdate(prevProps) {
+ if (this.props.value !== prevProps.value) {
+ this.setState({isEmpty: this.props.value === ''});
+ }
+ }
+
render() {
const {
clearIcon,
diff --git a/node_modules/react-native-elements/src/searchbar/SearchBar-ios.js b/node_modules/react-native-elements/src/searchbar/SearchBar-ios.js
index 41ef6be..dbf727b 100644
--- a/node_modules/react-native-elements/src/searchbar/SearchBar-ios.js
+++ b/node_modules/react-native-elements/src/searchbar/SearchBar-ios.js
@@ -41,6 +41,12 @@ class SearchBar extends Component {
};
}
+ componentDidUpdate(prevProps) {
+ if (this.props.value !== prevProps.value) {
+ this.setState({isEmpty: this.props.value === ''});
+ }
+ }
+
focus = () => {
this.input.focus();
};
@@ -259,7 +265,6 @@ const styles = StyleSheet.create({
paddingBottom: 13,
paddingTop: 13,
flexDirection: 'row',
- overflow: 'hidden',
alignItems: 'center',
},
input: {
@@ -270,7 +275,7 @@ const styles = StyleSheet.create({
borderBottomWidth: 0,
backgroundColor: '#dcdce1',
borderRadius: 9,
- minHeight: 36,
+ minHeight: 30,
marginLeft: 8,
marginRight: 8,
},

View file

@ -10,6 +10,7 @@ import {
SectionList,
Text,
View,
Platform,
} from 'react-native';
import {Preferences} from 'mattermost-redux/constants';
@ -251,6 +252,12 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
searchContainer: {
paddingBottom: 2,
height: 38,
...Platform.select({
ios: {
paddingLeft: 8,
},
}),
},
searchBarInput: {
backgroundColor: '#fff',