Skip to content

Commit

Permalink
feat(Input): Add Real Input
Browse files Browse the repository at this point in the history
  • Loading branch information
lulutia committed Mar 16, 2017
1 parent 7bced69 commit e020d69
Show file tree
Hide file tree
Showing 10 changed files with 848 additions and 0 deletions.
9 changes: 9 additions & 0 deletions components/Input/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
example/
__tests__/
.*
components/
coverage/
index.android.js
index.ios.js
android/
ios/
248 changes: 248 additions & 0 deletions components/Input/Input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import React, {Component} from 'react';
import {
View,
TextInput,
Text,
Animated,
Platform,
TouchableWithoutFeedback
} from 'react-native';
import Style from './InputStyle';

import StyleSheet from 'react-native-stylesheet-xg';

/** RichInput
* @example
* <RichInput label="姓名" value="" placeholder="请输入姓名" onChangeText={(text) => {}}}/>
* @props
* @extends TextInput
* label
* tips
* error
*/

const ANIMATION_TIME = 300;
// 末尾留余高度
const PLUS_HEIGHT = StyleSheet.r(20);
// 最小高度,末尾留余高度,防抖动
const MULTI_MIN_HEIGHT = StyleSheet.r(40) + PLUS_HEIGHT;
// 校正
const REVISES = {
ios: StyleSheet.r(20),
android: 0
};

class Input extends Component {

// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
isFocus: false,
multiHeight: MULTI_MIN_HEIGHT + REVISES[Platform.OS],
animatedFactor: new Animated.Value(0.0001)
};

this.lineAnimated = {
transform: [
{scaleX: this.state.animatedFactor}
]
};

this.focusInit = false;

this.renderLabel = this.renderLabel.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
this.onChange = this.onChange.bind(this);
this.onPressLabel = this.onPressLabel.bind(this);
this.getContentSize = this.getContentSize.bind(this);
this.onContentSizeChange = this.onContentSizeChange.bind(this);
}

onFocus() {
const {onFocus} = this.props;

this.focusInit = true;
this.setState({
isFocus: true
});

Animated.timing(
this.state.animatedFactor,
{
duration: ANIMATION_TIME,
toValue: 1
}
).start();

if (typeof onFocus === 'function') {
onFocus();
}
}

onBlur() {
const {onBlur} = this.props;

this.setState({
isFocus: false
});

Animated.timing(
this.state.animatedFactor,
{
duration: ANIMATION_TIME,
toValue: 0.0001
}
).start();

if (typeof onBlur === 'function') {
onBlur();
}
}

getContentSize(event) {
if (this.props.multiline) {
const newHeight = Math.max(
MULTI_MIN_HEIGHT,
event.nativeEvent.contentSize.height + PLUS_HEIGHT + REVISES[Platform.OS]
);


if (newHeight !== this.state.multiHeight) {
this.setState({multiHeight: newHeight});
}
}
}

onChange(event) {
const {onChange} = this.props;

this.getContentSize(event);

if (typeof onChange === 'function') {
onChange(event);
}
}

onContentSizeChange(event) {
const {onContentSizeChange} = this.props;

this.getContentSize(event);

if (typeof onContentSizeChange === 'function') {
onContentSizeChange(event);
}
}

onPressLabel() {
const {editable, readOnly} = this.props;

if (editable && !readOnly) {
this.input.focus();
}
}

renderLabel() {
const {label, labelStyle, multiline, tips, required} = this.props;
const isColumnMode = multiline || !!tips || (label ? label.length > 5 : false);

if (typeof label === 'undefined') {
return;
}

return (
<TouchableWithoutFeedback onPress={this.onPressLabel}>
<View style={[Style.labelCon, isColumnMode && Style.columnModeLabelCon]}>
<Text style={[Style.label, labelStyle]}>
{required && <Text style={[Style.required]}>*</Text>}{label}
</Text>
{!!tips && <Text style={Style.tips}>{tips}</Text>}
</View>
</TouchableWithoutFeedback>
);
}

render() {
const {
editable,
multiline,
error,
wrapperStyle,
focusStyle,
disabledStyle,
errorStyle,
initJudge,
readOnly,
label,
tips
} = this.props;
const {isFocus} = this.state;
const isColumnMode = multiline || !!tips || (label ? label.length > 5 : false);

return (
<View
style={[
Style.inputCon, wrapperStyle,
isColumnMode && Style.multiInputCon,
isFocus && Style.focus, isFocus && focusStyle,
initJudge && (error && Style.error), initJudge && (error && errorStyle),
!initJudge && (error && this.focusInit && Style.error), !initJudge && (error && errorStyle),
!editable && Style.disabled, !editable && disabledStyle
]}
>
{this.renderLabel()}
<TextInput
placeholderTextColor={'#ccc'}
underlineColorAndroid={'transparent'}
returnKeyType={'done'}
{...this.props}
editable={editable && !readOnly}
ref={input => this.input = input}
style={[
Style.input, isColumnMode && Style.columnModeInput,
this.props.style,
multiline && Style.multiInput,
multiline && {height: this.state.multiHeight}
]}
onContentSizeChange={this.onContentSizeChange}
onChange={this.onChange}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
<Animated.View style={[Style.lineBottom, this.lineAnimated]} />
{error && <View style={[Style.lineBottom, Style.lineError]} />}
</View>
);
}
}

Input.defaultProps = {
editable: true,
multiline: false,
error: false,
initJudge: true,
readOnly: false
};

Input.propTypes = {
...TextInput.propTypes,
label: React.PropTypes.string,
defaultValue: React.PropTypes.string,
editable: React.PropTypes.bool,
multiline: React.PropTypes.bool,
error: React.PropTypes.bool,
required: React.PropTypes.bool,
tips: React.PropTypes.string,
wrapperStyle: React.PropTypes.object,
focusStyle: React.PropTypes.object,
disabledStyle: React.PropTypes.object,
errorStyle: React.PropTypes.object,
labelStyle: React.PropTypes.object,
initJudge: React.PropTypes.bool,
readOnly: React.PropTypes.bool
};


export default Input;
90 changes: 90 additions & 0 deletions components/Input/InputStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import StyleSheet from 'react-native-stylesheet-xg';

const Style = StyleSheet.create({
inputCon: {
flexDirection: 'row',
alignItems: 'center',
height: 45,
paddingLeft: 10,
paddingRight: 10,
borderBottomWidth: 1,
borderBottomColor: '#e5e5e5'
},
multiInputCon: {
height: null,
flexDirection: 'column',
alignSelf: 'stretch'
},
focus: {
borderColor: '#ff9900'
},
error: {
borderColor: '#ec5330'
},
disabled: {
backgroundColor: '#e5e5e5'
},
input: {
flexGrow: 1,
height: 44,
padding: 0,
color: '#333',
fontSize: 14
},
columnModeInput: {
marginTop: -10,
marginBottom: -5,
alignSelf: 'stretch'
},
multiInput: {
marginBottom: -20,
textAlignVertical: 'top',
alignSelf: 'stretch',
android: {
paddingTop: 8
},
ios: {
marginTop: -3,
marginBottom: -28
}
},
labelCon: {
flexDirection: 'row',
width: 90,
height: 44,
alignItems: 'center',
justifyContent: 'flex-start'
},
columnModeLabelCon: {
width: null,
height: 35,
alignSelf: 'stretch'
},
label: {
fontSize: 14,
color: '#666'
},
lineBottom: {
position: 'absolute',
height: 2,
bottom: -1,
left: 0,
right: 0,
backgroundColor: '#ff9900'
},
lineError: {
backgroundColor: '#ec5330'
},
tips: {
marginTop: 1,
marginLeft: 5,
fontSize: 12,
color: '#06c1ae'
},
required: {
fontSize: 14,
color: '#ec5330'
}
});

export default Style;
29 changes: 29 additions & 0 deletions components/Input/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
### The react-native-input-xg
* react native Input component for both Android and iOS based on pure JavaScript

### Main
* This component provide some more functions besides the basic input RN provided

### Properties
* Besides the basic properties RN provided, we also provide belows:

![image](https://raw.githubusercontent.com/lulutia/images/master/react-native-components/Screen-Capture-39.gif)

### Properties

| Prop | Default | Type | Description |
| :------------ |:---------------:| :---------------:| :-----|
| label | undefined | `string` | Specify the label of the input |
| defaultValue | undefined | `string` | Specify the default value of the input |
| editable | true | `bool` | if you can edit the input|
| multiline | false | `bool` | if the input support the multiline|
| error | false | `bool` | for you to judge if the content is wrong |
| required| false | `bool` | give you an indicate to show this one is a must |
| tips | undefined | `string` | give some more explanation |
| wrapperStyle | - | `style` | Specify the wrapper style |
| focusStyle | - | `style` | Specify the style when focus |
| disabledStyle | - | `style` | Specify the style when disabled|
| errorStyle | - | `style` | Specify the style when there's some error|
| labelStyle | - | `style` | Specify the label style|
| initJudge | true | `bool` | if judge error when init|
| readOnly | false | `bool` | you can only see but can not operate|
Loading

0 comments on commit e020d69

Please sign in to comment.