From 87265577f200e0c8b08516de28946ed56e1a43aa Mon Sep 17 00:00:00 2001 From: Javi Velasco Date: Mon, 5 Oct 2015 09:12:16 +0200 Subject: [PATCH] Add checkbox component --- components/checkbox/index.jsx | 86 +++++++++++++++++++++++ components/checkbox/style.scss | 124 +++++++++++++++++++++++++++++++++ components/ripple/style.scss | 18 ++--- components/variables.scss | 7 ++ package.json | 1 - spec/components/checkbox.jsx | 47 +++++++++++++ 6 files changed, 273 insertions(+), 10 deletions(-) create mode 100644 components/checkbox/index.jsx create mode 100644 components/checkbox/style.scss create mode 100644 spec/components/checkbox.jsx diff --git a/components/checkbox/index.jsx b/components/checkbox/index.jsx new file mode 100644 index 00000000..e1900b13 --- /dev/null +++ b/components/checkbox/index.jsx @@ -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 ( + + ); + }, + + blur () { + this.refs.input.getDOMNode().blur(); + }, + + focus () { + this.refs.input.getDOMNode().focus(); + }, + + getValue () { + return this.state.checked; + }, + + setValue (value) { + this.setState({checked: value}); + } +}); diff --git a/components/checkbox/style.scss b/components/checkbox/style.scss new file mode 100644 index 00000000..103b2b88 --- /dev/null +++ b/components/checkbox/style.scss @@ -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; + } +} diff --git a/components/ripple/style.scss b/components/ripple/style.scss index f7f188c5..86f3d83b 100644 --- a/components/ripple/style.scss +++ b/components/ripple/style.scss @@ -1,28 +1,25 @@ @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 { @@ -30,6 +27,9 @@ $ripple-size: 15 * $unit; 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; @@ -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 { diff --git a/components/variables.scss b/components/variables.scss index f29fae44..370487b9 100644 --- a/components/variables.scss +++ b/components/variables.scss @@ -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; diff --git a/package.json b/package.json index 34ef7e77..896ee5e6 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/spec/components/checkbox.jsx b/spec/components/checkbox.jsx new file mode 100644 index 00000000..49487533 --- /dev/null +++ b/spec/components/checkbox.jsx @@ -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 ( +
+

Checkbox

+

Lorem ipsum...

+ + + +
+ ); + } +});