diff --git a/lib/FloatingLabel.js b/lib/FloatingLabel.js index 5f96eab..e96676c 100644 --- a/lib/FloatingLabel.js +++ b/lib/FloatingLabel.js @@ -1,6 +1,7 @@ 'use strict'; -import React, {Component, PropTypes} from "react"; -import {StyleSheet, Animated} from "react-native"; +import React, {Component} from "react"; +import PropTypes from 'prop-types'; +import {StyleSheet, Animated, View, ViewPropTypes} from "react-native"; export default class FloatingLabel extends Component { constructor(props: Object) { @@ -11,8 +12,8 @@ export default class FloatingLabel extends Component { this.fontLarge = 13; this.fontSmall = 13; } else { - this.posTop = 16; - this.posBottom = 37; + this.posTop = ( props.posTop != null ) ? props.posTop : 16; + this.posBottom = ( props.posBottom != null ) ? props.posBottom : 37; this.fontLarge = 16; this.fontSmall = 12; } @@ -50,6 +51,7 @@ export default class FloatingLabel extends Component { }) ]).start(); } + render() : Object { let { label, @@ -66,9 +68,7 @@ export default class FloatingLabel extends Component { }, styles.labelText, this.props.isFocused && { color: highlightColor }, style]} - onPress={()=> { - this.props.focusHandler(); - }} + onPress={this.props.focusHandler} > {label} @@ -82,7 +82,11 @@ FloatingLabel.propTypes = { labelColor: PropTypes.string, highlightColor: PropTypes.string, dense: PropTypes.bool, - style: PropTypes.object + style: PropTypes.oneOfType([ + PropTypes.object, + ViewPropTypes.style + ]), + focusHandler: PropTypes.func.isRequired }; const styles = StyleSheet.create({ diff --git a/lib/TextField.js b/lib/TextField.js index d42523a..eb8d959 100644 --- a/lib/TextField.js +++ b/lib/TextField.js @@ -1,11 +1,17 @@ 'use strict'; -import React, {Component, PropTypes} from "react"; -import {View, TextInput, StyleSheet} from "react-native"; +import React, {Component} from "react"; +import PropTypes from 'prop-types'; +import {View, ViewPropTypes, TextInput, StyleSheet} from "react-native"; import Underline from './Underline'; import FloatingLabel from './FloatingLabel'; export default class TextField extends Component { + _wrapper: null; + _input: null; + _underline: null; + _floatingLabel: null; + constructor(props: Object, context: Object) { super(props, context); this.state = { @@ -13,30 +19,94 @@ export default class TextField extends Component { text: props.value, height: props.height }; + + this.bindFunctions(); + } + + bindFunctions() { + this.onRefWrapper = this.onRefWrapper.bind(this); + this.onRefInput = this.onRefInput.bind(this); + this.onBlur = this.onBlur.bind(this); + this.onFocus = this.onFocus.bind(this); + this.onChangeText = this.onChangeText.bind(this); + this.onChange = this.onChange.bind(this); + this.onContentSizeChange = this.onContentSizeChange.bind(this); + this.onRefUnderline = this.onRefUnderline.bind(this); + this.onRefFloatingLabel = this.onRefFloatingLabel.bind(this); + this.focus = this.focus.bind(this); } + focus() { - this.refs.input.focus(); + if ( this.props.editable ) + this._input.focus(); } blur() { - this.refs.input.blur(); + this._input.blur(); } isFocused() { return this.state.isFocused; } measureLayout(...args){ - this.refs.wrapper.measureLayout(...args) + this._wrapper.measureLayout(...args) } componentWillReceiveProps(nextProps: Object){ if(this.props.text !== nextProps.value){ nextProps.value.length !== 0 ? - this.refs.floatingLabel.floatLabel() - : this.refs.floatingLabel.sinkLabel(); + this._floatingLabel.floatLabel() + : this._floatingLabel.sinkLabel(); this.setState({text: nextProps.value}); } if(this.props.height !== nextProps.height){ this.setState({height: nextProps.height}); } } + + onRefWrapper(_wrapper) { + this._wrapper = _wrapper + } + + onRefInput(_input) { + this._input = _input + } + + onBlur() { + this.setState({isFocused: false}); + !this.state.text.length && this._floatingLabel.sinkLabel(); + this._underline.shrinkLine(); + this.props.onBlur && this.props.onBlur(); + } + + onFocus() { + this.setState({isFocused: true}); + this._floatingLabel.floatLabel(); + this._underline.expandLine(); + this.props.onFocus && this.props.onFocus(); + } + + onChangeText(text) { + this.setState({text}); + this.props.onChangeText && this.props.onChangeText(text); + } + + onChange(e) { + this.props.onChange && this.props.onChange(e); + } + + onContentSizeChange(e) { + if(this.props.autoGrow){ + this.setState({height: e.nativeEvent.contentSize.height}); + } + this.props.onContentSizeChange && this.props.onContentSizeChange(e); + } + + onRefUnderline(_underline) { + this._underline = _underline + } + + onRefFloatingLabel(_floatingLabel) { + this._floatingLabel = _floatingLabel + } + render() { let { label, @@ -47,67 +117,50 @@ export default class TextField extends Component { textColor, textFocusColor, textBlurColor, - onFocus, - onBlur, - onChangeText, - onChange, value, dense, inputStyle, wrapperStyle, labelStyle, height, - autoGrow, multiline, ...props } = this.props; return ( - + { - this.setState({isFocused: true}); - this.refs.floatingLabel.floatLabel(); - this.refs.underline.expandLine(); - onFocus && onFocus(); - }} - onBlur={() => { - this.setState({isFocused: false}); - !this.state.text.length && this.refs.floatingLabel.sinkLabel(); - this.refs.underline.shrinkLine(); - onBlur && onBlur(); - }} - onChangeText={(text) => { - this.setState({text}); - onChangeText && onChangeText(text); - }} - onChange={(event) => { - if(autoGrow){ - this.setState({height: event.nativeEvent.contentSize.height}); - } - onChange && onChange(event); - }} - ref="input" value={this.state.text} {...props} + + onFocus={this.onFocus} + onBlur={this.onBlur} + onChangeText={this.onChangeText} + onChange={this.onChange} + onContentSizeChange={this.onContentSizeChange} /> ); @@ -133,15 +188,26 @@ TextField.propTypes = { onFocus: PropTypes.func, onBlur: PropTypes.func, onChangeText: PropTypes.func, + onContentSizeChange: PropTypes.func, onChange: PropTypes.func, value: PropTypes.string, dense: PropTypes.bool, - inputStyle: PropTypes.object, - wrapperStyle: PropTypes.object, - labelStyle: PropTypes.object, + inputStyle: PropTypes.oneOfType([ + PropTypes.object, + ViewPropTypes.style + ]), + wrapperStyle: PropTypes.oneOfType([ + PropTypes.object, + ViewPropTypes.style + ]), + labelStyle: PropTypes.oneOfType([ + PropTypes.object, + ViewPropTypes.style + ]), multiline: PropTypes.bool, autoGrow: PropTypes.bool, - height: PropTypes.oneOfType([PropTypes.oneOf(undefined), PropTypes.number]) + height: PropTypes.number, + editable: PropTypes.bool }; TextField.defaultProps = { @@ -154,7 +220,8 @@ TextField.defaultProps = { underlineColorAndroid: 'rgba(0,0,0,0)', multiline: false, autoGrow: false, - height: undefined + height: undefined, + editable: true }; const styles = StyleSheet.create({ diff --git a/lib/Underline.js b/lib/Underline.js index 4368733..6afe146 100644 --- a/lib/Underline.js +++ b/lib/Underline.js @@ -1,21 +1,25 @@ 'use strict'; -import React, {Component, PropTypes} from "react"; +import React, {Component} from "react"; +import PropTypes from 'prop-types'; import {View, StyleSheet, Animated} from "react-native"; export default class Underline extends Component { + _wrapper: null; + constructor(props: Object) { super(props); this.state = { lineLength: new Animated.Value(0), }; this.wrapperWidth = 0; + this.onRefWrapper = this.onRefWrapper.bind(this); } componentDidMount() { requestAnimationFrame(() => { - if (this.refs.wrapper == null) { + if (this._wrapper == null) { return; } - const container = this.refs.wrapper; // un-box animated view + const container = this._wrapper; // un-box animated view container.measure((left, top, width, height) => { this.wrapperWidth = width; }); @@ -33,6 +37,11 @@ export default class Underline extends Component { duration: this.props.duration }).start(); } + + onRefWrapper(_wrapper) { + this._wrapper = _wrapper + } + render() { let { borderColor, @@ -40,17 +49,19 @@ export default class Underline extends Component { } = this.props; return ( + style={[ + styles.wrapperHighlight, + { + width: this.state.lineLength, + backgroundColor: highlightColor + } + ]}> ); @@ -67,5 +78,8 @@ const styles = StyleSheet.create({ underlineWrapper: { height: 1, alignItems: 'center' + }, + wrapperHighlight: { + height: 1 } });