소스 검색

fonctionnal, to add scale

Benoit Sida 3 년 전
부모
커밋
b25299e1b7

+ 10
- 17
imports/ui/Actions.jsx 파일 보기

@@ -1,23 +1,16 @@
1
-import React, { Component, PropTypes } from 'react';
1
+import React, { PropTypes } from 'react';
2 2
 import './styles/Actions';
3 3
 
4
-export default class Actions extends Component {
5
-	render() {
6
-		return (
7
-			<div className="actions">
8
-				{
9
-					this.props.actions.map(action => (
10
-						<button key={action.label} onClick={action.action}>{action.label}</button>
11
-					))
12
-				}
13
-			</div>
14
-		);
15
-	}
16
-}
4
+const Actions = ({ actions }) => <div className="actions">
5
+	{ actions.map(action => <button key={action.label} disabled={!action.active()} onClick={action.action}>{action.label}</button>) }
6
+</div>;
17 7
 
18 8
 Actions.propTypes = {
19 9
 	actions: PropTypes.arrayOf(PropTypes.shape({
20
-		label: PropTypes.string.isRequired,
21
-		action: PropTypes.func
10
+		label: PropTypes.string,
11
+		action: PropTypes.func,
12
+		active: PropTypes.func
22 13
 	})).isRequired
23
-};
14
+};
15
+
16
+export default Actions;

+ 28
- 32
imports/ui/Board.jsx 파일 보기

@@ -1,24 +1,25 @@
1
-import React, { Component, PropTypes } from 'react';
1
+import React, { Component } from 'react';
2 2
 import update from 'react-addons-update';
3 3
 import ReactTimeout from 'react-timeout';
4
-import Square from './Square';
5
-import Trials from './Trials';
6
-import Actions from './Actions';
7
-import './styles/Board';
4
+import BoardLayout from './BoardLayout';
8 5
 
9 6
 class Board extends Component {
10 7
 	constructor(props) {
11 8
 		super(props);
12 9
 		this.state = {
10
+			end: false,
13 11
 			win: false,
14 12
 			trials: 0,
15
-			squares: [false, false, false, false],
16
-			image: ""
13
+			score: 0,
14
+			squares: [ false, false, false, false ],
15
+			image: "/images/img1.jpg"
17 16
 		};
18 17
 		this.actions = [
19
-			{ label: 'Reset', action: this.reset.bind(this) },
20
-			{ label: 'Pass', action: this.pass.bind(this) }
18
+			{ label: 'Reset', action: this.reset.bind(this), active: () => true },
19
+			{ label: 'Pass', action: this.pass.bind(this), active: () => !this.state.end }
21 20
 		];
21
+		this.choices = [];
22
+		[0, 1, 2, 3].map(i => this.choices.push(this.choice.bind(this, i)));
22 23
 	}
23 24
 
24 25
 	fail(corrected) {
@@ -29,42 +30,37 @@ class Board extends Component {
29 30
 	}
30 31
 
31 32
 	win() {
32
-		this.setState({ win: true });
33
-		this.props.setTimeout(() => this.setState({ win: false }), 1000);
33
+		this.setState({ win: true, score: this.state.score + 1 });
34
+		this.props.setTimeout(() => this.setState({ win: false }), 2000);
34 35
 	}
35 36
 
36 37
 	choice(answer) {
37 38
 		let corrected = Math.floor(Math.random() * 10) % 4;
38
-		this.setState({ trials: this.state.trials + 1 });
39
+		this.setState({
40
+			trials: this.state.trials + 1,
41
+			image: `images/img${this.state.trials + 1}.jpg`,
42
+			end: this.state.trials + 1 >= 24
43
+		});
39 44
 		if (answer === corrected)
40 45
 			this.win();
41 46
 		else
42 47
 			this.fail(corrected);
43 48
 	}
44 49
 
45
-	reset() {}
50
+	reset() {
51
+		this.setState({ trials: 0, image: 'images/img0.jpg', score: 0 });
52
+	}
46 53
 
47
-	pass() {}
54
+	pass() {
55
+		let corrected = Math.floor(Math.random() * 10) % 4;
56
+		this.fail(corrected);
57
+	}
48 58
 
49 59
 	render() {
50
-		return (
51
-			<div className="board">
52
-				<div className="squares">
53
-					<div className="row">
54
-						<Square color="green" click={this.choice.bind(this, 0)} highlighted={this.state.squares[0]} />
55
-						<Square color="yellow" click={this.choice.bind(this, 1)} highlighted={this.state.squares[1]} />
56
-					</div>
57
-					<div className="row">
58
-						<Square color="red" click={this.choice.bind(this, 2)} highlighted={this.state.squares[2]} />
59
-						<Square color="blue" click={this.choice.bind(this, 3)} highlighted={this.state.squares[3]} />
60
-					</div>
61
-				</div>
62
-				{ this.state.win ? <Image src={this.state.image}/> : null }
63
-				<Trials count={this.state.trials} />
64
-				<Actions actions={this.actions}/>
65
-			</div>
66
-		);
60
+		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}
62
+							trials={this.state.trials} image={this.state.image} />;
67 63
 	}
68 64
 }
69 65
 
70
-export default ReactTimeout(Board)
66
+export default ReactTimeout(Board);

+ 41
- 0
imports/ui/BoardLayout.jsx 파일 보기

@@ -0,0 +1,41 @@
1
+import React, { PropTypes } from 'react';
2
+import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
3
+import Square from './Square';
4
+import Trials from './Trials';
5
+import Actions from './Actions';
6
+import Image from './Image';
7
+import Score from './Score';
8
+import './styles/Board';
9
+
10
+const BoardLayout = ({ active, win, actions, trials, score, image, choices, highlighted }) =>
11
+	<div className="board">
12
+		<Score score={score} />
13
+		<div className="squares">
14
+			<div className="row">
15
+				<Square color="green" click={choices[0]} highlighted={highlighted[0]} active={active} />
16
+				<Square color="yellow" click={choices[1]} highlighted={highlighted[1]} active={active} />
17
+			</div>
18
+			<div className="row">
19
+				<Square color="red" click={choices[2]} highlighted={highlighted[2]} active={active} />
20
+				<Square color="blue" click={choices[3]} highlighted={highlighted[3]} active={active} />
21
+			</div>
22
+			<ReactCSSTransitionGroup transitionName="appear" transitionEnterTimeout={500} transitionLeaveTimeout={500} >
23
+				{ win ? <Image src={image}/> : null }
24
+			</ReactCSSTransitionGroup>
25
+		</div>
26
+		<Trials count={trials} />
27
+		<Actions actions={actions} />
28
+	</div>;
29
+
30
+BoardLayout.propTypes = {
31
+	active: PropTypes.bool,
32
+	win: PropTypes.bool.isRequired,
33
+	actions: Actions.propTypes.actions,
34
+	trials: Trials.propTypes.count,
35
+	score: Trials.propTypes.count,
36
+	image: Image.propTypes.src,
37
+	choices: PropTypes.arrayOf(PropTypes.func).isRequired,
38
+	highlighted: PropTypes.arrayOf(PropTypes.bool).isRequired
39
+};
40
+
41
+export default BoardLayout;

+ 5
- 13
imports/ui/Image.jsx 파일 보기

@@ -1,16 +1,8 @@
1
-import React, { Component, PropTypes } from 'react';
1
+import React, { PropTypes } from 'react';
2 2
 import './styles/Image';
3 3
 
4
-export default class Image extends Component {
5
-	render() {
6
-		return (
7
-			<div className="image">
8
-				<img src={this.props.image} alt=""/>
9
-			</div>
10
-		);
11
-	}
12
-}
4
+const Image = ({ src }) => <img className="image" src={src} alt=""/>;
13 5
 
14
-Image.propTypes = {
15
-	src: PropTypes.string.isRequired
16
-};
6
+Image.propTypes = { src: PropTypes.string.isRequired };
7
+
8
+export default Image;

+ 16
- 0
imports/ui/Score.jsx 파일 보기

@@ -0,0 +1,16 @@
1
+import React, { PropTypes } from 'react';
2
+import './styles/Score';
3
+
4
+const Score = ({ score }) =>
5
+	<div className="score">
6
+		<div className="scale">
7
+			{
8
+				[...Array(25).keys()].map(x => <div key={x} className="grade" style={{left: (x * 20) + 'px'}} />)
9
+			}
10
+		</div>
11
+		<div className="current-score" style={{ width: (score * 20) + 'px' }} />
12
+	</div>;
13
+
14
+Score.propTypes = { score: PropTypes.number.isRequired };
15
+
16
+export default Score;

+ 9
- 12
imports/ui/Square.jsx 파일 보기

@@ -1,18 +1,15 @@
1
-import React, { Component, PropTypes } from 'react';
1
+import React, { PropTypes } from 'react';
2 2
 import './styles/Square';
3 3
 
4
-export default class Square extends Component {
5
-	render() {
6
-		const classes = classNames('square', this.props.color, { 'highlighted': this.props.highlighted });
7
-		return (
8
-				<button className={classes} style={this.style}
9
-						onClick={this.props.click} />
10
-		);
11
-	}
12
-}
4
+const Square = ({color, click, highlighted, active}) =>
5
+	<button className={classNames('square', color, { highlighted, disabled: !active })}
6
+			onClick={click} disabled={!active}/>;
13 7
 
14 8
 Square.propTypes = {
15 9
 	color: PropTypes.string.isRequired,
16 10
 	click: PropTypes.func,
17
-	highlighted: PropTypes.bool
18
-};
11
+	highlighted: PropTypes.bool,
12
+	active: PropTypes.bool
13
+};
14
+
15
+export default Square;

+ 8
- 16
imports/ui/Trials.jsx 파일 보기

@@ -1,20 +1,12 @@
1
-import React, { Component, PropTypes } from 'react';
1
+import React, { PropTypes } from 'react';
2 2
 import './styles/Trials';
3 3
 
4
-export default class Trials extends Component {
5
-	render() {
6
-		return (
7
-			<div className="trials">
8
-				<div><span className="number">{this.props.count}</span> <span className="text">trials</span></div>
9
-			</div>
10
-		);
11
-	}
12
-}
4
+const Trials = ({ count }) =>
5
+	<div className="trials">
6
+		<div><span className="number">{count}</span> <span className="text">trials</span></div>
7
+	</div>;
13 8
 
14
-Trials.defaultProps = {
15
-	count: 0
16
-};
9
+Trials.defaultProps = { count: 0 };
10
+Trials.propTypes = { count: PropTypes.number };
17 11
 
18
-Trials.propTypes = {
19
-	count: PropTypes.number
20
-};
12
+export default Trials;

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

@@ -4,6 +4,7 @@
4 4
     align-items: center;
5 5
 
6 6
     .squares {
7
+        position: relative;
7 8
         display: flex;
8 9
         flex-direction: column;
9 10
 

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

@@ -0,0 +1,29 @@
1
+@import "shadow";
2
+
3
+.image {
4
+  position: absolute;
5
+  top: 9px;
6
+  left: 9px;
7
+  width: 224px;
8
+  border-radius: 5px;
9
+  border: 1px solid rgba(255, 255, 255, 0.5);
10
+  @include shadow();
11
+}
12
+
13
+.appear-enter {
14
+  opacity: 0.01;
15
+}
16
+
17
+.appear-enter.appear-enter-active {
18
+  opacity: 1;
19
+  transition: opacity 500ms ease-in;
20
+}
21
+
22
+.appear-leave {
23
+  opacity: 1;
24
+}
25
+
26
+.appear-leave.appear-leave-active {
27
+  opacity: 0.01;
28
+  transition: opacity 300ms ease-in;
29
+}

+ 33
- 0
imports/ui/styles/Score.scss 파일 보기

@@ -0,0 +1,33 @@
1
+.score {
2
+  display: flex;
3
+  width: 480px;
4
+  flex-direction: column;
5
+  margin-bottom: 50px;
6
+
7
+  .scale {
8
+    flex: 1;
9
+    position: relative;
10
+    //width: 220px;
11
+    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) */
17
+
18
+    .grade {
19
+      height:5px;
20
+      position: absolute;
21
+      top: 0;
22
+    }
23
+
24
+  }
25
+
26
+  .current-score {
27
+    background: #fff;
28
+    height: 3px;
29
+    //width: 480px;
30
+    margin-top: 3px;
31
+  }
32
+
33
+}

+ 7
- 0
imports/ui/styles/Square.scss 파일 보기

@@ -24,7 +24,14 @@ $square-yellow: darken(yellow, 20);
24 24
     @mixin square-color($color, $direction, $inverted) {
25 25
         @if($inverted) { @include gradient(lighten($color, 20), $color, $direction); }
26 26
         @else { @include gradient($color, lighten($color, 20), $direction); }
27
+
27 28
         &.highlighted { @include radial-gradient($color); }
29
+
30
+        &.disabled {
31
+            $color: desaturate($color, 50);
32
+            @if($inverted) { @include gradient(lighten($color, 20), $color, $direction); }
33
+            @else { @include gradient($color, lighten($color, 20), $direction); }
34
+        }
28 35
     }
29 36
 
30 37
     &.blue { @include square-color($square-blue, 'bottom', false); }

+ 1
- 0
package.json 파일 보기

@@ -8,6 +8,7 @@
8 8
     "babel-runtime": "^6.20.0",
9 9
     "meteor-node-stubs": "~0.2.4",
10 10
     "react": "^15.4.2",
11
+    "react-addons-css-transition-group": "^15.4.2",
11 12
     "react-addons-update": "^15.4.2",
12 13
     "react-dom": "^15.4.2",
13 14
     "react-timeout": "^1.0.0"

BIN
public/images/img1.jpg 파일 보기


BIN
public/images/img10.jpg 파일 보기


BIN
public/images/img11.jpg 파일 보기


BIN
public/images/img12.jpg 파일 보기


BIN
public/images/img13.jpg 파일 보기


BIN
public/images/img14.jpg 파일 보기


BIN
public/images/img15.jpg 파일 보기


BIN
public/images/img16.jpg 파일 보기


BIN
public/images/img17.jpg 파일 보기


BIN
public/images/img18.jpg 파일 보기


BIN
public/images/img19.jpg 파일 보기


BIN
public/images/img2.jpg 파일 보기


BIN
public/images/img20.jpg 파일 보기


BIN
public/images/img21.jpg 파일 보기


BIN
public/images/img22.jpg 파일 보기


BIN
public/images/img23.jpg 파일 보기


BIN
public/images/img24.jpg 파일 보기


BIN
public/images/img25.jpg 파일 보기


BIN
public/images/img26.jpg 파일 보기


BIN
public/images/img27.jpg 파일 보기


BIN
public/images/img28.jpg 파일 보기


BIN
public/images/img29.jpg 파일 보기


BIN
public/images/img3.jpg 파일 보기


BIN
public/images/img30.jpg 파일 보기


BIN
public/images/img4.jpg 파일 보기


BIN
public/images/img5.jpg 파일 보기


BIN
public/images/img6.jpg 파일 보기


BIN
public/images/img7.jpg 파일 보기


BIN
public/images/img8.jpg 파일 보기


BIN
public/images/img9.jpg 파일 보기