Skip to content

Commit

Permalink
Add react (electricitymaps#2098)
Browse files Browse the repository at this point in the history
* Add react

* Fix bindings

* Fix
  • Loading branch information
corradio authored Nov 23, 2019
1 parent 247f4ad commit 27e956d
Show file tree
Hide file tree
Showing 21 changed files with 2,071 additions and 2,742 deletions.
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,3 @@ services:
- './web/topogen.sh:/home/web/topogen.sh'
- './web/views:/home/web/views'
- './web/webpack.config.js:/home/web/webpack.config.js'


46 changes: 38 additions & 8 deletions web/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
{
"extends": "airbnb",
"env": {
"browser": true
},
"rules": {
"no-underscore-dangle": "off",
"class-methods-use-this": "off",
"linebreak-style": "off"
"extends": "airbnb",
"env": {
"browser": true,
},
"rules": {
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "ignore"
}],
"brace-style": "warn",
"class-methods-use-this": "warn",
"dot-notation": "warn",
"implicit-arrow-linebreak": "off",
"import/prefer-default-export": "off",
"max-len": "warn",
"no-await-in-loop": "warn",
"no-continue": "warn",
"no-nested-ternary": "warn",
"no-param-reassign": ["error", { "props": false }],
"no-trailing-spaces": "warn",
"no-underscore-dangle": "warn",
"no-unused-vars": "warn",
"react/destructuring-assignment": "warn",
"quote-props": "warn",
"react/jsx-one-expression-per-line": "warn",
"react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }],
"react/prop-types": "warn",
"react/sort-comp": "warn"
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".json"]
}
}
}
}
30 changes: 16 additions & 14 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,34 @@
"lodash": "^4.17.13",
"mapbox-gl": "^0.49.0",
"moment": "^2.23.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-redux": "^7.1.3",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",
"topojson": "^3.0.2"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.3",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-polyfill": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-runtime": "^6.26.0",
"@babel/core": "^7.7.2",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.1",
"@babel/preset-react": "^7.7.0",
"@babel/runtime": "^7.7.2",
"babel-loader": "^8.0.6",
"colors": "^1.3.2",
"css-loader": "^0.28.10",
"eslint": "^6.5.1",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.15.1",
"eslint": "^5.5.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "2.12.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.14.3",
"mini-css-extract-plugin": "^0.8.0",
"ndjson-cli": "^0.3.1",
"nodemon": "^1.17.1",
"readline-sync": "^1.4.9",
"shapefile": "^0.6.2",
"webpack": "^4.0.1",
"webpack-cli": "^2.0.10"
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
},
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/areagraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,4 +462,4 @@ AreaGraph.prototype.electricityMixMode = function(arg) {
return this;
};

module.exports = AreaGraph;
export default AreaGraph;
171 changes: 71 additions & 100 deletions web/src/components/circulargauge.js
Original file line number Diff line number Diff line change
@@ -1,115 +1,86 @@
const d3 = Object.assign(
{},
require('d3-selection'),
require('d3-transition'),
require('d3-shape'),
require('d3-interpolate'),
);
import React from 'react';

export default class CircularGauge {
constructor(selectorId, argConfig) {
const config = argConfig || {};
import * as d3Selection from 'd3-selection';
import * as d3Transition from 'd3-transition';
import * as d3Shape from 'd3-shape';
import * as d3Interpolate from 'd3-interpolate';

this.radius = config.radius || '32';
this.lineWidth = config.lineWidth || '6';
this.fontSize = config.fontSize || '1rem';
const d3 = {
...d3Selection,
...d3Transition,
...d3Shape,
...d3Interpolate,
};

this.arc = d3.arc()
.startAngle(0)
.innerRadius(this.radius - this.lineWidth)
.outerRadius(this.radius);

this.prevPercentage = 0;

this.percentage = config.percentage || null;

// main gauge component
const DEFAULT_RADIUS = '32';

const gauge = d3.select(`#${selectorId}`).append('svg')
.attr('width', this.radius * 2)
.attr('height', this.radius * 2)
// .attr("width", '100%') // makes gauge auto-resize
// .attr("height", '100%') // makes gauge auto-resize
// .attr("viewBox", "0 0 " + (this.radius * 2) + " " + (this.radius * 2)) // makes resizable
// .attr("preserveAspectRatio", "xMidYMid meet") // makes gauge resizable
.append('g')
.attr('transform', `translate(${this.radius},${this.radius})`)
.append('g')
.attr('class', 'circular-gauge');
export default class extends React.PureComponent {
constructor(props) {
super(props);

// background
this.background = gauge.append('path')
.attr('class', 'background')
.attr('d', this.arc.endAngle(2 * Math.PI));
this.foregroundRef = React.createRef();

// foreground
this.foreground = gauge.append('path')
.attr('class', 'foreground')
.attr('d', this.arc.endAngle(0)); // starts filling from 0
this.state = {
prevPercentage: 0,
};

this.percentageText = gauge.append('text')
.style('text-anchor', 'middle')
.attr('dy', '0.4em')
.style('font-weight', 'bold')
.style('font-size', this.fontSize)
.text(this.percentage != null ? `${Math.round(this.percentage)}%` : '?');
const radius = this.props.radius || DEFAULT_RADIUS;
const lineWidth = this.props.lineWidth || '6';

const that = this;

d3.select(`#${selectorId}`)
.on('mouseover', function (d) {
if (that.onGaugeMouseOver) that.onGaugeMouseOver(this);
})
.on('mouseout', function (d) {
if (that.onGaugeMouseOut) that.onGaugeMouseOut(this);
})
.on('mousemove', function (d) {
if (that.onGaugeMouseMove) that.onGaugeMouseMove(this);
});

this.draw();
this.arc = d3.arc()
.startAngle(0)
.innerRadius(radius - lineWidth)
.outerRadius(radius);
}

draw() {
const arc = this.arc;
const prevPercentage = this.prevPercentage != null ? this.prevPercentage / 100 : 0;
const percentage = this.percentage != null ? this.percentage / 100 : 0;

const i = d3.interpolate(prevPercentage * 2 * Math.PI, 2 * Math.PI * (percentage));

this.foreground.transition()
render() {
const fontSize = this.props.fontSize || '1rem';
const radius = this.props.radius || DEFAULT_RADIUS;
const percentage = Number.isNaN(this.props.percentage)
? 0
: (this.props.percentage || 0);

const i = d3.interpolate(
(this.state.prevPercentage / 100) * 2 * Math.PI,
(percentage / 100) * 2 * Math.PI
);
d3.select(this.foregroundRef.current)
.transition()
.duration(500)
.attrTween(
'd',
() => t => arc.endAngle(i(t))(),
);
}

onMouseOver(tooltip) {
this.onGaugeMouseOver = tooltip;
return this;
}

onMouseMove(tooltip) {
this.onGaugeMouseMove = tooltip;
return this;
}

onMouseOut(tooltip) {
this.onGaugeMouseOut = tooltip;
return this;
}

setPercentage(percentage) {
if (this.percentage === percentage) {
return;
}
if (Number.isNaN(percentage)) {
return;
}
this.prevPercentage = this.percentage;
this.percentage = percentage;
this.percentageText.text(this.percentage != null ? `${Math.round(this.percentage)}%` : '?');
this.draw();
() => t => this.arc.endAngle(i(t))(),
)
.on('end', () => this.setState({ prevPercentage: percentage }));

const { onMouseMove, onMouseOut, onMouseOver } = this.props;

return (
<div
onMouseOver={() => onMouseOver && onMouseOver()}
onMouseOut={() => onMouseOut && onMouseOut()}
onMouseMove={e => onMouseMove && onMouseMove(e.clientX, e.clientY)}
>
<svg width={radius * 2} height={radius * 2}>
<g transform={`translate(${radius},${radius})`}>
<g className="circular-gauge">
<path className="background" d={this.arc.endAngle(2 * Math.PI)()} />
<path
className="foreground"
d={this.arc.endAngle((this.state.prevPercentage / 100) * 2 * Math.PI)()}
ref={this.foregroundRef}
/>
<text style={{ textAnchor: 'middle', fontWeight: 'bold', fontSize }} dy="0.4em">
{
this.props.percentage != null && !Number.isNaN(this.props.percentage)
? `${Math.round(percentage)}%`
: '?'
}
</text>
</g>
</g>
</svg>
</div>
);
}
}
4 changes: 1 addition & 3 deletions web/src/components/countrytable.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var translation = require('../helpers/translation');
// This means drawing them once at `.data()` or at construction, and not
// during `render()`

function CountryTable(selector, modeColor, modeOrder) {
export default function CountryTable(selector, modeColor, modeOrder) {
var that = this;

this.root = d3.select(selector);
Expand Down Expand Up @@ -644,5 +644,3 @@ CountryTable.prototype.showNoDataMessageIf = function(condition, isRealtimeData)
this.wrapperNoDataOverlay.showIfElseHide(condition);
this.wrapperNoDataOverlay.text(translation.translate(isRealtimeData? 'country-panel.noLiveData' : 'country-panel.noDataAtTimestamp'));
}

module.exports = CountryTable;
2 changes: 1 addition & 1 deletion web/src/components/horizontalcolorbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,4 @@ HorizontalColorbar.prototype._setDomainAndRange = function _setDomainAndRange(d,
.domain(this._domain);
};

module.exports = HorizontalColorbar;
export default HorizontalColorbar;
2 changes: 1 addition & 1 deletion web/src/components/layers/exchange.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,4 @@ class ExchangeLayer {
}
}

module.exports = ExchangeLayer;
export default ExchangeLayer;
2 changes: 1 addition & 1 deletion web/src/components/layers/wind.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,4 @@ class WindLayer {
}
}

module.exports = WindLayer;
export default WindLayer;
2 changes: 1 addition & 1 deletion web/src/components/linegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,4 +337,4 @@ LineGraph.prototype.selectedIndex = function(arg) {
return this;
}

module.exports = LineGraph;
export default LineGraph;
2 changes: 1 addition & 1 deletion web/src/components/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@ Tooltip.prototype.hide = function() {
return this;
}

module.exports = Tooltip
export default Tooltip
12 changes: 12 additions & 0 deletions web/src/helpers/redux.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function getCurrentZoneData(state) {
const { grid } = state.data;
const zoneName = state.application.selectedZoneName;
const i = state.application.selectedZoneTimeIndex;
if (!grid || !zoneName) {
return null;
}
if (i == null) {
return grid.zones[zoneName];
}
return (state.data.histories[zoneName] || {})[i];
}
9 changes: 5 additions & 4 deletions web/src/helpers/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const flags = require('../helpers/flags');
const translation = require('../helpers/translation');

const formatting = require('./formatting');
const { dispatchApplication } = require('../store');

// Production
const FLAG_SIZE = 16;
Expand Down Expand Up @@ -172,7 +173,7 @@ module.exports.showExchange = function showExchange(tooltipInstance, key, countr
tooltipInstance.show();
};

module.exports.showMapCountry = function showMapCountry(tooltipInstance, countryData, co2color, co2Colorbars, lowCarbonGauge, renewableGauge, electricityMixMode) {
module.exports.showMapCountry = function showMapCountry(tooltipInstance, countryData, co2color, co2Colorbars, electricityMixMode) {
if (!countryData) {
tooltipInstance.hide();
return;
Expand All @@ -192,7 +193,7 @@ module.exports.showMapCountry = function showMapCountry(tooltipInstance, country
.text(translation.getFullZoneName(countryData.countryCode))
.style('font-weight', 'bold');

if (countryData.hasParser && lowCarbonGauge && renewableGauge) {
if (countryData.hasParser) {
tooltip.select('.emission-rect')
.style('background-color', co2intensity ? co2color(co2intensity) : 'gray');
tooltip.select('.country-emission-intensity')
Expand All @@ -204,7 +205,7 @@ module.exports.showMapCountry = function showMapCountry(tooltipInstance, country
const hasFossilFuelData = fossilFuelRatio != null;
if (hasFossilFuelData) {
const fossilFuelPercent = fossilFuelRatio * 100;
lowCarbonGauge.setPercentage(Math.round(100 - fossilFuelPercent));
dispatchApplication('tooltipLowCarbonGaugePercentage', Math.round(100 - fossilFuelPercent));
tooltip.select('.lowcarbon-percentage').text(Math.round(100 - fossilFuelPercent));
} else {
tooltip.select('.lowcarbon-percentage').text('?');
Expand All @@ -216,7 +217,7 @@ module.exports.showMapCountry = function showMapCountry(tooltipInstance, country
const hasRenewableData = renewableRatio != null;
if (hasRenewableData) {
const renewablePercent = renewableRatio * 100;
renewableGauge.setPercentage(Math.round(renewablePercent));
dispatchApplication('tooltipRenewableGaugePercentage', Math.round(renewablePercent));
tooltip.select('.renewable-percentage').text(Math.round(renewablePercent));
} else {
tooltip.select('.renewable-percentage').text('?');
Expand Down
Loading

0 comments on commit 27e956d

Please sign in to comment.