diff --git a/.eslintrc b/.eslintrc index 32c6d3f3..deff2827 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,6 +17,7 @@ "parser": "babel-eslint", "plugins": [ + "babel", "react" ], @@ -168,6 +169,7 @@ "operator-linebreak": [2, "after"], "padded-blocks": [0], "prefer-const": [2], + "prefer-spread": [2], "quote-props": [0], "radix": [0], "semi": [2], @@ -210,6 +212,7 @@ }], "react/jsx-uses-react": [2], "react/jsx-uses-vars": [2], - "react/react-in-jsx-scope": [2] + "react/react-in-jsx-scope": [2], + "babel/object-shorthand": [2] } } diff --git a/components/_mixins.scss b/components/_mixins.scss index da0db388..7a800dbb 100644 --- a/components/_mixins.scss +++ b/components/_mixins.scss @@ -1,4 +1,4 @@ -//-- Typography +// scss-lint:disable VendorPrefix @mixin typo-preferred-font($use-preferred: true) { @if $use-preferred { font-family: $preferred-font; @@ -262,21 +262,18 @@ @mixin ripple-loading($name, $width, $height, $opacity: 0.3) { @keyframes #{$name} { 0% { - width: 0; - height: 0; opacity: $opacity; + transform: translate3d(-50%, -50%, 0) scale(0); } 95% { - width: $width; - height: $height; opacity: 0; + transform: translate3d(-50%, -50%, 0) scale(1); } 100% { - width: 0; - height: 0; opacity: 0; + transform: translate3d(-50%, -50%, 0) scale(0); } } } diff --git a/components/button/index.jsx b/components/button/index.jsx index 5c0533e2..e4dd1706 100644 --- a/components/button/index.jsx +++ b/components/button/index.jsx @@ -51,8 +51,9 @@ class Button extends React.Component { }; render () { - const {accent, flat, floating, href, icon, label, loading, mini, - primary, raised, ripple, toggle, tooltip, tooltipDelay, ...others} = this.props; + const {accent, flat, floating, href, icon, label, + loading, mini, primary, raised, ripple, toggle, + tooltip, tooltipDelay, ...others} = this.props; //eslint-disable-line no-redeclare const element = href ? 'a' : 'button'; const level = primary ? 'primary' : accent ? 'accent' : 'neutral'; const shape = flat ? 'flat' : raised ? 'raised' : floating ? 'floating' : toggle ? 'toggle' : 'flat'; diff --git a/components/button/style.scss b/components/button/style.scss index a8cb4334..a84c6049 100644 --- a/components/button/style.scss +++ b/components/button/style.scss @@ -3,6 +3,7 @@ @import "./config"; %button { + @include typo-button(); position: relative; display: inline-block; height: $button-height; @@ -24,7 +25,6 @@ border: 0; } > span:not(.tooltip) { - @include typo-button(); display: inline-block; line-height: $button-height; vertical-align: middle; @@ -41,7 +41,7 @@ pointer-events: none; } [data-react-toolbox="ripple"] { - overflow: hidden; + @include rounded-overflow(); } } @@ -97,10 +97,8 @@ } .icon { font-size: 120%; - vertical-align: middle; - } - .icon { line-height: $button-height; + vertical-align: middle; } [data-react-toolbox="ripple"] { border-radius: 50%; diff --git a/components/commons.scss b/components/commons.scss index 32e4626f..d8c4b1c8 100644 --- a/components/commons.scss +++ b/components/commons.scss @@ -1,13 +1,9 @@ @import "./base"; +@import "~normalize.css"; -$import-normalize: true !default; $import-font: true !default; $import-flex-attributes: false !default; -@if $import-normalize == true { - @import "~normalize.css"; -} - @if $import-font == true { @import url($font-roboto-url); } diff --git a/components/dropdown/index.jsx b/components/dropdown/index.jsx index c2d435c7..8e2c929c 100644 --- a/components/dropdown/index.jsx +++ b/components/dropdown/index.jsx @@ -28,7 +28,7 @@ class Dropdown extends React.Component { const client = event.target.getBoundingClientRect(); const screen_height = window.innerHeight || document.documentElement.offsetHeight; const up = this.props.auto ? client.top > ((screen_height / 2) + client.height) : false; - this.setState({active: true, up: up}); + this.setState({active: true, up}); }; handleSelect = (item) => { diff --git a/components/ripple/index.jsx b/components/ripple/index.jsx index 297eedbc..cf2d96f2 100644 --- a/components/ripple/index.jsx +++ b/components/ripple/index.jsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import prefixer from '../utils/prefixer'; import style from './style'; class Ripple extends React.Component { @@ -25,32 +26,41 @@ class Ripple extends React.Component { width: null }; - handleEnd = (touch = false) => { - document.removeEventListener(touch ? 'touchend' : 'mouseup', this.handleEnd); + handleEnd = () => { + document.removeEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd); this.setState({active: false}); }; - start = ({ pageX, pageY }, touch = false) => { - document.addEventListener(touch ? 'touchend' : 'mouseup', this.handleEnd.bind(this, touch)); + start = ({pageX, pageY}, touch = false) => { + this.touch = touch; + document.addEventListener(this.touch ? 'touchend' : 'mouseup', this.handleEnd); const {top, left, width} = this._getDescriptor(pageX, pageY); - this.setState({active: false, restarting: true, width: 0}, () => { + this.setState({active: false, restarting: true, top, left, width}, () => { this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions - this.setState({active: true, restarting: false, top, left, width}); + this.setState({active: true, restarting: false}); }); }; _getDescriptor (pageX, pageY) { - const { left, top, height, width } = ReactDOM.findDOMNode(this).getBoundingClientRect(); + const {left, top, height, width} = ReactDOM.findDOMNode(this).getBoundingClientRect(); return { - left: this.props.centered ? width / 2 : pageX - left + window.scrollX, - top: this.props.centered ? height / 2 : pageY - top + window.scrollY, + left: this.props.centered ? 0 : pageX - left - width / 2 + window.scrollX, + top: this.props.centered ? 0 : pageY - top - height / 2 + window.scrollY, width: width * this.props.spread }; } render () { const { left, top, width } = this.state; - const rippleStyle = {left, top, width, height: width}; + const scale = this.state.restarting ? 0 : 1; + let rippleStyle = {width, height: width}; + + if (!this.props.loading) { + rippleStyle = prefixer({ + transform: `translate3d(${-width / 2 + left}px, ${-width / 2 + top}px, 0) scale(${scale})` + }, rippleStyle); + } + let className = style[this.props.loading ? 'loading' : 'normal']; if (this.state.active) className += ` ${style.active}`; if (this.state.restarting) className += ` ${style.restarting}`; diff --git a/components/ripple/style.scss b/components/ripple/style.scss index a25845fc..bddf5afe 100644 --- a/components/ripple/style.scss +++ b/components/ripple/style.scss @@ -3,14 +3,13 @@ %ripple { position: absolute; + top: 50%; + left: 50%; z-index: $z-index-high; pointer-events: none; background-color: currentColor; border-radius: 50%; - transform-style: preserve-3d; - transform: translate3d(-50%, -50%, 0); - backface-visibility: hidden; - will-change: width, height, opacity; + transform-origin: 50% 50%; } .wrapper { @@ -25,26 +24,24 @@ .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) { + &.restarting { + opacity: $ripple-final-opacity; + transition-property: none; + } + &.active { + opacity: $ripple-final-opacity; + transition-property: transform; + } + &:not(.active):not(.restarting) { opacity: 0; - transition-property: height, width, opacity; - &.restarting { - transition-property: none; - } + transition-property: opacity, transform; } } .loading { @extend %ripple; @include ripple-loading(ripple, $ripple-size, $ripple-size); - top: 50%; - left: 50%; width: $ripple-size; height: $ripple-size; opacity: $ripple-final-opacity; diff --git a/components/slider/__tests__/index.spec.jsx b/components/slider/__tests__/index.spec.jsx index e5e3d85e..07011034 100644 --- a/components/slider/__tests__/index.spec.jsx +++ b/components/slider/__tests__/index.spec.jsx @@ -103,7 +103,7 @@ describe('Slider', function () { describe('#events', function () { beforeEach(function () { onChange = sinon.spy(); - props = { min: -500, max: 500, onChange: onChange }; + props = { min: -500, max: 500, onChange }; state = { sliderStart: 0, sliderLength: 1000 }; slider = utils.renderComponent(Slider, props, state); slider.handleResize = (event, callback) => { callback(); }; diff --git a/components/tooltip/style.scss b/components/tooltip/style.scss index bc1b1a70..6b657c28 100644 --- a/components/tooltip/style.scss +++ b/components/tooltip/style.scss @@ -14,6 +14,7 @@ line-height: $font-size-small; color: $tooltip-color; text-align: center; + text-transform: none; background: $tooltip-background; border-radius: $tooltip-border-radius; transform: scale(0); diff --git a/components/utils/prefixer.js b/components/utils/prefixer.js index 1f3eeb13..17d6df65 100644 --- a/components/utils/prefixer.js +++ b/components/utils/prefixer.js @@ -25,8 +25,8 @@ function addPrefixesTo (style, property, value) { return style; } -function prefixer (style) { - const _style = {}; +function prefixer (style, defaultValue = {}) { + const _style = defaultValue; for (const property in style) { _style[property] = style[property]; if (properties[property]) { diff --git a/docs/app/components/preview/index.jsx b/docs/app/components/preview/index.jsx index d1badd05..f8e0a52d 100644 --- a/docs/app/components/preview/index.jsx +++ b/docs/app/components/preview/index.jsx @@ -39,7 +39,7 @@ const Preview = React.createClass({ setTimeout () { clearTimeout(this.timeoutID); - this.timeoutID = setTimeout.apply(null, arguments); + this.timeoutID = setTimeout(...arguments); }, compileCode () { @@ -72,7 +72,7 @@ const Preview = React.createClass({ const compiledCode = this.compileCode(); /*eslint-disable no-eval*/ - const Component = eval(compiledCode).apply(null, scope); + const Component = eval(compiledCode)(...scope); ReactDOM.render(Component, mountNode); if (this.state.error) { this.setState({error: null}); diff --git a/package.json b/package.json index 6b99c17d..32a827a8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-toolbox", "version": "0.12.12", - "homepage": "www.react-toolbox.com", + "homepage": "http://www.react-toolbox.com", "description": "A set of React components implementing Google's Material Design specification with the power of CSS Modules.", "author": "React Toolbox Team (http://github.com/react-toolbox)", "contributors": [ @@ -53,15 +53,16 @@ "autoprefixer": "^6.0.3", "babel": "^5.8.23", "babel-core": "^5.8.23", - "babel-eslint": "^4.1.3", + "babel-eslint": "^4.1.5", "babel-loader": "^5.3.2", "babel-plugin-react-transform": "^1.1.1", "core-js": "^1.2.6", "cpx": "^1.2.1", "cross-env": "^1.0.4", "css-loader": "^0.21.0", - "eslint": "^1.7.3", - "eslint-plugin-react": "^3.3.1", + "eslint": "^1.10.1", + "eslint-plugin-babel": "^2.1.1", + "eslint-plugin-react": "^3.10.0", "expect": "^1.8.0", "express": "^4.13.3", "extract-text-webpack-plugin": "^0.8.2",