Skip to content

Commit

Permalink
Add checkbox component
Browse files Browse the repository at this point in the history
  • Loading branch information
javivelasco committed Oct 5, 2015
1 parent f204ce4 commit 8726557
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 10 deletions.
86 changes: 86 additions & 0 deletions components/checkbox/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* global React */

import { addons } from 'react/addons';
import Ripple from '../ripple';
import style from './style.scss';
import events from '../utils/events';

export default React.createClass({
mixins: [addons.PureRenderMixin],

displayName: 'Checkbox',

propTypes: {
checked: React.PropTypes.bool,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
label: React.PropTypes.string,
onBlur: React.PropTypes.func,
onChange: React.PropTypes.func,
onFocus: React.PropTypes.func
},

getDefaultProps () {
return {
className: '',
disabled: false
};
},

getInitialState () {
return { checked: this.props.checked };
},

handleChange (event) {
this.setState({checked: !this.state.checked});
if (this.props.onChange) this.props.onChange(event, this);
},

handleClick (event) {
events.pauseEvent(event);
if (!this.props.disabled) this.handleChange(event);
},

handleMouseDown (event) {
if (!this.props.disabled) this.refs.ripple.start(event);
},

render () {
let labelClassName = style[this.props.disabled ? 'disabled' : 'field'];
if (this.props.className) labelClassName += ` ${this.props.className}`;
let checkboxClassName = style[this.state.checked ? 'checked' : 'check'];

return (
<label react-toolbox='checkbox' className={labelClassName} onClick={this.handleClick}>
<input
{...this.props}
ref='input'
type='checkbox'
className={style.input}
onChange={this.handleChange}
checked={this.state.checked}
/>
<span role='checkbox' className={checkboxClassName} onMouseDown={this.handleMouseDown}>
<Ripple ref='ripple' role='ripple' className={style.ripple} spread={3} centered />
</span>
{ this.props.label ? <span className={style.text}>{this.props.label}</span> : null }
</label>
);
},

blur () {
this.refs.input.getDOMNode().blur();
},

focus () {
this.refs.input.getDOMNode().focus();
},

getValue () {
return this.state.checked;
},

setValue (value) {
this.setState({checked: value});
}
});
124 changes: 124 additions & 0 deletions components/checkbox/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
@import "../variables";
$checkbox-total-height: 1.8 * $unit;
$checkbox-size: 1.8 * $unit;
$checkbox-transition-duration: .2s;
$checkbox-focus-size: $checkbox-size * 2.3;
$checkbox-color: unquote("rgb(#{$color-primary})") !default;
$checkbox-text-color: unquote("rgb(#{$color-black})") !default;
$checkbox-disabled-color: unquote("rgba(#{$color-black}, 0.26)") !default;
$checkbox-focus-color: unquote("rgba(#{$color-primary}, 0.26)") !default;

.field {
position: relative;
display: block;
height: $checkbox-size;
margin-bottom: 1.5 * $unit;
white-space: nowrap;
vertical-align: middle;
}

.text {
display: inline-block;
padding-left: $unit;
font-size: 1.4 * $unit;
line-height: $checkbox-size;
color: $checkbox-text-color;
white-space: nowrap;
vertical-align: top;
}

.input {
width: 0;
height: 0;
overflow: hidden;
opacity: 0;

&:focus:not(&:active) + .check:before {
position: absolute;
top: 50%;
left: 50%;
z-index: $z-index-low;
width: $checkbox-focus-size;
height: $checkbox-focus-size;
margin-top: - $checkbox-focus-size / 2;
margin-left: - $checkbox-focus-size / 2;
pointer-events: none;
content: "";
background-color: $checkbox-focus-color;
border-radius: 50%;
}
}

.check {
position: relative;
display: inline-block;
width: $checkbox-size;
height: $checkbox-size;
vertical-align: top;
cursor: pointer;
border: 2px solid $checkbox-text-color;
border-radius: 2px;
transition-timing-function: $animation-curve-default;
transition-duration: $checkbox-transition-duration;
transition-property: background-color;
}

.checked {
@extend .check;
background-color: $checkbox-color;
border-color: $checkbox-color;

&:after {
position: absolute;
top: -.1 * $unit;
left: .4 * $unit;
width: .7 * $unit;
height: 1.2 * $unit;
content: "";
border-color: #fff;
border-style: solid;
border-top: 0;
border-right-width: 2px;
border-bottom-width: 2px;
border-left: 0;
transform: rotate(45deg);
animation: checkmark-expand 140ms ease-out forwards;
}
}

.ripple {
background-color: $checkbox-color;
opacity: .3;
transition-duration: 650ms;
}

.disabled {
color: $checkbox-disabled-color;

.check {
cursor: auto;
border-color: $checkbox-disabled-color;
}

.checked {
cursor: auto;
background-color: $checkbox-disabled-color;
border-color: transparent;
}
}

@keyframes checkmark-expand {
0% {
top: .9 * $unit;
left: .6 * $unit;
width: 0;
height: 0;
}

100% {
top: -.1 * $unit;
left: .4 * $unit;
width: .7 * $unit;
height: 1.2 * $unit;
}
}
18 changes: 9 additions & 9 deletions components/ripple/style.scss
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
@import "../variables";

//-- Variables
$ripple-duration: 1.2s;
$ripple-final-opacity: .3;
$ripple-size: 15 * $unit;

//-- Mixins
%ripple {
position: absolute;
pointer-events: none;
background-color: currentColor;
border-radius: 50%;
transition-timing-function: $animation-curve-linear-out-slow-in;
transition-duration: $ripple-duration;
transition-property: height, width;
transform: translateX(-50%) translateY(-50%);
transform: translate3d(-50%, -50%, 0);
transform-style: preserve-3d;
backface-visibility: hidden;
}

//-- Local Styles
.wrapper {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}

.normal {
@extend %ripple;
width: 0;
height: 0;
opacity: $ripple-final-opacity;
transition-timing-function: $animation-curve-linear-out-slow-in;
transition-duration: $ripple-duration;
transition-property: height, width;

&:not(.active) {
opacity: 0;
Expand All @@ -51,7 +51,7 @@ $ripple-size: 15 * $unit;
animation-name: ripple;
animation-duration: $ripple-duration;
animation-timing-function: $animation-curve-linear-out-slow-in;
animation-iteration-count: infinite;
animation-iteration-count: 1;
}

@keyframes ripple {
Expand Down
7 changes: 7 additions & 0 deletions components/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ $animation-curve-default: $animation-curve-fast-out-slow-in !default;

//-- Input spaces
$input-margin-bottom: $unit * .8;

//-- Indexes
$z-index-higher: 200;
$z-index-high: 100;
$z-index-normal: 1;
$z-index-low: -100;
$z-index-lower: -200;
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"normalize.css": "^3.0.3",
"phantomjs-polyfill": "0.0.1",
"postcss-loader": "^0.4.3",
"react-css-modules": "^3.2.3",
"react-hot-loader": "^1.3.0",
"sass-loader": "^2.0.1",
"sinon": "git://github.com/cjohansen/Sinon.JS#sinon-2.0",
Expand Down
47 changes: 47 additions & 0 deletions spec/components/checkbox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* global React */

import Checkbox from '../../components/checkbox';

export default React.createClass({
handleChange () {
console.log('Changed!');
},

handleFocus () {
console.log('Focused');
},

handleBlur () {
console.log('Blur');
},

render () {
return (
<section>
<h2>Checkbox</h2>
<p style={{marginBottom: '10px'}}>Lorem ipsum...</p>
<Checkbox
label="Checked checkbox"
checked
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<Checkbox
label="Not checked biatch"
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<Checkbox
label="Disabled checkbox"
checked
disabled
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
</section>
);
}
});

0 comments on commit 8726557

Please sign in to comment.