API Docs for: 0.0.1
Show:

File: lib/itsa-react-input-4.16.0.4.js

"use strict";

/**
 * Description here
 *
 *
 *
 * <i>Copyright (c) 2016 ItsAsbreuk - http://itsasbreuk.nl</i><br>
 * New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
 *
 *
 * @module itsa-react-input
 * @class Input
 * @since 0.0.1
*/

require("itsa-dom");

const React = require("react"),
    PropTypes = require("prop-types"),
    ReactDom = require("react-dom"),
    later = require("itsa-utils").later,
    MAIN_CLASS = "itsa-input",
    MAIN_CLASS_PREFIX = MAIN_CLASS+"-",
    FORM_ELEMENT_CLASS_SPACED = " itsa-formelement",
    SPACED_MAIN_CLASS_PREFIX = " "+MAIN_CLASS_PREFIX,
    ELEMENT = "element";

class Input extends React.Component {
    constructor(props) {
        super(props);
        const instance = this;
        instance.state = {
            focussed: !!props.autoFocus
        };
        instance.getDefinedComponent = instance.getDefinedComponent.bind(instance);
        instance.focus = instance.focus.bind(instance);
        instance.handleBlur = instance.handleBlur.bind(instance);
        instance.handleChange = instance.handleChange.bind(instance);
        instance.handleClick = instance.handleClick.bind(instance);
        instance.handleContainerFocus = instance.handleContainerFocus.bind(instance);
        instance.handleFocus = instance.handleFocus.bind(instance);
        instance.handleKeyDown = instance.handleKeyDown.bind(instance);
        instance.handleKeyPress = instance.handleKeyPress.bind(instance);
        instance.handleKeyUp = instance.handleKeyUp.bind(instance);
        instance._mergeDataAttrs = instance._mergeDataAttrs.bind(instance);
    }

    /**
     * componentDidMount will call `this.activatePlaces()`;
     *
     * @method componentDidMount
     * @since 0.0.1
     */
    componentDidMount() {
        const instance = this;
        instance._inputNode = instance._domNode.querySelector("."+MAIN_CLASS_PREFIX+ELEMENT);
        if (instance.props.autoFocus) {
            instance._focusLater = later(() => instance.focus(), 50);
        }
    }

    /**
     * componentWilUnmount does some cleanup.
     *
     * @method componentWillUnmount
     * @since 0.0.1
     */
    componentWillUnmount() {
        this._focusLater && this._focusLater.cancel();
    }

    /**
     * Sets the focus on the Component.
     *
     * @method focus
     * @param [transitionTime] {Number} transition-time to focus the element into the view
     * @chainable
     * @since 0.0.1
     */
    focus(transitionTime) {
        let node, length;
        const props = this.props;
        if (!props.readOnly && !props.disabled) {
            node = this._inputNode;
            node.itsa_focus && node.itsa_focus(null, null, transitionTime);
            if (node.setSelectionRange) {
                length = node.value.length;
                node.setSelectionRange(length, length);
            }
        }
        return this;
    }

    /**
     * The method that is called whenever the input-Element gets blurred.
     * It will change its internal state, so that the css looks right and
     * also any validation-messages appear.
     *
     * @method handleBlur
     * @param e {Object} event-payload
     * @since 0.0.1
     */
    handleBlur(e) {
        const instance = this,
            props = instance.props;
        instance.changed = false;
        instance.setState({
            focussed: false
        });
        props.onBlur && props.onBlur(e);
    }

    /**
     * The method that is called whenever the input-Element changes its value.
     * Will update the Redux-store (signupuser.homeaddress);
     *
     * @method handleChange
     * @param e {Object} event-payload
     * @since 0.0.1
     */
    handleChange(e) {
        const instance = this,
            props = instance.props;
        if (!props.readOnly && !props.disabled) {
            instance.changed = true;
            props.onChange(e);
        }
    }

    /**
     * The method that is called whenever the input-Element gets clicked.
     *
     * @method handleClick
     * @param e {Object} event-payload
     * @since 0.1.0
     */
    handleClick(e) {
        const props = this.props;
        if (!props.readOnly && !props.disabled && props.onClick) {
            props.onClick(e);
        }
    }

    /**
     * Callback that sets the focus to the descendent element by calling `focus()`
     *
     * @method handleContainerFocus
     * @param e {Object} event-payload
     * @since 0.1.0
     */
    handleContainerFocus(e) {
        (e.target===e.currentTarget) && this.focus();
    }

    /**
     * The method that is called whenever the input-Element gets the focus.
     * It will change its internal state, so that the css looks right and
     * also any validation-messages disappear.
     *
     * @method handleFocus
     * @param e {Object} event-payload
     * @since 0.0.1
     */
    handleFocus(e) {
        const props = this.props;
        if (!props.readOnly && !props.disabled) {
            this.changed = false;
            this.setState({
                focussed: true
            });
            props.onFocus && props.onFocus(e);
        }
    }


    /**
     * The method that is called whenever the input-Element recieves a keyDown.
     *
     * @method handleKeyDown
     * @param e {Object} event-payload
     * @since 0.1.0
     */
    handleKeyDown(e) {
        const props = this.props;
        if (!props.readOnly && !props.disabled && props.onKeyDown) {
            props.onKeyDown(e);
        }
    }

    /**
     * The method that is called whenever the input-Element recieves a keyPress.
     *
     * @method handleKeyPress
     * @param e {Object} event-payload
     * @since 0.1.0
     */
    handleKeyPress(e) {
        const props = this.props;
        if (!props.readOnly && !props.disabled) {
            if (props.onKeyEnter && (e.charCode===13)) {
                props.onKeyEnter();
            }
            props.onKeyPress && props.onKeyPress(e);
        }
    }

    /**
     * The method that is called whenever the input-Element recieves a keyUp.
     *
     * @method handleKeyUp
     * @param e {Object} event-payload
     * @since 0.1.0
     */
    handleKeyUp(e) {
        const props = this.props;
        if (!props.readOnly && !props.disabled && props.onKeyUp) {
            props.onKeyUp(e);
        }
    }

    getDefinedComponent() {

    }

    getAdditionalProps() {
        return {};
    }

    /**
     * React render-method --> renderes the Component.
     *
     * @method render
     * @return ReactComponent
     * @since 0.0.1
     */
    render() {
        let wrapperClass = MAIN_CLASS+FORM_ELEMENT_CLASS_SPACED,
            inputClass = MAIN_CLASS_PREFIX+ELEMENT,
            label, errorMsg, help, inputProps, ariaRequired, element;
        const instance = this,
            props = instance.props,
            value = String(props.value),
            Component = instance.getDefinedComponent(!!value),
            type = props.type || "text",
            readOnly = props.readOnly || false,
            disabled = props.disabled || false,
            errored = (!instance.changed &&
                (props.validated===false) &&
                props.formValidated);

        props.className && (wrapperClass+=" "+props.className);
        props.classNameInput && (inputClass+=" "+props.classNameInput);
        errored && (wrapperClass+=SPACED_MAIN_CLASS_PREFIX+"error");
        instance.state.focused && (wrapperClass+=SPACED_MAIN_CLASS_PREFIX+"focus");
        disabled && (wrapperClass+=" disabled");
        readOnly && (wrapperClass+=" readonly");

        if (props.markValidated && !errored && !instance.state.focussed && !instance.changed && value && props.validated) {
            wrapperClass += SPACED_MAIN_CLASS_PREFIX+"feedback-success";
        }
        else if (props.markRequired && !value) {
            wrapperClass += SPACED_MAIN_CLASS_PREFIX+"required";
            ariaRequired = true;
        }

        if (errored && props.errorMsg) {
            errorMsg = (<div className={MAIN_CLASS_PREFIX+"error-text"}>{props.errorMsg}</div>);
        }

        if (props.helpText) {
            help = (<div className={MAIN_CLASS_PREFIX+"help-text"}>{props.helpText}</div>);
        }

        inputProps = {
            "aria-disabled": disabled,
            "aria-invalid": errored,
            "aria-readonly": readOnly,
            "aria-required": ariaRequired,
            className: inputClass,
            disabled,
            id: props.id,
            name: props.name,
            onBlur: instance.handleBlur,
            onChange: instance.handleChange,
            onClick: instance.handleClick,
            onFocus: instance.handleFocus,
            onKeyDown: instance.handleKeyDown,
            onKeyPress: instance.handleKeyPress,
            onKeyUp: instance.handleKeyUp,
            placeholder: props.placeholder,
            readOnly,
            ref: function(inst) {instance._inputElement = inst},
            role: "textbox",
            type,
            value
        };

        // merge all data-props:
        instance._mergeDataAttrs(inputProps);

        if (!Component) {
            element = (<input {...inputProps} />)
        }
        else {
            element = (<Component {...inputProps} {...instance.getAdditionalProps()} />);
        }
        return (
            <div
                className={wrapperClass}
                onFocus={instance.handleContainerFocus}
                ref={node => instance._domNode = node}
                style={props.style}
                tabIndex={props.tabIndex} >
                {label}
                <div className={MAIN_CLASS_PREFIX+"inputbox"}>
                    {element}
                    {errorMsg}
                </div>
                {help}
            </div>
        );
    }

    /**
     * Merges the `data-*` attributes from props into the object
     *
     * @method _mergeDataAttrs
     * @param inputProps {object} the source props which will be extended
     * @private
     * @return object all the data-* attributes
     * @since 0.2.0
     */
    _mergeDataAttrs(inputProps) {
        const props = this.props,
             keys = Object.keys(props);

        keys.forEach(function(key) {
            (key.substr(0,5).toLowerCase()==="data-") && (inputProps[key]=props[key]);
        });
    }
}

Input.propTypes = {
    /**
     * Whether to autofocus the Component.
     *
     * @property autoFocus
     * @type Boolean
     * @since 0.0.1
    */
    autoFocus: PropTypes.bool,

    /**
     * The class that should be set on the element
     *
     * @property className
     * @type String
     * @since 0.0.1
    */
    className: PropTypes.string,

    /**
     * The class that should be set on the underlying input-element
     *
     * @property classNameInput
     * @type String
     * @since 0.0.1
    */
    classNameInput: PropTypes.string,

    /**
     * The error-message that appears when the element is wrong validated.
     *
     * @property errorMsg
     * @type String
     * @since 0.0.1
    */
    errorMsg: PropTypes.string,

    /**
     * Whether the component is disabled
     *
     * @property disabled
     * @type Boolean
     * @since 0.0.1
    */
    disabled: PropTypes.bool,

    /**
     * Whether the parent-form has been validated.
     * This value is needed to determine if the validate-status should be set.
     *
     * @property formValidated
     * @type Boolean
     * @since 0.0.1
    */
    formValidated: PropTypes.bool,

    /**
     * The text that should appear when the element is wrong validated.
     *
     * @property helpText
     * @type String
     * @since 0.0.1
    */
    helpText: PropTypes.string,

    /**
     * The `id` of the element.
     *
     * @property id
     * @type String
     * @since 0.0.1
    */
    id: PropTypes.string,

    /**
     * Whether to mark the Component when successfully validated.
     *
     * @property markValidated
     * @type Boolean
     * @since 0.0.1
    */
    markValidated: PropTypes.bool,

    /**
     * Whether the Component should show an validate-reclamation (star)
     *
     * @property markValidated
     * @type Boolean
     * @since 0.0.1
    */
    markRequired: PropTypes.bool,

    /**
     * The `name` for the element.
     *
     * @property name
     * @type String
     * @since 0.0.1
    */
    name: PropTypes.string,

    /**
     * The `onBlur` function, when happening on the DOM-Element.
     *
     * @property onBlur
     * @type Function
     * @since 0.1.0
    */
    onBlur: PropTypes.func,

    /**
     * The `onChange` function, which should update the `state`.
     *
     * @property onChange
     * @type Function
     * @since 0.0.1
    */
    onChange: PropTypes.func.isRequired,

    /**
     * The `onClick` function, when happening on the DOM-Element.
     *
     * @property onClick
     * @type Function
     * @since 0.0.1
    */
    onClick: PropTypes.func,

    /**
     * The `onFocus` function, when happening on the DOM-Element.
     *
     * @property onFocus
     * @type Function
     * @since 0.1.0
    */
    onFocus: PropTypes.func,

    /**
     * The `onKeyDown` function, when happening on the DOM-Element.
     *
     * @property onKeyDown
     * @type Function
     * @since 0.1.0
    */
    onKeyDown: PropTypes.func,

    /**
     * The `onKeyEnter` function, when the enter-key is pressed.
     *
     * @property onKeyEnter
     * @type Function
     * @since 0.1.0
    */
    onKeyEnter: PropTypes.func,

    /**
     * The `onKeyPress` function, when happening on the DOM-Element.
     *
     * @property onKeyPress
     * @type Function
     * @since 0.1.0
    */
    onKeyPress: PropTypes.func,

    /**
     * The `onKeyUp` function, when happening on the DOM-Element.
     *
     * @property onKeyUp
     * @type Function
     * @since 0.1.0
    */
    onKeyUp: PropTypes.func,

    /**
     * The `placeholder` for the element.
     *
     * @property placeholder
     * @type String
     * @since 0.0.1
    */
    placeholder: PropTypes.string,

    /**
     * Inline style
     *
     * @property style
     * @type object
     * @since 0.0.1
    */
    style: PropTypes.object,

    /**
     * The `type` of the input-element.
     *
     * @property type
     * @type String
     * @since 0.0.1
    */
    type: PropTypes.string,

    /**
     * The tabindex of the Component.
     *
     * @property type
     * @type Number
     * @since 0.0.1
    */
    tabIndex: PropTypes.number,

    /**
     * Whether the property is validated right.
     *
     * @property validated
     * @type Boolean
     * @since 0.0.1
    */
    validated: PropTypes.bool,

    /**
     * The `value` of the input-element.
     *
     * @property value
     * @type String
     * @since 0.0.1
    */
    // not specifying --> may be anything
};

Input.defaultProps = {
    value: ""
};

module.exports = Input;