| |
| /* |
| * |
| * 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 mixin = require('../mixins/ButtonEventListener.js') |
| //TODO: Many values are hard coded, need to make bullet "smarter" |
| /** |
| * A bullet graph component. |
| * It's props values and a brief description below |
| * |
| * vertical: If true, the bar rises and falls vertically |
| * value: The value displayed |
| * min: The minimum value expected |
| * max: The maximum value expected |
| * bulletColor: The fill color of the value section of the graph |
| * radius: Radius of graph corners. |
| * containerMarginX: Container's margin along x-axis |
| * containerMarginY: Container's margin along y-axis |
| * width: Width of bullet graph in pixels |
| * height: Height of bullet graph in pixels |
| * fontSize: Font size of text in pixels |
| * textMarginX: margin for text on x-axis |
| * textMarginY: Margin for text on y-axis |
| * units: units displayed. Also changes whether a max value is displayed. |
| **/ |
| module.exports = React.createClass({ |
| displayName: 'Bullet', |
| mixins:mixin.MIXINS, |
| propTypes: { |
| vertical: React.PropTypes.bool, |
| value: React.PropTypes.number, |
| min: React.PropTypes.number, |
| max: React.PropTypes.number, |
| bulletColor: React.PropTypes.string, |
| radius: React.PropTypes.number, |
| containerMarginX: React.PropTypes.number, |
| containerMarginY: React.PropTypes.number, |
| bulletMargin: React.PropTypes.number, |
| width: React.PropTypes.number, |
| height: React.PropTypes.number, |
| markerX: React.PropTypes.number, |
| fontSize: React.PropTypes.number, |
| textMarginX: React.PropTypes.number, |
| textMarginY: React.PropTypes.number, |
| units: React.PropTypes.string |
| }, |
| |
| getDefaultProps: function() { |
| return { |
| vertical: false, |
| value: 0, |
| min: 0, |
| max: 100, |
| bulletColor: "blue", |
| radius: 4, |
| containerMarginX: 0, |
| containerMarginY: 0, |
| bulletMargin: 0, |
| width: 512, |
| height: 64, |
| markerX: -100, |
| fontSize: 16, |
| textMarginX: 5, |
| textMarginY: 42, |
| units:'' |
| } |
| }, |
| |
| /** |
| * Defines default state. |
| * value: The value displayed |
| */ |
| getInitialState: function() { |
| return { |
| value: this.props.value |
| } |
| }, |
| |
| /** |
| * Called when the props are updated. |
| * Syncs the state value with the prop value. |
| * @params nextProps |
| */ |
| componentWillReceiveProps: function(nextProps) { |
| this.setState({value:nextProps.value || this.state.value}) |
| }, |
| |
| /** |
| * Before the component will mount, the width value is recalculed based on browser. |
| */ |
| componentWillMount: function() { |
| var isIE = false || !!document.documentMode; |
| var range = this.props.max - this.props.min; |
| var normalizedValue = (this.state.value - this.props.min) / range; |
| this.isPercent = (!this.props.units || this.props.units == '')? true:false |
| if (isIE) { |
| this.bulletWidth = String(Math.round(100 * normalizedValue)) + "%"; |
| } else { |
| this.bulletWidth = this.props.width - (2 * this.props.containerMarginX); |
| } |
| this.displayValue = (this.isPercent)? String(Math.round(normalizedValue * 100)) + "%" : this.props.value |
| |
| }, |
| |
| /** |
| * When the component mounts, this function sets the animation for smooth transition on value change. This only |
| * happens if the user's browser is not IE. |
| */ |
| componentDidMount: function() { |
| var isIE = false || !!document.documentMode; |
| var range = this.props.max - this.props.min; |
| var normalizedValue = (this.state.value - this.props.min) / range; |
| if (!isIE) { |
| var transform = "scaleX(" + normalizedValue + ")"; |
| var bullet = React.findDOMNode(this.refs.bulletRef); |
| bullet.style.transform = transform; |
| bullet.style["-webkit-transform"] = transform; |
| } |
| }, |
| |
| /** |
| * On update, reaplies transformation and width changes made in componentWillMount and componentDidMount |
| * @param nextProps |
| * @param nextState |
| * @returns {boolean} |
| */ |
| shouldComponentUpdate: function(nextProps, nextState) { |
| |
| if (String(this.state.value) == String(nextState.value)) { |
| return false; |
| } else { |
| var isIE = false || !!document.documentMode; |
| var range = this.props.max - this.props.min; |
| var normalizedValue = (nextState.value - this.props.min) / range; |
| |
| if (isIE) { |
| this.bulletWidth = String(Math.round(100 * normalizedValue)) + "%"; |
| } else { |
| this.bulletWidth = this.props.width - (2 * this.props.containerMarginX); |
| var transform = "scaleX(" + normalizedValue + ")"; |
| var bullet = React.findDOMNode(this.refs.bulletRef); |
| bullet.style.transform = transform; |
| bullet.style["-webkit-transform"] = transform; |
| } |
| this.displayValue = (this.isPercent)? String(Math.round(normalizedValue * 100)) + "%" : nextState.value |
| |
| return true; |
| } |
| }, |
| |
| |
| |
| /** |
| * Renders the Bullet Component |
| * @returns {*} |
| */ |
| render: function() { |
| |
| // The text element that displays the difference between the max value and the current value. |
| var maxDOMEl = (!this.isPercent)? null : React.createElement("text", { |
| //X needs better logic |
| // x: this.props.width - this.props.height * 1.25, |
| x: this.props.width - (130 - this.props.textMarginX), |
| y: this.props.textMarginY, |
| fontFamily: "Verdana", |
| fontSize: this.props.fontSize, |
| fill: "#ffffff"}, String(this.props.max - this.state.value) + "%"); |
| |
| |
| // The main bullet element. Made up of a static black rect in the background, |
| // a moving colored rect overlayed on top and a text element for the current value. |
| var bulletDOM = React.createElement("svg", { |
| width: "100%", |
| height: "100%", |
| viewBox: "0 0 512 " + this.props.height, |
| preserveAspectRatio: "none"}, |
| React.createElement("rect", {className: "bullet-container", |
| width: this.props.width - (2 * this.props.containerMarginX), |
| height: this.props.height - (2 * this.props.containerMarginY), |
| x: this.props.containerMarginX, |
| y: this.props.containerMarginY, |
| rx: this.props.radius, |
| ry: this.props.radius}, null), |
| React.createElement("svg", { |
| x: this.props.containerMarginX, |
| y: this.props.bulletMargin}, |
| React.createElement("rect", { |
| className: "bullet", |
| ref:"bulletRef", |
| fill: this.props.bulletColor, |
| width: this.bulletWidth, |
| height: this.props.height - (2 * this.props.bulletMargin), |
| rx: this.props.radius, |
| ry: this.props.radius |
| }) |
| ), |
| React.createElement("line", {className: "bullet-marker", |
| x1: this.props.markerX, |
| x2: this.props.markerX, |
| y1: this.props.markerY1, |
| y2: this.props.markerY2}), |
| React.createElement("text", { |
| x: this.props.textMarginX, |
| y: this.props.textMarginY, |
| "fontFamily": "Verdana", |
| "fontSize": this.props.fontSize, |
| fill: "#ffffff"}, this.displayValue |
| ), |
| maxDOMEl |
| ); |
| |
| |
| return bulletDOM; |
| } |
| }); |