Browse code

Chart styling

Cinan Rakosnik authored on 10/03/2018 at 19:03:02
Showing 14 changed files
... ...
@@ -27,14 +27,20 @@
27 27
     "fs-extra": "3.0.1",
28 28
     "html-webpack-plugin": "2.29.0",
29 29
     "jest": "20.0.4",
30
+    "moment": "^2.21.0",
30 31
     "object-assign": "4.1.1",
31 32
     "postcss-flexbugs-fixes": "3.2.0",
32 33
     "postcss-loader": "2.0.8",
33 34
     "promise": "8.0.1",
34 35
     "raf": "3.4.0",
36
+    "rambda": "^1.0.12",
37
+    "rambdax": "^0.8.10",
38
+    "ramda": "^0.25.0",
35 39
     "react": "^16.2.0",
36 40
     "react-dev-utils": "^5.0.0",
37 41
     "react-dom": "^16.2.0",
42
+    "react-measure": "^2.0.0",
43
+    "recharts": "^1.0.0-beta.10",
38 44
     "style-loader": "0.19.0",
39 45
     "sw-precache-webpack-plugin": "0.11.4",
40 46
     "url-loader": "0.6.2",
... ...
@@ -1,5 +1,5 @@
1 1
 {
2
-  "short_name": "React App",
2
+  "short_name": "Scheduling",
3 3
   "name": "Create React App Sample",
4 4
   "icons": [
5 5
     {
6 6
deleted file mode 100644
... ...
@@ -1,28 +0,0 @@
1
-.App {
2
-  text-align: center;
3
-}
4
-
5
-.App-logo {
6
-  animation: App-logo-spin infinite 20s linear;
7
-  height: 80px;
8
-}
9
-
10
-.App-header {
11
-  background-color: #222;
12
-  height: 150px;
13
-  padding: 20px;
14
-  color: white;
15
-}
16
-
17
-.App-title {
18
-  font-size: 1.5em;
19
-}
20
-
21
-.App-intro {
22
-  font-size: large;
23
-}
24
-
25
-@keyframes App-logo-spin {
26
-  from { transform: rotate(0deg); }
27
-  to { transform: rotate(360deg); }
28
-}
... ...
@@ -1,18 +1,40 @@
1
-import React, { Component } from 'react';
2
-import logo from './logo.svg';
3
-import './App.css';
1
+import React, { PureComponent } from 'react';
2
+import Gantt from './components/Gantt';
3
+import { normalizeData } from './ganttUtils';
4 4
 
5
-class App extends Component {
5
+const algoData = [
6
+  [
7
+    { startTime: 1, endTime: 3, processor: 1 },
8
+    { startTime: 4, endTime: 6, processor: 1 },
9
+    { startTime: 6, endTime: 10, processor: 1 },
10
+  ],
11
+  [
12
+    { startTime: 6, endTime: 10, processor: 2 },
13
+    { startTime: 22, endTime: 23, processor: 2 },
14
+    { startTime: 23, endTime: 24, processor: 2 },
15
+    { startTime: 24, endTime: 25, processor: 2 },
16
+  ],
17
+  [
18
+    { startTime: 10, endTime: 16, processor: 3 },
19
+  ],
20
+  [
21
+    { startTime: 10, endTime: 16, processor: 4 },
22
+  ],
23
+  [
24
+    { startTime: 10, endTime: 16, processor: 5 },
25
+  ],
26
+  [
27
+    { startTime: 10, endTime: 16, processor: 6 },
28
+  ]
29
+];
30
+
31
+const data = normalizeData(algoData);
32
+
33
+class App extends PureComponent {
6 34
   render() {
7 35
     return (
8
-      <div className="App">
9
-        <header className="App-header">
10
-          <img src={logo} className="App-logo" alt="logo" />
11
-          <h1 className="App-title">Welcome to React</h1>
12
-        </header>
13
-        <p className="App-intro">
14
-          To get started, edit <code>src/App.js</code> and save to reload.
15
-        </p>
36
+      <div style={{ padding: 20 }}>
37
+        <Gantt data={data} />
16 38
       </div>
17 39
     );
18 40
   }
19 41
deleted file mode 100644
... ...
@@ -1,9 +0,0 @@
1
-import React from 'react';
2
-import ReactDOM from 'react-dom';
3
-import App from './App';
4
-
5
-it('renders without crashing', () => {
6
-  const div = document.createElement('div');
7
-  ReactDOM.render(<App />, div);
8
-  ReactDOM.unmountComponentAtNode(div);
9
-});
10 1
new file mode 100644
... ...
@@ -0,0 +1,109 @@
0
+import React from 'react';
1
+import { withContentRect } from 'react-measure'
2
+import { filter, range, map, pipe } from 'rambda';
3
+import { ScatterChart, Scatter, XAxis, YAxis, CartesianGrid, Brush, Label } from 'recharts';
4
+import Job from './Job';
5
+import { maxTime, minTime } from '../ganttUtils';
6
+import theme from '../theme';
7
+
8
+class Gannt extends React.PureComponent {
9
+  constructor(props) {
10
+    super(props);
11
+
12
+    this.state = {
13
+      startTime: minTime(props.data),
14
+      endTime: maxTime(props.data)
15
+    };
16
+  }
17
+
18
+  render() {
19
+    const { data, measureRef, contentRect: { bounds: dimensions } } = this.props;
20
+    const { startTime } = this.state;
21
+    const endTime = this.state.endTime === startTime ? startTime + 1 : this.state.endTime;
22
+
23
+    const scopeData = map(processorData =>
24
+      pipe(
25
+        filter(datum =>
26
+          (datum.startTime >= startTime
27
+            || (datum.startTime < startTime && datum.endTime >= startTime)
28
+          )
29
+          && datum.startTime <= endTime
30
+        ),
31
+        map(datum => {
32
+          let newDatum = datum;
33
+          if (newDatum.startTime < startTime) {
34
+            newDatum = { ...newDatum, startTime, formerStartTime: newDatum.startTime };
35
+          }
36
+
37
+          if (newDatum.endTime > endTime) {
38
+            newDatum = { ...newDatum, endTime, formerEndTime: newDatum.endTime };
39
+          }
40
+
41
+          return newDatum;
42
+        })
43
+      )(processorData),
44
+      data
45
+    );
46
+
47
+    const processors = range(1, data.length + 1);
48
+
49
+    const scopeMinTime = minTime(scopeData);
50
+    const scopeMaxTime = maxTime(scopeData);
51
+    const ticksCountX = scopeMinTime + scopeMaxTime;
52
+
53
+    const width = Math.max(theme.gantt.maxWidth, dimensions.width);
54
+    const height = 100 + 1.75 * theme.gantt.job.height * processors.length;
55
+
56
+    const horizontalGuideLines = map(
57
+      i => theme.gantt.margin.top + i * 1.75 * theme.gantt.job.height - i * 10,
58
+      range(0, processors.length)
59
+    );
60
+
61
+    return (
62
+      <div ref={measureRef}>
63
+        <ScatterChart width={width} height={height} data={data[0]} margin={theme.gantt.margin}>
64
+          <CartesianGrid
65
+            stroke={theme.gantt.grid.stroke}
66
+            strokeDasharray="5 5"
67
+            horizontalPoints={horizontalGuideLines}
68
+          />
69
+          <XAxis
70
+            type="number"
71
+            dataKey="startTime"
72
+            interval={0}
73
+            domain={[scopeMinTime, scopeMaxTime]}
74
+            tickCount={ticksCountX}
75
+            allowDecimals={false}
76
+          >
77
+            <Label offset={-30} position="insideBottom">Time</Label>
78
+          </XAxis>
79
+          <YAxis
80
+            type="number"
81
+            dataKey="processor"
82
+            interval={0}
83
+            name="processor"
84
+            allowDecimals={false}
85
+            domain={[0.5, processors.length + 0.5]}
86
+            ticks={processors}
87
+          >
88
+            <Label position="insideLeft" angle={-90}>Workers</Label>
89
+          </YAxis>
90
+          <Brush
91
+            dataKey="startTime"
92
+            y={height - theme.gantt.brush.height - 10}
93
+            height={theme.gantt.brush.height}
94
+            stroke={theme.gantt.brush.stroke}
95
+            onChange={({ startIndex, endIndex }) =>
96
+              this.setState({ startTime: startIndex + 1, endTime: endIndex + 1 })
97
+            }
98
+          />
99
+          {scopeData.map((processorData, i) => (
100
+            <Scatter key={i} data={processorData} shape={<Job />} />
101
+          ))}
102
+        </ScatterChart>
103
+      </div>
104
+    );
105
+  }
106
+}
107
+
108
+export default withContentRect('bounds')(Gannt);
0 109
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+import React from 'react';
1
+import { Rectangle } from 'recharts';
2
+import theme from '../theme';
3
+
4
+export default class Job extends React.PureComponent {
5
+  render() {
6
+    const {
7
+      x,
8
+      y,
9
+      payload: {
10
+        formerStartTime,
11
+        formerEndTime,
12
+        startTime,
13
+        endTime
14
+      },
15
+      xAxis: {
16
+        niceTicks,
17
+        width: axisWidth
18
+      }
19
+    } = this.props;
20
+
21
+    if (!endTime) return null;
22
+
23
+    const duration = Math.abs(endTime - startTime);
24
+
25
+    if (!duration) return null;
26
+
27
+    const formerDuration = Math.abs(
28
+      (typeof formerEndTime === 'undefined' ? endTime : formerEndTime)
29
+      - (typeof formerStartTime === 'undefined' ? startTime : formerStartTime)
30
+    );
31
+
32
+    const width = (axisWidth / (niceTicks.length - 1)) * duration;
33
+    const height = theme.gantt.job.height;
34
+
35
+    return <Box x={x + 5} y={y + 5} width={width} height={height} text={formerDuration} />
36
+  }
37
+}
38
+
39
+class Box extends React.PureComponent {
40
+  render() {
41
+    const { x, y, width, height, text } = this.props;
42
+    const { fill, stroke, textColor } = theme.gantt.job;
43
+
44
+    return (
45
+      <React.Fragment>
46
+        <Rectangle x={x} y={y - (height / 2)} width={width} height={height} fill={fill} stroke={stroke} />
47
+        <text x={x + (width / 2)} y={y} textAnchor="middle" alignmentBaseline="central" fill={textColor}>{text}</text>
48
+      </React.Fragment>
49
+    );
50
+  }
51
+}
0 52
\ No newline at end of file
1 53
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+import { forEach, last, multiply, range, map, prop, reduce } from 'rambda';
1
+import { max, min } from 'ramda';
2
+
3
+export const minTime = data => {
4
+  const processorsMinTime = map(
5
+    processorData => reduce(
6
+      min,
7
+      Infinity,
8
+      map(prop('startTime'), processorData)
9
+    ),
10
+    data
11
+  );
12
+
13
+  return reduce(min, Infinity, processorsMinTime);
14
+};
15
+
16
+export const maxTime = data => {
17
+  const processorsMaxTime = map(
18
+    processorData => reduce(
19
+      max,
20
+      -Infinity,
21
+      map(prop('startTime'), processorData)
22
+    ),
23
+    data
24
+  );
25
+
26
+  return reduce(max, -Infinity, processorsMaxTime);
27
+};
28
+
29
+export const normalizeData = data =>
30
+  map(processorData => {
31
+    // if startTime would be days (thousands of seconds), set step to day
32
+    const step = 1; // >= 1
33
+
34
+    const _minTime = Math.floor(minTime(data) / step);
35
+    const _maxTime = Math.floor(maxTime(data) / step);
36
+
37
+    const rangeTime = map(
38
+      multiply(step),
39
+      range(_minTime, _maxTime + 1)
40
+    );
41
+
42
+    const dataObj = reduce(
43
+      (obj, row) => ({ ...obj, [row.startTime]: row }),
44
+      {},
45
+      processorData
46
+    );
47
+
48
+    forEach(startTime => {
49
+      if (!dataObj[startTime]) {
50
+        dataObj[startTime] = { startTime };
51
+      }
52
+    }, rangeTime);
53
+
54
+    const filledData = Object.values(dataObj);
55
+
56
+    filledData.push({ startTime: last(processorData).endTime });
57
+
58
+    return filledData;
59
+  }, data);
0 60
\ No newline at end of file
... ...
@@ -1,5 +1,38 @@
1 1
 body {
2 2
   margin: 0;
3 3
   padding: 0;
4
-  font-family: sans-serif;
4
+  font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
5
+}
6
+
7
+svg * {
8
+  user-select: none;
9
+}
10
+
11
+.recharts-cartesian-axis-tick-value {
12
+  fill: #3A4168;
13
+}
14
+
15
+.recharts-cartesian-axis-line,
16
+.recharts-cartesian-axis-tick-line {
17
+  stroke: #3A4168;
18
+}
19
+
20
+.color1 {
21
+  color: #1B1B3A;
22
+}
23
+
24
+.color2 {
25
+  color: #3A4168;
26
+}
27
+
28
+.color3 {
29
+  color: #98798F;
30
+}
31
+
32
+.color4 {
33
+  color: #C89C93;
34
+}
35
+
36
+.color5 {
37
+  color: #F1D8CB;
5 38
 }
... ...
@@ -2,7 +2,5 @@ import React from 'react';
2 2
 import ReactDOM from 'react-dom';
3 3
 import './index.css';
4 4
 import App from './App';
5
-import registerServiceWorker from './registerServiceWorker';
6 5
 
7 6
 ReactDOM.render(<App />, document.getElementById('root'));
8
-registerServiceWorker();
9 7
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
2
-    <g fill="#61DAFB">
3
-        <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
4
-        <circle cx="420.9" cy="296.5" r="45.7"/>
5
-        <path d="M520.5 78.1z"/>
6
-    </g>
7
-</svg>
8 1
deleted file mode 100644
... ...
@@ -1,117 +0,0 @@
1
-// In production, we register a service worker to serve assets from local cache.
2
-
3
-// This lets the app load faster on subsequent visits in production, and gives
4
-// it offline capabilities. However, it also means that developers (and users)
5
-// will only see deployed updates on the "N+1" visit to a page, since previously
6
-// cached resources are updated in the background.
7
-
8
-// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9
-// This link also includes instructions on opting out of this behavior.
10
-
11
-const isLocalhost = Boolean(
12
-  window.location.hostname === 'localhost' ||
13
-    // [::1] is the IPv6 localhost address.
14
-    window.location.hostname === '[::1]' ||
15
-    // 127.0.0.1/8 is considered localhost for IPv4.
16
-    window.location.hostname.match(
17
-      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18
-    )
19
-);
20
-
21
-export default function register() {
22
-  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23
-    // The URL constructor is available in all browsers that support SW.
24
-    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25
-    if (publicUrl.origin !== window.location.origin) {
26
-      // Our service worker won't work if PUBLIC_URL is on a different origin
27
-      // from what our page is served on. This might happen if a CDN is used to
28
-      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29
-      return;
30
-    }
31
-
32
-    window.addEventListener('load', () => {
33
-      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34
-
35
-      if (isLocalhost) {
36
-        // This is running on localhost. Lets check if a service worker still exists or not.
37
-        checkValidServiceWorker(swUrl);
38
-
39
-        // Add some additional logging to localhost, pointing developers to the
40
-        // service worker/PWA documentation.
41
-        navigator.serviceWorker.ready.then(() => {
42
-          console.log(
43
-            'This web app is being served cache-first by a service ' +
44
-              'worker. To learn more, visit https://goo.gl/SC7cgQ'
45
-          );
46
-        });
47
-      } else {
48
-        // Is not local host. Just register service worker
49
-        registerValidSW(swUrl);
50
-      }
51
-    });
52
-  }
53
-}
54
-
55
-function registerValidSW(swUrl) {
56
-  navigator.serviceWorker
57
-    .register(swUrl)
58
-    .then(registration => {
59
-      registration.onupdatefound = () => {
60
-        const installingWorker = registration.installing;
61
-        installingWorker.onstatechange = () => {
62
-          if (installingWorker.state === 'installed') {
63
-            if (navigator.serviceWorker.controller) {
64
-              // At this point, the old content will have been purged and
65
-              // the fresh content will have been added to the cache.
66
-              // It's the perfect time to display a "New content is
67
-              // available; please refresh." message in your web app.
68
-              console.log('New content is available; please refresh.');
69
-            } else {
70
-              // At this point, everything has been precached.
71
-              // It's the perfect time to display a
72
-              // "Content is cached for offline use." message.
73
-              console.log('Content is cached for offline use.');
74
-            }
75
-          }
76
-        };
77
-      };
78
-    })
79
-    .catch(error => {
80
-      console.error('Error during service worker registration:', error);
81
-    });
82
-}
83
-
84
-function checkValidServiceWorker(swUrl) {
85
-  // Check if the service worker can be found. If it can't reload the page.
86
-  fetch(swUrl)
87
-    .then(response => {
88
-      // Ensure service worker exists, and that we really are getting a JS file.
89
-      if (
90
-        response.status === 404 ||
91
-        response.headers.get('content-type').indexOf('javascript') === -1
92
-      ) {
93
-        // No service worker found. Probably a different app. Reload the page.
94
-        navigator.serviceWorker.ready.then(registration => {
95
-          registration.unregister().then(() => {
96
-            window.location.reload();
97
-          });
98
-        });
99
-      } else {
100
-        // Service worker found. Proceed as normal.
101
-        registerValidSW(swUrl);
102
-      }
103
-    })
104
-    .catch(() => {
105
-      console.log(
106
-        'No internet connection found. App is running in offline mode.'
107
-      );
108
-    });
109
-}
110
-
111
-export function unregister() {
112
-  if ('serviceWorker' in navigator) {
113
-    navigator.serviceWorker.ready.then(registration => {
114
-      registration.unregister();
115
-    });
116
-  }
117
-}
118 1
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+export default {
1
+  gantt: {
2
+    maxWidth: 700,
3
+    margin: { top: 40, right: 20, bottom: 60, left: 20 },
4
+    grid: {
5
+      stroke: 'rgba(241, 216, 203, 0.6)'
6
+    },
7
+    brush: {
8
+      height: 30,
9
+      stroke: '#C89C93'
10
+    },
11
+    job: {
12
+      height: 50,
13
+      fill: '#3A4168',
14
+      stroke: '#1B1B3A',
15
+      textColor: '#e8e9f2'
16
+    },
17
+    label: {
18
+      color: '#3A4168'
19
+    }
20
+  },
21
+
22
+}
0 23
\ No newline at end of file
... ...
@@ -1347,6 +1347,10 @@ center-align@^0.1.1:
1347 1347
     align-text "^0.1.3"
1348 1348
     lazy-cache "^1.0.3"
1349 1349
 
1350
+chain-function@^1.0.0:
1351
+  version "1.0.0"
1352
+  resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
1353
+
1350 1354
 chalk@1.1.3, chalk@^1.1.3:
1351 1355
   version "1.1.3"
1352 1356
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
... ...
@@ -1432,6 +1436,10 @@ class-utils@^0.3.5:
1432 1432
     isobject "^3.0.0"
1433 1433
     static-extend "^0.1.1"
1434 1434
 
1435
+classnames@2.2.5, classnames@^2.2.5:
1436
+  version "2.2.5"
1437
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
1438
+
1435 1439
 clean-css@4.1.x:
1436 1440
   version "4.1.9"
1437 1441
   resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301"
... ...
@@ -1638,6 +1646,10 @@ copy-descriptor@^0.1.0:
1638 1638
   version "0.1.1"
1639 1639
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
1640 1640
 
1641
+core-js@2.5.1:
1642
+  version "2.5.1"
1643
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
1644
+
1641 1645
 core-js@^1.0.0:
1642 1646
   version "1.2.7"
1643 1647
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
... ...
@@ -1843,6 +1855,60 @@ currently-unhandled@^0.4.1:
1843 1843
   dependencies:
1844 1844
     array-find-index "^1.0.1"
1845 1845
 
1846
+d3-array@^1.2.0:
1847
+  version "1.2.1"
1848
+  resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
1849
+
1850
+d3-collection@1:
1851
+  version "1.0.4"
1852
+  resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2"
1853
+
1854
+d3-color@1:
1855
+  version "1.0.3"
1856
+  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b"
1857
+
1858
+d3-format@1:
1859
+  version "1.2.2"
1860
+  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a"
1861
+
1862
+d3-interpolate@1, d3-interpolate@^1.1.5:
1863
+  version "1.1.6"
1864
+  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6"
1865
+  dependencies:
1866
+    d3-color "1"
1867
+
1868
+d3-path@1:
1869
+  version "1.0.5"
1870
+  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764"
1871
+
1872
+d3-scale@1.0.6:
1873
+  version "1.0.6"
1874
+  resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.6.tgz#bce19da80d3a0cf422c9543ae3322086220b34ed"
1875
+  dependencies:
1876
+    d3-array "^1.2.0"
1877
+    d3-collection "1"
1878
+    d3-color "1"
1879
+    d3-format "1"
1880
+    d3-interpolate "1"
1881
+    d3-time "1"
1882
+    d3-time-format "2"
1883
+
1884
+d3-shape@1.2.0:
1885
+  version "1.2.0"
1886
+  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777"
1887
+  dependencies:
1888
+    d3-path "1"
1889
+
1890
+d3-time-format@2:
1891
+  version "2.1.1"
1892
+  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31"
1893
+  dependencies:
1894
+    d3-time "1"
1895
+
1896
+d3-time@1:
1897
+  version "1.0.8"
1898
+  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84"
1899
+
1846 1900
 d@1:
1847 1901
   version "1.0.0"
1848 1902
   resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
... ...
@@ -2050,6 +2116,10 @@ dom-converter@~0.1:
2050 2050
   dependencies:
2051 2051
     utila "~0.3"
2052 2052
 
2053
+dom-helpers@^3.2.0:
2054
+  version "3.3.1"
2055
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
2056
+
2053 2057
 dom-serializer@0:
2054 2058
   version "0.1.0"
2055 2059
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
... ...
@@ -2851,7 +2921,7 @@ fs.realpath@^1.0.0:
2851 2851
   version "1.0.0"
2852 2852
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
2853 2853
 
2854
-fsevents@^1.0.0, fsevents@^1.1.3:
2854
+fsevents@^1.0.0:
2855 2855
   version "1.1.3"
2856 2856
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8"
2857 2857
   dependencies:
... ...
@@ -2900,6 +2970,10 @@ get-caller-file@^1.0.1:
2900 2900
   version "1.0.2"
2901 2901
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
2902 2902
 
2903
+get-node-dimensions@^1.2.0:
2904
+  version "1.2.1"
2905
+  resolved "https://registry.yarnpkg.com/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz#fb7b4bb57060fb4247dd51c9d690dfbec56b0823"
2906
+
2903 2907
 get-stdin@^4.0.1:
2904 2908
   version "4.0.1"
2905 2909
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
... ...
@@ -4275,7 +4349,7 @@ lodash.uniq@^4.5.0:
4275 4275
   version "4.5.0"
4276 4276
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
4277 4277
 
4278
-"lodash@>=3.5 <5", lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0:
4278
+"lodash@>=3.5 <5", lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.3.0, lodash@~4.17.4:
4279 4279
   version "4.17.5"
4280 4280
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
4281 4281
 
... ...
@@ -4510,6 +4584,10 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0, mkdi
4510 4510
   dependencies:
4511 4511
     minimist "0.0.8"
4512 4512
 
4513
+moment@^2.21.0:
4514
+  version "2.21.0"
4515
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a"
4516
+
4513 4517
 ms@2.0.0:
4514 4518
   version "2.0.0"
4515 4519
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
... ...
@@ -5390,7 +5468,7 @@ promise@^7.1.1:
5390 5390
   dependencies:
5391 5391
     asap "~2.0.3"
5392 5392
 
5393
-prop-types@^15.5.10, prop-types@^15.6.0:
5393
+prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0:
5394 5394
   version "15.6.1"
5395 5395
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
5396 5396
   dependencies:
... ...
@@ -5466,12 +5544,30 @@ querystringify@~1.0.0:
5466 5466
   version "1.0.0"
5467 5467
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
5468 5468
 
5469
-raf@3.4.0:
5469
+raf@3.4.0, raf@^3.2.0:
5470 5470
   version "3.4.0"
5471 5471
   resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
5472 5472
   dependencies:
5473 5473
     performance-now "^2.1.0"
5474 5474
 
5475
+rambda@^1.0.12:
5476
+  version "1.0.12"
5477
+  resolved "https://registry.yarnpkg.com/rambda/-/rambda-1.0.12.tgz#041f942e7ad56599a0a0248cd92d9313b069b923"
5478
+
5479
+"rambda@https://github.com/selfrefactor/rambda#1.1.0":
5480
+  version "1.0.9"
5481
+  resolved "https://github.com/selfrefactor/rambda#ee04b4005da84b29d166364100d50e2a9be56a60"
5482
+
5483
+rambdax@^0.8.10:
5484
+  version "0.8.10"
5485
+  resolved "https://registry.yarnpkg.com/rambdax/-/rambdax-0.8.10.tgz#d8cc090c754e2225a39aa8a5a8d69475e9acd9ed"
5486
+  dependencies:
5487
+    rambda "https://github.com/selfrefactor/rambda#1.1.0"
5488
+
5489
+ramda@^0.25.0:
5490
+  version "0.25.0"
5491
+  resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9"
5492
+
5475 5493
 randomatic@^1.1.3:
5476 5494
   version "1.1.7"
5477 5495
   resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
... ...
@@ -5537,7 +5633,7 @@ react-dev-utils@^5.0.0:
5537 5537
     strip-ansi "3.0.1"
5538 5538
     text-table "0.2.0"
5539 5539
 
5540
-react-dom@16.2.0:
5540
+react-dom@^16.2.0:
5541 5541
   version "16.2.0"
5542 5542
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
5543 5543
   dependencies:
... ...
@@ -5550,51 +5646,41 @@ react-error-overlay@^4.0.0:
5550 5550
   version "4.0.0"
5551 5551
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
5552 5552
 
5553
-react-scripts@1.1.1:
5554
-  version "1.1.1"
5555
-  resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-1.1.1.tgz#279d449f7311fed910506987a1ade014027788a8"
5556
-  dependencies:
5557
-    autoprefixer "7.1.6"
5558
-    babel-core "6.26.0"
5559
-    babel-eslint "7.2.3"
5560
-    babel-jest "20.0.3"
5561
-    babel-loader "7.1.2"
5562
-    babel-preset-react-app "^3.1.1"
5563
-    babel-runtime "6.26.0"
5564
-    case-sensitive-paths-webpack-plugin "2.1.1"
5565
-    chalk "1.1.3"
5566
-    css-loader "0.28.7"
5567
-    dotenv "4.0.0"
5568
-    dotenv-expand "4.2.0"
5569
-    eslint "4.10.0"
5570
-    eslint-config-react-app "^2.1.0"
5571
-    eslint-loader "1.9.0"
5572
-    eslint-plugin-flowtype "2.39.1"
5573
-    eslint-plugin-import "2.8.0"
5574
-    eslint-plugin-jsx-a11y "5.1.1"
5575
-    eslint-plugin-react "7.4.0"
5576
-    extract-text-webpack-plugin "3.0.2"
5577
-    file-loader "1.1.5"
5578
-    fs-extra "3.0.1"
5579
-    html-webpack-plugin "2.29.0"
5580
-    jest "20.0.4"
5581
-    object-assign "4.1.1"
5582
-    postcss-flexbugs-fixes "3.2.0"
5583
-    postcss-loader "2.0.8"
5584
-    promise "8.0.1"
5585
-    raf "3.4.0"
5586
-    react-dev-utils "^5.0.0"
5587
-    style-loader "0.19.0"
5588
-    sw-precache-webpack-plugin "0.11.4"
5589
-    url-loader "0.6.2"
5590
-    webpack "3.8.1"
5591
-    webpack-dev-server "2.9.4"
5592
-    webpack-manifest-plugin "1.3.2"
5593
-    whatwg-fetch "2.0.3"
5594
-  optionalDependencies:
5595
-    fsevents "^1.1.3"
5553
+react-measure@^2.0.0:
5554
+  version "2.0.2"
5555
+  resolved "https://registry.yarnpkg.com/react-measure/-/react-measure-2.0.2.tgz#072a9a5fafc01dfbadc1fa5fb09fc351037f636c"
5556
+  dependencies:
5557
+    get-node-dimensions "^1.2.0"
5558
+    prop-types "^15.5.10"
5559
+    resize-observer-polyfill "^1.4.2"
5560
+
5561
+react-resize-detector@1.1.0:
5562
+  version "1.1.0"
5563
+  resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-1.1.0.tgz#4a9831fa3caad32230478dd0185cbd2aa91a5ebf"
5564
+  dependencies:
5565
+    prop-types "^15.5.10"
5566
+
5567
+react-smooth@1.0.0:
5568
+  version "1.0.0"
5569
+  resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.0.tgz#b29dbebddddb06d21b5b08962167fb9eac1897d8"
5570
+  dependencies:
5571
+    lodash "~4.17.4"
5572
+    prop-types "^15.6.0"
5573
+    raf "^3.2.0"
5574
+    react-transition-group "^2.2.1"
5575
+
5576
+react-transition-group@^2.2.1:
5577
+  version "2.2.1"
5578
+  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.1.tgz#e9fb677b79e6455fd391b03823afe84849df4a10"
5579
+  dependencies:
5580
+    chain-function "^1.0.0"
5581
+    classnames "^2.2.5"
5582
+    dom-helpers "^3.2.0"
5583
+    loose-envify "^1.3.1"
5584
+    prop-types "^15.5.8"
5585
+    warning "^3.0.0"
5596 5586
 
5597
-react@16.2.0:
5587
+react@^16.2.0:
5598 5588
   version "16.2.0"
5599 5589
   resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
5600 5590
   dependencies:
... ...
@@ -5663,6 +5749,26 @@ readdirp@^2.0.0:
5663 5663
     readable-stream "^2.0.2"
5664 5664
     set-immediate-shim "^1.0.1"
5665 5665
 
5666
+recharts-scale@0.3.2:
5667
+  version "0.3.2"
5668
+  resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.3.2.tgz#dac7621714a4765d152cb2adbc30c73b831208c9"
5669
+
5670
+recharts@^1.0.0-beta.10:
5671
+  version "1.0.0-beta.10"
5672
+  resolved "https://registry.yarnpkg.com/recharts/-/recharts-1.0.0-beta.10.tgz#d3cd15df6b7879d5968da3c942b5fcdaf2504fe1"
5673
+  dependencies:
5674
+    classnames "2.2.5"
5675
+    core-js "2.5.1"
5676
+    d3-interpolate "^1.1.5"
5677
+    d3-scale "1.0.6"
5678
+    d3-shape "1.2.0"
5679
+    lodash "~4.17.4"
5680
+    prop-types "^15.6.0"
5681
+    react-resize-detector "1.1.0"
5682
+    react-smooth "1.0.0"
5683
+    recharts-scale "0.3.2"
5684
+    reduce-css-calc "1.3.0"
5685
+
5666 5686
 recursive-readdir@2.2.1:
5667 5687
   version "2.2.1"
5668 5688
   resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
... ...
@@ -5676,7 +5782,7 @@ redent@^1.0.0:
5676 5676
     indent-string "^2.1.0"
5677 5677
     strip-indent "^1.0.1"
5678 5678
 
5679
-reduce-css-calc@^1.2.6:
5679
+reduce-css-calc@1.3.0, reduce-css-calc@^1.2.6:
5680 5680
   version "1.3.0"
5681 5681
   resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
5682 5682
   dependencies:
... ...
@@ -5867,6 +5973,10 @@ requires-port@1.0.x, requires-port@1.x.x, requires-port@~1.0.0:
5867 5867
   version "1.0.0"
5868 5868
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
5869 5869
 
5870
+resize-observer-polyfill@^1.4.2:
5871
+  version "1.5.0"
5872
+  resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.0.tgz#660ff1d9712a2382baa2cad450a4716209f9ca69"
5873
+
5870 5874
 resolve-cwd@^2.0.0:
5871 5875
   version "2.0.0"
5872 5876
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
... ...
@@ -6880,6 +6990,12 @@ walker@~1.0.5:
6880 6880
   dependencies:
6881 6881
     makeerror "1.0.x"
6882 6882
 
6883
+warning@^3.0.0:
6884
+  version "3.0.0"
6885
+  resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
6886
+  dependencies:
6887
+    loose-envify "^1.0.0"
6888
+
6883 6889
 watch@~0.10.0:
6884 6890
   version "0.10.0"
6885 6891
   resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"