Katochimoto/xblocks

View on GitHub
src/blocks/input/index.jsx

Summary

Maintainability
A
0 mins
Test Coverage
import { xv } from 'context';
import { PropTypes } from 'react';
import { view } from 'xblocks-core';
import classnames from 'classnames';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import mixinViewCommonAttrs from 'mixin/view/commonAttrs';
import filterProps from 'utils/filterProps';
import exportPropTypes from 'utils/exportPropTypes';
import ConstantInput from 'constants/input';

import Controller from './controller.jsx';

/**
 * The template node xb-input
 *
 * @class xv.Input
 * @memberof xv
 * @mixes React.addons.PureRenderMixin
 * @mixes xblocks.mixin.vCommonAttrs
 */
export default xv.Input = view.register('xb-input', [
    mixinViewCommonAttrs,
    exportPropTypes('xb-link'),

    {
        displayName: 'xb-input',

        mixins: [ PureRenderMixin ],

        /**
         * @prop {string} [name]
         * @prop {string} [type=text] text|number|date|datetime|email|month|range|search|tel|time|url|week|color
         * @prop {string} [size=m] s|m|l|xl
         * @prop {string} [autoComplete] on|off
         * @prop {string} [rows=4]
         * @prop {string} [cols]
         * @prop {string} [placeholder]
         * @prop {string} [value]
         * @prop {string} [prefix]
         * @prop {string} [postfix]
         * @prop {string} [tabindex]
         * @prop {boolean} [disabled=false]
         * @prop {boolean} [autosize=false]
         * @prop {boolean} [multiline=false]
         * @prop {boolean} [required=false]
         * @prop {boolean} [readonly=false]
         * @prop {boolean} [reset=false]
         * @prop {boolean} [autofocus=false]
         * @prop {boolean} [ghost=false]
         */
        propTypes: {
            'autocomplete': PropTypes.oneOf([ 'on', 'off' ]),
            'autofocus': PropTypes.bool,
            'autosize': PropTypes.bool,
            'cols': PropTypes.string,
            'ghost': PropTypes.bool,
            'multiline': PropTypes.bool,
            'name': PropTypes.string,
            'placeholder': PropTypes.string,
            'postfix': PropTypes.string,
            'prefix': PropTypes.string,
            'readonly': PropTypes.bool,
            'required': PropTypes.bool,
            'reset': PropTypes.bool,
            'rows': PropTypes.string,
            'size': PropTypes.oneOf([ 's', 'm', 'l', 'xl' ]).isRequired,
            'type': PropTypes.oneOf([ 'text', 'number', 'date', 'datetime', 'email', 'month', 'range', 'search', 'tel', 'time', 'url', 'week', 'color', 'wysiwyg' ]).isRequired,
            'value': PropTypes.string,
            'xb-link': PropTypes.string
        },

        getDefaultProps: function () {
            return {
                'autofocus': false,
                'autosize': false,
                'ghost': false,
                'multiline': false,
                'readonly': false,
                'required': false,
                'reset': false,
                'rows': '4',
                'size': 'm',
                'type': 'text',
                'value': undefined
            };
        },

        getInitialState: function () {
            return {
                value: this.props.value
            };
        },

        componentDidMount: function () {
            // check show or hide placeholder after mount element
            this._controller.dispatchEventToggleHint('', this.props.value);
        },

        componentDidUpdate: function () {
            this.context.container[ ConstantInput.VALUE ] = this.state.value;
        },

        /**
         * Remember current value in state
         * @param {Event} event
         * @private
         */
        onChange: function (event) {
            this.setState({ value: event.target.value });
        },

        /**
         * Show or hide placeholder
         * @param {boolean} toggle
         * @private
         */
        onHintToggle: function (toggle) {
            this._placeholder.style.visibility = (toggle ? 'inherit' : 'hidden');
        },

        /**
         * Click reset button
         * @private
         */
        onClickReset: function () {
            this.setState({ value: '' });
        },

        /**
         * Check show complex input
         * @returns {boolean}
         * @private
         */
        isComplex: function () {
            return Boolean(
                this.props.postfix ||
                this.props.prefix ||
                this.props.reset ||
                this.props.autosize ||
                this.props[ 'xb-link' ] ||
                this.props.placeholder
            );
        },

        render: function () {
            const isComplex = this.isComplex();
            const classes = classnames({
                'xb-input': true,
                '_disabled': this.props.disabled,
                '_autosize': this.props.autosize,
                '_ghost': this.props.ghost,
                [ `_${isComplex ? 'complex' : 'simple'}_size-${this.props.size}` ]: true
            });

            const controllerProps = {
                'autoFocus': this.props.autofocus,
                'autoComplete': this.props.autocomplete,
                'autosize': this.props.autosize,
                'className': '_controller',
                'cols': this.props.cols,
                'disabled': this.props.disabled,
                'key': 'controller',
                'multiline': this.props.multiline,
                'name': this.props.name,
                'onChange': this.onChange,
                'onHintToggle': this.onHintToggle,
                'readOnly': this.props.readonly,
                'ref': (ref) => this._controller = ref,
                'required': this.props.required,
                'rows': this.props.rows,
                'tabIndex': this.getTabIndex(),
                'value': this.state.value
            };

            if (isComplex) {
                const children = [];

                if (this.props[ 'xb-link' ]) {
                    const linkProps = filterProps(/^xb-link-/, this.props);

                    children.push(
                        <xb-link {...linkProps} theme="empty" key="label">{this.props[ 'xb-link' ]}</xb-link>
                    );
                }

                if (this.props.prefix) {
                    children.push(
                        <span key="prefix" className="_left">{this.props.prefix}</span>
                    );
                }

                if (this.props.reset) {
                    children.push(
                        <span key="reset" className="_reset" onClick={this.onClickReset}></span>
                    );
                }

                if (this.props.postfix) {
                    children.push(
                        <span key="postfix" className="_right">{this.props.postfix}</span>
                    );
                }

                const placeholder = do {
                    if (this.props.placeholder) {
                        (
                            <span ref={ref => this._placeholder = ref} key="placeholder" className="_hint">
                                <span className="_hint-inner">{this.props.placeholder}</span>
                            </span>
                        );
                    } else {
                        null;
                    }
                };

                children.push(
                    <span key="content" className="_content">
                        {placeholder}
                        <Controller {...controllerProps} isPlaceholderHint={Boolean(placeholder)} />
                        <span key="view" className="_view">
                            {String.fromCharCode(160)}
                        </span>
                    </span>
                );

                return (
                    <label className={classes}>{children}</label>
                );

            } else {

                return (
                    <Controller {...controllerProps} className={classes} isPlaceholderHint={false} />
                );
            }
        }
    }
]);