diff --git a/components/_mixins.scss b/components/_mixins.scss index c4bc7ee6..da0db388 100644 --- a/components/_mixins.scss +++ b/components/_mixins.scss @@ -252,6 +252,11 @@ transition-duration: $duration; } +@mixin rounded-overflow() { + overflow: hidden; + -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC); +} + // The frames are this way to prevent a flicker in Safari // See https://goo.gl/5luFDk @mixin ripple-loading($name, $width, $height, $opacity: 0.3) { diff --git a/components/app/style.scss b/components/app/style.scss index 55c9f8cf..1de44941 100644 --- a/components/app/style.scss +++ b/components/app/style.scss @@ -1,5 +1,3 @@ -@import "../commons"; - .root { position: absolute; top: 0; diff --git a/components/autocomplete/index.jsx b/components/autocomplete/index.jsx index e757d327..e707fd6f 100644 --- a/components/autocomplete/index.jsx +++ b/components/autocomplete/index.jsx @@ -59,7 +59,7 @@ class Autocomplete extends React.Component { }; handleQueryChange = (event) => { - this.setState({event: event.target.value}); + this.setState({query: event.target.value}); }; handleQueryFocus = () => { diff --git a/components/autocomplete/style.scss b/components/autocomplete/style.scss index 382c6185..853f0947 100644 --- a/components/autocomplete/style.scss +++ b/components/autocomplete/style.scss @@ -17,7 +17,7 @@ } &.errored { .suggestions { - margin-top: - $input-error-height; + margin-top: - $input-underline-height; } } } diff --git a/components/button/index.jsx b/components/button/index.jsx index 2e314a3d..31fde0e6 100644 --- a/components/button/index.jsx +++ b/components/button/index.jsx @@ -43,6 +43,12 @@ class Button extends React.Component { if (this.props.onMouseDown) this.props.onMouseDown(event); }; + handleTouchStart = (event) => { + events.pauseEvent(event); + if (this.refs.ripple) this.refs.ripple.start(event.touches[0], true); + if (this.props.onTouchStart) this.props.onTouchStart(event); + }; + render () { const {accent, flat, floating, href, icon, label, loading, mini, primary, raised, ripple, toggle, tooltip, ...others} = this.props; @@ -59,7 +65,8 @@ class Button extends React.Component { href, className, disabled: this.props.disabled || this.props.loading, - onMouseDown: this.handleMouseDown + onMouseDown: this.handleMouseDown, + onTouchStart: this.handleTouchStart }; return React.createElement(element, props, diff --git a/components/button/style.scss b/components/button/style.scss index a8cb4334..d9fc253b 100644 --- a/components/button/style.scss +++ b/components/button/style.scss @@ -41,7 +41,7 @@ pointer-events: none; } [data-react-toolbox="ripple"] { - overflow: hidden; + @include rounded-overflow(); } } diff --git a/components/commons.scss b/components/commons.scss index e71c736a..32e4626f 100644 --- a/components/commons.scss +++ b/components/commons.scss @@ -1,6 +1,16 @@ -@import "~normalize.css"; @import "./base"; -@import url($font-roboto-url); + +$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); +} html { font-size: 62.5%; @@ -93,97 +103,99 @@ p { @include typo-body-1; } -//-- Flex -[data-flex] { - display: flex; -} +@if $import-flex-attributes == true { + //-- Flex + [data-flex] { + display: flex; + } -body[data-flex] { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - overflow: hidden; -} + body[data-flex] { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + overflow: hidden; + } -// -- Direction -[data-flex^="horizontal"] { - flex-direction: row; -} + // -- Direction + [data-flex^="horizontal"] { + flex-direction: row; + } -[data-flex^="vertical"] { - flex-direction: column; -} + [data-flex^="vertical"] { + flex-direction: column; + } -// -- Size { -[data-flex*="grow"] { - > *:not([data-column]):not([data-flex-grow]) { - flex-grow: 1; + // -- Size { + [data-flex*="grow"] { + > *:not([data-column]):not([data-flex-grow]) { + flex-grow: 1; + } + } + + [data-flex-grow="min"] { + flex-grow: 0; + } + + [data-flex-grow="max"] { + flex-grow: 2; + } + + // -- Container properties + [data-flex*="wrap"] { + flex-wrap: wrap; + } + + [data-flex*="center"] { + align-content: center; + align-items: center; + justify-content: center; + } + + [data-flex-justify="start"] { + justify-content: flex-start; + } + + [data-flex-justify="center"] { + justify-content: center; + } + + [data-flex-justify="end"] { + justify-content: flex-end; + } + + [data-flex-content="start"] { + align-content: flex-start; + } + + [data-flex-content="center"] { + align-content: center; + } + + [data-flex-content="end"] { + align-content: flex-end; + } + + [data-flex-items="center"] { + align-items: center; + } + + [data-flex-items="start"] { + align-items: flex-start; + } + + [data-flex-items="end"] { + align-items: flex-end; + } + + // -- Children properties + [data-flex-order="first"] { + order: -1; + } + + [data-flex-order="last"] { + order: 999999; } } - -[data-flex-grow="min"] { - flex-grow: 0; -} - -[data-flex-grow="max"] { - flex-grow: 2; -} - -// -- Container properties -[data-flex*="wrap"] { - flex-wrap: wrap; -} - -[data-flex*="center"] { - align-content: center; - align-items: center; - justify-content: center; -} - -[data-flex-justify="start"] { - justify-content: flex-start; -} - -[data-flex-justify="center"] { - justify-content: center; -} - -[data-flex-justify="end"] { - justify-content: flex-end; -} - -[data-flex-content="start"] { - align-content: flex-start; -} - -[data-flex-content="center"] { - align-content: center; -} - -[data-flex-content="end"] { - align-content: flex-end; -} - -[data-flex-items="center"] { - align-items: center; -} - -[data-flex-items="start"] { - align-items: flex-start; -} - -[data-flex-items="end"] { - align-items: flex-end; -} - -// -- Children properties -[data-flex-order="first"] { - order: -1; -} - -[data-flex-order="last"] { - order: 999999; -} diff --git a/components/input/_config.scss b/components/input/_config.scss index d89a4911..487e100a 100644 --- a/components/input/_config.scss +++ b/components/input/_config.scss @@ -11,7 +11,7 @@ $input-text-highlight-color: unquote("rgb(#{$color-primary})") !default; $input-text-disabled-color: $input-text-bottom-border-color !default; $input-text-disabled-text-color: $input-text-label-color !default; $input-text-error-color: unquote("rgb(222, 50, 38)") !default; -$input-error-height: 1.8 * $unit; +$input-underline-height: 2 * $unit; $input-icon-font-size: 2.4 * $unit; $input-icon-size: 2 * $input-icon-font-size; $input-icon-offset: 1.6 * $unit; diff --git a/components/input/index.jsx b/components/input/index.jsx index 2466e68d..e2135b85 100644 --- a/components/input/index.jsx +++ b/components/input/index.jsx @@ -12,6 +12,7 @@ class Input extends React.Component { floating: React.PropTypes.bool, icon: React.PropTypes.string, label: React.PropTypes.string, + maxLength: React.PropTypes.number, multiline: React.PropTypes.bool, onBlur: React.PropTypes.func, onChange: React.PropTypes.func, @@ -41,6 +42,16 @@ class Input extends React.Component { } } + renderUnderline () { + const error = this.props.error ? {this.props.error} : null; + let counter = null; + if (this.props.maxLength) { + const length = this.props.value ? this.props.value.length : 0; + if (length > 0) counter = {length} / {this.props.maxLength}; + } + if (error || counter) return {error}{counter}; + } + render () { const className = classNames({ [style.root]: true, @@ -62,7 +73,7 @@ class Input extends React.Component { { this.props.icon ? : null } { this.props.label ? : null } - { this.props.error ? {this.props.error} : null } + { this.renderUnderline() } { this.props.tooltip ? : null } ); diff --git a/components/input/readme.md b/components/input/readme.md index cc06f3cd..11afe733 100644 --- a/components/input/readme.md +++ b/components/input/readme.md @@ -38,6 +38,7 @@ class InputTest extends React.Component { | `icon` | `String` | | Name of an icon to use as a label for the input.| | `floating` | `Boolean` | `true` | Indicates if the label is floating in the input field or not.| | `label` | `String` | | The text string to use for the floating label element.| +| `maxLength` | `number` | |Specifies the maximum number of characters allowed in the component.| | `multiline` | `Boolean` | `false` | If true, a textarea element will be rendered. The textarea also grows and shrinks according to the number of lines.| | `onBlur` | `Function` | | Callback function that is fired when components is blured.| | `onChange` | `Function` | | Callback function that is fired when the components's value changes.| diff --git a/components/input/style.scss b/components/input/style.scss index b36350da..9c58a70a 100644 --- a/components/input/style.scss +++ b/components/input/style.scss @@ -90,11 +90,23 @@ } } -.error { - display: block; +.underline { + display: flex; + margin-bottom: - $input-underline-height; font-size: $input-label-font-size; - line-height: $input-error-height; - color: $input-text-error-color; + line-height: $input-underline-height; + color: $input-text-label-color; + .error, .counter { + flex-grow: 1; + } +} + +.error { + text-align: left; +} + +.counter { + text-align: right; } .disabled > .input { @@ -115,6 +127,9 @@ } } } + > .underline { + color: $input-text-error-color; + } } .hidden { diff --git a/components/radio/style.scss b/components/radio/style.scss index c6c25976..fcd206e1 100644 --- a/components/radio/style.scss +++ b/components/radio/style.scss @@ -60,7 +60,7 @@ background-color: $radio-inner-color; border-radius: 50%; transition-property: transform; - transform: scale3d(0, 0, 0); + transform: scale(0); } } @@ -68,7 +68,7 @@ @extend .radio; border: .2 * $unit solid $radio-inner-color; &:before { - transform: scale3d(1, 1, 1); + transform: scale(1); } } diff --git a/components/ripple/index.jsx b/components/ripple/index.jsx index 0ed431c1..297eedbc 100644 --- a/components/ripple/index.jsx +++ b/components/ripple/index.jsx @@ -25,13 +25,13 @@ class Ripple extends React.Component { width: null }; - handleEnd = () => { - document.removeEventListener('mouseup', this.handleEnd); + handleEnd = (touch = false) => { + document.removeEventListener(touch ? 'touchend' : 'mouseup', this.handleEnd); this.setState({active: false}); }; - start = ({ pageX, pageY }) => { - document.addEventListener('mouseup', this.handleEnd); + start = ({ pageX, pageY }, touch = false) => { + document.addEventListener(touch ? 'touchend' : 'mouseup', this.handleEnd.bind(this, touch)); const {top, left, width} = this._getDescriptor(pageX, pageY); this.setState({active: false, restarting: true, width: 0}, () => { this.refs.ripple.offsetWidth; //eslint-disable-line no-unused-expressions @@ -42,8 +42,8 @@ class Ripple extends React.Component { _getDescriptor (pageX, pageY) { const { left, top, height, width } = ReactDOM.findDOMNode(this).getBoundingClientRect(); return { - left: this.props.centered ? width / 2 : pageX - left, - top: this.props.centered ? height / 2 : pageY - top, + left: this.props.centered ? width / 2 : pageX - left + window.scrollX, + top: this.props.centered ? height / 2 : pageY - top + window.scrollY, width: width * this.props.spread }; } diff --git a/components/tabs/readme.md b/components/tabs/readme.md index 39ba55d4..a93baccb 100644 --- a/components/tabs/readme.md +++ b/components/tabs/readme.md @@ -6,15 +6,31 @@ ```jsx import {Tab, Tabs} from 'react-toolbox'; -const TabsExample = () => ( - - Primary content - Secondary content - Disabled content - - Fifth content - -); +class TabsTest extends React.Component { + state = { + index: 1 + }; + + handleTabChange = (index) => { + this.setState({index}); + }; + + handleActive = () => { + console.log('Special one activated'); + }; + + render () { + return ( + + Primary content + Secondary content + Disabled content + + Fifth content + + ); + } +} ``` ## Tabs diff --git a/docs/app/components/_globals.scss b/docs/app/components/_globals.scss index 41bcea68..a89dee8a 100644 --- a/docs/app/components/_globals.scss +++ b/docs/app/components/_globals.scss @@ -1,4 +1,5 @@ @import "~react-toolbox/base"; +@import "~react-toolbox/commons"; $unit: 1rem; $color-primary-dark: unquote("rgb(#{$color-primary-dark})"); diff --git a/docs/app/components/layout/home/index.jsx b/docs/app/components/layout/home/index.jsx index 1167bc34..8720d631 100644 --- a/docs/app/components/layout/home/index.jsx +++ b/docs/app/components/layout/home/index.jsx @@ -6,18 +6,6 @@ import Navigation from '../../navigation'; import style from './style'; import authors from './modules/authors'; -const GithubIcon = () => ( - - - -); - -const TwitterIcon = () => ( - - - -); - const Home = () => (
@@ -59,23 +47,6 @@ const Home = () => (
    { authors.map((author, index) => { - author.actions.map((action) => { - if (action.id === 'github') { - action.style = {color: '#000'}; - action.children = [ - , - Github - ]; - action.id = null; - } else { - action.style = {color: '#55acee'}; - action.children = [ - , - Twitter - ]; - action.id = null; - } - }); return ; }) } diff --git a/docs/app/components/layout/home/modules/authors.js b/docs/app/components/layout/home/modules/authors.js deleted file mode 100644 index fb837973..00000000 --- a/docs/app/components/layout/home/modules/authors.js +++ /dev/null @@ -1,24 +0,0 @@ -export default [ - { - title: 'Javi Velasco', - subtitle: 'javivelasco', - actions: [ - { id: 'github', href: 'http://github.com/javivelasco', target: '_blank' }, - { id: 'twitter', href: 'http://twitter.com/javivelasco', target: '_blank' } - ], - image: '/images/javivelasco.jpg', - text: 'Software gardener • Film, music & comic lover • Frontend Engineer at SocialBro • Any biographer in the room?', - color: '#3f51b5' - }, - { - title: 'Javi Jimenez', - subtitle: 'soyjavi', - actions: [ - { id: 'github', href: 'http://github.com/soyjavi', target: '_blank' }, - { id: 'twitter', href: 'http://twitter.com/soyjavi', target: '_blank' } - ], - image: '/images/soyjavi.jpg', - text: 'Creative Doer · A complicated #human who builds stuff · #author · #opensource lover · #traveller · with a dark past being CEO & CTO', - color: '#3f51b5' - } -]; diff --git a/docs/app/components/layout/home/modules/authors.jsx b/docs/app/components/layout/home/modules/authors.jsx new file mode 100644 index 00000000..a56322ee --- /dev/null +++ b/docs/app/components/layout/home/modules/authors.jsx @@ -0,0 +1,56 @@ +import React from 'react'; + +const GithubIcon = () => ( + + + +); + +const TwitterIcon = () => ( + + + +); + +export default [ + { + title: 'Javi Velasco', + subtitle: 'javivelasco', + actions: [ + { + href: 'http://github.com/javivelasco', + children: [, Github], + target: '_blank' + }, + { + href: 'http://twitter.com/javivelasco', + children: [, Twitter], + style: {color: '#55acee'}, + target: '_blank' + } + ], + image: '/images/javivelasco.jpg', + text: 'Software gardener • Film, music & comic lover • Frontend Engineer at SocialBro • Any biographer in the room?', + color: '#3f51b5' + }, + { + title: 'Javi Jimenez', + subtitle: 'soyjavi', + actions: [ + { + href: 'http://github.com/soyjavi', + children: [, Github], + target: '_blank' + }, + { + href: 'http://twitter.com/soyjavi', + children: [, Twitter], + style: {color: '#55acee'}, + target: '_blank' + } + ], + image: '/images/soyjavi.jpg', + text: 'Creative Doer · A complicated #human who builds stuff · #author · #opensource lover · #traveller · with a dark past being CEO & CTO', + color: '#3f51b5' + } +]; diff --git a/docs/app/components/layout/main/modules/examples/input_example_1.txt b/docs/app/components/layout/main/modules/examples/input_example_1.txt index 7787fd23..a30ff0a8 100644 --- a/docs/app/components/layout/main/modules/examples/input_example_1.txt +++ b/docs/app/components/layout/main/modules/examples/input_example_1.txt @@ -10,7 +10,7 @@ class InputTest extends React.Component { render () { return (
    - + diff --git a/docs/app/components/layout/main/modules/examples/tabs_example_1.txt b/docs/app/components/layout/main/modules/examples/tabs_example_1.txt index 84373983..617d2c66 100644 --- a/docs/app/components/layout/main/modules/examples/tabs_example_1.txt +++ b/docs/app/components/layout/main/modules/examples/tabs_example_1.txt @@ -1,11 +1,23 @@ -const TabsExample = () => ( - - Primary content - Secondary content - Disabled content - - Fifth content - -); +class TabsExample extends React.Component { + state = { + index: 1 + }; + + handleTabChange = (index) => { + this.setState({index}); + }; + + render () { + return ( + + Primary content + Secondary content + Disabled content + + Fifth content + + ); + } +} return ; diff --git a/docs/app/components/layout/main/style.scss b/docs/app/components/layout/main/style.scss index 42c11ffd..74e82f3a 100644 --- a/docs/app/components/layout/main/style.scss +++ b/docs/app/components/layout/main/style.scss @@ -19,7 +19,8 @@ right: 0; bottom: 0; left: 0; - transition: all $animation-duration $animation-curve-default; + z-index: $z-index-normal; + transition: padding $animation-duration $animation-curve-default; } .navigation { @@ -29,24 +30,25 @@ left: 0; z-index: $z-index-high; box-shadow: $documentation-left-shadow; - transition: all $animation-duration $animation-curve-default; + transition: transform $animation-duration $animation-curve-default; } .playground { position: fixed; top: $appbar-height; + right: 0; bottom: 0; z-index: $z-index-high; width: $playground-width; background: $color-background; box-shadow: $documentation-right-shadow; - transition: right $animation-duration $animation-curve-default; + transition: transform $animation-duration $animation-curve-default; } .root { &:not(.with-playground) { > .playground { - right: - ($playground-width * 1.1); + transform: translateX(100%); } > .documentation { padding-right: 0; @@ -58,7 +60,7 @@ } &.with-playground { > .playground { - right: 0; + transform: translateY(0); } > .documentation { padding-right: $playground-width; diff --git a/package.json b/package.json index 91ab1dcd..55869407 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-toolbox", - "version": "0.12.7", + "version": "0.12.11", "homepage": "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)", @@ -19,7 +19,7 @@ "main": "./lib", "scripts": { "start": "cross-env NODE_ENV=development UV_THREADPOOL_SIZE=100 node ./server", - "lint": "eslint ./ --ext .js,.jsx && scss-lint", + "lint": "eslint ./ --ext .js,.jsx", "babel": "babel ./components --out-dir ./lib", "sass": "cpx './components/**/*.scss' ./lib", "build": "cross-env NODE_ENV=production npm run babel && npm run sass", @@ -58,8 +58,8 @@ "babel-plugin-react-transform": "^1.1.1", "classnames-minimal": "^1.0.0", "core-js": "^1.2.6", - "cross-env": "^1.0.4", "cpx": "^1.2.1", + "cross-env": "^1.0.4", "css-loader": "^0.21.0", "eslint": "^1.7.3", "eslint-plugin-react": "^3.3.1", diff --git a/spec/components/input.jsx b/spec/components/input.jsx index ebda48e7..affe8808 100644 --- a/spec/components/input.jsx +++ b/spec/components/input.jsx @@ -3,7 +3,7 @@ import Input from '../../components/input'; class InputTest extends React.Component { state = { - normal: '', + normal: 'hello world', fixedLabel: '', withIcon: '' }; @@ -19,7 +19,12 @@ class InputTest extends React.Component {
    Inputs

    lorem ipsum...

    - + diff --git a/spec/components/tabs.jsx b/spec/components/tabs.jsx index 39318409..73d037f2 100644 --- a/spec/components/tabs.jsx +++ b/spec/components/tabs.jsx @@ -2,7 +2,6 @@ import React from 'react'; import { Tabs, Tab } from '../../components/tabs'; class TabsTest extends React.Component { - state = { index: 1 }; @@ -20,27 +19,12 @@ class TabsTest extends React.Component {
    Tabs

    This tabs can be disabled or hidden

    - - - Primary content - - - - Secondary content - - - - Disabled content - - - - - - Fifth content - + Primary content + Secondary content + Disabled content + + Fifth content
    ); diff --git a/spec/style.scss b/spec/style.scss index 7dda066e..72a927b2 100644 --- a/spec/style.scss +++ b/spec/style.scss @@ -1,4 +1,7 @@ -@import "../components/base"; +$import-normalize: false; +$import-flex-attributes: true; + +@import "../components/commons"; @import "../components/app_bar/config"; @import "../components/button/config";