Files
react-native-web/src/components/TextInput/index.js
T

294 lines
7.4 KiB
JavaScript

import applyNativeMethods from '../../modules/applyNativeMethods';
import createDOMElement from '../../modules/createDOMElement';
import omit from 'lodash/omit';
import pick from 'lodash/pick';
import ReactDOM from 'react-dom';
import StyleSheet from '../../apis/StyleSheet';
import Text from '../Text';
import TextareaAutosize from 'react-textarea-autosize';
import TextInputState from './TextInputState';
import UIManager from '../../apis/UIManager';
import View from '../View';
import ViewStylePropTypes from '../View/ViewStylePropTypes';
import React, { Component, PropTypes } from 'react';
const viewStyleProps = Object.keys(ViewStylePropTypes);
class TextInput extends Component {
static displayName = 'TextInput';
static propTypes = {
...View.propTypes,
autoCapitalize: PropTypes.oneOf([ 'characters', 'none', 'sentences', 'words' ]),
autoComplete: PropTypes.bool,
autoFocus: PropTypes.bool,
clearTextOnFocus: PropTypes.bool,
defaultValue: PropTypes.string,
editable: PropTypes.bool,
keyboardType: PropTypes.oneOf([
'default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search'
]),
maxLength: PropTypes.number,
maxNumberOfLines: PropTypes.number,
multiline: PropTypes.bool,
numberOfLines: PropTypes.number,
onBlur: PropTypes.func,
onChange: PropTypes.func,
onChangeText: PropTypes.func,
onFocus: PropTypes.func,
onKeyPress: PropTypes.func,
onSelectionChange: PropTypes.func,
placeholder: PropTypes.string,
placeholderTextColor: PropTypes.string,
secureTextEntry: PropTypes.bool,
selectTextOnFocus: PropTypes.bool,
style: Text.propTypes.style,
testID: Text.propTypes.testID,
value: PropTypes.string
};
static defaultProps = {
autoCapitalize: 'sentences',
editable: true,
keyboardType: 'default',
multiline: false,
numberOfLines: 2,
secureTextEntry: false,
style: {}
};
constructor(props, context) {
super(props, context);
this.state = { showPlaceholder: !props.value && !props.defaultValue };
}
blur() {
TextInputState.blurTextInput(ReactDOM.findDOMNode(this._inputRef));
}
clear() {
this.setNativeProps({ text: '' });
}
focus() {
TextInputState.focusTextInput(ReactDOM.findDOMNode(this._inputRef));
}
isFocused() {
return TextInputState.currentlyFocusedField() === ReactDOM.findDOMNode(this._inputRef);
}
setNativeProps(props) {
UIManager.updateView(this._inputRef, props, this);
}
render() {
const {
accessibilityLabel, // eslint-disable-line
autoCapitalize,
autoComplete,
autoFocus,
defaultValue,
editable,
keyboardType,
maxLength,
maxNumberOfLines,
multiline,
numberOfLines,
onKeyPress,
onLayout,
onSelectionChange,
placeholder,
placeholderTextColor,
secureTextEntry,
style,
testID,
value
} = this.props;
let type;
switch (keyboardType) {
case 'email-address':
type = 'email';
break;
case 'numeric':
type = 'number';
break;
case 'phone-pad':
type = 'tel';
break;
case 'search':
case 'web-search':
type = 'search';
break;
case 'url':
type = 'url';
break;
default:
type = 'text';
}
if (secureTextEntry) {
type = 'password';
}
// In order to support 'Text' styles on 'TextInput', we split the 'Text'
// and 'View' styles and apply them to the 'Text' and 'View' components
// used in the implementation
const flattenedStyle = StyleSheet.flatten(style);
const rootStyles = pick(flattenedStyle, viewStyleProps);
const textStyles = omit(flattenedStyle, viewStyleProps);
const propsCommon = {
autoCapitalize,
autoComplete: autoComplete && 'on',
autoFocus,
defaultValue,
maxLength,
onBlur: this._handleBlur,
onChange: this._handleChange,
onFocus: this._handleFocus,
onKeyPress,
onSelect: onSelectionChange && this._handleSelectionChange,
readOnly: !editable,
ref: this._setInputRef,
style: [ styles.input, textStyles, { outline: style.outline } ],
value
};
const propsMultiline = {
...propsCommon,
maxRows: maxNumberOfLines || numberOfLines,
minRows: numberOfLines
};
const propsSingleline = {
...propsCommon,
type
};
const component = multiline ? TextareaAutosize : 'input';
const props = multiline ? propsMultiline : propsSingleline;
const optionalPlaceholder = placeholder && this.state.showPlaceholder && (
<View pointerEvents='none' style={styles.placeholder}>
<Text
children={placeholder}
style={[
styles.placeholderText,
textStyles,
placeholderTextColor && { color: placeholderTextColor }
]}
/>
</View>
);
return (
<View
accessibilityLabel={accessibilityLabel}
onClick={this._handleClick}
onLayout={onLayout}
style={[ styles.initial, rootStyles ]}
testID={testID}
>
<View style={styles.wrapper}>
{createDOMElement(component, props)}
{optionalPlaceholder}
</View>
</View>
);
}
_handleBlur = (e) => {
const { onBlur } = this.props;
const text = e.target.value;
this.setState({ showPlaceholder: text === '' });
this.blur();
if (onBlur) { onBlur(e); }
}
_handleChange = (e) => {
const { onChange, onChangeText } = this.props;
const text = e.target.value;
e.nativeEvent.text = text;
this.setState({ showPlaceholder: text === '' });
if (onChange) { onChange(e); }
if (onChangeText) { onChangeText(text); }
if (!this._inputRef) {
// calling `this.props.onChange` or `this.props.onChangeText`
// may clean up the input itself. Exits here.
return;
}
}
_handleClick = (e) => {
this.focus();
}
_handleFocus = (e) => {
const { clearTextOnFocus, onFocus, selectTextOnFocus } = this.props;
const node = ReactDOM.findDOMNode(this._inputRef);
const text = e.target.value;
if (onFocus) { onFocus(e); }
if (clearTextOnFocus) { this.clear(); }
if (selectTextOnFocus) { node.select(); }
this.setState({ showPlaceholder: text === '' });
}
_handleSelectionChange = (e) => {
const { onSelectionChange } = this.props;
try {
const { selectionDirection, selectionEnd, selectionStart } = e.target;
const event = {
selectionDirection,
selectionEnd,
selectionStart,
nativeEvent: e.nativeEvent
};
if (onSelectionChange) { onSelectionChange(event); }
} catch (e) {}
}
_setInputRef = (component) => {
this._inputRef = component;
}
}
const styles = StyleSheet.create({
initial: {
borderColor: 'black'
},
wrapper: {
flex: 1
},
input: {
appearance: 'none',
backgroundColor: 'transparent',
borderRadius: 0,
borderWidth: 0,
boxSizing: 'border-box',
color: 'inherit',
flex: 1,
font: 'inherit',
minHeight: '100%', // center small inputs (fix #139)
padding: 0,
zIndex: 1
},
placeholder: {
bottom: 0,
justifyContent: 'center',
left: 0,
position: 'absolute',
right: 0,
top: 0
},
placeholderText: {
color: 'darkgray',
overflow: 'hidden',
whiteSpace: 'pre'
}
});
module.exports = applyNativeMethods(TextInput);