Rift.IO OSM R1 Initial Submission
[osm/UI.git] / skyquake / framework / widgets / gauge / gauge.js
1
2 /*
3 *
4 * Copyright 2016 RIFT.IO Inc
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 var React = require("react");
20 var ReactDOM = require('react-dom');
21 var MIXINS = require("../mixins/ButtonEventListener.js");
22 var Gauge = require("../../js/gauge-modified.js");
23 var GUID = require("utils/guid");
24 import _ from 'underscore'
25
26
27
28
29 /**
30 * Gauge Component
31 * It's props values and a brief description below
32 *
33 * min: minimum value expected
34 * max: maximum value expected
35 * width: width of gauge in px
36 * height: height of gauge in px
37 * value: the number displayed on the gauge
38 * resize: should the gauge resize with container
39 * unit: the units displayed on the gauge
40 * valueFormat: An object with an 'int' and 'dec' property. The 'int' is the min number of integer digits displayed
41 * and the 'dec' object is the min number of fractional digits displayed.
42 *
43 *
44 **/
45 module.exports = React.createClass({
46 displayName: 'Gauge',
47 mixins:MIXINS,
48 propTypes: {
49 min: React.PropTypes.number,
50 max: React.PropTypes.number,
51 width: React.PropTypes.number,
52 height: React.PropTypes.string,
53 value: React.PropTypes.number,
54 resize: React.PropTypes.bool,
55 isAggregate: React.PropTypes.bool,
56 units: React.PropTypes.string,
57 valueFormat: React.PropTypes.shape({
58 'int': React.PropTypes.number,
59 'dec': React.PropTypes.number
60 })
61 },
62 clone: function(obj) {
63 if (null == obj || "object" != typeof obj) return obj;
64 var copy = obj.constructor();
65 for (var attr in obj) {
66 if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
67 }
68 return copy;
69 },
70
71 /**
72 * Defines default state.
73 *
74 * min: minimum value expected
75 * max: maximum value expected
76 * nSteps: fixed number for now. The number of ticks in the gauge.
77 * width: width of gauge in px
78 * height: height of gauge in px
79 * value: the number displayed on the gauge
80 * resize: should the gauge resize with container
81 * unit: the units displayed on the gauge
82 * valueFormat: An object with an 'int' and 'dec' property. The 'int' is the min number of integer digits displayed
83 * and the 'dec' object is the min number of fractional digits displayed.
84 */
85 getInitialState: function() {
86 var valueFormatState = null
87 this.gauge = null;
88 this.gaugeID = GUID();
89 if (!this.props.valueFormat) {
90 if ((this.props.max && this.props.max > 1000) || this.props.value) {
91 valueFormatState = {
92 "int": 1,
93 "dec": 0
94 };
95 } else {
96 valueFormatState = {
97 "int": 1,
98 "dec": 2
99 };
100 }
101 } else {
102 valueFormatState = this.props.valueFormat;
103 }
104 return {
105 //sizeOfButton: this.props.size || '', //There is no Medium value in CSS, default size is the absence of a value
106 min: this.props.min || 0,
107 max: this.props.max || 0,
108 nSteps: 14,
109 height: this.props.height || 200,
110 width: this.props.width || 200,
111 color: this.props.color || 'hsla(212, 57%, 50%, 1)',
112 value: this.props.value || 0,
113 valueFormat: valueFormatState,
114 isAggregate: this.props.isAggregate || false,
115 units: this.props.units || '',
116 resize:this.props.resize || false
117
118 }
119 },
120
121
122 /**
123 * Called when props are changed. Syncs props with state.
124 */
125 componentWillReceiveProps: function(nextProps) {
126 this.setState({
127 max:nextProps.max || this.state.max,
128 value:nextProps.value || 0,
129 valueFormat:nextProps.valueFormat || this.state.valueFormat
130 });
131 },
132
133 /**
134 * Calls the render on the gauge object once the component first mounts
135 */
136 componentDidMount: function() {
137 this.canvasRender(this.state);
138 },
139
140 /**
141 * If any of the state variables have changed, the component should update.
142 * Note, this is where the render step occures for the gauge object.
143 */
144 shouldComponentUpdate: function(nextProps, nextState) {
145 var currentStateString = String(this.state.max) + String(this.state.valueFormat.int) + String(this.state.valueFormat.dec) + String(this.state.value);
146 var nextStateString = String(nextState.max) + String(nextState.valueFormat.int) + String(nextState.valueFormat.dec) + String(nextState.value);
147 if (currentStateString == nextStateString) {
148 return false;
149 }
150 this.state.valueFormat = this.determineValueFormat(nextState.value);
151 this.canvasRender(nextState);
152 return true;
153 },
154
155 /**
156 * Default value format based on units.
157 */
158 determineValueFormat: function(value) {
159 if (value > 999 || this.state.units == "%") {
160 return {
161 "int": 1,
162 "dec": 0
163 }
164 }
165
166 return {
167 "int": 1,
168 "dec": 2
169 }
170 },
171
172
173 /**
174 * Render step for the gauge object. Sets some defaults, passes some of the component's state down.
175 */
176 canvasRender: function(state) {
177 if (state.max == state.min) {
178 state.max = 14;
179 }
180 var range = state.max - state.min;
181 var step = Math.round(range / state.nSteps);
182 var majorTicks = [];
183 for (var i = 0; i <= state.nSteps; i++) {
184 majorTicks.push(state.min + (i * step));
185 }
186 var redLine = state.min + (range * 0.9);
187 var config = {
188 isAggregate: state.isAggregate,
189 renderTo: ReactDOM.findDOMNode(document.getElementById(this.gaugeID)),
190 width: state.width,
191 height: state.height,
192 glow: false,
193 units: state.units,
194 title: false,
195 minValue: state.min,
196 maxValue: state.max,
197 majorTicks: majorTicks,
198 valueFormat: this.determineValueFormat(self.value),
199 minorTicks: 0,
200 strokeTicks: false,
201 highlights: [],
202 colors: {
203 plate: 'rgba(0,0,0,0)',
204 majorTicks: 'rgba(15, 123, 182, .84)',
205 minorTicks: '#ccc',
206 title: 'rgba(50,50,50,100)',
207 units: 'rgba(50,50,50,100)',
208 numbers: '#fff',
209 needle: {
210 start: 'rgba(255, 255, 255, 1)',
211 end: 'rgba(255, 255, 255, 1)'
212 }
213 }
214 };
215
216 var min = config.minValue;
217 var max = config.maxValue;
218 var N = 1000;
219 var increment = (max - min) / N;
220 for (i = 0; i < N; i++) {
221 var temp_color = 'rgb(0, 172, 238)';
222 if (i > 0.5714 * N && i <= 0.6428 * N) {
223 temp_color = 'rgb(0,157,217)';
224 } else if (i >= 0.6428 * N && i < 0.7142 * N) {
225 temp_color = 'rgb(0,142,196)';
226 } else if (i >= 0.7142 * N && i < 0.7857 * N) {
227 temp_color = 'rgb(0,126,175)';
228 } else if (i >= 0.7857 * N && i < 0.8571 * N) {
229 temp_color = 'rgb(0,122,154)';
230 } else if (i >= 0.8571 * N && i < 0.9285 * N) {
231 temp_color = 'rgb(0,96,133)';
232 } else if (i >= 0.9285 * N) {
233 temp_color = 'rgb(0,80,112)';
234 }
235 config.highlights.push({
236 from: i * increment,
237 to: increment * (i + 2),
238 color: temp_color
239 })
240 }
241 var updateSize = _.debounce(function() {
242 config.maxValue = state.max;
243 }, 500);
244 if (state.resize) $(window).resize(updateSize)
245
246 if (this.gauge) {
247 this.gauge.setValue(Math.ceil(state.value* 100) / 100)
248 this.gauge.updateConfig(config);
249 } else {
250 this.gauge = new Gauge(config);
251 this.gauge.setValue(Math.ceil(state.value* 100) / 100)
252 this.gauge.draw();
253 }
254 },
255
256 /**
257 * Renders the Gauge Component
258 * Returns the canvas element the gauge will be housed in.
259 * @returns {*}
260 */
261 render: function() {
262 var gaugeDOM = React.createElement("div", null,
263 React.createElement("canvas",
264 {className: "rwgauge", style:
265 {width:'100%','maxWidth':this.state.width + 'px','maxHeight':this.state.width},
266 id:this.gaugeID
267 }
268 )
269 )
270
271
272
273 return gaugeDOM;
274 }
275 });