Rift.IO OSM R1 Initial Submission
[osm/UI.git] / skyquake / framework / widgets / button / rw.button.js
diff --git a/skyquake/framework/widgets/button/rw.button.js b/skyquake/framework/widgets/button/rw.button.js
new file mode 100644 (file)
index 0000000..41730eb
--- /dev/null
@@ -0,0 +1,261 @@
+
+/*
+ * 
+ *   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.
+ *
+ */
+import './button.scss';
+import Loader from '../loading-indicator/loadingIndicator.jsx';
+var React = require('react');
+var ButtonEventListenerMixin = require('../mixins/ButtonEventListener.js');
+
+
+/**
+ *  A generic button component.
+ *  It's props values and a brief description below
+ *
+ *  Label: The label of the button. What it displays at any given time.
+ *  Icon: A url for an icon that will be displayed on the button.  Leave blank if no icon required.
+ *  Class: Css Classes applied to the element.
+ *  sizeOfButton: The preset sizes for the button (small, default, large, xlarge, expand).
+ *  minWidth: Minimum Width of the button.
+ *  maxWidth: Maximum Width of the button.
+ **/
+module.exports = React.createClass({
+  displayName: "Button",
+  mixins:[ButtonEventListenerMixin],
+  propTypes: {
+    label:           React.PropTypes.string,
+    icon:            React.PropTypes.array,
+    className:       React.PropTypes.string,
+    //sizeOfButton:    React.PropTypes.string,
+    minWidth:        React.PropTypes.string,
+    maxWidth:        React.PropTypes.string,
+    //isActive:        React.PropTypes.bool,
+    //isFocused:       React.PropTypes.bool,
+    //isHovered:       React.PropTypes.bool,
+    isDisabled:      React.PropTypes.bool
+  },
+
+
+  /**
+   * Defines default state.
+   * sizeOfButton: See Prop type definitions.
+   * class: See Prop type definitions.
+   * label: See Prop type definitions.
+   * isActive: Boolean to indicate if button is active.
+   * isHovered: Boolean to indicate if the button is being hovered over.
+   * isFocused: Boolean to indicate if the button has been focused.
+   * isDisabled: Boolean to indicate if button has been disabled.
+   * @returns {{sizeOfButton: (*|string), class: *, isActive: boolean, isHovered: boolean,
+   *            isFocused: boolean, isDisabled: (*|boolean), label: *}}
+   */
+  getInitialState: function() {
+    return {
+      //sizeOfButton:   this.props.size || '',  //There is no Medium value in CSS, default size is the absence of a value
+      className:      this.props.className || 'rw-button-primary', //Default value is 'rw-button-primary' which is the primary one
+      label:          this.props.label,
+      isActive:       false,
+      isHovered:      false,
+      isFocused:      false,
+      isLoading:      this.props.isLoading || false,
+      isDisabled:     this.props.isDisabled || false
+    }
+  },
+
+
+  /**
+   * 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.label + this.state.isDisabled + this.state.isActive + this.state.isFocused +
+      this.state.isHovered + this.props.isLoading;
+    var nextStateString = nextState.label + nextState.isDisabled + nextState.isActive + nextState.isFocused +
+      nextState.isHovered + nextProps.isLoading;
+
+    if (currentStateString == nextStateString) {
+      return false;
+    }
+    return true;
+  },
+
+
+  /**
+   * Returns a string reflecting the current state of the button.
+   * If the button state "isDisabled" is true, returns a string "disabled".
+   * 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}
+   */
+  setButtonState: function() {
+    var ret = "";
+    if (this.state.isDisabled) {
+      return "disabled";
+    }
+    if (this.state.isActive) {
+      ret += "active ";
+    }
+    if (this.state.isHovered) {
+      ret += "hovered ";
+    }
+    if (this.state.isFocused) {
+      ret += "focused ";
+    }
+    return ret;
+  },
+
+
+
+  /**
+   * Track the width if set and write into markup using Style attribute
+   * Returns the minWidth and maxWidth prop in a string
+   * @returns {{}}
+   */
+  setButtonWidth:function(){

+    var width = {};


+
+    if (this.props.minWidth) {

+      width.minWidth = String(this.props.minWidth);

+    }

+    if (this.props.maxWidth) {

+      width.maxWidth = String(this.props.maxWidth);

+    }

+
+    return width;

+  },
+
+
+
+  /**
+   * Apply the size of the button to the icon directly
+   * Returns a string indicating the icon size.
+   * @returns {string}
+   */
+  /*
+  setIconSize:function(){
+
+    var iconClass = "rw-icon";
+
+    if(this.props.size){
+      iconClass += "-" + this.props.size;
+    }
+    return iconClass;
+  },
+  */
+
+
+
+  /**
+   * Builds the list of classes.
+   * Returns a string holding each class seperated by a space.
+   * @returns {string}
+   */
+  /*
+  setButtonClass:function() {
+    var buttonClass = "";
+    var buttonClassType = "rw-button-primary";
+    // If the size is declared, add it in
+    if (this.state.sizeOfButton) {
+      buttonClass += this.state.sizeOfButton;
+    }
+    //
+    if (typeof(this.props.className) != "undefined") {
+      this.props.className.push("rw-button-secondary");
+
+      // Run through the array and check all the values
+      for (var i = 0; i < this.props.className.length; i++) {
+
+        if (this.props.className[i].indexOf("secondary") > -1) {
+          buttonClassType = "rw-button-secondary";  // If the value of the array is equal to the string "secondary", add a predefined string
+        } else {
+          buttonClass += " " + this.props.className[i];  // Else just write the value of the array
+        }
+      }
+    }
+    buttonClass += " " + buttonClassType;  //Add the button style type either primary or secondary
+    return buttonClass;
+  },
+  */
+
+  /**
+   * Builds an array of html elements for the icons and returns them.
+   * @returns {Array}
+   */
+  setIconElement: function() {
+    var button_icon = [];
+
+    if (typeof(this.props.icon) != "undefined") {
+      for (var i = 0; i < this.props.icon.length; i++) {
+        button_icon.push(React.createElement('svg', {
+          className: "rw-button__icon",
+          key: i,
+          dangerouslySetInnerHTML: {__html: '<use xlink:href="#' + this.props.icon[i] + '" />'}  //Using a React method to drop in a hack since React does not support xlink:href yet
+        }));
+      }
+    }
+    return button_icon;
+  },
+
+  /**
+   * Renders the Button Component
+   * Returns a react component that constructs the html that houses the button component.
+   * @returns {*}
+   */
+  render: function() {
+    var button = null;
+    var button_style = this.setButtonWidth();
+    var button_state = this.setButtonState();
+    var button_class = this.state.className;
+    var button_icon = this.setIconElement();
+    var display = this.state.label;
+    if (this.props.isLoading) {
+      display = React.createElement(Loader, {show: true, size: '1rem', color: this.props.loadingColor});
+    }
+
+    button = React.createElement("button", {
+        className:         button_class,
+        "data-state":      button_state,
+        style:             button_style,
+
+        // onClick:           this.onClick,
+        onClick:           this.props.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.onKeyUp,
+        onFocus:           this.onFocus,
+        onBlur:            this.onBlur
+      },
+      button_icon,
+      React.createElement("span", {className: "rw-button__label"}, display)
+    );
+    return button;
+  }
+});