UNPKG

weex-nuke

Version:

基于 Rax 、Weex 的高性能组件体系 ~~

367 lines (351 loc) 9.14 kB
'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 Icon from 'nuke-icon'; import BaseInput from 'nuke-base-input'; import { connectStyle } from 'nuke-theme-provider'; import { isWeex } from 'nuke-env'; import { textKeys } from 'nuke-helper'; import stylesProvider from '../styles'; class NormalInput 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; } [ 'inputHandler', 'changeHander', 'clearHandler', 'focusHandler', 'blurHandler', 'getRef', 'getValue', 'focus', 'blur', 'clear', ].forEach((m) => { this[m] = this[m].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, }); } } getInput() { const { readOnly, disabled, onInput, onChange, inputStyle = {}, hasClear, rows, multiple, ...others } = this.props; const { value } = this.state; const styles = this.props.themeStyle; const inputAttrs = { ...others, value: typeof value !== null ? value : null, readOnly, multiple, style: Object.assign( {}, styles['input-ele'], hasClear ? styles['input-has-clear'] : null, multiple ? styles['multiple-input'] : styles['single-input'], inputStyle ), 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; } textKeys.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(); } 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); } inputHandler(text, eventObj) { const { maxLength } = this.props; this.setState({ inputValue: text, count: text.length, maxLengthError: text.length > maxLength, }); this.trigger('onInput', eventObj); } renderClear(styles) { const { count } = this.state; const { clearIconStyle = {} } = this.props; return count ? ( <Icon name="delete-fill" onClick={this.clearHandler} style={[styles.clear, clearIconStyle]} /> ) : null; } renderCustomIcon(styles) { const { icon = {} } = this.props; const { uri, onPress = () => {}, style = {} } = icon; if (!uri) return null; return ( <View style={[styles.icon, 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 data-role="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> ); } calcInputStyle() { const { readOnly, disabled, style = {}, hideErrorWhenFocus, multiple, status, themeStyle: styles, } = this.props; 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`] : {} ); if ((status === 'error' && !hideErrorWhenFocus) || maxLengthError) { inputWrapperStyle = Object.assign( inputWrapperStyle, styles['error-input-wrap'] ); } if (disabled) { inputWrapperStyle = Object.assign( inputWrapperStyle, styles[`${multiple ? 'multiple' : 'single'}-disabled`] ); } return { ...inputWrapperStyle, ...style }; } render() { const { renderCount, hasClear, status, errorMessage } = this.props; const styles = this.props.themeStyle; const { focus } = this.state; const inputWrapperStyle = this.calcInputStyle(); return ( <View data-role="normal-wrap" style={inputWrapperStyle}> {this.getInput()} {hasClear ? this.renderClear(styles) : null} {!hasClear ? this.renderCustomIcon(styles) : null} {(status === 'error' && errorMessage) || renderCount ? ( <View data-role="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> ); } } NormalInput.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, }; NormalInput.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, }; NormalInput.contextTypes = { androidConfigs: PropTypes.any, commonConfigs: PropTypes.any, }; NormalInput.displayName = 'Input'; const StyledInput = connectStyle(stylesProvider, { withRef: true })( NormalInput ); export default StyledInput;