Skip to content

Commit 8726557

Browse files
committed
Add checkbox component
1 parent f204ce4 commit 8726557

File tree

6 files changed

+273
-10
lines changed

6 files changed

+273
-10
lines changed

components/checkbox/index.jsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/* global React */
2+
3+
import { addons } from 'react/addons';
4+
import Ripple from '../ripple';
5+
import style from './style.scss';
6+
import events from '../utils/events';
7+
8+
export default React.createClass({
9+
mixins: [addons.PureRenderMixin],
10+
11+
displayName: 'Checkbox',
12+
13+
propTypes: {
14+
checked: React.PropTypes.bool,
15+
className: React.PropTypes.string,
16+
disabled: React.PropTypes.bool,
17+
label: React.PropTypes.string,
18+
onBlur: React.PropTypes.func,
19+
onChange: React.PropTypes.func,
20+
onFocus: React.PropTypes.func
21+
},
22+
23+
getDefaultProps () {
24+
return {
25+
className: '',
26+
disabled: false
27+
};
28+
},
29+
30+
getInitialState () {
31+
return { checked: this.props.checked };
32+
},
33+
34+
handleChange (event) {
35+
this.setState({checked: !this.state.checked});
36+
if (this.props.onChange) this.props.onChange(event, this);
37+
},
38+
39+
handleClick (event) {
40+
events.pauseEvent(event);
41+
if (!this.props.disabled) this.handleChange(event);
42+
},
43+
44+
handleMouseDown (event) {
45+
if (!this.props.disabled) this.refs.ripple.start(event);
46+
},
47+
48+
render () {
49+
let labelClassName = style[this.props.disabled ? 'disabled' : 'field'];
50+
if (this.props.className) labelClassName += ` ${this.props.className}`;
51+
let checkboxClassName = style[this.state.checked ? 'checked' : 'check'];
52+
53+
return (
54+
<label react-toolbox='checkbox' className={labelClassName} onClick={this.handleClick}>
55+
<input
56+
{...this.props}
57+
ref='input'
58+
type='checkbox'
59+
className={style.input}
60+
onChange={this.handleChange}
61+
checked={this.state.checked}
62+
/>
63+
<span role='checkbox' className={checkboxClassName} onMouseDown={this.handleMouseDown}>
64+
<Ripple ref='ripple' role='ripple' className={style.ripple} spread={3} centered />
65+
</span>
66+
{ this.props.label ? <span className={style.text}>{this.props.label}</span> : null }
67+
</label>
68+
);
69+
},
70+
71+
blur () {
72+
this.refs.input.getDOMNode().blur();
73+
},
74+
75+
focus () {
76+
this.refs.input.getDOMNode().focus();
77+
},
78+
79+
getValue () {
80+
return this.state.checked;
81+
},
82+
83+
setValue (value) {
84+
this.setState({checked: value});
85+
}
86+
});

components/checkbox/style.scss

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
@import "../variables";
2+
$checkbox-total-height: 1.8 * $unit;
3+
$checkbox-size: 1.8 * $unit;
4+
$checkbox-transition-duration: .2s;
5+
$checkbox-focus-size: $checkbox-size * 2.3;
6+
$checkbox-color: unquote("rgb(#{$color-primary})") !default;
7+
$checkbox-text-color: unquote("rgb(#{$color-black})") !default;
8+
$checkbox-disabled-color: unquote("rgba(#{$color-black}, 0.26)") !default;
9+
$checkbox-focus-color: unquote("rgba(#{$color-primary}, 0.26)") !default;
10+
11+
.field {
12+
position: relative;
13+
display: block;
14+
height: $checkbox-size;
15+
margin-bottom: 1.5 * $unit;
16+
white-space: nowrap;
17+
vertical-align: middle;
18+
}
19+
20+
.text {
21+
display: inline-block;
22+
padding-left: $unit;
23+
font-size: 1.4 * $unit;
24+
line-height: $checkbox-size;
25+
color: $checkbox-text-color;
26+
white-space: nowrap;
27+
vertical-align: top;
28+
}
29+
30+
.input {
31+
width: 0;
32+
height: 0;
33+
overflow: hidden;
34+
opacity: 0;
35+
36+
&:focus:not(&:active) + .check:before {
37+
position: absolute;
38+
top: 50%;
39+
left: 50%;
40+
z-index: $z-index-low;
41+
width: $checkbox-focus-size;
42+
height: $checkbox-focus-size;
43+
margin-top: - $checkbox-focus-size / 2;
44+
margin-left: - $checkbox-focus-size / 2;
45+
pointer-events: none;
46+
content: "";
47+
background-color: $checkbox-focus-color;
48+
border-radius: 50%;
49+
}
50+
}
51+
52+
.check {
53+
position: relative;
54+
display: inline-block;
55+
width: $checkbox-size;
56+
height: $checkbox-size;
57+
vertical-align: top;
58+
cursor: pointer;
59+
border: 2px solid $checkbox-text-color;
60+
border-radius: 2px;
61+
transition-timing-function: $animation-curve-default;
62+
transition-duration: $checkbox-transition-duration;
63+
transition-property: background-color;
64+
}
65+
66+
.checked {
67+
@extend .check;
68+
background-color: $checkbox-color;
69+
border-color: $checkbox-color;
70+
71+
&:after {
72+
position: absolute;
73+
top: -.1 * $unit;
74+
left: .4 * $unit;
75+
width: .7 * $unit;
76+
height: 1.2 * $unit;
77+
content: "";
78+
border-color: #fff;
79+
border-style: solid;
80+
border-top: 0;
81+
border-right-width: 2px;
82+
border-bottom-width: 2px;
83+
border-left: 0;
84+
transform: rotate(45deg);
85+
animation: checkmark-expand 140ms ease-out forwards;
86+
}
87+
}
88+
89+
.ripple {
90+
background-color: $checkbox-color;
91+
opacity: .3;
92+
transition-duration: 650ms;
93+
}
94+
95+
.disabled {
96+
color: $checkbox-disabled-color;
97+
98+
.check {
99+
cursor: auto;
100+
border-color: $checkbox-disabled-color;
101+
}
102+
103+
.checked {
104+
cursor: auto;
105+
background-color: $checkbox-disabled-color;
106+
border-color: transparent;
107+
}
108+
}
109+
110+
@keyframes checkmark-expand {
111+
0% {
112+
top: .9 * $unit;
113+
left: .6 * $unit;
114+
width: 0;
115+
height: 0;
116+
}
117+
118+
100% {
119+
top: -.1 * $unit;
120+
left: .4 * $unit;
121+
width: .7 * $unit;
122+
height: 1.2 * $unit;
123+
}
124+
}

components/ripple/style.scss

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
@import "../variables";
2-
3-
//-- Variables
42
$ripple-duration: 1.2s;
53
$ripple-final-opacity: .3;
64
$ripple-size: 15 * $unit;
75

8-
//-- Mixins
96
%ripple {
107
position: absolute;
8+
pointer-events: none;
119
background-color: currentColor;
1210
border-radius: 50%;
13-
transition-timing-function: $animation-curve-linear-out-slow-in;
14-
transition-duration: $ripple-duration;
15-
transition-property: height, width;
16-
transform: translateX(-50%) translateY(-50%);
11+
transform: translate3d(-50%, -50%, 0);
12+
transform-style: preserve-3d;
13+
backface-visibility: hidden;
1714
}
1815

19-
//-- Local Styles
2016
.wrapper {
2117
position: absolute;
2218
top: 0;
2319
right: 0;
2420
bottom: 0;
2521
left: 0;
22+
pointer-events: none;
2623
}
2724

2825
.normal {
2926
@extend %ripple;
3027
width: 0;
3128
height: 0;
3229
opacity: $ripple-final-opacity;
30+
transition-timing-function: $animation-curve-linear-out-slow-in;
31+
transition-duration: $ripple-duration;
32+
transition-property: height, width;
3333

3434
&:not(.active) {
3535
opacity: 0;
@@ -51,7 +51,7 @@ $ripple-size: 15 * $unit;
5151
animation-name: ripple;
5252
animation-duration: $ripple-duration;
5353
animation-timing-function: $animation-curve-linear-out-slow-in;
54-
animation-iteration-count: infinite;
54+
animation-iteration-count: 1;
5555
}
5656

5757
@keyframes ripple {

components/variables.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,10 @@ $animation-curve-default: $animation-curve-fast-out-slow-in !default;
3030

3131
//-- Input spaces
3232
$input-margin-bottom: $unit * .8;
33+
34+
//-- Indexes
35+
$z-index-higher: 200;
36+
$z-index-high: 100;
37+
$z-index-normal: 1;
38+
$z-index-low: -100;
39+
$z-index-lower: -200;

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
"normalize.css": "^3.0.3",
5454
"phantomjs-polyfill": "0.0.1",
5555
"postcss-loader": "^0.4.3",
56-
"react-css-modules": "^3.2.3",
5756
"react-hot-loader": "^1.3.0",
5857
"sass-loader": "^2.0.1",
5958
"sinon": "git://github.com/cjohansen/Sinon.JS#sinon-2.0",

spec/components/checkbox.jsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* global React */
2+
3+
import Checkbox from '../../components/checkbox';
4+
5+
export default React.createClass({
6+
handleChange () {
7+
console.log('Changed!');
8+
},
9+
10+
handleFocus () {
11+
console.log('Focused');
12+
},
13+
14+
handleBlur () {
15+
console.log('Blur');
16+
},
17+
18+
render () {
19+
return (
20+
<section>
21+
<h2>Checkbox</h2>
22+
<p style={{marginBottom: '10px'}}>Lorem ipsum...</p>
23+
<Checkbox
24+
label="Checked checkbox"
25+
checked
26+
onChange={this.handleChange}
27+
onFocus={this.handleFocus}
28+
onBlur={this.handleBlur}
29+
/>
30+
<Checkbox
31+
label="Not checked biatch"
32+
onChange={this.handleChange}
33+
onFocus={this.handleFocus}
34+
onBlur={this.handleBlur}
35+
/>
36+
<Checkbox
37+
label="Disabled checkbox"
38+
checked
39+
disabled
40+
onChange={this.handleChange}
41+
onFocus={this.handleFocus}
42+
onBlur={this.handleBlur}
43+
/>
44+
</section>
45+
);
46+
}
47+
});

0 commit comments

Comments
 (0)