소스 검색

added chart

Benoit Sida 3 년 전
부모
커밋
9108395cce

+ 1
- 0
.meteor/packages 파일 보기

22
 meyerweb-reset
22
 meyerweb-reset
23
 fourseven:scss
23
 fourseven:scss
24
 maxharris9:classnames
24
 maxharris9:classnames
25
+momentjs:moment

+ 1
- 0
.meteor/versions 파일 보기

47
 mobile-status-bar@1.0.14
47
 mobile-status-bar@1.0.14
48
 modules@0.7.9
48
 modules@0.7.9
49
 modules-runtime@0.7.9
49
 modules-runtime@0.7.9
50
+momentjs:moment@2.10.6
50
 mongo@1.1.16
51
 mongo@1.1.16
51
 mongo-id@1.0.6
52
 mongo-id@1.0.6
52
 npm-mongo@2.2.24
53
 npm-mongo@2.2.24

+ 1
- 1
imports/ui/App.jsx 파일 보기

6
 	render() {
6
 	render() {
7
 		return (
7
 		return (
8
 			<div id="app-container">
8
 			<div id="app-container">
9
-				<header>-</header>
9
+				<header>ESPTrainer</header>
10
 				<main id="content">
10
 				<main id="content">
11
 					{/*<div className="left" />*/}
11
 					{/*<div className="left" />*/}
12
 					<div className="center">
12
 					<div className="center">

+ 18
- 10
imports/ui/Board.jsx 파일 보기

7
 	constructor(props) {
7
 	constructor(props) {
8
 		super(props);
8
 		super(props);
9
 		this.state = {
9
 		this.state = {
10
-			end: false,
11
-			win: false,
12
-			trials: 0,
13
-			score: 0,
10
+			end: false, win: false,
11
+			trials: 0, score: 0,
14
 			squares: [ false, false, false, false ],
12
 			squares: [ false, false, false, false ],
15
-			image: "/images/img1.jpg"
13
+			image: "/images/img1.jpg",
14
+			scores: JSON.parse(localStorage.getItem('esp-scores') || "{}")
16
 		};
15
 		};
17
 		this.actions = [
16
 		this.actions = [
18
 			{ label: 'Reset', action: this.reset.bind(this), active: () => true },
17
 			{ label: 'Reset', action: this.reset.bind(this), active: () => true },
19
-			{ label: 'Pass', action: this.pass.bind(this), active: () => !this.state.end }
18
+			{ label: 'Pass', action: this.pass.bind(this), active: () => !this.state.end },
19
+			{ label: 'AddScore', action: () => this.registerScore(Math.floor(Math.random() * 100) % 24), active: () => true }
20
 		];
20
 		];
21
-		this.choices = [];
22
-		[0, 1, 2, 3].map(i => this.choices.push(this.choice.bind(this, i)));
21
+		this.choices = [0, 1, 2, 3].map(i => this.choice.bind(this, i));
23
 	}
22
 	}
24
 
23
 
25
 	fail(corrected) {
24
 	fail(corrected) {
34
 		this.props.setTimeout(() => this.setState({ win: false }), 2000);
33
 		this.props.setTimeout(() => this.setState({ win: false }), 2000);
35
 	}
34
 	}
36
 
35
 
36
+	registerScore(score) {
37
+		this.setState({ scores: update(this.state.scores, {
38
+			$merge: { [moment().unix()]: score } })
39
+		}, () => localStorage.setItem('esp-scores', JSON.stringify(this.state.scores)));
40
+	}
41
+
37
 	choice(answer) {
42
 	choice(answer) {
38
 		let corrected = Math.floor(Math.random() * 10) % 4;
43
 		let corrected = Math.floor(Math.random() * 10) % 4;
39
 		this.setState({
44
 		this.setState({
40
 			trials: this.state.trials + 1,
45
 			trials: this.state.trials + 1,
41
 			image: `images/img${this.state.trials + 1}.jpg`,
46
 			image: `images/img${this.state.trials + 1}.jpg`,
42
 			end: this.state.trials + 1 >= 24
47
 			end: this.state.trials + 1 >= 24
48
+		}, () => {
49
+			if (this.state.trials >= 24)
50
+				this.registerScore(this.state.score);
43
 		});
51
 		});
44
 		if (answer === corrected)
52
 		if (answer === corrected)
45
 			this.win();
53
 			this.win();
48
 	}
56
 	}
49
 
57
 
50
 	reset() {
58
 	reset() {
51
-		this.setState({ trials: 0, image: 'images/img0.jpg', score: 0 });
59
+		this.setState({ trials: 0, image: 'images/img0.jpg', score: 0, end: false });
52
 	}
60
 	}
53
 
61
 
54
 	pass() {
62
 	pass() {
59
 	render() {
67
 	render() {
60
 		return <BoardLayout win={this.state.win} choices={this.choices} score={this.state.score}
68
 		return <BoardLayout win={this.state.win} choices={this.choices} score={this.state.score}
61
 							highlighted={this.state.squares} actions={this.actions} active={!this.state.end}
69
 							highlighted={this.state.squares} actions={this.actions} active={!this.state.end}
62
-							trials={this.state.trials} image={this.state.image} />;
70
+							trials={this.state.trials} image={this.state.image} scores={this.state.scores}/>;
63
 	}
71
 	}
64
 }
72
 }
65
 
73
 

+ 4
- 1
imports/ui/BoardLayout.jsx 파일 보기

5
 import Actions from './Actions';
5
 import Actions from './Actions';
6
 import Image from './Image';
6
 import Image from './Image';
7
 import Score from './Score';
7
 import Score from './Score';
8
+import ScoreChart from './ScoreChart';
8
 import './styles/Board';
9
 import './styles/Board';
9
 
10
 
10
-const BoardLayout = ({ active, win, actions, trials, score, image, choices, highlighted }) =>
11
+const BoardLayout = ({ active, win, actions, trials, score, scores, image, choices, highlighted }) =>
11
 	<div className="board">
12
 	<div className="board">
12
 		<Score score={score} />
13
 		<Score score={score} />
13
 		<div className="squares">
14
 		<div className="squares">
25
 		</div>
26
 		</div>
26
 		<Trials count={trials} />
27
 		<Trials count={trials} />
27
 		<Actions actions={actions} />
28
 		<Actions actions={actions} />
29
+		<ScoreChart id="scoreChart" data={scores}/>
28
 	</div>;
30
 	</div>;
29
 
31
 
30
 BoardLayout.propTypes = {
32
 BoardLayout.propTypes = {
33
 	actions: Actions.propTypes.actions,
35
 	actions: Actions.propTypes.actions,
34
 	trials: Trials.propTypes.count,
36
 	trials: Trials.propTypes.count,
35
 	score: Trials.propTypes.count,
37
 	score: Trials.propTypes.count,
38
+	scores: Chart.propTypes.data,
36
 	image: Image.propTypes.src,
39
 	image: Image.propTypes.src,
37
 	choices: PropTypes.arrayOf(PropTypes.func).isRequired,
40
 	choices: PropTypes.arrayOf(PropTypes.func).isRequired,
38
 	highlighted: PropTypes.arrayOf(PropTypes.bool).isRequired
41
 	highlighted: PropTypes.arrayOf(PropTypes.bool).isRequired

+ 2
- 4
imports/ui/Score.jsx 파일 보기

4
 const Score = ({ score }) =>
4
 const Score = ({ score }) =>
5
 	<div className="score">
5
 	<div className="score">
6
 		<div className="scale">
6
 		<div className="scale">
7
-			{
8
-				[...Array(25).keys()].map(x => <div key={x} className="grade" style={{left: (x * 20) + 'px'}} />)
9
-			}
7
+			{ [...Array(25).keys()].map(x => <div key={x} className="grade" />) }
10
 		</div>
8
 		</div>
11
-		<div className="current-score" style={{ width: (score * 20) + 'px' }} />
9
+		<div className="current-score" style={{ width: (score / 24) * 100 + '%' }} />
12
 	</div>;
10
 	</div>;
13
 
11
 
14
 Score.propTypes = { score: PropTypes.number.isRequired };
12
 Score.propTypes = { score: PropTypes.number.isRequired };

+ 77
- 0
imports/ui/ScoreChart.jsx 파일 보기

1
+import React, { Component, PropTypes} from 'react';
2
+import Chart from 'chart.js';
3
+
4
+Chart.defaults.global.legend.labels.fontColor = '#fff';
5
+const dateFormat = 'DD/MM/YYYY H:ss';
6
+const ScoreScales = {
7
+	yAxes: [{
8
+		ticks: {
9
+			fontColor: "#fff",
10
+			stepSize: 6,
11
+			max: 24,
12
+		}
13
+	}],
14
+	xAxes: [{
15
+		type: 'time',
16
+		time: {
17
+			fontColor: "#fff",
18
+			fontSize: 10,
19
+			parser: moment.unix,
20
+			tooltipFormat: dateFormat,
21
+			//unit: 'day'
22
+		},
23
+		ticks: {
24
+			fontColor: "#fff"
25
+		}
26
+	}]
27
+};
28
+
29
+
30
+export default class ScoreChart extends Component {
31
+	constructor(props) {
32
+		super(props);
33
+		this.chart = null;
34
+	}
35
+
36
+	shouldComponentUpdate(nextProps) {
37
+		return this.props.data !== nextProps.data;
38
+	}
39
+
40
+	componentDidMount() {
41
+		let labels = Object.keys(this.props.data);
42
+		const data = labels.map(key => this.props.data[key]);
43
+		// labels = labels.map(d => moment.unix(d).format(dateFormat).split(' '));
44
+		this.chart = new Chart(this.canvas, {
45
+			type: 'line',
46
+			data: {
47
+				labels: labels,
48
+				datasets: [{
49
+					label: 'Progression',
50
+					data: data,
51
+					backgroundColor: 'rgba(255, 99, 132, 0.2)',
52
+					borderColor: 'rgba(255,99,132,1)',
53
+					borderWidth: 1
54
+				}]
55
+			},
56
+			options: { scales: ScoreScales }
57
+		});
58
+	}
59
+
60
+	componentDidUpdate() {
61
+		let labels = Object.keys(this.props.data);
62
+		const data = labels.map(key => this.props.data[key]);
63
+		// labels = labels.map(d => moment.unix(d).format(dateFormat).split(' '));
64
+		this.chart.data.labels = labels;
65
+		this.chart.data.datasets[0].data = data;
66
+		this.chart.update();
67
+	}
68
+
69
+	render() {
70
+		return <canvas ref={(node) => this.canvas = node} width={400}/>;
71
+	}
72
+}
73
+
74
+Chart.propTypes = {
75
+	id: PropTypes.string.isRequired,
76
+	data: PropTypes.object.isRequired
77
+};

+ 0
- 1
imports/ui/styles/Actions.scss 파일 보기

4
   display: flex;
4
   display: flex;
5
   justify-content: center;
5
   justify-content: center;
6
   flex-wrap: wrap;
6
   flex-wrap: wrap;
7
-  width: 300px;
8
   margin-top: 20px;
7
   margin-top: 20px;
9
 
8
 
10
   button {
9
   button {

+ 6
- 2
imports/ui/styles/App.scss 파일 보기

10
 main {
10
 main {
11
     flex: 1;
11
     flex: 1;
12
     display: flex;
12
     display: flex;
13
+    flex-direction: row;
13
     justify-content: center;
14
     justify-content: center;
14
-    flex-direction: column;
15
+    align-items: stretch;
15
 }
16
 }
16
 
17
 
17
 header {
18
 header {
31
     margin: 30px;
32
     margin: 30px;
32
     padding: 20px;
33
     padding: 20px;
33
     display: flex;
34
     display: flex;
34
-    flex: 1;
35
+    min-width: 440px;
36
+    align-items: stretch;
35
     flex-direction: column;
37
     flex-direction: column;
36
     background-color: rgba(0,0,0,0.1);
38
     background-color: rgba(0,0,0,0.1);
37
 }
39
 }
43
 
45
 
44
     main {
46
     main {
45
         flex-direction: row;
47
         flex-direction: row;
48
+        justify-content: center;
49
+        align-items: flex-start;
46
     }
50
     }
47
 
51
 
48
     .center {
52
     .center {

+ 2
- 1
imports/ui/styles/Board.scss 파일 보기

1
 .board {
1
 .board {
2
     display: flex;
2
     display: flex;
3
     flex-direction: column;
3
     flex-direction: column;
4
-    align-items: center;
4
+    align-items: stretch;
5
+    max-width: 800px;
5
 
6
 
6
     .squares {
7
     .squares {
7
         position: relative;
8
         position: relative;

+ 1
- 1
imports/ui/styles/Image.scss 파일 보기

3
 .image {
3
 .image {
4
   position: absolute;
4
   position: absolute;
5
   top: 9px;
5
   top: 9px;
6
-  left: 9px;
6
+  left: calc(50% - 112px);
7
   width: 224px;
7
   width: 224px;
8
   border-radius: 5px;
8
   border-radius: 5px;
9
   border: 1px solid rgba(255, 255, 255, 0.5);
9
   border: 1px solid rgba(255, 255, 255, 0.5);

+ 40
- 19
imports/ui/styles/Score.scss 파일 보기

1
+@import "gradient";
2
+
1
 .score {
3
 .score {
2
   display: flex;
4
   display: flex;
3
-  width: 480px;
4
   flex-direction: column;
5
   flex-direction: column;
5
   margin-bottom: 50px;
6
   margin-bottom: 50px;
6
 
7
 
8
+  .current-score {
9
+    background: #fff;
10
+    height: 3px;
11
+    margin-top: 3px;
12
+  }
13
+
7
   .scale {
14
   .scale {
15
+    display: flex;
8
     flex: 1;
16
     flex: 1;
9
-    position: relative;
10
-    //width: 220px;
11
     height: 3px;
17
     height: 3px;
12
-    background: red; /* For browsers that do not support gradients */
13
-    background: -webkit-linear-gradient(left, cyan, blue, green, yellow, orange, red, violet); /* For Safari 5.1 to 6.0 */
14
-    background: -o-linear-gradient(right, cyan, blue, green, yellow, orange, red, violet); /* For Opera 11.1 to 12.0 */
15
-    background: -moz-linear-gradient(right, cyan, blue, green, yellow, orange, red, violet); /* For Firefox 3.6 to 15 */
16
-    background: linear-gradient(to right, cyan, blue, green, yellow, orange, red, violet); /* Standard syntax (must be last) */
18
+    justify-content: space-between;
19
+    margin-top: 1em;
20
+    position: relative;
21
+    @include rainbow-gradient();
17
 
22
 
18
     .grade {
23
     .grade {
19
-      height:5px;
20
-      position: absolute;
21
-      top: 0;
22
-    }
24
+      position: relative;
23
 
25
 
24
-  }
26
+      &:after {
27
+        background: white;
28
+        content: ' ';
29
+        display: block;
30
+        height: 5px;
31
+        position: absolute;
32
+        top: -1px;
33
+        width: 1px;
34
+      }
25
 
35
 
26
-  .current-score {
27
-    background: #fff;
28
-    height: 3px;
29
-    //width: 480px;
30
-    margin-top: 3px;
31
-  }
36
+      @mixin grade-label($number) {
37
+        content: quote(inspect($number));
38
+        position: absolute;
39
+        top: -20px;
40
+        @if($number > 10) { left: -8px; }
41
+        @else { left: -4px; }
42
+        color: white;
43
+      }
44
+
45
+      @mixin grade-labels($numbers...) {
46
+        @each $number in $numbers {
47
+          &:nth-child(#{$number + 1}):before { @include grade-label($number); }
48
+        }
49
+      }
32
 
50
 
51
+      @include grade-labels(0, 6, 12, 18, 24);
52
+    }
53
+  }
33
 }
54
 }

+ 8
- 0
imports/ui/styles/_gradient.scss 파일 보기

40
   background: -ms-radial-gradient(center, ellipse cover, $lighten 0%, $color 100%);
40
   background: -ms-radial-gradient(center, ellipse cover, $lighten 0%, $color 100%);
41
   background: radial-gradient(ellipse at center, $lighten 0%, $color 100%);
41
   background: radial-gradient(ellipse at center, $lighten 0%, $color 100%);
42
   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#{ie_hex_str($lighten)}', endColorstr='#{ie_hex_str($color)}', GradientType=1 );
42
   filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#{ie_hex_str($lighten)}', endColorstr='#{ie_hex_str($color)}', GradientType=1 );
43
+}
44
+
45
+@mixin rainbow-gradient() {
46
+  background: cyan; /* For browsers that do not support gradients */
47
+  background: -webkit-linear-gradient(left, cyan, blue, green, yellow, orange, red, violet); /* For Safari 5.1 to 6.0 */
48
+  background: -o-linear-gradient(right, cyan, blue, green, yellow, orange, red, violet); /* For Opera 11.1 to 12.0 */
49
+  background: -moz-linear-gradient(right, cyan, blue, green, yellow, orange, red, violet); /* For Firefox 3.6 to 15 */
50
+  background: linear-gradient(to right, cyan, blue, green, yellow, orange, red, violet); /* Standard syntax (must be last) */
43
 }
51
 }

+ 1
- 0
package.json 파일 보기

6
   },
6
   },
7
   "dependencies": {
7
   "dependencies": {
8
     "babel-runtime": "^6.20.0",
8
     "babel-runtime": "^6.20.0",
9
+    "chart.js": "^2.5.0",
9
     "meteor-node-stubs": "~0.2.4",
10
     "meteor-node-stubs": "~0.2.4",
10
     "react": "^15.4.2",
11
     "react": "^15.4.2",
11
     "react-addons-css-transition-group": "^15.4.2",
12
     "react-addons-css-transition-group": "^15.4.2",