@deboxsoft/react-native-elements
Version:
React Native Elements & UI Toolkit
203 lines (182 loc) • 4.63 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import {
Text,
View,
TextInput,
Animated,
Easing,
Platform,
StyleSheet,
} from 'react-native';
import { nodeType, renderNode } from '../helpers';
import { fonts, withTheme, ViewPropTypes, TextPropTypes } from '../config';
import Icon from '../icons/Icon';
const renderText = (content, defaultProps, style) =>
renderNode(Text, content, {
...defaultProps,
style: StyleSheet.flatten([style, defaultProps && defaultProps.style]),
});
class Input extends React.Component {
shakeAnimationValue = new Animated.Value(0);
focus() {
this.input.focus();
}
blur() {
this.input.blur();
}
clear() {
this.input.clear();
}
isFocused() {
return this.input.isFocused();
}
shake = () => {
const { shakeAnimationValue } = this;
shakeAnimationValue.setValue(0);
// Animation duration based on Material Design
// https://material.io/guidelines/motion/duration-easing.html#duration-easing-common-durations
Animated.timing(shakeAnimationValue, {
duration: 375,
toValue: 3,
ease: Easing.bounce,
}).start();
};
render() {
const {
containerStyle,
inputContainerStyle,
leftIcon,
leftIconContainerStyle,
rightIcon,
rightIconContainerStyle,
inputComponent: InputComponent = TextInput,
inputStyle,
errorProps,
errorStyle,
errorMessage,
label,
labelStyle,
labelProps,
theme,
...attributes
} = this.props;
const translateX = this.shakeAnimationValue.interpolate({
inputRange: [0, 0.5, 1, 1.5, 2, 2.5, 3],
outputRange: [0, -15, 0, 15, 0, -15, 0],
});
return (
<View style={StyleSheet.flatten([{ width: '90%' }, containerStyle])}>
{renderText(
label,
{ style: labelStyle, ...labelProps },
styles.label(theme)
)}
<Animated.View
style={StyleSheet.flatten([
styles.inputContainer(theme),
inputContainerStyle,
{ transform: [{ translateX }] },
])}
>
{leftIcon && (
<View
style={StyleSheet.flatten([
styles.iconContainer,
leftIconContainerStyle,
])}
>
{renderNode(Icon, leftIcon)}
</View>
)}
<InputComponent
underlineColorAndroid="transparent"
{...attributes}
ref={ref => (this.input = ref)}
style={StyleSheet.flatten([styles.input, inputStyle])}
/>
{rightIcon && (
<View
style={StyleSheet.flatten([
styles.iconContainer,
rightIconContainerStyle,
])}
>
{renderNode(Icon, rightIcon)}
</View>
)}
</Animated.View>
{!!errorMessage && (
<Text
{...errorProps}
style={StyleSheet.flatten([
styles.error(theme),
errorStyle && errorStyle,
])}
>
{errorMessage}
</Text>
)}
</View>
);
}
}
Input.propTypes = {
containerStyle: ViewPropTypes.style,
inputContainerStyle: ViewPropTypes.style,
leftIcon: nodeType,
leftIconContainerStyle: ViewPropTypes.style,
rightIcon: nodeType,
rightIconContainerStyle: ViewPropTypes.style,
inputStyle: TextPropTypes.style,
inputComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
shake: PropTypes.any,
errorProps: PropTypes.object,
errorStyle: TextPropTypes.style,
errorMessage: PropTypes.string,
label: PropTypes.node,
labelStyle: TextPropTypes.style,
labelProps: PropTypes.object,
theme: PropTypes.object,
};
const styles = {
inputContainer: theme => ({
flexDirection: 'row',
borderBottomWidth: 1,
alignItems: 'center',
borderColor: theme.colors.grey3,
}),
iconContainer: {
height: 40,
justifyContent: 'center',
alignItems: 'center',
marginLeft: 15,
},
input: {
alignSelf: 'center',
color: 'black',
fontSize: 18,
marginLeft: 10,
flex: 1,
height: 40,
},
error: theme => ({
margin: 5,
fontSize: 12,
color: theme.colors.error,
}),
label: theme => ({
fontSize: 16,
color: theme.colors.grey3,
...Platform.select({
ios: {
fontWeight: 'bold',
},
android: {
...fonts.android.bold,
},
}),
}),
};
export { Input };
export default withTheme(Input, 'Input');