Home Reference Source Repository

src/TextBox.js

/**
 * @file TextBox
 * @author leon([email protected])
 */

import React, {PropTypes} from 'react';
import ReactDOM from 'react-dom';
import FloatingLabel from './textbox/FloatLabel';
import TextBoxInput from './textbox/Input';
import Validity from 'melon-core/Validity';
import InputComponent from 'melon-core/InputComponent';

import {create} from 'melon-core/classname/cxBuilder';

const cx = create('TextBox');

export default class TextBox extends InputComponent {

    constructor(props, context) {

        super(props, context);

        const value = this.state.value;

        this.state = {
            ...this.state,
            isFloating: !!value,
            isFocus: false
        };

        this.onFocus = this.onFocus.bind(this);
        this.onBlur = this.onBlur.bind(this);
        this.onChange = this.onChange.bind(this);


    }

    componentWillReceiveProps(nextProps) {

        const value = nextProps.value;

        // 多行文本框应该可以自动更新高度
        if (nextProps.multiline && this.state.value !== value) {
            this.syncTextareaHeight();
        }

        const {
            isFloating,
            isFocus
        } = this.state;

        const nextIsFloating = !!value || isFocus;

        if (isFloating !== nextIsFloating) {
            this.setState({
                isFloating: nextIsFloating
            });
        }

        super.componentWillReceiveProps(nextProps);

    }

    componentDidMount() {

        super.componentDidMount();

        if (this.props.multiline && this.state.value) {
            this.syncTextareaHeight();
        }

    }

    onFocus(e) {

        this.setState({
            isFocus: true,
            isFloating: true
        });

        const onFocus = this.props.onFocus;

        if (onFocus)  {
            onFocus({
                type: 'focus',
                target: this
            });
            return;
        }

        if (this.needValidate('focus')) {
            this.validate(this.state.value);
        }

    }

    onBlur(e) {

        const value = e.target.value;

        this.setState({
            isFloating: !!value,
            isFocus: false
        });

        const onBlur = this.props.onBlur;

        if (onBlur)  {
            onBlur({
                type: 'blur',
                target: this
            });
            return;
        }

        this.setState({value});


        if (this.needValidate('blur')) {
            this.validate(value);
        }

    }

    onChange(e) {

        super.onChange({
            type: 'change',
            target: this,
            value: e.target.value
        });

    }

    syncTextareaHeight() {

        const input = this.input;

        if (input) {
            input.style.height = 'auto';
            input.style.height = input.scrollHeight + 'px';
        }

    }

    needValidate(eventName) {
        return this.props.validateEvents.indexOf(eventName) !== -1;
    }

    renderFloatingLabel(floatingLabel, isFloating, isFocus) {

        if (!floatingLabel) {
            return null;
        }

        return (
            <FloatingLabel
                floating={isFloating || isFocus}
                focused={isFocus}
                label={floatingLabel} />
        );

    }

    render() {

        const {
            onFocus,
            onBlur,
            onChange,
            props
        } = this;

        const {
            floatingLabel,
            className,
            ...rest
        } = props;

        const {
            validity,
            isFocus,
            isFloating,
            value
        } = this.state;

        const statefulClassName = cx(props)
            .addStates({
                focus: isFocus,
                floating: isFloating,
                fulfilled: !!value
            })
            .addStates(this.getStyleStates())
            .build();

        return (
            <div className={statefulClassName}>
                {this.renderFloatingLabel(floatingLabel, isFloating, isFocus)}
                <TextBoxInput
                    {...rest}
                    onFocus={onFocus}
                    onBlur={onBlur}
                    onChange={onChange}
                    isFocus={isFocus}
                    value={value}
                    ref={input => {
                        if (input) {
                            this.input = ReactDOM.findDOMNode(input);
                        }
                    }} />
                <Validity validity={validity} />
            </div>
        );

    }



}

TextBox.displayName = 'TextBox';

TextBox.defaultProps = {
    ...InputComponent.defaultProps,
    validateEvents: ['change', 'blur']
};

TextBox.propTypes = {

    ...InputComponent.propTypes,

    type: PropTypes.oneOf(['text', 'password']),

    placeholder: PropTypes.string,
    floatingLabel: PropTypes.string,

    multiline: PropTypes.bool,

    onFocus: PropTypes.func,
    onBlur: PropTypes.func

};

TextBox.childContextTypes = InputComponent.childContextTypes;
TextBox.contextTypes = InputComponent.contextTypes;