blob: dc1a0839e496835de49f48e7649f8e097723e9ff [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 ReactDOM = require('react-dom');
var MIXINS = require("../mixins/ButtonEventListener.js");
var Gauge = require("../../js/gauge-modified.js");
var GUID = require("utils/guid");
import _ from 'underscore'
/**
* Gauge Component
* It's props values and a brief description below
*
* min: minimum value expected
* max: maximum value expected
* width: width of gauge in px
* height: height of gauge in px
* value: the number displayed on the gauge
* resize: should the gauge resize with container
* unit: the units displayed on the gauge
* valueFormat: An object with an 'int' and 'dec' property. The 'int' is the min number of integer digits displayed
* and the 'dec' object is the min number of fractional digits displayed.
*
*
**/
module.exports = React.createClass({
displayName: 'Gauge',
mixins:MIXINS,
propTypes: {
min: React.PropTypes.number,
max: React.PropTypes.number,
width: React.PropTypes.number,
height: React.PropTypes.string,
value: React.PropTypes.number,
resize: React.PropTypes.bool,
isAggregate: React.PropTypes.bool,
units: React.PropTypes.string,
valueFormat: React.PropTypes.shape({
'int': React.PropTypes.number,
'dec': React.PropTypes.number
})
},
clone: function(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
},
/**
* Defines default state.
*
* min: minimum value expected
* max: maximum value expected
* nSteps: fixed number for now. The number of ticks in the gauge.
* width: width of gauge in px
* height: height of gauge in px
* value: the number displayed on the gauge
* resize: should the gauge resize with container
* unit: the units displayed on the gauge
* valueFormat: An object with an 'int' and 'dec' property. The 'int' is the min number of integer digits displayed
* and the 'dec' object is the min number of fractional digits displayed.
*/
getInitialState: function() {
var valueFormatState = null
this.gauge = null;
this.gaugeID = GUID();
if (!this.props.valueFormat) {
if ((this.props.max && this.props.max > 1000) || this.props.value) {
valueFormatState = {
"int": 1,
"dec": 0
};
} else {
valueFormatState = {
"int": 1,
"dec": 2
};
}
} else {
valueFormatState = this.props.valueFormat;
}
return {
//sizeOfButton: this.props.size || '', //There is no Medium value in CSS, default size is the absence of a value
min: this.props.min || 0,
max: this.props.max || 0,
nSteps: 14,
height: this.props.height || 200,
width: this.props.width || 200,
color: this.props.color || 'hsla(212, 57%, 50%, 1)',
value: this.props.value || 0,
valueFormat: valueFormatState,
isAggregate: this.props.isAggregate || false,
units: this.props.units || '',
resize:this.props.resize || false
}
},
/**
* Called when props are changed. Syncs props with state.
*/
componentWillReceiveProps: function(nextProps) {
this.setState({
max:nextProps.max || this.state.max,
value:nextProps.value || 0,
valueFormat:nextProps.valueFormat || this.state.valueFormat
});
},
/**
* Calls the render on the gauge object once the component first mounts
*/
componentDidMount: function() {
this.canvasRender(this.state);
},
/**
* If any of the state variables have changed, the component should update.
* Note, this is where the render step occures for the gauge object.
*/
shouldComponentUpdate: function(nextProps, nextState) {
var currentStateString = String(this.state.max) + String(this.state.valueFormat.int) + String(this.state.valueFormat.dec) + String(this.state.value);
var nextStateString = String(nextState.max) + String(nextState.valueFormat.int) + String(nextState.valueFormat.dec) + String(nextState.value);
if (currentStateString == nextStateString) {
return false;
}
this.state.valueFormat = this.determineValueFormat(nextState.value);
this.canvasRender(nextState);
return true;
},
/**
* Default value format based on units.
*/
determineValueFormat: function(value) {
if (value > 999 || this.state.units == "%") {
return {
"int": 1,
"dec": 0
}
}
return {
"int": 1,
"dec": 2
}
},
/**
* Render step for the gauge object. Sets some defaults, passes some of the component's state down.
*/
canvasRender: function(state) {
if (state.max == state.min) {
state.max = 14;
}
var range = state.max - state.min;
var step = Math.round(range / state.nSteps);
var majorTicks = [];
for (var i = 0; i <= state.nSteps; i++) {
majorTicks.push(state.min + (i * step));
}
var redLine = state.min + (range * 0.9);
var config = {
isAggregate: state.isAggregate,
renderTo: ReactDOM.findDOMNode(document.getElementById(this.gaugeID)),
width: state.width,
height: state.height,
glow: false,
units: state.units,
title: false,
minValue: state.min,
maxValue: state.max,
majorTicks: majorTicks,
valueFormat: this.determineValueFormat(self.value),
minorTicks: 0,
strokeTicks: false,
highlights: [],
colors: {
plate: 'rgba(0,0,0,0)',
majorTicks: 'rgba(15, 123, 182, .84)',
minorTicks: '#ccc',
title: 'rgba(50,50,50,100)',
units: 'rgba(50,50,50,100)',
numbers: '#fff',
needle: {
start: 'rgba(255, 255, 255, 1)',
end: 'rgba(255, 255, 255, 1)'
}
}
};
var min = config.minValue;
var max = config.maxValue;
var N = 1000;
var increment = (max - min) / N;
for (i = 0; i < N; i++) {
var temp_color = 'rgb(0, 172, 238)';
if (i > 0.5714 * N && i <= 0.6428 * N) {
temp_color = 'rgb(0,157,217)';
} else if (i >= 0.6428 * N && i < 0.7142 * N) {
temp_color = 'rgb(0,142,196)';
} else if (i >= 0.7142 * N && i < 0.7857 * N) {
temp_color = 'rgb(0,126,175)';
} else if (i >= 0.7857 * N && i < 0.8571 * N) {
temp_color = 'rgb(0,122,154)';
} else if (i >= 0.8571 * N && i < 0.9285 * N) {
temp_color = 'rgb(0,96,133)';
} else if (i >= 0.9285 * N) {
temp_color = 'rgb(0,80,112)';
}
config.highlights.push({
from: i * increment,
to: increment * (i + 2),
color: temp_color
})
}
var updateSize = _.debounce(function() {
config.maxValue = state.max;
}, 500);
if (state.resize) $(window).resize(updateSize)
if (this.gauge) {
this.gauge.setValue(Math.ceil(state.value* 100) / 100)
this.gauge.updateConfig(config);
} else {
this.gauge = new Gauge(config);
this.gauge.setValue(Math.ceil(state.value* 100) / 100)
this.gauge.draw();
}
},
/**
* Renders the Gauge Component
* Returns the canvas element the gauge will be housed in.
* @returns {*}
*/
render: function() {
var gaugeDOM = React.createElement("div", null,
React.createElement("canvas",
{className: "rwgauge", style:
{width:'100%','maxWidth':this.state.width + 'px','maxHeight':this.state.width},
id:this.gaugeID
}
)
)
return gaugeDOM;
}
});