blob: 62e1bcc10544353d9081d5cc3ae48847d60b41ac [file] [log] [blame]
/*
*
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var React = require('react');
var ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
var validator = require('validator');
/**
* A text input component.
* It's props values and a brief description below
*
* value: Holds the initial text content of the input.
* label: The text content of the label.
* requiredText: The text content of the "if required" message.
* errorText: The text content of the error message.
* placeholder: The initial placeholder text of the input
* ClassName: Css Classes applied to the element.
* size: The size of the element.
* minWidth: Minimum width of the element.
* maxWidth: Maximum width of the element.
* isRequired: A boolean indicating whether or not the input is required.
* isDisabled: A boolean indicating the disabled state of the element.
* isReadOnly: A boolean indicating whether or not the input is read only.
* pattern: A regex putting constraints on what the user input can be.
* maxLength: The hard limit on how many characters can be in the input.
**/
module.exports = React.createClass({
displayName: "TextInput",
mixins:[ButtonEventListenerMixin],
propTypes: {
value: React.PropTypes.string,
label: React.PropTypes.string,
requiredText: React.PropTypes.string,
errorText: React.PropTypes.string,
placeholder: React.PropTypes.string,
className: React.PropTypes.string,
size: React.PropTypes.string,
minWidth: React.PropTypes.number,
maxWidth: React.PropTypes.number,
isRequired: React.PropTypes.bool,
isDisabled: React.PropTypes.bool,
isReadOnly: React.PropTypes.bool,
pattern: React.PropTypes.string,
maxLength: React.PropTypes.number,
instructions: React.PropTypes.string
},
/**
* Sets the default input state.
* If there is no description for the variable, assume it's the same as it's props counterpart.
*
* value: The current text contents of the input.
* isActive: Boolean to indicate if input is active.
* isHovered: Boolean to indicate if the input is being hovered over.
* isFocused: Boolean to indicate if the input has been focused.
* isDisabled: Boolean to indicate if input has been disabled.
*
* @returns {{sizeOfButton: (*|string), isActive: boolean, isHovered: boolean, isFocused: boolean, isDisabled: (*|boolean),
* isReadOnly: (*|.textInput.isReadOnly|.exports.propTypes.isReadOnly|.exports.getInitialState.isReadOnly|boolean),
* isRequired: (*|.textInput.isRequired|.exports.propTypes.isRequired|.exports.getInitialState.isRequired|isRequired|null),
* isValid: null, isSuccess: null}}
*/
getInitialState: function() {
return {
value: this.props.value || '',
label: this.props.label || "",
requiredText: this.props.requiredText || "Required",
instructionsText: this.props.instructions || "",
errorText: "",
size: this.props.size || '',
isActive: false,
isHovered: false,
isFocused: false,
isDisabled: this.props.isDisabled || false,
isReadOnly: this.props.isReadOnly || false,
isRequired: this.props.isRequired || null,
isValid: null, // Three way bool. Valid: true. Invalid: false. Not acted on: null.
isSuccess: null // Three way bool. Success: true. Error: false. Not acted on: null.
}
},
/**
* If any of the state variables have changed, the component should update.
* "nextProps" and "nextState" hold the state and property variables as they will be after the update.
* "this.props" and "this.state" hold the state and property variables as they were before the update.
* returns true if the state have changed. Otherwise returns false.
* @param nextProps
* @param nextState
* @returns {boolean}
*/
shouldComponentUpdate: function(nextProps, nextState) {
var currentStateString = this.state.isReadOnly + this.state.isDisabled + this.state.isActive + this.state.isFocused +
this.state.isHovered + this.state.isValid + this.state.isSuccess + this.state.value;
var nextStateString = nextState.isReadOnly + nextState.isDisabled + nextState.isActive + nextState.isFocused +
nextState.isHovered + nextState.isValid + nextState.isSuccess + nextState.value;
if (currentStateString == nextStateString) {
return false;
}
return true;
},
/**
* Makes sure that when the user types new input, the contents of the input changes accordingly.
*
* @param event
*/
handleChange: function(event) {
this.setState({value:event.target.value});
},
/**
* Makes sure that when the user types new input, the contents of the input changes accordingly.
*
* @param event
*/
handleValidation: function(event) {
if (this.props.isRequired) {
if (validator.isNull(event.target.value) || event.target.value == '') {
this.setState({
errorText: this.props.errorText,
isValid: false
});
} else {
this.setState({
errorText: '',
isValid: true
});
}
}
if (this.props.pattern) {
if (!validator.matches(event.target.value, this.props.pattern)) {
this.setState({
errorText: this.props.errorText,
isValid: false
});
} else {
this.setState({
errorText: '',
isValid: true
});
}
}
},
/**
* Returns a string reflecting the current state of the input.
* If the component state "isDisabled" is true, returns a string "disabled".
* If the component state "isReadOnly" is true, returns a string "readonly".
* Otherwise returns a string containing a word for each state that has been set to true.
* (ie "active focused" if the states active and focused are true, but hovered is false).
* @returns {string}
*/
setComponentState: function() {
var ret = "";
if (this.state.isDisabled) {
return "disabled";
}
if (this.state.isReadOnly) {
return "readonly";
}
if (this.state.isActive) {
ret += "active ";
}
if (this.state.isHovered) {
ret += "hovered ";
}
if (this.state.isFocused) {
ret += "focused ";
}
return ret;
},
/**
* Renders the Text Input component.
*
**/
render: function() {
var value = this.state.value;
var input = null;
var input_style = {};
var input_state = this.setComponentState();
// The following if statements translates the min/max width from a number into a string.
if (this.props.minWidth) {
input_style['min-width'] = String(this.props.minWidth) + 'px';
}
if (this.props.maxWidth) {
input_style['max-width'] = String(this.props.maxWidth) + 'px';
}
// The input element.
input = React.createElement("input", {
ref: "inputRef",
"data-state": input_state,
"data-validate": this.state.isValid,
value: value,
placeholder: this.props.placeholder,
pattern: this.props.pattern,
maxLength: this.props.maxLength,
required: this.state.isRequired,
disabled: this.state.isDisabled,
readOnly: this.state.isReadOnly,
onChange: this.handleChange,
onClick: this.onClick,
onMouseUp: this.onMouseUp,
onMouseDown: this.onMouseDown,
onMouseOver: this.onMouseOver,
onMouseEnter: this.onMouseEnter,
onMouseLeave: this.onMouseLeave,
onMouseOut: this.onMouseOut,
onTouchCancel: this.onTouchCancel,
onTouchEnd: this.onTouchEnd,
onTouchMove: this.onTouchMove,
onTouchStart: this.onTouchStart,
onKeyDown: this.onKeyDown,
onKeyPress: this.onKeyPress,
onKeyUp: this.handleValidation,
onFocus: this.onFocus,
onBlur: this.handleValidation,
className: (this.props.className || "rw-textinput"),
tabIndex: 0
},
null
);
// The "if required" element. It displays a label if the element is required.
if(this.props.isRequired == true){
var requiredEle = React.createElement("small", {className: "rw-form__required-label"}, this.state.requiredText);
}
//
if(this.state.isValid == false) {
var validations = React.createElement("svg", {className: "rw-form__icon"}, this.state.errorText);
}
// The label element associated with the input.
var label = React.createElement("label", {className: "rw-form__label"}, this.state.label, requiredEle, input, validations);
// The "error" element. It pops up as a message if there is an error with the input.
if(this.state.errorText != "") {
var error = React.createElement("p", {className: "rw-form__message-error"}, this.state.errorText);
}
//
if(this.state.instructionsText != ""){
var instructions = React.createElement("p", {className: "rw-form__instructions"}, this.state.instructionsText)
}
// The parent element for all.
var ret = React.createElement("div", {
"data-state": input_state,
required: this.state.isRequired,
disabled: this.state.isDisabled,
readOnly: this.state.isReadOnly,
"data-validate": this.state.isValid,
className: "rw-form"
}, label, error, instructions);
return ret;
}
});