weex-nuke
Version:
基于 Rax 、Weex 的高性能组件体系 ~~
363 lines (344 loc) • 9.87 kB
JSX
'use strict';
/** @jsx createElement */
import { createElement, Component, PropTypes } from 'rax';
import View from 'nuke-view';
import Text from 'nuke-text';
import Image from 'nuke-image';
import { isWeex } from 'nuke-env';
import { bubbleKeys } from 'nuke-helper';
import BaseInput from 'nuke-base-input';
import TEXTINHERITKEYS from '../mods/inherit-keys';
import IMAGE_URL from '../mods/image';
class Input extends Component {
constructor(props, context) {
super(props);
let value = null;
if ('value' in props) {
value = props.value;
} else if ('defaultValue' in props) {
value = props.defaultValue;
}
const count = (value && value.length) || 0;
this.state = {
value,
inputValue: value,
count: (value && value.length) || 0,
maxLengthError: count > props.maxLength,
};
this.fixedFont = context.commonConfigs && context.commonConfigs.fixedFont;
if ('fixedFont' in props) {
this.fixedFont = props.fixedFont;
}
this.inputHandler = this.inputHandler.bind(this);
this.changeHander = this.changeHander.bind(this);
this.clearHandler = this.clearHandler.bind(this);
this.focusHandler = this.focusHandler.bind(this);
this.blurHandler = this.blurHandler.bind(this);
this.getRef = this.getRef.bind(this);
this.getValue = this.getValue.bind(this);
this.focus = this.focus.bind(this);
this.blur = this.blur.bind(this);
this.clear = this.clear.bind(this);
}
componentWillReceiveProps(nextProps) {
if (
'value' in nextProps &&
(typeof nextProps.value === 'string' || typeof nextProps.value === 'number') &&
nextProps.value !== this.state.inputValue
) {
this.setState({
value: nextProps.value,
inputValue: nextProps.value,
count: nextProps.value.length,
maxLengthError: nextProps.value.length > this.props.maxLength,
});
}
}
inputHandler(text, eventObj) {
const { maxLength } = this.props;
this.setState({
// value: text,
inputValue: text,
count: text.length,
maxLengthError: text.length > maxLength,
});
this.trigger('onInput', eventObj);
}
changeHander(text, eventObj) {
const { maxLength } = this.props;
this.setState({
value: text,
inputValue: text,
count: text.length,
maxLengthError: text.length > maxLength,
});
this.trigger('onChange', text, eventObj);
}
getInput() {
const {
readOnly,
disabled,
onInput,
onChange,
hasClear,
inputStyle,
rows,
multiple,
style,
...others
} = this.props;
const { value } = this.state;
const styles = this.props.themeStyle;
const inputAttrs = {
...others,
value: typeof value !== null ? value : null,
readOnly,
multiple,
onInput: this.inputHandler,
onChange: this.changeHander,
onFocus: this.focusHandler,
onBlur: this.blurHandler,
disabled,
};
if (isWeex) {
// 如果是多行文本,添加 returnKeyType 会导致无法换换
if (multiple && !this.props.onReturn) {
delete inputAttrs.onReturn;
delete inputAttrs.returnKeyType;
}
} else if (!disabled) {
delete inputAttrs.disabled;
}
if (multiple) {
inputAttrs.rows = rows || 3;
inputAttrs.style = Object.assign({}, styles['multiple-input-ele']);
if (inputStyle.height || style.height) {
inputAttrs.style.height = inputStyle.height || style.height;
}
if (inputStyle.lineHeight || style.lineHeight) {
inputAttrs.style.lineHeight = inputStyle.lineHeight || style.lineHeight;
}
} else {
inputAttrs.style = Object.assign({}, styles['single-input-ele']);
if (inputStyle.height || style.height) {
inputAttrs.style.height = inputStyle.height || style.height;
}
inputAttrs.style.lineHeight = inputStyle.lineHeight || style.lineHeight || inputAttrs.style.height;
}
TEXTINHERITKEYS.map((item) => {
if (styles[item]) {
inputAttrs.style[item] = styles[item];
}
});
return (
<BaseInput
fixedFont={this.fixedFont}
{...inputAttrs}
ref={(n) => {
this.input = n;
}}
/>
);
}
clear() {
this.setState({
value: '',
count: 0,
});
}
clearHandler(e) {
this.setState({
value: this.state.inputValue,
});
this.setState({
value: '',
inputValue: '',
count: 0,
});
this.focus();
this.trigger('onClear', '');
}
focusHandler(e) {
this.setState({
focus: true,
});
this.trigger('onFocus', e);
}
trigger(fn, ...attrs) {
if (typeof fn === 'string') fn = this.props[fn];
if (!(typeof fn === 'function')) return;
return fn.apply(this, attrs);
}
blurHandler(e) {
this.setState({
focus: false,
});
this.trigger('onBlur', e);
}
focus() {
this.input.focus();
}
getRef() {
return this.input.getRef();
}
blur() {
this.input.blur();
}
getValue() {
return this.input.getValue();
}
renderClear(styles, height) {
const { count } = this.state;
return count ? (
<View style={[styles.clearWrap, { height }]} onClick={this.clearHandler}>
<Image
source={{ uri: IMAGE_URL.clear }}
// onClick={this.clearHandler}
style={styles.clear}
/>
</View>
) : null;
}
renderCustomIcon(styles, height) {
const { icon = {} } = this.props;
const { uri, onPress = () => {}, style = {} } = icon;
if (!uri) return null;
return (
<View style={[styles.icon, { height }, style]} onClick={onPress}>
<Image source={{ uri }} style={[styles['icon-image'], style]} />
</View>
);
}
renderCount(styles) {
const { maxLength, multiple, renderCount } = this.props;
const { maxLengthError } = this.state;
if (!maxLength || !renderCount) return null;
const { count } = this.state;
return (
<View x="count" style={styles[`${multiple ? 'multiple-count-wrap' : 'single-count-wrap'}`]}>
<Text
fixedFont={this.fixedFont}
style={[
styles[`${multiple ? 'multiple-count-text' : 'single-count-text'}`],
maxLengthError ? styles['count-error'] : {},
]}
>
{count} / {maxLength}
</Text>
</View>
);
}
render() {
const {
readOnly,
disabled,
style = {},
renderCount,
hideErrorWhenFocus,
hasClear,
multiple,
status,
errorMessage,
} = this.props;
const styles = this.props.themeStyle;
const { focus, maxLengthError } = this.state;
let inputWrapperStyle = Object.assign(
{},
styles['input-wrap'],
styles[`${multiple ? 'multiple-wrap' : 'single-wrap'}`],
readOnly ? styles.readonly : {},
status === 'error' ? styles['error-input-wrap'] : {},
focus ? styles['focus-input-wrap'] : {},
disabled ? styles[`${multiple ? 'multiple' : 'single'}-disabled`] : {},
style
);
const customHeight = inputWrapperStyle.height;
// debugger;
if ((status === 'error' && !hideErrorWhenFocus) || maxLengthError) {
inputWrapperStyle = Object.assign(inputWrapperStyle, styles['error-input-wrap']);
}
if (disabled) {
inputWrapperStyle = Object.assign(inputWrapperStyle, styles[`${multiple ? 'multiple' : 'single'}-disabled`]);
}
const outerStyle = {};
bubbleKeys.map((item) => {
if (item in inputWrapperStyle) {
outerStyle[item] = inputWrapperStyle[item];
}
});
if (outerStyle.height) {
delete outerStyle.height;
}
if (outerStyle.backgroundColor) {
delete outerStyle.backgroundColor;
}
return (
<View x="normal-outter" style={outerStyle}>
<View x="input-wrap" style={inputWrapperStyle}>
{this.getInput()}
</View>
{hasClear ? this.renderClear(styles, customHeight) : null}
{!hasClear ? this.renderCustomIcon(styles, customHeight) : null}
{(status === 'error' && errorMessage) || renderCount ? (
<View x="help" style={styles.help}>
{!focus && status === 'error' && errorMessage ? (
<Text fixedFont={this.fixedFont} style={styles['error-text']}>
{errorMessage}
</Text>
) : null}
{this.renderCount(styles)}
</View>
) : null}
</View>
);
}
}
Input.propTypes = {
defaultValue: PropTypes.any,
onFocus: PropTypes.func,
onInput: PropTypes.func,
onBlur: PropTypes.func,
onReturn: PropTypes.func,
placeholder: PropTypes.string,
readOnly: PropTypes.boolean,
disabled: PropTypes.boolean,
style: PropTypes.any,
maxLength: PropTypes.number,
multiple: PropTypes.boolean,
inputStyle: PropTypes.any,
returnKeyType: PropTypes.any,
hasClear: PropTypes.boolean,
rows: PropTypes.number,
maxRows: PropTypes.number,
type: PropTypes.oneOf(['text', 'url', 'password', 'tel', 'date', 'time', 'email']),
status: PropTypes.oneOf(['success', 'error']),
errorMessage: PropTypes.string,
renderCount: PropTypes.boolean,
value: PropTypes.string,
themeStyle: PropTypes.any,
hideErrorWhenFocus: PropTypes.boolean,
};
Input.defaultProps = {
onFocus: () => {},
onInput: () => {},
onBlur: () => {},
onReturn: () => {},
placeholder: '',
readOnly: false,
disabled: false,
style: {},
multiple: false,
inputStyle: {},
hasClear: false,
type: 'text',
status: 'success',
themeStyle: {},
renderCount: false,
placeholderColor: '#999999',
hideErrorWhenFocus: true,
};
Input.contextTypes = {
androidConfigs: PropTypes.any,
commonConfigs: PropTypes.any,
};
export default Input;