| |
| /* |
| * |
| * 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; |
| } |
| }); |